# Inboxes

> Manage inboxes (channels) and their configuration.

Source: https://tickos.dev/docs/inboxes

Inboxes represent channels through which tickets arrive. Each inbox has its own ticket numbering prefix, auto-response settings, custom metadata fields and optional webhooks. The channel_type decides how tickets come in:

| channel_type | How tickets arrive | Notes |
|---|---|---|
| email | Inbound email to the inbox address | Two-way: replies are sent from your verified domain. Supports signature and reply delimiter. |
| form | A public form: hosted page, embed or your own HTML form | Gets a form_slug and form_config. See Public forms below. |
| api | POST /v1/tickets with this inbox_id | For your own integrations. Auto-responses are sent from the inbox address if one is set. |

## GET /v1/inboxes -- List inboxes

Permission: read. Returns the non-deleted inboxes of the account, newest first. Paginated with page and limit (note: this endpoint uses page number, not offset). With a read-only key, webhook secrets are masked (whsec_********); write and admin keys see them.

| Query param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number, starting at 1. |
| limit | number | 50 | Max 100. |

200 OK:

```json
{
  "data": [ { "id": "inb_xxxxxxxx", "name": "Support", "channel_type": "email", ... } ],
  "pagination": { "page": 1, "limit": 50, "total": 3, "pages": 1 }
}
```

> **display_name vs name:** In request bodies the inbox name is sent as display_name. In every response it comes back as name.

## POST /v1/inboxes -- Create an inbox

Permission: write. Returns 201 with { inbox } (the full inbox object, name holds the display_name you sent). Form inboxes get a unique form_slug generated from display_name (support, support-2...) and start with the form turned on.

| Field | Type | Required | Description |
|---|---|---|---|
| display_name | string | Yes | Inbox display name. |
| channel_type | string | Yes | email, form, or api. |
| prefix | string | Yes | Ticket ID prefix. 2-5 uppercase alphanumeric characters (e.g. SUP). |
| number_length | number | Yes | Ticket number digit length: 4-8 (e.g. 6 = SUP-000001). |
| connection_id | string | No | Domain connection ID. Must belong to your account and be active, else 404. |
| email_address | string | No | Inbox email. If omitted and the connection has a domain, it is generated as {prefix in lowercase}@{connection domain} (e.g. sup@acme.com). Required for the email channel type when it cannot be generated. |
| auto_response_text | string | No | Auto-reply text sent when a ticket is created. |
| auto_response_subject | string | No | Auto-reply subject. Default: "Ticket Created - @idTicket". |
| signature_html | string | No | HTML signature appended to outbound emails. |
| webhooks | array | No | Webhook endpoints (available on all plans). HMAC secrets generated automatically. |
| language | string | No | Inbox language. Default: english. |

| Status | When |
|---|---|
| 400 | Missing display_name, channel_type, prefix or number_length; invalid channel_type, prefix, number_length or email format; email channel without an email address. |
| 404 | connection_id not found in your account or inactive. |
| 409 | email_address is already used by another inbox on the platform (including the auto-generated one). |
| 429 | The plan inbox limit is reached. This is a plan quota, not a rate limit: retrying will not help until you upgrade or delete inboxes. |

prefix must also be unique among the inboxes of your workspace. Create does not pre-check it: a duplicate prefix currently fails with 500 "Failed to create inbox".

```bash
curl -X POST https://api.tickos.dev/v1/inboxes \
  -H 'Authorization: Bearer sk_your_api_key' \
  -H 'Content-Type: application/json' \
  -d '{
    "display_name": "Support",
    "channel_type": "email",
    "prefix": "SUP",
    "number_length": 6,
    "connection_id": "con_xxxxxxxx"
  }'
```

## GET /v1/inboxes/{id} -- Get inbox details

Permission: read. Returns { inbox } with its domain connection (id, display_name, domain, verification_status, verification_type) and tickets_count. Form inboxes also include form_urls: page_url, embed_url, embed_script_url and submit_url (null until the inbox has a form_slug). Deleted inboxes return 404. With a read-only key, webhook secrets are masked (whsec_********).

## PATCH /v1/inboxes/{id} -- Update an inbox

Permission: write. Accepts the creation fields except channel_type (the channel cannot be changed; channel_type is silently ignored), plus: reply_delimiter, email_from_response, forward_config ({ enabled, emails[] } max 10), status, is_active, and for form inboxes form_slug and form_config. Send only the fields you want to change. Returns { inbox } and fires the inbox.updated webhook.

