> ## Documentation Index
> Fetch the complete documentation index at: https://developers.1-funnel.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit a bulk SMS

> Durably accept a bulk SMS (ADR-018 SMS-04, DEC-SMS-B01/B02). Inline
`recipients` only in v1. Exceeding the recipient limit rejects the entire
request with `422` / `recipient_limit_exceeded` before acceptance.
`202` is not delivery completion.




## OpenAPI

````yaml /specs/external-integration-api.openapi.yaml post /v1/businesses/{businessId}/sms/bulk-messages
openapi: 3.1.0
info:
  title: 1Funnel External Integration API
  version: 0.1.0-draft
  summary: External Integration API — Developer Preview (contract only, non-GA).
  description: |
    OpenAPI 3.1 for the **1Funnel External Integration API** (foundation through
    webhooks). Companion AsyncAPI documents outbound webhook event payloads.

    ## Status (read carefully)

    | Layer | Status |
    |-------|--------|
    | Architecture (Platform ADRs / STD-API-01) | Approved |
    | This OpenAPI document | **Developer Preview — contract only** (non-GA) |
    | Sandbox API host | Architecture **target** — not claimed operational |
    | Production API host | Architecture **target** — not claimed operational |

    This specification is published for integrator review. Documented endpoints
    and authentication services are **not** available for public integration.
    Sandbox and production availability will be announced separately. This is
    not general availability (ADR-011 DEC-5A-07 Preview API documentation).

    ## Authentication and authorization

    Callers authenticate with OAuth 2.0 Client Credentials
    (`client_secret_basic`) against the external authorization server
    (ADR-015, ADR-023). Access tokens must present audience
    `1funnel-external-api`.

    After token validation, every business-scoped operation requires:

    1. Valid application identity
    2. Active application-to-business grant for `{businessId}`
    3. Required resource permission (ADR-022)
    4. Resource ownership / isolation (ADR-016)

    `{businessId}` is a selector, not proof. OAuth scopes (when introduced) are
    coarse token vocabulary and are **not** the ADR-022 permission catalogue.
    Operations document required permissions with `x-required-permissions`.

    ## Servers and OAuth URLs

    API `servers` and OAuth token URLs are environment-specific architecture
    targets (ADR-023). OpenAPI cannot automatically switch the OAuth
    `tokenUrl` when a client selects a `servers` entry. See
    `x-1funnel-oauth` and the security scheme description.
  contact:
    name: 1Funnel Platform
    email: support@1funnel.ng
  license:
    name: Proprietary
    url: https://1funnel.ng
servers:
  - url: https://api.1-funnel.org
    description: |
      Production External Integration API host (ADR-023 target).
      Not claimed deployed or generally available.
  - url: https://api.sandbox.1-funnel.org
    description: |
      Sandbox External Integration API host (ADR-023 target).
      Not claimed deployed or generally available.
security:
  - OAuth2ClientCredentials: []
tags:
  - name: Foundation
    description: Shared conventions and cross-cutting components.
  - name: SMS
    description: |
      Individual SMS, bulk SMS, reporting, and SMS templates (ADR-018).
      Public bulk resource is `/sms/bulk-messages`. There is no `/sms/batches`.
  - name: Wallet
    description: Read-only wallet balance (ADR-019).
  - name: OTP
    description: |
      Managed OTP challenges (ADR-020 / ADR-008). Secrets are never returned.
      Service availability is not claimed by this draft.
  - name: Webhooks
    description: |
      Webhook endpoint management, event history, and delivery inspection
      (ADR-021). Signature protocol DEC-WH-S01 approved. Ownership
      verification deferred in v1. Public manual replay and public secret
      rotation are not available. See WEBHOOK-DECISION-REGISTER.md.
