openapi: 3.1.0
info:
  title: unLocked CRM API (Public Subset)
  version: 1.0.0
  description: >-
    Complete API surface of the unLocked CRM platform.


    **Two tiers:**

    1. **Public API** (`/public-api`) — stable, documented REST API for external
    integrations. API-key or OAuth Bearer auth, scoped permissions, 100 req/min
    rate limit. The `resource` query parameter selects the collection; the HTTP
    method selects the operation.

    2. **Edge Functions** (everything else) — internal serverless endpoints that
    power the app UI, AI features, telephony, quoting, automations, and
    third-party webhooks. These are documented for completeness; request shapes
    are function-specific and not covered by the public API stability guarantee.


    All endpoints are Supabase Edge Functions invoked over HTTPS with JSON
    bodies. `OPTIONS` preflight is supported on every function.


    Slim public subset: internal/system operations removed. Full spec:
    https://unlockedcrm.ai/openapi.json
  contact:
    name: unLocked Support
    email: support@unlockedcrm.ai
  x-rate-limit:
    description: Default 120 requests/minute per API key (burst 20/second).
      Exceeding it returns 429 with a Retry-After header.
    requestsPerMinute: 120
    burstPerSecond: 20
    retryAfterHeader: Retry-After
servers:
  - url: https://gzigyepfasiumngxilai.supabase.co/functions/v1
    description: Production
