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

# Convert a Contact

> Advance a contact lead → contact (won/customer).

The contact already exists; this stamps stage='contact' + contact_at and,
optionally, creates/links an account and creates a deal. Steps run
sequentially (no transaction) — a deal failure is reported but does not
block the stage advance.

The most involved endpoint on this router. Advances a **lead → contact** (won/customer), stamping `contact_at` and `converted_at` (and backfilling `lead_at` if the contact skipped straight from `target`). Optionally also creates or links an account, and optionally creates a deal on that account — all in one call.

Steps run **sequentially, not in a transaction**:

1. If `create_account` is true, create a new account (`new_account.name` → falls back to the contact's `company`, then its display name, then `"Untitled Account"`). Otherwise use `account_id` (body) or the contact's existing `account_id`.
2. If `create_deal` is true **and** an account is now known, create a deal on it (`deal_payload.name` → falls back to `"Deal with {company}"`).
3. Patch the contact: `stage = "contact"`, `contact_at`, `converted_at`, and (if an account was resolved) `account_id` + `converted_to_account_id`, and (if a deal was created) `converted_to_deal_id`.

A failure in step 2 (deal creation) does **not** abort the request — it's reported back as `deal_error` and the contact is still advanced in step 3. A failure in step 3 is reported as `convert_status_error` rather than raising, so a client must check for these keys rather than assuming a `200` means every requested side-effect happened.

### Auth

Requires a CRM manage [scope](/authentication#scopes) and an active organization on the token. Any `*:manage` scope qualifies — in practice `contacts:manage`, `deals:manage`, `companies:manage`, or `activities:manage`.

### Response

| Field                  | Type                      | Description                                                            |
| ---------------------- | ------------------------- | ---------------------------------------------------------------------- |
| `contact_id`           | `string` (uuid)           | Echo of the path parameter.                                            |
| `account_id`           | `string` (uuid) \| `null` | The account that ended up linked (existing, newly created, or none).   |
| `deal_id`              | `string` (uuid)           | Present only if a deal was created.                                    |
| `deal_error`           | `string`                  | Present only if deal creation was requested and failed.                |
| `contact`              | `object`                  | The updated contact row, present unless the final patch itself failed. |
| `convert_status_error` | `string`                  | Present only if the final stage-advance patch failed.                  |

### Errors

| Status        | Cause                                                                                                                                                                                                                                                  |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400`         | Both `account_id` and `create_account=true` given, or no active organization.                                                                                                                                                                          |
| `404`         | Contact not found, or `account_id` doesn't resolve to an account in the caller's organization.                                                                                                                                                         |
| `500` / `502` | Account creation (step 1) failed — `502` if the failure came back from the database call, `500` for any other error creating the account. Deal-creation failures (step 2) never raise; they're reported as `deal_error` in the `200` response instead. |


## OpenAPI

````yaml POST /contacts/{contact_id}/convert
openapi: 3.1.0
info:
  title: anycrm-api
  version: 0.0.1
servers: []
security: []
tags:
  - name: Customer Intelligence
    description: >-
      Company research and ICP-fit scoring — create a research run, track its
      progress, and read back scored companies as leads.
  - name: Outreach
    description: >-
      The cold-email management console — domains, mailboxes, and campaigns — as
      a thin control plane over the SalesForge stack.
  - name: AnyCard
    description: >-
      Authenticated CRUD for AnyCard, the org's digital business-card /
      lead-capture product.
  - name: AnyCard Events
    description: >-
      Event-attribution analytics for AnyCard — which captured leads converted,
      broken down by source, owner, and deal.
  - name: AnyCard Share Links
    description: >-
      Unauthenticated endpoints reached by anyone who scans a QR code or opens a
      shared AnyCard link.
  - name: AI
    description: >-
      A streaming (SSE) AI chat endpoint with account-commit actions it can take
      on the caller's behalf.
  - name: Analytics Assistant
    description: >-
      The natural-language analytics assistant — a guarded text-to-SQL loop
      (SSE) that answers ad-hoc questions over the org's CRM data as a
      least-privilege, read-only database role.
  - name: Account Readiness
    description: >-
      Account Readiness Profiles — AI-scored signals on whether an account is
      ready for outreach or expansion, computed via a Temporal workflow.
  - name: Integrations
    description: >-
      Pipedream Connect — issuing connect tokens and managing the org's
      connected third-party accounts.
  - name: Feedback
    description: >-
      User-submitted platform feedback (bug reports, feature requests) — global,
      not scoped to one organization.
  - name: Public Media
    description: >-
      Unauthenticated image reads for publicly-embeddable assets (card photos,
      inline email images) — allowlisted by key shape; everything else in the
      storage bucket stays private.
  - name: Service Health
    description: Service liveness.
paths:
  /contacts/{contact_id}/convert:
    post:
      tags:
        - Contacts
      summary: Convert Contact
      description: >-
        Advance a contact lead → contact (won/customer).


        The contact already exists; this stamps stage='contact' + contact_at
        and,

        optionally, creates/links an account and creates a deal. Steps run

        sequentially (no transaction) — a deal failure is reported but does not

        block the stage advance.
      operationId: convert_contact_route_contacts__contact_id__convert_post
      parameters:
        - name: contact_id
          in: path
          required: true
          schema:
            type: string
            description: The contact's id.
            title: Contact Id
          description: The contact's id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactConvertBody'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: >-
                  Response Convert Contact Route Contacts  Contact Id  Convert
                  Post
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
        - HTTPBearer: []
components:
  schemas:
    ContactConvertBody:
      properties:
        account_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Account Id
          description: >-
            Existing account to link the contact to. Mutually exclusive with
            create_account.
        create_account:
          type: boolean
          title: Create Account
          description: >-
            If true, create a new account from new_account (or fall back to the
            contact's company/name).
          default: false
        new_account:
          anyOf:
            - $ref: '#/components/schemas/NewAccountFields'
            - type: 'null'
          description: Fields for the new account when create_account is true.
        create_deal:
          type: boolean
          title: Create Deal
          description: If true, also create a deal linked to the resulting account.
          default: false
        deal_payload:
          anyOf:
            - $ref: '#/components/schemas/DealFields'
            - type: 'null'
          description: Fields for the new deal when create_deal is true.
      type: object
      title: ContactConvertBody
      description: >-
        Advance a contact lead→contact (won/customer), optionally creating or

        linking an account and creating a deal. The contact already exists, so
        there

        is no "create contact" step (unlike the old lead-convert flow).
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    NewAccountFields:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: >-
            Account name; falls back to the contact's company or display name if
            omitted.
        domain:
          anyOf:
            - type: string
            - type: 'null'
          title: Domain
          description: Primary domain for the new account.
        industry:
          anyOf:
            - $ref: '#/components/schemas/AccountIndustry'
            - type: 'null'
          description: Industry taxonomy value; blank/empty is treated as unset.
        website:
          anyOf:
            - type: string
            - type: 'null'
          title: Website
          description: Company website URL.
      type: object
      title: NewAccountFields
    DealFields:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Deal name; defaults to 'Deal with {company}' if omitted.
        stage_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Stage Id
          description: Pipeline stage id; left null (no stage) if omitted.
        value:
          anyOf:
            - type: number
            - type: 'null'
          title: Value
          description: Deal value.
        close_date:
          anyOf:
            - type: string
            - type: 'null'
          title: Close Date
          description: Expected close date (ISO date string).
        currency:
          type: string
          title: Currency
          description: ISO currency code.
          default: USD
        notes:
          anyOf:
            - type: string
            - type: 'null'
          title: Notes
          description: >-
            Deal notes. Currently accepted but not persisted by the convert
            flow.
      type: object
      title: DealFields
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    AccountIndustry:
      type: string
      enum:
        - Food & Beverage
        - Technology
        - Healthcare
        - Financial Services
        - Retail
        - Manufacturing
        - Professional Services
        - Real Estate
        - Education
        - Media & Entertainment
        - Transportation & Logistics
        - Energy
        - Agriculture
        - Construction
        - Hospitality & Travel
        - Other
      title: AccountIndustry
      description: >-
        Account industry taxonomy — keep in sync with frontend
        industry-options.ts.


        Pydantic renders this as a JSON-schema enum, so it is at once the API

        validation, the constraint handed to the AI, and the account form's
        option

        list. StrEnum so members serialize as their labels.
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer

````