paths:
  /v1/businesses/{businessId}/sms/bulk-messages:
    parameters:
      - $ref: '#/components/parameters/BusinessId'
    post:
      tags:
        - SMS
      summary: Submit a bulk SMS
      description: >
        Durably accept a bulk SMS (ADR-018 SMS-04, DEC-SMS-B01/B02). Inline

        `recipients` only in v1. Exceeding the recipient limit rejects the
        entire

        request with `422` / `recipient_limit_exceeded` before acceptance.

        `202` is not delivery completion.
      operationId: createSmsBulkMessage
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyRequired'
        - $ref: '#/components/parameters/RequestId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SmsBulkMessageCreateRequest'
            examples:
              inlineRecipients:
                value:
                  from: 1FUNNEL
                  classification: promotional
                  message: Weekend offer for Ada.
                  recipients:
                    - to: '+2348012345678'
                    - to: '+2348098765432'
      responses:
        '202':
          description: Durably accepted bulk message
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestIdResponse'
            Location:
              $ref: '#/components/headers/Location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SmsBulkMessage'
              examples:
                accepted:
                  value:
                    id: 018f3c4e-7b2a-7c3d-8e9f-bbbbbbbbbbbb
                    business_id: 018f3c4e-7b2a-7c3d-8e9f-0123456789ab
                    from: 1FUNNEL
                    classification: promotional
                    status: queued
                    settled: false
                    message: Weekend offer for Ada.
                    template: null
                    accepted_at: '2026-10-13T09:00:00.000Z'
                    intake:
                      good: 2
                      bad: 0
                      empty: 0
                    submission: null
                    delivery_summary: null
                    warnings: []
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableContent'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  parameters:
    BusinessId:
      name: businessId
      in: path
      required: true
      description: >
        Business resource selector in `/v1/businesses/{businessId}/…` (ADR-006).

        UUIDv7-compatible opaque identifier. `{businessId}` is **not**
        authorization

        proof. The resource server must independently verify an active

        application-to-business grant, required permission, and ownership
        (ADR-016,

        ADR-022).
      schema:
        $ref: '#/components/schemas/UuidV7'
      examples:
        grantedBusiness:
          summary: Example business id
          value: 018f3c4e-7b2a-7c3d-8e9f-0123456789ab
    IdempotencyKeyRequired:
      name: Idempotency-Key
      in: header
      required: true
      description: >
        Required for this designated state-changing operation (STD-API-01 D09,

        ADR-017 SECOP-05/06, DEC-SMS-I01 shared external idempotency model).


        Scope: `(application_id, business_id, operation, idempotency_key)`.


        Permanent uniqueness within scope; seven-day response replay for
        equivalent

        payloads; `409` `idempotency_conflict` on payload mismatch; reuse after

        replay expiry must never create a new operation. See the optional

        `Idempotency-Key` component description for concurrent and

        failed-before-acceptance rules.
      schema:
        type: string
        minLength: 1
        maxLength: 256
        examples:
          - idem_01HZX3K9EXAMPLE
    RequestId:
      name: X-Request-Id
      in: header
      required: false
      description: >
        Request correlation identifier (STD-API-01 D12). Clients may supply a
        value

        of 1–128 characters matching `A-Za-z0-9._-`. Missing or invalid values
        are

        replaced by a server-generated identifier. Distinct from W3C
        `traceparent`,

        resource ids, `Idempotency-Key`, and webhook event ids.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[A-Za-z0-9._-]+$
        examples:
          - req_01HZX3K9EXAMPLE
  schemas:
    SmsBulkMessageCreateRequest:
      type: object
      description: >
        Bulk SMS submission (ADR-018 SMS-04 / SMS-10, DEC-SMS-B01 / B02 / N01 /
        T02).

        Public v1 uses an **inline** `recipients` array. File-based ingestion is
        not

        in v1 (deferred to a future additive capability). Provide exactly one of

        `message` or `template_id`.


        `recipients` length counts **all submitted entries**, including
        duplicates

        and invalid numbers. Default maximum is **1000**. Exceeding the
        effective

        limit rejects the **entire** request with `422` /
        `recipient_limit_exceeded`

        before durable acceptance, dispatch, or billing. No partial acceptance.
      additionalProperties: false
      required:
        - from
        - classification
        - recipients
      properties:
        from:
          $ref: '#/components/schemas/SenderId'
        classification:
          $ref: '#/components/schemas/MessageClassification'
        message:
          type: string
          description: Shared Unicode content when not using a template.
          minLength: 1
          maxLength: 4096
        template_id:
          $ref: '#/components/schemas/UuidV7'
          description: Active revision resolved and pinned at durable acceptance.
        recipients:
          type: array
          minItems: 1
          maxItems: 1000
          description: >
            Inline recipients. `maxItems` reflects the default maximum
            (DEC-SMS-B02).

            Business-specific higher quotas are planned but not available
            initially

            and do not change this request schema.
          items:
            $ref: '#/components/schemas/BulkRecipientInput'
      examples:
        - from: 1FUNNEL
          classification: promotional
          message: Weekend offer for {{first_name}}.
          recipients:
            - to: '+2348012345678'
              template_variables:
                first_name: Ada
            - to: '+2348098765432'
              template_variables:
                first_name: Chidi
    SmsBulkMessage:
      type: object
      description: |
        Bulk SMS resource. Processing status is distinct from recipient delivery
        outcomes (ADR-018 SMS-11–SMS-15). `settled` means
        `status == completed && retryable_failed_count == 0` when authoritative
        submission counters are available — not delivery completion.
      additionalProperties: false
      required:
        - id
        - business_id
        - from
        - classification
        - status
        - settled
        - accepted_at
      properties:
        id:
          $ref: '#/components/schemas/UuidV7'
        business_id:
          $ref: '#/components/schemas/UuidV7'
        from:
          $ref: '#/components/schemas/SenderId'
        classification:
          $ref: '#/components/schemas/MessageClassification'
        status:
          $ref: '#/components/schemas/BulkProcessingStatus'
        settled:
          type:
            - boolean
            - 'null'
          description: >
            Derived when authoritative `retryable_failed_count` is available:

            `status == completed && retryable_failed_count == 0`. Null when
            settlement

            cannot be computed. Independent of `delivery_summary`.
        message:
          type:
            - string
            - 'null'
        template:
          oneOf:
            - $ref: '#/components/schemas/TemplateRevisionPin'
            - type: 'null'
        accepted_at:
          $ref: '#/components/schemas/Timestamp'
        intake:
          oneOf:
            - $ref: '#/components/schemas/IntakeCounts'
            - type: 'null'
        submission:
          oneOf:
            - $ref: '#/components/schemas/SubmissionCounts'
            - type: 'null'
        delivery_summary:
          description: >
            Null when authoritative delivery reporting is unavailable, with a

            machine-readable `warnings` entry. Do not fabricate zero counts. Do
            not

            treat unavailable reporting as delivery failure.
          oneOf:
            - $ref: '#/components/schemas/DeliverySummary'
            - type: 'null'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/ContractWarning'
      examples:
        - id: 018f3c4e-7b2a-7c3d-8e9f-bbbbbbbbbbbb
          business_id: 018f3c4e-7b2a-7c3d-8e9f-0123456789ab
          from: 1FUNNEL
          classification: promotional
          status: completed
          settled: true
          message: Weekend offer
          template: null
          accepted_at: '2026-10-13T09:00:00.000Z'
          intake:
            good: 1000
            bad: 2
            empty: 0
          submission:
            processed: 1000
            success: 980
            failed: 20
            retryable_failed_count: 0
            exhausted: 20
          delivery_summary:
            as_of: '2026-10-13T09:00:00.000Z'
            complete: false
            outcomes_resolved: 850
          warnings: []
    UuidV7:
      type: string
      format: uuid
      pattern: >-
        ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-7[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$
      description: >
        UUID with version nibble `7` (UUIDv7). STD-API-01 prefers UUIDv7 for new

        platform-generated external resource identifiers. Clients must still
        treat

        the value as opaque. Not every path parameter is guaranteed to be v7
        solely

        because it is a UUID; use this schema where the contract asserts UUIDv7.
      examples:
        - 018f3c4e-7b2a-7c3d-8e9f-0123456789ab
    SenderId:
      type: string
      description: >
        Authorized sender identity for the business (`from`). The backend
        validates

        that the application/business may use this sender. Arbitrary sender IDs
        are

        not permitted. Public sender-ID management resources remain future

        (ADR-018 SMS-19 / target architecture).
      minLength: 1
      maxLength: 20
      examples:
        - 1FUNNEL
    MessageClassification:
      type: string
      description: |
        Public message classification (lowercase snake_case). Transactional vs
        promotional distinction for compliance and routing policy.
      enum:
        - transactional
        - promotional
      examples:
        - transactional
    BulkRecipientInput:
      type: object
      description: >
        Inline bulk recipient row for public bulk submission. ADR-005 merchant
        CSV

        portal ingestion is not this contract (ADR-018 SMS-10).
      additionalProperties: false
      required:
        - to
      properties:
        to:
          $ref: '#/components/schemas/E164PhoneNumber'
        template_variables:
          $ref: '#/components/schemas/TemplateVariables'
    BulkProcessingStatus:
      type: string
      description: >
        Bulk-message **processing** lifecycle (ADR-018 SMS-13 / ADR-003 Model
        B).

        `completed` means the primary processing pass finished — not all
        delivered

        and not all retries settled.
      enum:
        - created
        - scheduled
        - queued
        - processing
        - paused
        - completed
        - failed
        - cancelled
      examples:
        - processing
    TemplateRevisionPin:
      type: object
      description: >
        Immutable revision pin recorded on an accepted SMS (DEC-SMS-T02 /
        ADR-018

        SMS-08). Present on accepted message representations after the backend

        resolves the active revision at durable acceptance.
      additionalProperties: false
      required:
        - template_id
        - revision_id
      properties:
        template_id:
          $ref: '#/components/schemas/UuidV7'
        revision_id:
          $ref: '#/components/schemas/UuidV7'
      examples:
        - template_id: 018f3c4e-7b2a-7c3d-8e9f-0123456789ab
          revision_id: 018f3c4e-7b2a-7c3d-8e9f-0123456789cd
    Timestamp:
      type: string
      format: date-time
      description: >
        RFC 3339 date-time. Response timestamps are UTC with a `Z` offset

        (STD-API-01 D06). Client-supplied timestamps must include an explicit
        offset;

        local times without an offset are rejected.
      examples:
        - '2026-10-13T09:00:00.000Z'
    IntakeCounts:
      type: object
      description: |
        Intake classification counts (ADR-018 SMS-14). Omit this object when
        authoritative intake totals are unavailable — do not fabricate zeros.
      additionalProperties: false
      properties:
        good:
          type: integer
          minimum: 0
        bad:
          type: integer
          minimum: 0
        empty:
          type: integer
          minimum: 0
    SubmissionCounts:
      type: object
      description: >
        Submission/processing counts (ADR-018 SMS-14). Not delivery outcomes.
        Omit

        when unavailable — do not fabricate zeros. Do not present `success` as
        delivery.
      additionalProperties: false
      properties:
        processed:
          type: integer
          minimum: 0
        success:
          type: integer
          minimum: 0
          description: Provider submission successes — not delivery successes.
        failed:
          type: integer
          minimum: 0
        retryable_failed_count:
          type: integer
          minimum: 0
          description: >
            Outstanding submission retries. Settlement uses

            `status == completed && retryable_failed_count == 0` (ADR-003 /
            ADR-018).
        exhausted:
          type: integer
          minimum: 0
    DeliverySummary:
      type: object
      description: >
        Delivery-lifecycle projection (ADR-018 SMS-14a). Not intake, not
        submission

        processing, and not `settled`. When authoritative delivery data is
        unavailable,

        omit this field or set it to `null` with a warning — do not fabricate
        zeros.
      additionalProperties: false
      required:
        - as_of
        - complete
        - outcomes_resolved
      properties:
        as_of:
          $ref: '#/components/schemas/Timestamp'
        complete:
          type: boolean
          description: >
            Authoritative reporting completeness for delivery outcomes. `true`
            only

            when the platform can assert delivery reporting is complete for the
            scope.
        outcomes_resolved:
          type: integer
          minimum: 0
          description: >
            Count of recipients with resolved delivery outcomes in the
            authoritative

            delivery store. Must not be fabricated as `0` when the source is
            unavailable.
      examples:
        - as_of: '2026-10-13T09:00:00.000Z'
          complete: false
          outcomes_resolved: 850
    ContractWarning:
      type: object
      description: |
        Non-fatal contract warning used for null-and-warning / stale-projection
        signalling (STD-API-01 representation rules; ADR-018 SMS-14a).
      additionalProperties: false
      required:
        - code
        - detail
      properties:
        code:
          type: string
          examples:
            - delivery_summary_unavailable
            - delivery_summary_stale
        detail:
          type: string
          examples:
            - Authoritative delivery reporting is temporarily unavailable.
    ProblemDetails:
      type: object
      description: >
        RFC 9457 Problem Details for HTTP APIs (`application/problem+json`).

        STD-API-01 D08 requires `type`, `title`, `status`, `detail`, `code`, and

        `request_id`. `instance` is recommended. Validation failures include
        `errors`.


        Problem `type` namespace (DEC-SMS-E01):

        `https://developers.1-funnel.org/problems/{code}`

        These identifiers do not imply documentation pages are deployed.
        Resolvable

        problem-type documentation is required before GA.
      additionalProperties: true
      required:
        - type
        - title
        - status
        - detail
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri-reference
          description: >-
            Problem class identifier using the developers.1-funnel.org
            namespace.
          examples:
            - https://developers.1-funnel.org/problems/validation_failed
        title:
          type: string
          description: Short, stable summary safe for clients.
          examples:
            - Validation failed
        status:
          type: integer
          minimum: 100
          maximum: 599
          description: HTTP status code for this occurrence.
          examples:
            - 400
        detail:
          type: string
          description: >-
            Occurrence-specific explanation. Must not include secrets or stack
            traces.
          examples:
            - The request failed validation.
        instance:
          type: string
          format: uri-reference
          description: URI reference for this occurrence (RFC 9457 SHOULD).
        code:
          $ref: '#/components/schemas/ProblemCode'
        request_id:
          type: string
          minLength: 1
          maxLength: 128
          pattern: ^[A-Za-z0-9._-]+$
          description: Same value as response header `X-Request-Id` (STD-API-01 MUST).
          examples:
            - req_01HZX3K9EXAMPLE
        errors:
          type: array
          description: >-
            Present for validation failures (STD-API-01). Each item has
            `pointer` and `code`.
          items:
            type: object
            additionalProperties: false
            required:
              - pointer
              - code
            properties:
              pointer:
                type: string
                description: JSON Pointer into the request body, or a query parameter name.
              code:
                type: string
                description: Stable field-level error code.
      examples:
        - type: https://developers.1-funnel.org/problems/validation_failed
          title: Validation failed
          status: 400
          detail: The request failed validation.
          instance: /v1/businesses/018f3c4e-7b2a-7c3d-8e9f-0123456789ab/sms/messages
          code: validation_failed
          request_id: req_01HZX3K9EXAMPLE
          errors:
            - pointer: /to
              code: field_invalid
    E164PhoneNumber:
      type: string
      description: >
        Recipient MSISDN in E.164 format (`+` followed by country code and
        subscriber

        number). Leading `+` required.
      pattern: ^\+[1-9]\d{7,14}$
      minLength: 9
      maxLength: 16
      examples:
        - '+2348012345678'
    TemplateVariables:
      type: object
      description: >
        Per-recipient or per-send template variables. Keys must match
        placeholders

        defined by the referenced template revision. Values are strings. Live
        OTP

        codes must not be supplied as template variables for the managed OTP
        product

        (ADR-008 / ADR-018 SMS-09).
      additionalProperties:
        type: string
        maxLength: 512
      examples:
        - first_name: Ada
          order_id: ORD-1001
    ProblemCode:
      type: string
      description: |
        Stable machine-readable Problem Details `code` values for the External
        Integration API. Type URI form:
        `https://developers.1-funnel.org/problems/{code}`.
        Resolvable problem-type documentation is required before GA; these
        identifiers do not imply pages are deployed today.
      enum:
        - validation_failed
        - authentication_required
        - permission_denied
        - resource_not_found
        - idempotency_conflict
        - recipient_limit_exceeded
        - sender_not_authorized
        - template_not_active
        - invalid_recipient
        - rate_limit_exceeded
        - service_unavailable
        - internal_error
        - balance_unavailable
        - otp_verification_failed
        - otp_challenge_expired
        - otp_challenge_locked
        - otp_challenge_consumed
        - otp_challenge_invalidated
        - otp_resend_cooldown
        - otp_resend_limit_exceeded
      examples:
        - validation_failed
        - otp_verification_failed
  headers:
    RequestIdResponse:
      description: >
        Correlation identifier for this response (STD-API-01 D12). Echoes a
        valid

        client `X-Request-Id` or a server-generated value.
      schema:
        type: string
        minLength: 1
        maxLength: 128
        pattern: ^[A-Za-z0-9._-]+$
        examples:
          - req_01HZX3K9EXAMPLE
    Location:
      description: >
        URI of the created or accepted resource when a `Location` header applies

        (typically 201 or selected 202 responses). Capability contracts define
        when

        `Location` is present.
      schema:
        type: string
        format: uri-reference
    RetryAfter:
      description: >
        Hint for when the client may retry (for example 429 or 503). Value may
        be

        delay-seconds or an HTTP-date per RFC 9110.
      schema:
        type: string
        examples:
          - '60'
  responses:
    BadRequest:
      description: |
        Bad Request — malformed request or field validation failure
        (`code: validation_failed`, DEC-SMS-E01).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            validation:
              summary: Validation failure
              value:
                type: https://developers.1-funnel.org/problems/validation_failed
                title: Validation failed
                status: 400
                detail: One or more fields are invalid.
                code: validation_failed
                request_id: req_01HZX3K9EXAMPLE
                errors:
                  - pointer: /from
                    code: field_invalid
    Unauthorized:
      description: |
        Unauthorized — missing or invalid authentication
        (`code: authentication_required`, DEC-SMS-E01).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            authenticationRequired:
              value:
                type: >-
                  https://developers.1-funnel.org/problems/authentication_required
                title: Authentication required
                status: 401
                detail: A valid OAuth 2.0 access token is required.
                code: authentication_required
                request_id: req_01HZX3K9EXAMPLE
    Forbidden:
      description: >
        Forbidden — authenticated caller lacks required permission, business
        grant,

        or sender authorization (`permission_denied` or
        `sender_not_authorized`).

        Cross-business existence may be concealed with 404 instead (ADR-016).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            permissionDenied:
              value:
                type: https://developers.1-funnel.org/problems/permission_denied
                title: Forbidden
                status: 403
                detail: >-
                  The application is not permitted to perform this operation for
                  the selected business.
                code: permission_denied
                request_id: req_01HZX3K9EXAMPLE
            senderNotAuthorized:
              value:
                type: https://developers.1-funnel.org/problems/sender_not_authorized
                title: Sender not authorized
                status: 403
                detail: The sender identity is not authorized for this business.
                code: sender_not_authorized
                request_id: req_01HZX3K9EXAMPLE
    Conflict:
      description: >
        Conflict — state conflict or idempotency key reused with a different
        payload

        (STD-API-01 D08/D09).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            idempotencyConflict:
              summary: Idempotency key conflict
              value:
                type: https://developers.1-funnel.org/problems/idempotency_conflict
                title: Idempotency conflict
                status: 409
                detail: The Idempotency-Key was reused with a different payload.
                code: idempotency_conflict
                request_id: req_01HZX3K9EXAMPLE
    UnprocessableContent:
      description: >
        Unprocessable Content — syntactically valid request that cannot be
        processed

        due to semantic constraints (DEC-SMS-E01). Examples include recipient
        limit,

        inactive template, or invalid recipient.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            recipientLimit:
              summary: Bulk recipient limit exceeded
              value:
                type: >-
                  https://developers.1-funnel.org/problems/recipient_limit_exceeded
                title: Recipient limit exceeded
                status: 422
                detail: >-
                  The request exceeds the maximum of 1000 submitted recipient
                  entries.
                code: recipient_limit_exceeded
                request_id: req_01HZX3K9EXAMPLE
            templateNotActive:
              value:
                type: https://developers.1-funnel.org/problems/template_not_active
                title: Template not active
                status: 422
                detail: The template is archived or not active for new submissions.
                code: template_not_active
                request_id: req_01HZX3K9EXAMPLE
            invalidRecipient:
              value:
                type: https://developers.1-funnel.org/problems/invalid_recipient
                title: Invalid recipient
                status: 422
                detail: One or more recipients failed semantic validation.
                code: invalid_recipient
                request_id: req_01HZX3K9EXAMPLE
    TooManyRequests:
      description: |
        Too Many Requests — rate limit exceeded
        (`code: rate_limit_exceeded`, DEC-SMS-E01).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            rateLimited:
              value:
                type: https://developers.1-funnel.org/problems/rate_limit_exceeded
                title: Rate limit exceeded
                status: 429
                detail: The client has exceeded an applicable rate limit.
                code: rate_limit_exceeded
                request_id: req_01HZX3K9EXAMPLE
    InternalServerError:
      description: Internal Server Error — unexpected failure.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            internal:
              value:
                type: https://developers.1-funnel.org/problems/internal_error
                title: Internal server error
                status: 500
                detail: An unexpected error occurred.
                code: internal_error
                request_id: req_01HZX3K9EXAMPLE
    ServiceUnavailable:
      description: |
        Service Unavailable (`code: service_unavailable`, DEC-SMS-E01).
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestIdResponse'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
          examples:
            unavailable:
              value:
                type: https://developers.1-funnel.org/problems/service_unavailable
                title: Service unavailable
                status: 503
                detail: The service is temporarily unavailable.
                code: service_unavailable
                request_id: req_01HZX3K9EXAMPLE
  securitySchemes:
    OAuth2ClientCredentials:
      type: oauth2
      description: >
        OAuth 2.0 Client Credentials for the External Integration API (ADR-015,
        ADR-023).


        Client authentication: HTTP Basic (`client_secret_basic`).

        Access-token audience: `1funnel-external-api`.


        The `tokenUrl` below is the **production** target. Sandbox uses a
        different

        host. OpenAPI cannot bind `tokenUrl` to the selected `servers` entry;
        see

        `x-1funnel-oauth` on the root document and the authentication guide when
        published.


        Coarse OAuth scopes (if any) are token-level vocabulary (ADR-013 IA-06).
        They

        are **not** the ADR-022 resource permission catalogue. Resource
        permissions

        are enforced after token validation via application-to-business grants

        (ADR-016) and are documented per operation with
        `x-required-permissions`.


        These URLs are architecture targets. They are not claimed to be deployed
        or

        generally available.
      flows:
        clientCredentials:
          tokenUrl: https://auth.1-funnel.org/oauth2/token
          scopes: {}
      x-1funnel-client-authentication: client_secret_basic
      x-1funnel-audience: 1funnel-external-api

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.