security:
  - OAuth2:
      - read:contacts
      - write:contacts
      - read:leads
      - write:leads
      - read:policies
      - write:policies
      - read:quotes
      - write:quotes
      - read:commissions
      - write:commissions
      - read:tasks
      - write:tasks
      - read:appointments
      - write:appointments
      - read:activities
      - write:activities
      - read:communications
      - write:communications
      - read:agent_ai
      - write:agent_ai
  - ApiKeyAuth: []
  - BearerAuth: []
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: Public API key (scoped, rate-limited). Created in Settings → API.
        Keys are issued with an explicit subset of the named scopes listed in
        x-scopes (same scope names as the OAuth2 scheme).
      x-scopes:
        read:contacts: Read contact records
        write:contacts: Create and update contact records
        read:leads: Read leads and pipeline records
        write:leads: Create and update leads
        read:policies: Read policy records, renewals, and lapse status
        write:policies: Create and update policy records
        read:quotes: Read quotes and comparison results
        write:quotes: Create and update quotes
        read:commissions: Read commission statements and reconciliation data
        write:commissions: Create and update commission records
        read:tasks: Read tasks and reminders
        write:tasks: Create and update tasks
        read:appointments: Read calendar appointments
        write:appointments: Create and update appointments
        read:activities: Read activity and timeline history
        write:activities: Write activity and timeline entries
        read:communications: Read calls, SMS, and email history
        write:communications: Send SMS, email, and place calls
        read:agent_ai: Read AI agent configuration and run history
        write:agent_ai: Trigger AI agent runs and update configuration
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "Supabase user JWT (Authorization: Bearer <token>) — the session
        token of a signed-in app user."
    ToolSecretAuth:
      type: apiKey
      in: header
      name: X-Tool-Secret
      description: Shared server-to-server secret for AI agent tool webhooks.
    WebhookSignature:
      type: apiKey
      in: header
      name: Stripe-Signature / X-Twilio-Signature / provider secret
      description: Provider-signed webhook verification (varies by provider).
    OAuth2:
      type: oauth2
      description: "OAuth 2.0 authorization code flow with PKCE against the unLocked
        CRM authorization server. Discovery:
        https://unlockedcrm.ai/.well-known/oauth-authorization-server.
        Protected-resource metadata (RFC 9728):
        https://unlockedcrm.ai/.well-known/oauth-protected-resource. Request
        least-privilege scopes."
      flows:
        authorizationCode:
          authorizationUrl: https://mcp.unlockedcrm.ai/oauth?action=authorize
          tokenUrl: https://mcp.unlockedcrm.ai/oauth?action=token
          refreshUrl: https://mcp.unlockedcrm.ai/oauth?action=token
          scopes:
            read:contacts: Read contact records
            write:contacts: Create and update contact records
            read:leads: Read leads and pipeline records
            write:leads: Create and update leads
            read:policies: Read policy records, renewals, and lapse status
            write:policies: Create and update policy records
            read:quotes: Read quotes and comparison results
            write:quotes: Create and update quotes
            read:commissions: Read commission statements and reconciliation data
            write:commissions: Create and update commission records
            read:tasks: Read tasks and reminders
            write:tasks: Create and update tasks
            read:appointments: Read calendar appointments
            write:appointments: Create and update appointments
            read:activities: Read activity and timeline history
            write:activities: Write activity and timeline entries
            read:communications: Read calls, SMS, and email history
            write:communications: Send SMS, email, and place calls
            read:agent_ai: Read AI agent configuration and run history
            write:agent_ai: Trigger AI agent runs and update configuration
  schemas:
    Lead:
      type: object
      description: Standard lead fields are written to the leads table. Unknown
        top-level keys (or keys nested under `custom_fields`) are resolved
        against the workspace's custom_fields definitions. Common Zapier/GHL
        aliases are auto-rewritten (see `x-lead-field-aliases`).
      additionalProperties: true
      properties:
        first_name:
          type: string
        last_name:
          type: string
        name:
          type: string
          description: Auto-split into first_name/last_name when those are absent.
        email:
          type: string
          format: email
        phone:
          type: string
        status:
          type: string
        source:
          type: string
        custom_fields:
          type: object
          additionalProperties: true
          example:
            income: "85000"
            preferred_contact_time: Evenings
      example:
        first_name: Jane
        last_name: Doe
        email: jane@example.com
        income: "85000"
        custom_fields:
          preferred_contact_time: Evenings
    LeadWriteResult:
      type: object
      properties:
        data:
          type: object
          description: The created/updated lead row.
        matched_custom_fields:
          type: array
          items:
            type: string
        unknown_fields:
          type: array
          items:
            type: string
        custom_field_errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              error:
                type: string
    BulkRequest:
      type: object
      description: Envelope for `action=bulk`. Up to 1,000 records per request.
      properties:
        mode:
          type: string
          enum:
            - create
            - update
            - upsert
          default: create
        records:
          type: array
          items:
            type: object
            additionalProperties: true
          maxItems: 1000
      required:
        - records
    Error:
      type: object
      description: Every non-2xx response from the unLocked CRM API is
        `application/json` with this envelope. HTML error pages are never
        returned to API clients.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - status
            - retryable
          properties:
            code:
              type: string
              description: Stable machine-readable error code.
              enum:
                - invalid_request
                - validation_failed
                - unauthenticated
                - permission_denied
                - not_found
                - conflict
                - rate_limited
                - upstream_error
                - internal_error
            message:
              type: string
              description: Human-readable description of what went wrong.
            status:
              type: integer
              description: HTTP status code, repeated in the body for agents that only read
                bodies.
            retryable:
              type: boolean
              description: True when re-sending the same request may succeed after a delay.
            resolution:
              type: string
              description: Actionable hint describing how to fix or recover from the error.
            required_scope:
              type: string
              description: On 403, the OAuth scope the caller is missing.
            retry_after_seconds:
              type: integer
              description: On 429/503, how long to wait before retrying.
            details:
              type: array
              description: Field-level problems, when applicable.
              items:
                type: object
                properties:
                  field:
                    type: string
                  issue:
                    type: string
            request_id:
              type: string
              description: Correlation id to quote in support requests.
            docs_url:
              type: string
              format: uri
              description: Link to documentation for this error code.
    GenericResponse:
      type: object
      description: Standard JSON response envelope. `success` indicates whether the
        operation completed; `data` carries the operation-specific payload.
      properties:
        success:
          type: boolean
          description: True when the operation succeeded.
        data:
          description: Operation-specific payload. Shape depends on the endpoint.
        message:
          type: string
          description: Human-readable status message.
      required:
        - success
  responses:
    BadRequest:
      description: Request was malformed or referenced an unknown resource.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            invalid_request:
              summary: Request was malformed or referenced an unknown resource.
              value:
                error:
                  code: invalid_request
                  message: Request was malformed or referenced an unknown resource.
                  status: 400
                  retryable: false
                  resolution: Check the `resource` query parameter and required body fields, then
                    retry.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
                  details:
                    - field: resource
                      issue: unknown resource value
    Unauthorized:
      description: Missing or invalid credentials.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            unauthenticated:
              summary: Missing or invalid credentials.
              value:
                error:
                  code: unauthenticated
                  message: Missing or invalid credentials.
                  status: 401
                  retryable: false
                  resolution: "Send a valid `x-api-key` header or `Authorization: Bearer <jwt>`
                    and retry."
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
    Forbidden:
      description: Permission denied for this resource or action.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            permission_denied:
              summary: Permission denied for this resource or action.
              value:
                error:
                  code: permission_denied
                  message: Permission denied for this resource or action.
                  status: 403
                  retryable: false
                  resolution: Request the scope named in `error.required_scope` (see the scopes
                    table) and retry.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
                  required_scope: leads:write
    NotFound:
      description: The requested record or endpoint does not exist.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            not_found:
              summary: The requested record or endpoint does not exist.
              value:
                error:
                  code: not_found
                  message: The requested record or endpoint does not exist.
                  status: 404
                  retryable: false
                  resolution: Verify the `id` and workspace, then retry. Do not retry with the
                    same id.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
    Conflict:
      description: The write conflicts with existing state.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            conflict:
              summary: The write conflicts with existing state.
              value:
                error:
                  code: conflict
                  message: The write conflicts with existing state.
                  status: 409
                  retryable: false
                  resolution: Fetch the current record, merge, and retry once.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
    UnprocessableEntity:
      description: One or more field values failed validation.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            validation_failed:
              summary: One or more field values failed validation.
              value:
                error:
                  code: validation_failed
                  message: One or more field values failed validation.
                  status: 422
                  retryable: false
                  resolution: Read `error.details[]` for the offending fields and correct them
                    before retrying.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
                  details:
                    - field: email
                      issue: must be a valid email address
    RateLimited:
      description: Rate limit exceeded (100 requests/minute).
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            rate_limited:
              summary: Rate limit exceeded (100 requests/minute).
              value:
                error:
                  code: rate_limited
                  message: Rate limit exceeded (100 requests/minute).
                  status: 429
                  retryable: true
                  resolution: Wait the number of seconds in the `Retry-After` header, then retry
                    with backoff.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
                  retry_after_seconds: 30
    ServerError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            internal_error:
              summary: Unexpected server error.
              value:
                error:
                  code: internal_error
                  message: Unexpected server error.
                  status: 500
                  retryable: true
                  resolution: Retryable. Retry with exponential backoff and include
                    `error.request_id` in any support request.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
    BadGateway:
      description: A downstream carrier or provider API failed.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          examples:
            upstream_error:
              summary: A downstream carrier or provider API failed.
              value:
                error:
                  code: upstream_error
                  message: A downstream carrier or provider API failed.
                  status: 502
                  retryable: true
                  resolution: Retryable. Retry with exponential backoff; if it persists, contact
                    info@unlockedcrm.ai.
                  request_id: req_01J9ZC7Q2X5T
                  docs_url: https://unlockedcrm.ai/api#errors
