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

# Activate a reserved line

> Turns a line you reserved into an active line.

* **Credential:** an API key. See [Activation](/guides/getting-a-line#activation).
* **Account:** wholesale partners only: any other account receives `402 checkout_required`. A direct account buys a line in the iSnap dashboard: see [Accounts](/concepts#accounts).
* **Line-scoped keys:** a [line-scoped key](/authentication#line-scoped-keys) gets `403 forbidden` unless the line is in its list.
* **Test keys:** an activation sent with an `isnap_test_` account key activates a real line, billed like any other: see [Test keys](/authentication#test-keys).
* **`Idempotency-Key`:** not honored. After a lost response, read [`GET /v1/lines/my`](/api-reference/lines/list-my-lines) before you call again: see [Retries](/guides/getting-a-line#retries).
* **Events:** none. See [Events](/guides/getting-a-line#events).

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | The body is invalid: for example, `billing_anchor_at` is not an ISO 8601 timestamp with a `Z` suffix. |
| 400 | `billing_anchor_too_far` | `billing_anchor_at` is more than 31 days after the activation. |
| 400 | `inbound_only_not_rental` | `inbound_only` is `true` and the line is not a rented line. |
| 400 | A code that starts with `line_` | The reserved line can no longer be acquired. The codes are those of [`reserve`](/api-reference/lines/reserve-line), with status `400` here. Release the line and choose another one. |
| 402 | `checkout_required` | Your account is not a wholesale partner. |
| 403 | `forbidden` | The request uses a line-scoped key, and the line is not in its list. |
| 409 | `line_not_reserved` | The line is not reserved by your account within the 15 minutes: the reservation expired or was released, the line was never reserved, or no line has this id. |
| 409 | `line_already_subscribed`, `subscription_cancel_failed` | The line carries a per-line subscription that could not be closed. |

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


## OpenAPI

````yaml POST /v1/lines/{id}/activate
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}/activate:
    post:
      summary: Activate a reserved line
      description: >-
        Turns a line you reserved into an `active` line, within the 15 minutes
        of the reservation. Accepts an API key. Wholesale partners only: any
        other account gets `402 checkout_required`, and no operation that an API
        key can call activates its reserved line.


        The activated line has no per-line subscription and is billed on the
        monthly wholesale invoice; the response carries `expires_at: null` and
        `anniversary_at`. The body is optional. `inbound_only: true` activates a
        rented line as an inbound-only line. `billing_anchor_at` states the
        billing anchor of the line. The `metadata` of the line is left 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
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActivateLine'
      responses:
        '200':
          description: The activated line.
          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. `validation_failed`: the path or the body does not
            match its schema. `billing_anchor_too_far`: `billing_anchor_at` is
            more than 31 days after the request. `inbound_only_not_rental`:
            `inbound_only` is `true` and the line is not a rented line. A code
            that starts with `line_`: the reserved line can no longer be
            acquired; choose another line.
          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'
        '402':
          description: >-
            Payment required. `checkout_required`: the account is not a
            wholesale partner. The line is not activated.
          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 returned by this operation: a line that does not exist returns
            `409 line_not_reserved`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            Conflict. `line_not_reserved`: the line is not reserved by your
            account, or its reservation has expired. `line_already_subscribed`
            or `subscription_cancel_failed`: the line still has a per-line
            subscription that could not be closed.
          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:
    ActivateLine:
      type: object
      properties:
        inbound_only:
          type: boolean
          description: >-
            `true` to activate the line as an inbound-only line. Rented lines
            only (`400 inbound_only_not_rental` otherwise). Defaults to `false`:
            a two-way line.
        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'
      description: >-
        Optional settings of the activated line: its variant and its billing
        anchor.
    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.