> ## 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.

# Create a pre-order

> Creates a draft pre-order for one rented iMessage line, which has no effect until you confirm it.

* **Credential:** an API key.
* **Line-scoped keys:** the list of lines of a [line-scoped key](/authentication#line-scoped-keys) is not applied.
* **`Idempotency-Key`:** not honored. A repeated call creates another draft. See [Operations that honor the key](/idempotency#operations-that-honor-the-key).
* **Events:** none. See [Events](/guides/pre-orders#events).
* **Effect:** a draft creates no line and takes no payment. [`POST /v1/pre-orders/{id}/confirm`](/api-reference/pre-orders/confirm-pre-order) commits it: see [How a pre-order works](/guides/pre-orders#how-a-pre-order-works).
* **Caching:** the response carries `Cache-Control: no-store`, whatever its status.

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | An unknown field, `quantity` included, more than 5 area codes, a value of the wrong type, or a `metadata` over 20 keys or 8,192 bytes. |
| 400 | `android_rental_not_purchasable` | `billing_tier` is `rental_android`. |
| 400 | `tier_not_pre_orderable` | `billing_tier` is `byod_imessage`, `byod_android`, `shared_pool` or `shared_imessage`. |
| 400 | `billing_anchor_too_far` | `billing_anchor_at` is more than 31 days after the call. |

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


## OpenAPI

````yaml POST /v1/pre-orders
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:
    post:
      summary: Create a pre-order
      description: >-
        Creates a draft pre-order for one rented iMessage line. Accepts an API
        key. A line-scoped key is not limited to its list on this operation. A
        draft has no effect: it creates no line, takes no payment, and never
        expires. Call `POST /v1/pre-orders/{id}/confirm` to commit it. There is
        no limit on the number of drafts. `Idempotency-Key` is ignored: a
        repeated call creates another draft. Creating a draft sends no event.


        `preferred_area_codes` lists up to 5 area codes in order of preference,
        and `inbound_only` chooses between an inbound-only line and a two-way
        line. `billing_tier`, when sent, must be `rental_iphone`:
        `rental_android` returns `400 android_rental_not_purchasable`, and the
        four other values return `400 tier_not_pre_orderable`.
        `billing_anchor_at` is stored on the pre-order and carried onto the line
        when the line is created: a value more than 31 days after the call
        returns `400 billing_anchor_too_far`, and it is not checked again. A key
        that is not listed, `quantity` included, returns `400
        validation_failed`: one pre-order is one line.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                preferred_area_codes:
                  type: array
                  items:
                    type: string
                    minLength: 1
                    maxLength: 10
                  maxItems: 5
                  default: []
                  description: >-
                    Area codes you accept for the number of the line, in order
                    of preference: 0 to 5 strings of 1 to 10 characters.
                    Default: an empty list, which accepts any area code.
                  example:
                    - '415'
                    - '628'
                inbound_only:
                  type: boolean
                  default: false
                  description: >-
                    `true` orders an inbound-only line, `false` a two-way line.
                    Default: `false`.
                billing_tier:
                  type: string
                  enum:
                    - rental_iphone
                    - rental_android
                    - byod_imessage
                    - byod_android
                    - shared_pool
                    - shared_imessage
                  description: >-
                    Optional. Only `rental_iphone` is accepted: `rental_android`
                    returns `400 android_rental_not_purchasable`, and
                    `byod_imessage`, `byod_android`, `shared_pool` and
                    `shared_imessage` return `400 tier_not_pre_orderable`. The
                    value is neither stored nor returned.
                  example: rental_iphone
                billing_anchor_at:
                  type: string
                  format: date-time
                  description: >-
                    The billing anchor of the line: an ISO 8601 timestamp in
                    UTC, with a `Z` suffix. A line billed on the monthly
                    wholesale invoice is charged one full month at each monthly
                    occurrence of this timestamp: the same day of the month,
                    moved to the last day of a shorter month, at the same time
                    of day. At the first occurrence after its activation, the
                    line is also credited for the days that the month charged at
                    activation had already paid for past that occurrence. Omit
                    it to bill the line at each monthly occurrence of its
                    activation. A past timestamp is accepted. A timestamp more
                    than 31 days after the request returns `400
                    billing_anchor_too_far`.
                  example: '2026-09-11T09:15:00Z'
                metadata:
                  type: object
                  additionalProperties:
                    nullable: true
                  description: >-
                    Your own key-value data. iSnap stores it as sent and does
                    not interpret it. Events about this resource carry it in
                    `data.metadata`. At most 20 keys and 8,192 bytes once
                    serialized as JSON.
                  example:
                    crm_account_id: acc_123
              additionalProperties: false
      responses:
        '201':
          description: The pre-order, in status `draft`.
          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
            `android_rental_not_purchasable` when `billing_tier` is
            `rental_android`, `tier_not_pre_orderable` when `billing_tier` is
            `byod_imessage`, `byod_android`, `shared_pool` or `shared_imessage`,
            and `billing_anchor_too_far` when `billing_anchor_at` is more than
            31 days after the call.
          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'
        '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'
      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.