> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hacktionbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Identify a Contact

> Resolve an existing contact and mark it identified — never creates one. Requires a public API key (`hb_public_*`). Processing is asynchronous — the submission is accepted and queued; resolution happens downstream (submissions with no matching contact are dropped).




## OpenAPI

````yaml POST /contacts/identify
openapi: 3.0.3
info:
  title: HacktionBase Public API
  version: 1.2.0
  description: >
    Public API for users, accounts, events tracking, contact capture, and
    development tickets.


    Ticket reads (`GET /tickets`, `GET /tickets/{id}`) are synchronous and
    return `200`. All mutations, including `PATCH /tickets/{id}`, remain
    asynchronous: they are validated at the edge, accepted (`202`) and queued;
    processing happens downstream.


    **Read-after-write.** Because writes are queued, a client that `PATCH`es a
    ticket and immediately `GET`s it may observe the previous value. Reads are
    additionally served from a secondary replica. Do not treat a `202` as
    confirmation that the change is visible; poll, or rely on your own state.


    **Draft tickets are never returned by this API.** They are unvalidated
    AI-generated drafts awaiting review inside the workspace. Consequently an
    empty `data` array is a valid and expected response for a tenant whose
    tickets are all still drafts — it does not indicate an error.


    **Ticket scopes.** `tickets:read` and `tickets:write` must be granted
    explicitly on an `ak_*` key. Existing keys carry no scopes and are denied by
    default. `hb_public_*` contact-capture keys can never carry them: a ticket
    description is prose derived from conversation content and must not be
    readable from a browser bundle.
servers:
  - url: https://api.hacktionbase.com/v1
security:
  - bearerAuth: []
paths:
  /contacts/identify:
    post:
      summary: Identify a contact
      description: >
        Resolve an existing contact and mark it identified — never creates one.
        Requires a public API key (`hb_public_*`). Processing is asynchronous —
        the submission is accepted and queued; resolution happens downstream
        (submissions with no matching contact are dropped).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactCaptureRequest'
      responses:
        '202':
          description: Submission accepted and queued for processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AcceptedResponse'
        '400':
          description: Invalid or empty JSON body
        '401':
          description: Invalid public API key
        '429':
          description: Rate limited
      security:
        - bearerAuth: []
components:
  schemas:
    ContactCaptureRequest:
      type: object
      description: >-
        At least one of externalUserId, email, phone, or anonymousId is
        required.
      properties:
        externalUserId:
          type: string
          description: Your business identifier for the person (strongest matching key).
          example: user_123
        anonymousId:
          type: string
          description: SDK anonymous visitor id, if known.
        email:
          type: string
          format: email
          description: Normalized (trim + lowercase) — the default dedup key.
          example: jane@acme.com
        phone:
          type: string
          description: Used for matching only when confidently parseable as E.164.
          example: '+33612345678'
        firstName:
          type: string
        lastName:
          type: string
        source:
          type: string
          description: Where the submission came from.
          example: website
        formId:
          type: string
          example: quote-form
        formType:
          type: string
          example: quote
        event:
          type: string
          description: Optional business event emitted alongside system events.
          example: quote.requested
        traits:
          type: object
          additionalProperties: true
          description: Arbitrary custom attributes (max 100 keys), merged per key.
        consent:
          type: object
          properties:
            marketing:
              type: boolean
            termsAccepted:
              type: boolean
    AcceptedResponse:
      type: object
      properties:
        ok:
          type: boolean
          example: true
        requestId:
          type: string
          format: uuid
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: APIKey

````