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

# Confirm a pre-order

> Commits a draft pre-order: the build of the line starts immediately for a wholesale partner, and once the first month is paid for a direct account.

* **Credential:** an API key of the account that owns the pre-order.
* **Line-scoped keys:** the list of lines of a [line-scoped key](/authentication#line-scoped-keys) is not applied.
* **Test keys:** a confirmation sent with an `isnap_test_` account key is a real order: see [Test keys](/authentication#test-keys).
* **`Idempotency-Key`:** not honored. A direct account can repeat the confirmation: while the pre-order is `awaiting_payment`, a repeat returns the same `stripe_client_secret` and starts no second billing, and after the payment it returns the pre-order in `provisioning`. A wholesale partner's repeat returns `400 pre_order_not_draft`. See [Confirm and payment](/guides/pre-orders#confirm-and-payment).
* **Events:** none. The response is the only signal. `pre_order.fulfilled` is sent later, when iSnap hands the line over, and `pre_order.cancelled` when the first month is not paid within 23 hours: see [Events](/guides/pre-orders#events).
* **Response:** a wholesale partner receives the pre-order in `provisioning`, with `stripe_client_secret: null`. A direct account receives it in `awaiting_payment`, with no line yet: the build starts once the first month is paid. When that first month is already paid, the response is in `provisioning`.
* **Secret:** `stripe_client_secret` is the secret of the first monthly payment, returned only by this response, while the pre-order is `awaiting_payment`. No operation that an API key can call pays it: pay the first month in the iSnap dashboard, by card, within 23 hours of the confirmation. See [Accounts](/concepts#accounts). The response carries `Cache-Control: no-store`, whatever its status.
* **No way back:** once its build has started, a pre-order cannot be canceled through the API. See [Canceling](/guides/pre-orders#canceling).

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | `id` is not a UUID. |
| 400 | `pre_order_not_draft` | The pre-order is neither a draft nor `awaiting_payment`: it is `fulfilled`, `cancelled`, or `provisioning` after the confirmation of a wholesale partner. A pre-order canceled because its first month was not paid within 23 hours returns it too: create a new draft. |
| 404 | `not_found` | The id is unknown, or the pre-order belongs to another account. |
| 409 | `pre_order_not_draft` | A cancellation, or for a wholesale partner another confirmation, of the same draft ran at the same time and won. Read the pre-order. |
| 409 | `payment_setup_failed` | The payment provider refused the request for your account. Nothing changed, and resending the call does not help: write to [support@isnap.ai](mailto:support@isnap.ai) with the `trace_id` and `request_id` of the response. |
| 500 | `internal_error` | An unexpected failure. Read the pre-order before you resend the confirmation: see [Confirm and payment](/guides/pre-orders#confirm-and-payment). If the `500` repeats, write to [support@isnap.ai](mailto:support@isnap.ai) with the `trace_id` and `request_id` of the response. |
| 503 | `payment_provider_unavailable` | iSnap could not reach its payment provider. Nothing changed: resend the confirmation. |

The errors that every operation shares are in [Errors](/errors-and-rate-limits#errors).


## OpenAPI

````yaml POST /v1/pre-orders/{id}/confirm
openapi: 3.0.0
info:
  title: iSnap API
  version: 2.45.0
  description: >-
    The iSnap API sends and receives iMessage, RCS and SMS on real phone lines,
    manages those lines, and reports events through webhooks. Every message is
    sent to a real recipient, with an `isnap_live_` key and with an
    `isnap_test_` key alike. Retry a `429` after `Retry-After` seconds. Retry a
    `500` or a `503` up to 2 times, with exponential backoff. Do not retry any
    other `4xx`: fix the request. Retry a write only on an operation that honors
    `Idempotency-Key`, with the key of the first attempt. Responses carry
    personal data: handles, names and message text.
servers:
  - url: https://api.isnap.ai
security:
  - bearerAuth: []
paths:
  /v1/pre-orders/{id}/confirm:
    post:
      summary: Confirm a pre-order
      description: >-
        Commits a draft pre-order. Accepts an API key. A line-scoped key is not
        limited to its list on this operation: it can confirm any draft of the
        account. The confirmation sends no event: iSnap sends
        `pre_order.fulfilled`, which carries the `line_id`, when the line is
        handed over.


        The build of a pre-order sets `status` to `provisioning` and
        `estimated_fulfillment` to the time the build starts plus 72 hours, and
        creates on your account a rented iMessage line with `status:
        provisioning`, with the `inbound_only` and the `billing_anchor_at` of
        the pre-order. When a number is in stock in one of your area codes, in
        their order of preference, it is assigned to the line when the build
        starts. A pre-order with no `preferred_area_codes` takes any number in
        stock.


        For a wholesale partner the build starts with the confirmation. A
        wholesale partner pays nothing at confirmation: `stripe_client_secret`
        is `null`, and the line is billed on the monthly invoice from the day it
        is handed over.


        For a direct account the build starts when the first month is paid.
        Confirm starts a monthly subscription, sets `status` to
        `awaiting_payment` and returns `stripe_client_secret`, a client secret
        that your payment page passes to the payment form of Stripe to collect
        the first month. Only card payments are accepted. Until that payment
        succeeds, no line is created and `estimated_fulfillment` is `null`.
        After paying, read the pre-order with `GET /v1/pre-orders/{id}` until
        `status` is `provisioning`. When the first month is not paid within 23
        hours of the confirmation, `status` becomes `cancelled` and iSnap sends
        `pre_order.cancelled`. When a credit balance of the account at Stripe
        covers the first month, there is nothing to pay: the build starts with
        the confirmation and `stripe_client_secret` is `null`. The first month
        starts at the confirmation, not when the line is handed over, and the
        next invoice is issued one month after the confirmation. The hand-over
        of the line takes no payment and does not change when the subscription
        is invoiced.


        Confirm can be repeated while `status` is `awaiting_payment`: it returns
        the same client secret and starts no second subscription. Repeated after
        the payment succeeded, it returns the pre-order in `provisioning`.


        Any other pre-order returns `400 pre_order_not_draft`, including one
        that was canceled because its first month was not paid in time. A
        confirmation that runs at the same time as a cancellation, or for a
        wholesale partner as another confirmation, returns `409
        pre_order_not_draft`. When Stripe cannot be reached, the call returns
        `503 payment_provider_unavailable`: nothing changed, and the call can be
        repeated. When Stripe refuses the request for the account, the call
        returns `409 payment_setup_failed`: nothing changed, and repeating the
        call does not help. Once its build has started, a pre-order cannot be
        canceled through the API.
      parameters:
        - schema:
            type: string
            format: uuid
            description: >-
              Id of the pre-order: the UUID returned in `id` by `POST
              /v1/pre-orders`.
            example: 7f3b9c1e-2a4d-4f6b-8e0a-5c1d2e3f4a5b
          required: true
          description: >-
            Id of the pre-order: the UUID returned in `id` by `POST
            /v1/pre-orders`.
          name: id
          in: path
      responses:
        '200':
          description: >-
            The pre-order: in status `awaiting_payment` with its
            `stripe_client_secret` for a direct account whose first month is
            due, in status `provisioning` otherwise.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                    description: Always `true` on a success response.
                  data:
                    $ref: '#/components/schemas/PreOrder'
                  trace_id:
                    type: string
                    description: >-
                      Trace id of the request that produced this response: 32
                      lowercase hexadecimal characters. Quote it when you report
                      a problem to iSnap.
                  request_id:
                    type: string
                    description: >-
                      Id of the request that produced this response: `req_`
                      followed by 25 letters or digits.
                required:
                  - success
                  - data
                  - trace_id
                  - request_id
        '400':
          description: >-
            Invalid request. `error.code` is `validation_failed` when a
            parameter, a header or the body does not match its schema:
            `error.issues` then lists each problem. It is `pre_order_not_draft`
            when the pre-order is neither a draft nor in `awaiting_payment`,
            except a pre-order in `provisioning` whose first month was paid,
            which a repeated confirm returns.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: No valid credential. `error.code` is `unauthorized`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: >-
            Not found. `error.code` is `not_found`: the account has no pre-order
            with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            Conflict. `error.code` is `pre_order_not_draft` when a cancellation,
            or for a wholesale partner another confirmation, ran at the same
            time and won. It is `payment_setup_failed` when Stripe refused the
            request for the account: repeating the call does not help.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: >-
            Too many requests. `error.code` is `rate_limit_exceeded` when more
            than 100 requests came from the same network address in 60 seconds,
            whatever their API key or account: wait `error.retry_after` seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            Unexpected failure. `error.code` is `internal_error`. An operation
            that takes a JSON body also returns it when the body, sent as
            `application/json`, is not valid JSON. Quote `trace_id` when you
            report it to iSnap.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: >-
            Service unavailable. `error.code` is `payment_provider_unavailable`:
            Stripe could not be reached. Nothing changed, and the call can be
            repeated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - bearerAuth: []
components:
  schemas:
    PreOrder:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: 'Id of the pre-order: a UUID.'
        preferred_area_codes:
          type: array
          items:
            type: string
          description: The area codes you listed, in order of preference.
        inbound_only:
          type: boolean
          description: '`true` for an inbound-only line, `false` for a two-way line.'
        billing_anchor_at:
          type: string
          nullable: true
          description: >-
            The billing anchor you set when creating the pre-order, carried onto
            the line when the line is created. `null` when you set none.
        status:
          $ref: '#/components/schemas/PreOrderStatus'
        stripe_client_secret:
          type: string
          nullable: true
          description: >-
            Client secret for the payment form of Stripe, to collect the first
            month. Set only in the response of `POST
            /v1/pre-orders/{id}/confirm`, when that response carries the
            pre-order in status `awaiting_payment`: the first time and every
            time confirm is repeated. `null` in every other response.
        estimated_fulfillment:
          type: string
          nullable: true
          description: >-
            Estimated time of the hand-over of the line: the time the build
            starts plus 72 hours. Set once, when the build starts, and never
            revised. `null` while the pre-order is `draft` or
            `awaiting_payment`, and on a pre-order canceled before its build
            started.
        device_status:
          $ref: '#/components/schemas/DeviceFulfillmentStatus'
        created_at:
          type: string
          description: Timestamp of creation, ISO 8601 in UTC.
        updated_at:
          type: string
          description: Timestamp of the last change of the pre-order, ISO 8601 in UTC.
        metadata:
          type: object
          nullable: true
          additionalProperties:
            nullable: true
          description: >-
            Your metadata on the pre-order, set at creation or with `PATCH
            /v1/pre-orders/{id}`, and sent as `data.metadata` on every
            `pre_order.*` event. `null` when the pre-order has none. It is not
            copied onto the line that the pre-order produces.
          example:
            crm_account_id: acc_123
      required:
        - id
        - preferred_area_codes
        - inbound_only
        - billing_anchor_at
        - status
        - stripe_client_secret
        - estimated_fulfillment
        - created_at
        - updated_at
        - metadata
      description: 'A pre-order: an order for one rented iMessage line.'
    ErrorEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
          description: Always `false` on an error response.
        data:
          nullable: true
          description: Always `null` on an error response.
        error:
          type: object
          properties:
            status:
              type: integer
              description: The HTTP status of the response.
            code:
              type: string
              description: >-
                Stable machine-readable code that names the refusal. Branch on
                this value.
            message:
              type: string
              description: >-
                Text for a person. It can change between versions: do not branch
                on it.
            retry_after:
              type: integer
              nullable: true
              description: >-
                Seconds to wait before the next attempt. Set on the `429`
                responses that know a delay, where the `Retry-After` header
                carries the same value. When it is above 60 seconds, do not wait
                in a request: schedule the call, or report the time. `null` on
                every other error.
            retry_at:
              type: string
              nullable: true
              description: >-
                ISO 8601 timestamp at which the refusal ends. Set only on `429
                rebind_cap_reached`. `null` on every other error.
            issues:
              type: array
              nullable: true
              items:
                type: object
                properties:
                  path:
                    type: array
                    items:
                      anyOf:
                        - type: string
                        - type: number
                    description: >-
                      Where the problem is: the field names and array indexes
                      that lead to it.
                  message:
                    type: string
                    description: Text for a person.
                  code:
                    type: string
                    description: >-
                      Kind of problem. On `validation_failed`, for example
                      `invalid_type`, `invalid_value`, `invalid_format`,
                      `invalid_union`, `invalid_key`, `invalid_element`,
                      `too_small`, `too_big`, `not_multiple_of`,
                      `unrecognized_keys` or `custom`. The list is open: read
                      `path` and `message`.
                  min:
                    type: integer
                    description: >-
                      Smallest accepted value. Present only on
                      `limit_out_of_bounds`, `mac_quantity_not_a_pack`,
                      `byop_reduction_strands_mac_sessions` and
                      `byop_reduction_strands_byod_lines`.
                  max:
                    type: integer
                    description: >-
                      Largest accepted value. Present only on
                      `limit_out_of_bounds` and
                      `mac_sessions_exceed_byop_lines`, and on the `too_big`
                      issue of an `Idempotency-Key` header longer than 255
                      characters.
                required:
                  - path
                  - message
                  - code
              description: >-
                Problems found in the request, one entry each. Set on most `400
                validation_failed` responses and on the refusals that name a
                field: a refused `handle` of `POST /v1/lookup` has `issues:
                null`. `null` on every other error.
          required:
            - status
            - code
            - message
            - retry_after
            - retry_at
            - issues
          description: >-
            What went wrong. The six fields are always present; `retry_after`,
            `retry_at` and `issues` are `null` when they do not apply.
        trace_id:
          type: string
          description: >-
            Trace id of the request that produced this response: 32 lowercase
            hexadecimal characters. Quote it when you report a problem to iSnap.
        request_id:
          type: string
          description: >-
            Id of the request that produced this response: `req_` followed by 25
            letters or digits.
      required:
        - success
        - data
        - error
        - trace_id
        - request_id
      description: The body of an error response.
    PreOrderStatus:
      type: string
      enum:
        - draft
        - awaiting_payment
        - provisioning
        - fulfilled
        - cancelled
      description: >-
        `draft`: created, with no effect. `awaiting_payment`: confirmed by a
        direct account, and the first month is due; nothing is built until it is
        paid. `provisioning`: the build has started, and the line is being
        prepared. `fulfilled`: the line is handed over. `cancelled`: the
        pre-order ended with no line. You canceled it, its first month was not
        paid within 23 hours of the confirmation, or its subscription was
        canceled before the first payment. A pre-order whose build has started
        never becomes `cancelled`.
    DeviceFulfillmentStatus:
      type: string
      nullable: true
      enum:
        - awaiting_hardware
        - received
        - setup_in_progress
        - ready
        - deployed
        - null
      description: >-
        Progress of the line. `awaiting_hardware`: the line is not ready; also
        the value of a pre-order that has no line: a draft, a pre-order in
        `awaiting_payment`, a canceled pre-order. `ready`: the line is prepared.
        `deployed`: the line is handed over. `received` and `setup_in_progress`
        are declared and never returned. Always present on a pre-order, and
        never `null`.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        An API key created in the iSnap dashboard, or a trial key, sent as
        `Authorization: Bearer <key>`. An API key starts with `isnap_live_` or
        `isnap_test_`; both act on the same account, lines and billing. A trial
        key acts on its trial only.

````

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