> ## Documentation Index
> Fetch the complete documentation index at: https://inkbox.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Connections

> Connect and disconnect recipients by agent identity, with request-only access to organization-owned shared iMessage pools

Create a 1:1 connection and submit its opening message in one request. You choose
the agent identity and recipient. Inkbox chooses the sending number from your
organization's pool.

API base URL:

```text theme={null}
https://inkbox.ai/api/v1/imessage
```

Both endpoints accept an [admin API key](/docs/api-keys) or an authenticated
[Inkbox Console](https://inkbox.ai/console) session for your organization.
Identity-scoped API keys cannot connect or disconnect recipients.
The examples below use an admin API key.

## Request headers and retries

For API-key authentication:

| Header | Required | Description |
| :- | :- | :- |
| `X-API-Key` | Yes | Your organization's admin API key |
| `Content-Type` | Yes | `application/json` |
| `Idempotency-Key` | Yes | A client-generated key for this logical action, sent on the first attempt and every retry |

Generate a random UUID before the first request. Reuse that key and the same body
when retrying the same action, including after a timeout. A completed request
replays its original status and response without repeating the action. Unlike
the optional retry mode on message sends, these endpoints do not require a
`Prefer` header.

Use a new key for each new connect or disconnect action. Reconnecting after a
disconnect is a new action: replaying an old connect key returns the old result
and does not reactivate the connection. Keys are scoped to your organization,
agent identity, and operation: connect and disconnect have separate scopes.
After an action commits, reusing its key with different input returns `409` with
`error: "idempotency_key_reused"`. Admission rejections do not save a completed
result; fix the cause and retry. A timeout does not prove that an action failed
to commit, so retry an uncertain action with the original key and body.

## Connect a recipient

```text theme={null}
POST /connect
```

<Note>
  **Beta — available only by request to organizations with their own
  organization-owned shared iMessage pool.** Contact [support@inkbox.ai](mailto:support@inkbox.ai) to request
  access. Standard shared service requires the
  [recipient-initiated router flow](/docs/api/imessage/router). Organizations with a pool
  can also use a router; router-created connections can use their pool numbers.
</Note>

The identity must belong to your organization and have `imessage_enabled: true`.
Its contact rules must allow this recipient in both inbound and outbound directions.
Connecting does not change those rules.
For a new connection, Inkbox assigns an eligible pool number and accepts the
required opening message together. A rejected request creates neither a new
connection nor an opening message. There is no waiting list for requests that
cannot be admitted; your application handles retries.

### Request body

| Field | Type | Required | Description |
| :- | :- | :- | :- |
| `agent_identity_id` | UUID | Yes | The iMessage-enabled identity to connect |
| `recipient_number` | string | Yes | The recipient's E.164 phone number |
| `first_message` | string | Yes | Non-empty opening text; surrounding whitespace is trimmed |

There is no sending-number parameter. Unsupported request fields are rejected.

Before a qualifying recipient reply, use fewer than 160 characters of plain text
(159 maximum), without links, phone numbers, or media. A reply on another number
does not satisfy this requirement. Connect accepts text only, even when prior
engagement allows later messages to contain media.

```bash bash theme={null}
curl -X POST "https://inkbox.ai/api/v1/imessage/connect" \
  -H "X-API-Key: YOUR_ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: d19a6d91-1b25-4ab8-a260-6b82f2d6e500" \
  -d '{
    "agent_identity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "recipient_number": "+14155550100",
    "first_message": "Hi, this is my-agent. Ready to get started?"
  }'
```

Generate your own key rather than reusing the example value for multiple actions.

### Response

* **201:** A new connection was created and its opening message was accepted.
  This does **not** mean the message was delivered.
* **200:** The identity and recipient already have an active connection.
  `already_connected` is `true`, `message` is `null`, and no greeting is sent.
  Existing connections stay on their current number, including connections
  created through the standard shared service.

Example new-connection response (`201`, selected fields):

```json JSON theme={null}
{
  "assignment": {
    "id": "9b2e4a68-8cb1-4f18-b97d-a2324c8b4d1f",
    "remote_number": "+14155550100",
    "agent_identity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "organization_id": "org_2abc123def456",
    "status": "active",
    "released_at": null,
    "created_at": "2026-10-02T12:00:00Z",
    "updated_at": "2026-10-02T12:00:00Z"
  },
  "conversation_id": "e8bff3ea-6052-4adc-8529-23da91703c84",
  "message": {
    "id": "e73a593b-a2e5-4bfd-a1d8-22c2d36dd392",
    "status": "pending",
    "content": "Hi, this is my-agent. Ready to get started?"
  },
  "already_connected": false
}
```

Example already-connected response (`200`):

```json JSON theme={null}
{
  "assignment": {
    "id": "9b2e4a68-8cb1-4f18-b97d-a2324c8b4d1f",
    "remote_number": "+14155550100",
    "agent_identity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "organization_id": "org_2abc123def456",
    "status": "active",
    "released_at": null,
    "created_at": "2026-10-02T12:00:00Z",
    "updated_at": "2026-10-02T12:00:00Z"
  },
  "conversation_id": "e8bff3ea-6052-4adc-8529-23da91703c84",
  "message": null,
  "already_connected": true
}
```

An idempotency replay returns the original status and body, rather than checking
the connection's current state again.

| Field | Type | Description |
| :- | :- | :- |
| `assignment` | object | Connection metadata; uses the [connection object](/docs/api/imessage/conversations#connection-object) |
| `conversation_id` | UUID | The agent's conversation with this recipient; use it for later reads and sends |
| `message` | object \| null | The accepted [message object](/docs/api/imessage/messages#message-object), or `null` for an already-active connection |
| `already_connected` | boolean | Whether the request reused an active connection without another opening message |

Check [`GET /messages/{message_id}`](/docs/api/imessage/messages#get-message) for the
current delivery status, or subscribe to
[delivery-lifecycle webhooks](/docs/api/imessage/webhooks#delivery-lifecycle-events).

### Number selection and recipient replies

* Inkbox selects an available, eligible number from your organization’s pool.
  Do not depend on a particular number or selection order.
* One pool number can serve different recipients, but it cannot connect the same
  recipient to two agents simultaneously. A two-number pool therefore supports
  at most two simultaneous agent connections for each recipient.
* Without prior qualifying inbound on the chosen number, the opening message is
  the only message allowed on that connection until the recipient replies. Further sends return
  `409` with `error: "imessage_awaiting_inbound"`.
* A prior direct message to that number within your organization's current
  ownership can satisfy the reply requirement, even if it was sent to a different
  agent. Messaging another number or a group does not satisfy it. Tapbacks do not count
  as replies on pool numbers, even when you receive `imessage.reaction_received`.
* Each new connection can accept one opening message, up to **three total
  unanswered messages** for the same recipient and pool number before their first
  reply. Disconnecting and reconnecting can accept openings two and three; a
  fourth countable message is blocked. Changing agents does not reset the cumulative count.
  Pending messages and uncertain outcomes count. A terminal failure is excluded
  only when Inkbox can confirm the message was never sent; this does not refund quota usage.
* Replaying a request or connecting an already-active pair never sends another
  opening. Reconnects can use another eligible pool number; they are not pinned
  to the previous number. Configured quotas still apply.

Reusing a number preserves the recipient's existing iMessage thread with that
number. A reply arriving after reassignment routes to the newly connected agent.
Each agent's API conversation history remains separate.

### Limits and errors

Pool-number limits, including new-contact capacity and spacing, are configured
for your organization and enforced per number. They determine whether a number
is eligible. Account and identity
quotas also apply. Limits are not request parameters.

| Status | Meaning |
| :- | :- |
| 400 | The identity is not enabled for iMessage. |
| 401 | Authentication is missing or invalid. |
| 402 | The operation exceeds an account entitlement or allowance. |
| 403 | The caller is not authorized, or the request is rejected by policy. |
| 404 | The identity is not found or is not available to your organization. |
| 409 | Connection availability, recipient engagement, request contention, or an idempotency conflict prevents the operation; inspect `detail.error`. |
| 422 | A required header or body field is missing, input is invalid, or the message is rejected by messaging policy. |
| 429 | A configured messaging limit prevents the opening send. Follow the response's retry guidance. |

Common `409` errors:

| `detail.error` | What to do |
| :- | :- |
| `imessage_pool_unavailable` | Request access or check that your organization has an available organization-owned shared pool. New connections do not fall back to standard shared numbers. |
| `imessage_recipient_connections_full` | Every available pool number is already connected to this recipient. Disconnect an existing agent connection before connecting another. |
| `imessage_awaiting_inbound` | Available numbers require a recipient reply before another opening can be accepted. Waiting for a quota reset alone does not resolve this. |
| `imessage_identity_has_dedicated_line` | This identity has its own dedicated line; use the [message endpoint](/docs/api/imessage/messages#send-message). |
| `imessage_connection_busy` | Another connection operation is in progress. Honor `Retry-After` and retry the same action with its original key. |
| `imessage_connection_line_unavailable` | The selected connection could not be established. Check availability and retry the same logical action with its original key. |
| `idempotency_key_reused` | Restore the original committed request body, or generate a new key for a genuinely new action. |
| `idempotency_in_progress` | The original action is still running. Honor `Retry-After` and retry with the same key and body. |
| `result_unavailable` | The original result cannot be replayed. Inspect your current connections and messages before attempting a new action. |

Common `422` errors:

| `detail.error` | Meaning |
| :- | :- |
| `invalid_idempotency_key` | Supply a non-empty key of at most 255 printable ASCII characters. |
| `imessage_opening_message_too_long` | Before a reply, each opening must contain fewer than 160 characters. |
| `imessage_reply_required_for_links_or_media` | A recipient reply is required before links or media can be sent. |
| `imessage_reply_required_for_phone_numbers` | A recipient reply is required before phone numbers can be sent. |

A timed quota response includes `Retry-After` in seconds. Wait at least that long
before retrying with the same key and body. Some limits require another condition
to change and have no timed reset; do not assume every `429` includes
`Retry-After`.

## Disconnect a recipient

```text theme={null}
POST /disconnect
```

Disconnect the active connection for an identity and recipient. This operation
does not require the organization to have an active pool and can also release an
existing standard shared-service connection.

### Request body

| Field | Type | Required | Description |
| :- | :- | :- | :- |
| `agent_identity_id` | UUID | Yes | The identity to disconnect from this recipient |
| `recipient_number` | string | Yes | The recipient's E.164 phone number |

```bash bash theme={null}
curl -X POST "https://inkbox.ai/api/v1/imessage/disconnect" \
  -H "X-API-Key: YOUR_ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ecaa8f26-1c8a-4e58-aeac-fc7a5c064d22" \
  -d '{
    "agent_identity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "recipient_number": "+14155550100"
  }'
```

### Response (200)

```json JSON theme={null}
{
  "assignment_id": "9b2e4a68-8cb1-4f18-b97d-a2324c8b4d1f",
  "disconnected": true
}
```

If there is no active connection, this is a successful no-op:

```json JSON theme={null}
{
  "assignment_id": null,
  "disconnected": true
}
```

Disconnect stops routing new messages to that agent and cancels outgoing messages
whose sending has not started. Messages already being sent cannot be recalled.
Canceled messages remain in history with `status: "error"` and
`error_code: "imessage_assignment_inactive"`. For pool connections, their
`imessage.delivery_failed` event reports that cancellation. An uncertain delivery
outcome is not a confirmed failure. The conversation history remains readable,
and the recipient is not notified.

Use a fresh `Idempotency-Key` for a later reconnect. Neither disconnect nor a new
request key resets recipient engagement requirements or messaging limits.

Missing authentication returns `401`; an unauthorized caller receives `403`.
An unknown or inaccessible identity returns `404`. Invalid requests return `422`.
Conflicting idempotency input or concurrent connection changes return `409`;
honor `Retry-After` when present.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.