tags:
  - name: Public API
    description: Stable external REST API — the only surface covered by the
      stability guarantee.
  - name: AI & Agents
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Admin & Provisioning
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Automation & Scheduled Jobs
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Billing & Payments
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Calendar & Booking
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Core CRM
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Email & Notifications
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Integrations & Webhooks
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Quoting & Enrollment
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
  - name: Telephony & Messaging
    description: Internal edge functions (documented for completeness; no stability
      guarantee).
paths:
  /public-api:
    get:
      tags:
        - Public API
      summary: List a collection, or fetch one record
      description: Omit `id` to list the collection, or pass `id` to fetch a single
        record. `resource=health` needs no credentials; `resource=whoami`
        reports the workspace and permissions the credentials resolve to.
      parameters:
        - name: resource
          in: query
          required: true
          description: Which collection the request addresses.
          schema:
            type: string
            enum:
              - leads
              - clients
              - activities
              - tasks
              - policies
              - appointments
              - communications
              - webhooks
              - pipelines
              - pipeline_stages
              - opportunities
              - workflows
              - email_campaigns
              - commissions
              - aca_leads
              - agent_ai_call_logs
              - agent_ai_scripts
              - agent_ai_campaigns
              - quotes
              - tags
              - call_recordings
              - call_disposition_logs
              - agent_presence
              - inbound_campaigns
              - inbound_vendors
              - health
              - whoami
          example: leads
        - name: id
          in: query
          description: Record id. Omit to list; required for single-record fetch.
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            maximum: 100
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: sort
          in: query
          description: field:direction
          schema:
            type: string
            example: created_at:desc
        - name: fields
          in: query
          description: Comma-separated columns to return.
          schema:
            type: string
        - name: search
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-error-format: application/json; envelope=error
      operationId: getPublicApi
    post:
      tags:
        - Public API
      summary: Create records
      description: Creates one record from the request body. Pass `action=bulk` with a
        `records` array to create up to 1,000 at once. For `resource=leads` the
        body follows the Lead schema — unknown keys are resolved against the
        workspace's custom fields.
      parameters:
        - name: resource
          in: query
          required: true
          schema:
            type: string
          example: leads
        - name: action
          in: query
          description: Set to `bulk` to submit a records array.
          schema:
            type: string
            enum:
              - bulk
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/Lead"
                - $ref: "#/components/schemas/BulkRequest"
                - type: object
                  additionalProperties: true
                  description: Record body for any non-lead resource.
      responses:
        "200":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LeadWriteResult"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-error-format: application/json; envelope=error
      operationId: postPublicApi
    put:
      tags:
        - Public API
      summary: Update a record
      parameters:
        - name: resource
          in: query
          required: true
          schema:
            type: string
        - name: id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/Lead"
                - type: object
                  additionalProperties: true
                  description: Record body for any non-lead resource.
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LeadWriteResult"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-error-format: application/json; envelope=error
      operationId: putPublicApi
      description: Update a record
    delete:
      tags:
        - Public API
      summary: Delete a record
      parameters:
        - name: resource
          in: query
          required: true
          schema:
            type: string
        - name: id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-error-format: application/json; envelope=error
      operationId: deletePublicApi
      description: Delete a record
  /openapi-spec:
    get:
      tags:
        - Public API
      summary: OpenAPI 3.1 specification for the Public API
      description: Machine-readable spec for the public REST API. Anonymous.
        `?format=yaml` is reserved.
      security: []
      responses:
        "200":
          description: OpenAPI JSON document
          content:
            application/json:
              schema:
                type: object
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      x-error-format: application/json; envelope=error
      operationId: getOpenapiSpec
  /mcp-server:
    post:
      tags:
        - Core CRM
      summary: MCP (Model Context Protocol) server wrapper around the unLocked Public
        API.
      description: >-
        MCP (Model Context Protocol) server wrapper around the unLocked Public
        API. Lets AI agents (ChatGPT, Claude, Cursor) discover and call CRM
        tools natively.


        **Auth:** CRM API key via x-api-key header OR OAuth 2.0 access token via
        Authorization: Bearer (MCP clients).
      security:
        - ApiKeyAuth: []
      x-internal: false
      x-function-name: mcp-server
      responses:
        "200":
          description: Success (JSON body; shape is function-specific)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenericResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/ServerError"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              description: Function-specific payload. This is an internal endpoint — see
                api.md for the auth contract; request shapes are defined by the
                app that calls it.
      x-error-format: application/json; envelope=error
      operationId: postMcpServer