form_config is a partial update: send only the keys you want to change. form_slug must be lowercase letters, numbers and hyphens (max 60) and unique in your workspace.

| Status | When |
|---|---|
| 400 | No updatable fields; invalid prefix, number_length, email or forward_config; form_slug or form_config on a non-form inbox; invalid form_slug; form_slug already used by another inbox in your workspace. |
| 404 | Inbox not found, or connection_id not in your account. |
| 409 | prefix already used by another inbox in your workspace, or email_address already used by another inbox on the platform. |

Turn on a form and ask for an order number:

```bash
curl -X PATCH https://api.tickos.dev/v1/inboxes/inb_xxxxxxxx \
  -H 'Authorization: Bearer sk_your_api_key' \
  -H 'Content-Type: application/json' \
  -d '{
    "form_slug": "support",
    "form_config": { "enabled": true, "metadata_fields": ["order_number"] }
  }'
```

## DELETE /v1/inboxes/{id} -- Delete an inbox

Permission: admin. Soft-deletes the inbox and returns { "deleted": true, "id": "..." }. Fires the inbox.deleted webhook. Cannot delete if it has active tickets (open, pending, or review status): returns 409 in that case.

## Public forms (form inboxes)

Every form inbox publishes a contact form. Name, email and message are always required; phone, subject and the inbox metadata fields you pick are optional. Each submission creates a ticket in the inbox (customer source "form"), with the custom fields in ticket.metadata plus source, form_slug and page_url.

| form_config key | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | false | When false, the form page, embeds and submit URL return 404. |
| title | string | inbox name | Form heading. |
| description | string | null | Text under the heading. |
| subject | string | optional | hidden, optional or required. Hidden subjects become "New message from {name}". |
| phone | string | hidden | hidden, optional or required. Saved on the contact if it has no phone yet. |
| metadata_fields | string[] | [] | Names of the inbox metadata fields to show, in order. |
| submit_label | string | null | Button text. Empty uses the help center language default. |
| success_message | string | null | Message after sending. Empty uses the language default. |
| redirect_url | string | null | http(s) URL to send people to after a successful submission. |
| theme | string | auto | auto (follows the help center), light or dark. Applies to embeds. |
| consent | object | off | { enabled, text, links: [{ label, url }] } (max 3 links). Adds a required "I agree to the Terms and Privacy Policy" checkbox; the acceptance time is saved as ticket.metadata.consent_accepted_at. |

| URL | Description |
|---|---|
| https://{slug}.tickos.dev/f/{form_slug} | Hosted form page (also on your custom help center domain). |
| https://{slug}.tickos.dev/embed/form/{form_slug} | Embed URL for data-tickos-form, data-tickos-form-popup, data-tickos-form-launcher or an iframe. |
| https://{slug}.tickos.dev/embed.js | Embed script. Fires a tickos:submitted event on your page. |
| https://{slug}.tickos.dev/api/portal/{slug}/forms/{form_slug} | Submit URL. No API key: it is public. |

### POST {submit_url} -- Submit a form

Send JSON to get a JSON response, or post a regular HTML form (urlencoded or multipart) to be redirected to redirect_url or the hosted confirmation page. Field names are name, email, message, phone, subject, the metadata field names and consent (true, when the form asks for it). Leave the honeypot field _tickos_hp empty. Limited to 5 submissions per minute per IP.

JSON submission:

```bash
curl -X POST https://acme.tickos.dev/api/portal/acme/forms/support \
  -H 'Content-Type: application/json' \
  -d '{"name": "Jane Doe", "email": "jane@example.com", "message": "My order has not arrived", "order_number": 48213}'
```

201 Created:

```json
{
  "ticket": { "number": "WEB-000143" }
}
```

400 Validation error:

```json
{
  "error": "Please check the highlighted fields",
  "code": "validation_error",
  "fields": { "email": "Enter a valid email address" }
}
```

Your own HTML form:

```html
<form action="https://acme.tickos.dev/api/portal/acme/forms/support" method="POST">
  <input name="name" placeholder="Name" required>
  <input type="email" name="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <input name="_tickos_hp" style="display:none" tabindex="-1" autocomplete="off">
  <button type="submit">Send message</button>
</form>
```

> **Tip:** GET on the submit URL returns the public form definition (title, fields and texts) if you want to render your own UI.
