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

# Cancel a pre-order

> Cancels a pre-order whose build has not started: a draft, or a pre-order whose first month is not paid.

* **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.
* **`Idempotency-Key`:** not honored. A second cancel returns `400 pre_order_already_confirmed`.
* **Events:** sends `pre_order.cancelled`, which only a subscription without a line filter receives: see [Events](/guides/pre-orders#events).
* **Statuses:** a pre-order in `draft` or `awaiting_payment` can be canceled. For a pre-order in `awaiting_payment`, the payment of the first month is canceled first: the `stripe_client_secret` of the confirmation can no longer be used. Once its build has started, a pre-order cannot be canceled through the API. See [Canceling](/guides/pre-orders#canceling).
* **Caching:** the response carries `Cache-Control: no-store`, whatever its status.

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | `id` is not a UUID. |
| 400 | `pre_order_already_confirmed` | The pre-order is `provisioning`, `fulfilled` or already `cancelled`. |
| 404 | `not_found` | The id is unknown, or the pre-order belongs to another account. |
| 409 | `pre_order_already_paid` | The first month is already paid: the build goes on. Read the pre-order. |
| 409 | `pre_order_already_confirmed` | A confirmation of a wholesale partner, or another cancel, of the same draft ran at the same time and won. |
| 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. |
| 503 | `payment_provider_unavailable` | iSnap could not reach its payment provider. Nothing changed: resend the cancel. |

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


## OpenAPI

````yaml POST /v1/pre-orders/{id}/cancel
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}/cancel:
    post:
      summary: Cancel a pre-order
      description: >-
        Cancels a pre-order in status `draft` or `awaiting_payment`: nothing was
        created and nothing was charged. Accepts an API key. A line-scoped key
        is not limited to its list on this operation. Sets `status` to
        `cancelled` and sends `pre_order.cancelled`. For a pre-order in
        `awaiting_payment`, the first-month payment is canceled first: the
        client secret returned by confirm can no longer be used. When that first
        month is already paid, the call returns `409 pre_order_already_paid` and
        the build goes on. A pre-order that is `provisioning`, `fulfilled` or
        already `cancelled` returns `400 pre_order_already_confirmed`, and the
        cancellation of a draft that runs at the same time as another
        cancellation, or as the confirmation of a wholesale partner, returns
        `409 pre_order_already_confirmed`. 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 `cancelled`.
          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_already_confirmed` when the pre-order is `provisioning`,
            `fulfilled` or already `cancelled`.
          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_already_paid` when the first
            month is already paid. It is `pre_order_already_confirmed` when a
            confirmation or another cancellation of the same draft 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.