x-lead-field-aliases:
  income: household_income
  annual_income: household_income
  dob: date_of_birth
  zipcode: zip_code
  postal_code: zip_code
  phone_number: phone
  mobile: phone
  company: employer
  job_title: occupation
x-webhook-events:
  - lead.created
  - lead.updated
  - lead.deleted
  - lead.status_changed
  - client.created
  - client.updated
  - task.created
  - task.completed
  - policy.created
  - policy.renewed
  - appointment.created
  - appointment.cancelled
  - activity.created
  - opportunity.created
  - opportunity.updated
  - opportunity.stage_changed
  - workflow.activated
  - workflow.deactivated
  - email_campaign.sent
  - email_campaign.completed
  - commission.created
  - commission.updated
  - sms.sent
  - sms.received
  - sms.delivered
  - sms.failed
  - quote.saved
x-cron-functions:
  - agent-ai-campaign-processor
  - agent-ai-elevenlabs-reconcile-stuck
  - agent-builtin-scheduler
  - agent-lead-conversion
  - agent-user-executor
  - ai-tool-drift-check
  - calendly-poll-sync
  - capability-scan
  - csg-eapp-sync
  - lead-cap-billing-trigger
  - meta-refresh-tokens
  - meta-retry-leads
  - notify-agency-new-signup
  - phone-system-alerts
  - process-birthday-reminders
  - process-booking-nudges
  - process-campaigns
  - process-custom-date-reminders
  - process-email-campaign-retries
  - process-enrollment-period
  - process-medicare-eligibility
  - process-medicare-pending-eligible
  - process-policy-renewal
  - process-scheduled-nudges
  - process-scheduled-opportunities
  - process-scheduled-quote-sends
  - process-single-email-queue
  - recover-missed-onboarding
  - send-affiliate-signup-notification
  - system-threshold-monitor
  - trigger-workflow
  - webhook-dispatcher
  - webhook-emit
  - webhook-retry-worker
  - workflow-queue-listener
