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

# Start a trial

> Starts a trial on a pool line and returns the pairing code that the prospect texts to that line.

* **Credential:** an API key. A trial key returns `401 unauthorized`.
* **Line-scoped keys:** the list of lines of a [line-scoped key](/authentication#line-scoped-keys) is not applied.
* **Test keys:** an `isnap_test_` account key starts a real trial: see [Test keys](/authentication#test-keys).
* **`Idempotency-Key`:** not honored. For a wholesale partner, a repeated call starts another trial. A direct account receives `409 trial_already_active`, a repeat of a call whose response was lost included. See [How a trial works](/guides/trials#how-a-trial-works).
* **Events:** none. `trial.linked` is sent later, when the prospect pairs: see [Events](/guides/trials#events).
* **Secret:** `token` is the pairing code. The response carries `Cache-Control: no-store`, whatever its status. See [How a trial works](/guides/trials#how-a-trial-works).
* **Limit:** a direct account has one live trial at a time: see [Limits on live trials](/guides/trials#limits-on-live-trials).

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | An unknown field, a value out of range, or a `metadata` over 20 keys or 8,192 bytes. |
| 409 | `trial_already_active` | A direct account already has a live trial. Revoke it first. If you lost its `trial_id` before the prospect paired, the limit frees when its pairing window ends, 15 minutes after the call. |
| 503 | `line_pool_exhausted` | No pool line is available. No trial is started: retry up to 2 times with exponential backoff, then write to [support@isnap.ai](mailto:support@isnap.ai) with the `trace_id` and `request_id` of the response. |
| 503 | `pairing_code_unavailable` | iSnap could not allocate a pairing code. No trial is started: call again. |

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


## OpenAPI

````yaml POST /v1/trial/init
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/trial/init:
    post:
      summary: Start a trial
      description: >-
        Starts a trial: a prospect exchanges messages between their own phone
        and an iSnap line, and you send and read the messages of the trial with
        a trial key. Accepts an API key. A line-scoped key is not limited to its
        list on this operation. Returns `token`, a 7-character pairing code: the
        prospect sends a message that contains it to `pool_line_number` within
        15 minutes (`pairing_expires_at`). The code is matched
        case-insensitively, as a word of its own. When it matches, the trial
        becomes `active`, iSnap sends `trial.linked`, and the prospect receives
        one confirmation message. Starting a trial sends no event.


        The trial key is not in this response: read it once with `GET
        /v1/trial/status` after pairing. It sends only from the line of the
        trial and only to the handle of the prospect, and every message it sends
        ends with your `footer`, or with the footer of iSnap. A trial that is
        `active` or `dormant` lasts until it is revoked.


        Every field of the body is optional, and a request without a body is
        valid; a key that is not listed returns `400 validation_failed`. An
        account that is not a wholesale partner can have one trial at a time
        that is `active`, `dormant`, or `pairing_pending` with its pairing
        window still open: another call returns `409 trial_already_active`. A
        `pairing_pending` trial whose pairing window ended does not count. A
        wholesale partner can start any number of trials. `Idempotency-Key` is
        ignored: a repeated call starts another trial for a wholesale partner.
        Returns `503 line_pool_exhausted` when no line is available for a trial,
        and `503 pairing_code_unavailable` when iSnap could not draw a new
        pairing code: repeat the call.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TrialInitBody'
      responses:
        '201':
          description: The trial, waiting for the prospect to send the pairing code.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrialInitEnvelope'
        '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'
        '409':
          description: >-
            Conflict. `error.code` is `trial_already_active`: the account is not
            a wholesale partner and already has a trial that is `active`,
            `dormant`, or `pairing_pending` with its pairing window still open.
            A `pairing_pending` trial whose pairing window ended does not count.
          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:
    TrialInitBody:
      type: object
      properties:
        max_outbound_without_inbound:
          type: integer
          minimum: 1
          maximum: 100
          description: >-
            Number of messages the trial key can send in a row without a reply,
            a reaction or a read receipt from the prospect. When the trial runs
            on an iMessage line, a read does not count: only a reply or a
            reaction does. From 1 to 100. Default: 10. The send that reaches
            this number succeeds and makes the trial `dormant`.
          example: 10
        welcome_message:
          type: string
          minLength: 1
          maxLength: 280
          description: >-
            Text of the confirmation message that the prospect receives once,
            when pairing succeeds. From 1 to 280 characters. Default: a
            confirmation text written by iSnap. The message is not retried when
            its delivery fails, and it does not count as a send of the trial.
          example: You are connected to Acme. Reply here at any time.
        footer:
          type: string
          minLength: 1
          maxLength: 300
          description: >-
            Text appended, after a blank line, to every message the trial key
            sends, in place of the footer of iSnap. From 1 to 300 characters.
            Default: `Sent for free with iSnap - isnap.ai`. A footer cannot be
            turned off. Characters outside GSM-7 make each SMS segment shorter.
          example: Sent free via Acme
        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
      description: Options of a trial. Every field is optional.
    TrialInitEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
          description: Always `true` on a success response.
        data:
          $ref: '#/components/schemas/TrialInitData'
        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
    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.
    TrialInitData:
      type: object
      properties:
        trial_id:
          type: string
          description: >-
            Id of the trial: `trl_` followed by 25 letters or digits. Keep it:
            no operation lists trials.
          example: trl_4Lp8Rn2xY7wQ9mK3vB5nH8dF2
        token:
          type: string
          description: >-
            Pairing code: 7 characters from `23456789ABCDEFGHJKMNPQRSTVWXYZ`.
            The prospect sends it in a message to `pool_line_number`. It is also
            the `token` parameter of `GET /v1/trial/status`. No two trials share
            a code.
          example: K7M2XQ9
        pool_line_number:
          type: string
          description: >-
            Phone number of the line the trial runs on: the prospect sends the
            pairing code to this number.
        pairing_expires_at:
          type: string
          description: 'End of the pairing window: 15 minutes after the call.'
        instructions_url:
          type: string
          description: >-
            URL of an iSnap page that explains the pairing to the prospect. The
            same page for every trial.
        metadata:
          type: object
          nullable: true
          additionalProperties:
            nullable: true
          description: >-
            The metadata sent in the request. `null` when the request carried
            none.
      required:
        - trial_id
        - token
        - pool_line_number
        - pairing_expires_at
        - instructions_url
        - metadata
      description: A trial that was just started.
  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.