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

# Release a reserved line

> Gives back a line you reserved, which becomes available again.

* **Credential:** an API key. The line must be reserved by your account. See [Reservations](/guides/getting-a-line#reservations).
* **Line-scoped keys:** a [line-scoped key](/authentication#line-scoped-keys) gets `403 forbidden` unless the line is in its list.
* **`Idempotency-Key`:** not honored. A second call returns `404 not_found`, because the line is no longer reserved by you.
* **Events:** none. See [Events](/guides/getting-a-line#events).
* **Billing:** a payment of the line that was started in the iSnap dashboard and not submitted is canceled first, and can then no longer be made. A payment is submitted once the card payment is being processed. A payment that waits for the cardholder's authentication is not submitted. When a payment succeeded more than one hour ago without activating the line, the release ends its billing, and iSnap refunds the payment to your account; no operation of the API makes or reports that refund. No other billing changes. See [A line paid in the dashboard](/guides/getting-a-line#a-line-paid-in-the-dashboard).
* **Not an end:** this operation does not end an `active` line: see [Ending a line](/guides/managing-lines#ending-a-line).

## Errors

| Status | `error.code` | When |
| - | - | - |
| 403 | `forbidden` | The request uses a line-scoped key, and the line is not in its list. |
| 404 | `not_found` | The line is not reserved by your account: no line has this id, the line is not yours, or it is in another status, `active` and `provisioning` included. |
| 409 | `purchase_payment_in_progress` | A payment of the line by your account has been submitted, or has succeeded within the last hour, and the line is not `active` yet. Nothing is released, and the line stays reserved for your account. Do not retry: read the line with [`GET /v1/lines/{id}`](/api-reference/lines/get-line) until it is `active` or no longer `reserved`. |
| 409 | `purchase_in_progress` | The request waited 10 seconds for another payment or release request of your account. Nothing is released: resend the request. |
| 500 | `internal_error` | iSnap could not check the payments of the line, or another unexpected failure. Nothing is released. |

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


## OpenAPI

````yaml POST /v1/lines/{id}/release
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/lines/{id}/release:
    post:
      summary: Release a reserved line
      description: >-
        Gives back a line that you have reserved: it returns to `available`
        immediately and no longer belongs to your account. Accepts an API key.
        Any other line returns `404 not_found`, an `active` line included: to
        end a line you use, call `POST /v1/lines/{id}/terminate` or `POST
        /v1/lines/{id}/cancel`. When a payment was started for the line, the
        release first cancels its unpaid subscription, which can no longer be
        paid. While the first payment of a subscription of yours for the line
        has been submitted, or has succeeded within the last hour, and the line
        is not `active` yet, the call returns `409 purchase_payment_in_progress`
        and releases nothing: the line stays reserved for your account. Read it
        with `GET /v1/lines/{id}` until it is `active` or no longer `reserved`.
        One hour after a payment that succeeded and did not activate the line,
        the release goes through: the subscription of that payment is canceled,
        and the payment is recorded as a refund owed to the account. When the
        payments of the line cannot be checked, the call fails and releases
        nothing. A release changes no other billing and leaves the `metadata` of
        the line as it is. This operation sends no event.
      parameters:
        - schema:
            type: string
            pattern: ^line_[0-9A-Za-z]{25}$
            description: 'Id of a line: `line_` followed by 25 letters or digits.'
          required: true
          description: 'Id of a line: `line_` followed by 25 letters or digits.'
          name: id
          in: path
      responses:
        '200':
          description: 'The released line, with `status: available`.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                    description: Always `true` on a success response.
                  data:
                    $ref: '#/components/schemas/Line'
                  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.
          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'
        '403':
          description: >-
            Refused. `forbidden`: the request carries neither an API key in its
            `Authorization` header nor the header `X-Requested-With:
            XMLHttpRequest`, or the key is line-scoped and this line is outside
            its list.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: 'Not found. `not_found`: the line is not reserved by your account.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            Conflict. `error.code` is `purchase_payment_in_progress` when the
            first payment of a subscription of yours for the line has been
            submitted, or has succeeded within the last hour, and the line is
            not `active` yet: the line is not released, and stays reserved for
            your account. Read the line with `GET /v1/lines/{id}` until it is
            `active` or no longer `reserved`. It is `purchase_in_progress` when
            the request has waited 10 seconds for another payment or release
            request of the same account: resend the request.
          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:
    Line:
      type: object
      properties:
        id:
          type: string
          pattern: ^line_[0-9A-Za-z]{25}$
          description: 'Id of a line: `line_` followed by 25 letters or digits.'
        phone_number:
          type: string
          description: >-
            The phone number of the line in E.164 format. An empty string, never
            `null`, when the line has no number: a BYOD line whose device has
            not reported one, or a shared line. `GET /v1/attention` returns
            `null` for such a line. Masked on `GET /v1/lines`, and on `GET
            /v1/lines/{id}` unless the caller owns the line: `+1 (AAA) ***-**DD`
            for a North American number, the country code, asterisks and the
            last two digits for any other number.
        area_code:
          type: string
          nullable: true
          description: >-
            The 3-digit North American area code of the number. `null` for any
            other number, and while the number is not known.
        country_code:
          type: string
          nullable: true
          description: >-
            ISO 3166-1 alpha-2 country of the number. `null` while the number is
            not known.
        state:
          type: string
          nullable: true
          description: >-
            Two-letter code of the US state the area code belongs to. `null`
            outside the US, and while the number is not known.
        state_name:
          type: string
          nullable: true
          description: >-
            Name of that US state, for example `California`. `null` whenever
            `state` is `null`.
        billing_tier:
          type: string
          enum:
            - rental_iphone
            - rental_android
            - byod_imessage
            - byod_android
            - shared_pool
            - shared_imessage
          description: >-
            The kind of line. `rental_iphone`: a rented line that sends iMessage
            and SMS. `rental_android`: a rented line that sends SMS and RCS.
            `byod_imessage`: a BYOD line on your own Mac and iPhone.
            `byod_android`: a BYOD line on your own Android phone.
            `shared_imessage`: a shared line. `shared_pool`: a test line when
            `test_line` is `true`, otherwise a dedicated pool line: a line of
            iSnap that is dedicated to your account.
        byod:
          type: boolean
          description: >-
            `true` for a BYOD line, which runs on your own device:
            `billing_tier` is `byod_imessage` or `byod_android`.
        inbound_only:
          type: boolean
          description: >-
            `true` for an inbound-only line: it can message a contact only after
            that contact has messaged it. A send to any other handle returns
            `403 outbound_first_forbidden`. Only a rented line can be
            inbound-only: always `false` on BYOD, shared and test lines.
        capabilities:
          nullable: true
          description: >-
            The services the line sends, as an array of lowercase names:
            `["imessage", "sms"]` on `rental_iphone` and `byod_imessage` lines,
            `["sms", "rcs"]` on `rental_android` and `byod_android` lines,
            `["imessage"]` on a shared line. A test line that you made from a
            rented line keeps the array of that line. On a dedicated pool line,
            and on a test line that iSnap created for your account, the array
            does not tell the services and can be `["sms"]`: such a line is an
            iMessage line.
        status:
          type: string
          description: >-
            One of six values. `provisioning`: the line is being prepared for
            one of your pre-orders. `available`: the line belongs to no account
            and can be reserved or assigned. `reserved`: the line is reserved
            for one account until `expires_at`; it is not billed and cannot
            send. `active`: the line belongs to its owner; it is the only status
            in which a line sends. `suspended`: the line still belongs to its
            owner but cannot send (`403 line_suspended`); a line billed by a
            per-line subscription is suspended when a payment for it fails,
            iSnap can also suspend a line, and no API operation ends a
            suspension. `cancelled`: the line is ended for good and keeps its
            owner.
        monthly_price:
          type: integer
          minimum: 0
          deprecated: true
          description: >-
            Deprecated. Catalog price of this kind of line, in US cents. It is
            not the amount billed for the line. Removed in the next major
            version: no removal date is set.
        stripe_subscription_id:
          type: string
          nullable: true
          description: >-
            Id of the per-line subscription that bills the line. `null` on a
            line billed on the monthly wholesale invoice, on BYOD, shared and
            test lines, and on a rented line that a direct account received by
            transfer while the line was not `active`.
        activated_at:
          type: string
          nullable: true
          description: >-
            Timestamp at which the line became `active` (ISO 8601, UTC). `null`
            on a line that was never activated, and again on a line that
            returned to the marketplace. A transfer to a wholesale partner sets
            it to the time of the transfer; a transfer to a direct account keeps
            the earlier value.
        expires_at:
          type: string
          nullable: true
          description: >-
            End of the reservation (ISO 8601, UTC): 15 minutes after the line
            was reserved. Read it only while `status` is `reserved`: in any
            other status it is `null` or a past timestamp.
        anniversary_at:
          type: string
          nullable: true
          description: >-
            The anniversary of the line: the timestamp at which you took it (ISO
            8601, UTC). `POST /v1/lines/{id}/cancel` ends the line at the next
            occurrence of this timestamp. iSnap sets it each time a line becomes
            a wholesale partner's, a transfer included, and `POST
            /v1/lines/{id}/anniversary` replaces it. A conversion to a test line
            and back keeps it. `null` on a line owned by a direct account or by
            no account, and on a line of a wholesale partner whose anniversary
            was never set.
        cancel_at:
          type: string
          nullable: true
          description: >-
            Timestamp at which iSnap ends this line (ISO 8601, UTC). Set by
            `POST /v1/lines/{id}/cancel` and cleared by `POST
            /v1/lines/{id}/uncancel`. The line works and is billed as before
            until then. `null` when no end is scheduled. A line that ended at
            this date keeps the value. On a test line ended with `POST
            /v1/lines/{id}/terminate`, it holds the time the line ended.
        billing_anchor_at:
          type: string
          nullable: true
          description: >-
            The billing anchor you stated for this line (ISO 8601, UTC): a line
            billed on the monthly wholesale invoice is billed at each monthly
            occurrence of this timestamp. `null` when you stated none: the line
            is then billed at each monthly occurrence of `activated_at`. State
            it with `POST /v1/pre-orders`, `POST /v1/lines/{id}/activate` or
            `PATCH /v1/lines/{id}`. A change applies from the next occurrence
            and does not alter a month already billed.
        next_anchor_at:
          type: string
          nullable: true
          description: >-
            For a line billed on the monthly wholesale invoice: the next
            timestamp at which it is billed (ISO 8601, UTC). It is the next
            occurrence, strictly after the read, of `billing_anchor_at`, or of
            `activated_at` when no billing anchor is stated: the same day of the
            month, moved to the last day of a shorter month, at the same time of
            day. `null` when the line is not `active` or `suspended`, when it
            was never activated, and when its `cancel_at` falls on or before
            that occurrence. A line billed by a per-line subscription also
            carries a value here, which does not tell when that line is billed.
        pending_variant:
          type: object
          nullable: true
          properties:
            inbound_only:
              type: boolean
              description: The value `inbound_only` takes when the change applies.
            effective_at:
              type: string
              description: >-
                The billing anchor occurrence at which the change applies (ISO
                8601, UTC). iSnap applies it after this timestamp, and before it
                charges the line for the month that starts then: the API does
                not bound the delay.
          required:
            - inbound_only
            - effective_at
          description: >-
            The variant change scheduled for this line, or `null` when none is
            pending. The `inbound_only` field of the line keeps its value until
            the change applies. Schedule a change with `POST
            /v1/lines/{id}/variant` and `effective: "next_anchor"`; withdraw it
            with `DELETE /v1/lines/{id}/variant`. It becomes `null` when the
            change is applied, when it is withdrawn, when it can no longer apply
            at its date, when the line becomes a test line, and when the line
            leaves its owner without ending. A line that ends keeps its
            `pending_variant`, and the change never applies.
        created_at:
          type: string
          description: >-
            Timestamp at which iSnap created the line (ISO 8601, UTC). It is not
            the date you acquired the line.
        metadata:
          type: object
          nullable: true
          additionalProperties:
            nullable: true
          description: >-
            Your own key-value data on the line. `null` when the line has none.
            A line created with `"metadata": {}` reads `{}`: treat `null` and
            `{}` the same. It is part of the line object for every caller, `GET
            /v1/lines` and `GET /v1/lines/{id}` included, and a line that
            returns to the marketplace keeps it: never put personal data or
            secrets in it.
        test_line:
          type: boolean
          description: >-
            `true` for a test line: a line you set aside to host the trials of
            your own prospects. Its `billing_tier` is `shared_pool`.
        shared:
          type: boolean
          description: >-
            `true` for a shared line, which has no device and no phone number of
            its own: its `billing_tier` is `shared_imessage` and its
            `phone_number` is an empty string.
        fulfillment_status:
          type: string
          nullable: true
          description: >-
            Present only on `GET /v1/lines/my`. `open` while an order for this
            line is still being fulfilled, `null` otherwise.
      required:
        - id
        - phone_number
        - area_code
        - country_code
        - state
        - state_name
        - billing_tier
        - byod
        - inbound_only
        - status
        - monthly_price
        - stripe_subscription_id
        - activated_at
        - expires_at
        - anniversary_at
        - cancel_at
        - billing_anchor_at
        - next_anchor_at
        - pending_variant
        - created_at
        - metadata
        - test_line
        - shared
      description: >-
        A line. `billing_tier`, `byod`, `shared`, `test_line` and `inbound_only`
        tell the kinds of line apart: a rented line (two-way or inbound-only), a
        BYOD line, a shared line, a test 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.
  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.