x-function-count: 538
x-scopes:
  read:contacts: Read contact records
  write:contacts: Create and update contact records
  read:leads: Read leads and pipeline records
  write:leads: Create and update leads
  read:policies: Read policy records, renewals, and lapse status
  write:policies: Create and update policy records
  read:quotes: Read quotes and comparison results
  write:quotes: Create and update quotes
  read:commissions: Read commission statements and reconciliation data
  write:commissions: Create and update commission records
  read:tasks: Read tasks and reminders
  write:tasks: Create and update tasks
  read:appointments: Read calendar appointments
  write:appointments: Create and update appointments
  read:activities: Read activity and timeline history
  write:activities: Write activity and timeline entries
  read:communications: Read calls, SMS, and email history
  write:communications: Send SMS, email, and place calls
  read:agent_ai: Read AI agent configuration and run history
  write:agent_ai: Trigger AI agent runs and update configuration
x-error-format:
  media_type: application/json
  envelope: '{"error":{"code","message","status","retryable","resolution"}}'
  html_errors: false
  description: All non-2xx API responses are JSON with a stable `error.code`,
    human `error.message`, and an actionable `error.resolution`. Agents should
    branch on `error.code` and honor `error.retryable` / `Retry-After`.
  codes:
    - code: invalid_request
      http_status: 400
      meaning: Request was malformed, missing a required parameter, or referenced an
        unknown resource.
      resolution: Check the `resource` query parameter and required body fields, then
        retry.
      retryable: false
    - code: validation_failed
      http_status: 422
      meaning: One or more field values failed validation.
      resolution: Read `error.details[]` for the offending fields and correct them
        before retrying.
      retryable: false
    - code: unauthenticated
      http_status: 401
      meaning: Missing, malformed, or expired credentials.
      resolution: "Send a valid `x-api-key` header or `Authorization: Bearer <jwt>`
        and retry."
      retryable: false
    - code: permission_denied
      http_status: 403
      meaning: The credentials are valid but lack the scope required for this operation.
      resolution: Request the scope named in `error.required_scope` (see the scopes
        table) and retry.
      retryable: false
    - code: not_found
      http_status: 404
      meaning: The record or endpoint does not exist, or is outside the caller's
        workspace.
      resolution: Verify the `id` and workspace, then retry. Do not retry with the same id.
      retryable: false
    - code: conflict
      http_status: 409
      meaning: The write conflicts with existing state (duplicate key, concurrent
        update).
      resolution: Fetch the current record, merge, and retry once.
      retryable: false
    - code: rate_limited
      http_status: 429
      meaning: Rate limit exceeded (100 requests/minute per key).
      resolution: Wait the number of seconds in the `Retry-After` header, then retry
        with backoff.
      retryable: true
    - code: upstream_error
      http_status: 502
      meaning: A downstream carrier or provider API failed.
      resolution: Retryable. Retry with exponential backoff; if it persists, contact
        info@unlockedcrm.ai.
      retryable: true
    - code: internal_error
      http_status: 500
      meaning: Unexpected server error.
      resolution: Retryable. Retry with exponential backoff and include
        `error.request_id` in any support request.
      retryable: true
