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

# Rent a hosted macOS session

> Rents one hosted macOS session from iSnap's stock for a wholesale partner.

* **Credential:** an API key.
* **Account:** wholesale partners only: any other account receives `403 not_a_wholesale_partner`. A direct account buys sessions in the iSnap dashboard. See [Who can rent through the API](/guides/hosted-macos-sessions#who-can-rent-through-the-api).
* **Line-scoped keys:** the list of lines of a [line-scoped key](/authentication#line-scoped-keys) is not applied.
* **Test keys:** a rent sent with an `isnap_test_` account key rents a billable session: see [Test keys](/authentication#test-keys).
* **`Idempotency-Key`:** honored, and remembered for 24 hours. Send it with every rent. Without a key, every call rents one more billable session. See [Renting a session](/guides/hosted-macos-sessions#renting-a-session) and [Lines and hosted macOS sessions](/idempotency#lines-and-hosted-macos-sessions).
* **Events:** none. `macos_session.rented` is sent only when iSnap attaches a session to your account on its own: see [Events](/guides/hosted-macos-sessions#events).
* **No list:** no operation lists sessions. Store the `id` of the response.

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | An `Idempotency-Key` longer than 255 characters, a field other than `metadata`, or a `metadata` over 20 keys or 8,192 bytes. |
| 403 | `not_a_wholesale_partner` | The account is not a wholesale partner. |
| 409 | `no_macos_session_in_stock` | No session is free. Nothing is rented and nothing is recorded: see [When no session is in stock](/guides/hosted-macos-sessions#when-no-session-is-in-stock). |
| 409 | `idempotency_key_collision` | The key was already used with another body or on another operation. |
| 503 | `idempotency_in_flight` | The first request with this key is still running. Retry with the same key. |

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


## OpenAPI

````yaml POST /v1/macos-sessions/rent
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/macos-sessions/rent:
    post:
      summary: Rent a hosted macOS session
      description: >-
        Rents one hosted macOS session from the stock of iSnap. Wholesale
        partners only: any other account is refused with `403
        not_a_wholesale_partner`, and no other operation that an API key can
        call rents a session. Accepts an API key. A line-scoped key is not
        limited to its list on this operation. A hosted macOS session is a macOS
        user account on a Mac that iSnap hosts, signed in to no Apple ID when
        you receive it. It is not a line: it has no number and sends nothing. It
        runs the bridge app of a BYOD line. The rental starts immediately and
        `anniversary_at` is set to the time of the call: the session is billed
        every month on that day of the month, at that time of day, in UTC; in a
        shorter month, on its last day.


        Returns `409 no_macos_session_in_stock` when no session is free. Nothing
        is recorded in that case; iSnap can assign a session to your account
        later, which `macos_session.rented` announces, and that assignment is
        not linked to the refused call. Send an `Idempotency-Key` header:
        without one, every call rents another billable session, a retry
        included.


        You can rent any number of sessions; on the monthly invoice, the
        sessions beyond your number of BYOD lines are also billed as BYOD lines.
        The body is optional and accepts `metadata` only. The rent call itself
        sends no event: `macos_session.rented` is sent only when iSnap assigns a
        session without a rent call. No operation lists sessions: keep the `id`
        from this response or from the event.
      parameters:
        - schema:
            type: string
            maxLength: 255
            description: >-
              Optional key that makes a retry safe. A request repeated with the
              same key, by the same API key, with the same method, path and
              body, within 24 hours, returns the first successful response
              unchanged, its `trace_id` and `request_id` included, and does not
              act again. The body is compared byte for byte: a request with no
              body and a request with `{}` are two different requests. The same
              key with another method, path or body returns `409
              idempotency_key_collision`. While the first request is still
              running, a retry returns `503 idempotency_in_flight`; a request
              that never completes frees its key after 60 seconds. Only a `2xx`
              response is recorded: after a failed request, the retry runs
              again. At most 255 characters: a longer key returns `400
              validation_failed`. The `X-Idempotency-Key` header is read when
              `Idempotency-Key` is absent.
          required: false
          description: >-
            Optional key that makes a retry safe. A request repeated with the
            same key, by the same API key, with the same method, path and body,
            within 24 hours, returns the first successful response unchanged,
            its `trace_id` and `request_id` included, and does not act again.
            The body is compared byte for byte: a request with no body and a
            request with `{}` are two different requests. The same key with
            another method, path or body returns `409
            idempotency_key_collision`. While the first request is still
            running, a retry returns `503 idempotency_in_flight`; a request that
            never completes frees its key after 60 seconds. Only a `2xx`
            response is recorded: after a failed request, the retry runs again.
            At most 255 characters: a longer key returns `400
            validation_failed`. The `X-Idempotency-Key` header is read when
            `Idempotency-Key` is absent.
          name: idempotency-key
          in: header
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RentMacosSession'
      responses:
        '201':
          description: The hosted macOS session, rented from now.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MacosSessionEnvelope'
        '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. `error.code` is `not_a_wholesale_partner` when the account
            is not a wholesale partner. It is `forbidden` when the request
            carries neither an API key in its `Authorization` header nor the
            header `X-Requested-With: XMLHttpRequest`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: >-
            Conflict. `error.code` is `no_macos_session_in_stock` when no
            session is free. It is `idempotency_key_collision` when the
            `Idempotency-Key` was already used with another body or on another
            operation.
          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: >-
            Unavailable. `error.code` is `idempotency_in_flight` when a request
            with the same `Idempotency-Key` is still running: retry the same
            request once it has completed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - bearerAuth: []
components:
  schemas:
    RentMacosSession:
      type: object
      properties:
        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: Metadata for the rental. The body is optional.
    MacosSessionEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
          description: Always `true` on a success response.
        data:
          $ref: '#/components/schemas/MacosSession'
        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.
    MacosSession:
      type: object
      properties:
        id:
          type: string
          pattern: ^mcs_[0-9A-Za-z]{25}$
          description: >-
            Id of the hosted macOS session: `mcs_` followed by 25 letters or
            digits. Keep it: no operation lists sessions.
          example: mcs_7Kd2Nq8Rb4Xw1Ty6Ue9Ao3Ph5
        rented:
          type: boolean
          description: >-
            `true` on every session you can read: once a session is released,
            every operation on it returns `404`.
        anniversary_at:
          type: string
          nullable: true
          format: date-time
          description: >-
            Start of the rental. The session is billed every month on the day of
            the month of this timestamp, in UTC, at the same time of day; in a
            shorter month, on its last day. Never `null` on a session you can
            read.
          example: '2026-09-07T14:32:00.000Z'
        cancel_at:
          type: string
          nullable: true
          format: date-time
          description: >-
            End of the rental when a cancellation is scheduled: the next
            occurrence of `anniversary_at` strictly after the cancel call. The
            session works and is billed until then. `null` when no cancellation
            is scheduled.
          example: '2026-10-07T14:32:00.000Z'
        status:
          type: string
          enum:
            - created
            - pending_provisioning
          description: >-
            `created`: the Mac that hosts the session is delivered.
            `pending_provisioning`: iSnap marked that Mac as not delivered. A
            session is rented out on a delivered Mac only, so
            `pending_provisioning` appears only when the mark changes after the
            rental started.
        created_at:
          type: string
          format: date-time
          description: >-
            Timestamp at which iSnap created the session. It can be earlier than
            the start of your rental, which is `anniversary_at`.
        metadata:
          type: object
          nullable: true
          additionalProperties:
            nullable: true
          description: >-
            Your metadata on the rental, set with `POST /v1/macos-sessions/rent`
            or `PATCH /v1/macos-sessions/{id}`, and sent as `data.metadata` on
            the `macos_session.*` events. `null` when the rental has none.
            Cleared when the session is released.
          example:
            crm_account_id: acc_123
      required:
        - id
        - rented
        - anniversary_at
        - cancel_at
        - status
        - created_at
        - metadata
      description: A hosted macOS session that the account rents.
  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.