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

# Conversations

> How a conversation is identified and why the 24h window decides what you can send.

A conversation is the WhatsApp thread between your business and a contact.
Almost every send and action endpoint (`/conversation/*`) takes it in the
request body under the `conversation` object.

## Identifying a conversation

The `conversation` object accepts **one** of four identifiers, plus `channel`
(**required**), the target channel's public key. The resolver
evaluates them in this order: `uuid` → `whatsapp_user_id` → `username` →
`phone`: `whatsapp_user_id` looks up by exact match and `username` by a
case-insensitive match (case is ignored); `phone` is normalized to digits.
When the contact has a BSUID, `phone` and
`whatsapp_user_id` identify different values (see Note).

<ParamField body="uuid" type="string">
  Stable key of a conversation you already know (for example, persisted from a
  previous integration). The most direct path, though it can become stale if two
  conversations of the same contact are merged — see the merge note below.
</ParamField>

<ParamField body="whatsapp_user_id" type="string">
  The contact's BSUID (Meta's opaque identity). Resolves by exact match against
  the stored BSUID; if it does not resolve, the endpoint returns `404` without
  falling back to `phone`. Recommended identifier going forward.
</ParamField>

<ParamField body="username" type="string">
  The contact's public WhatsApp username, with or without a leading `@` and
  case-insensitive. Best-effort resolution against the last username observed by
  the platform (users can change their handle); if several contacts share it,
  the endpoint returns `409` (`USERNAME_AMBIGUOUS`).
</ParamField>

<ParamField body="phone" type="string">
  Number in E.164 format (`+5215512345678`). Normalized to digits before the
  lookup. Useful when all you have is the phone.
</ParamField>

<ParamField body="channel" type="string" required>
  The channel's public key — copy it from the dashboard under **Settings → Channels**
  (the **Channel key** field), or list them with `GET /channels`. **Required** in every
  request: it sets the target channel. It is case-insensitive (normalized
  server-side). It does not identify on its own. On the contact-based paths
  (`phone`, `username`, `whatsapp_user_id`) it restricts resolution to the given
  channel; with `uuid` the engine ignores it, but the contract still requires it.
  It disambiguates the **channel**, not the **contact**: if several contacts
  share a `phone` or `username`, that ambiguity is resolved with
  `whatsapp_user_id` or `uuid`. On the contact-based paths, if you send a key
  that does not exist, the API returns `404` (`CHANNEL_NOT_FOUND`).
</ParamField>

```jsonc theme={null}
{ "conversation": { "phone": "+52 55 1234 5678", "channel": "ch_7A9K2M4Q" } }
// equivalent: { "conversation": { "uuid": "7b8a1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d", "channel": "ch_7A9K2M4Q" } }
```

<Warning>
  The phone number can be **duplicated** across contacts (recycled numbers or
  separate identities sharing a number): in that case the API returns `409`
  (`PHONE_NUMBER_AMBIGUOUS`) instead of guessing — identify the conversation by
  `whatsapp_user_id` or `uuid`. Also, `whatsapp_user_id` can **change** if the
  user re-registers their WhatsApp account (identity rotation on Meta's side).
  Use the `uuid` as the stable key in your system and `whatsapp_user_id` as the
  preferred resolution identifier.
</Warning>

<Note>
  `phone` and `whatsapp_user_id` are **different** identifiers: `phone` is the
  phone number (wa\_id without `+`) and `whatsapp_user_id` is the contact's BSUID.
  They coincide only while the contact has no BSUID. In responses,
  `whatsapp_user_id` returns the real BSUID when present, with a fallback to the
  phone so there is always a resolvable value. Watch the round-trip: if the contact
  has no BSUID yet, this field carries the phone; re-send it as `phone` then, not as
  `whatsapp_user_id` — the resolver matches `whatsapp_user_id` by exact match and
  would return `404`.
</Note>

<Note>
  When the same contact ended up recorded twice by identity (for example, first only
  with their phone and later with their BSUID), the two conversations are
  **merged automatically** into one: the full history moves to the survivor. The
  absorbed conversation's `uuid` stops resolving and returns `404`; the phone and the
  BSUID migrate to the survivor. If a `uuid` you stored starts returning `404`,
  re-resolve the conversation by `phone` or `whatsapp_user_id`.
</Note>

<Warning>
  `channel` is **required in every request**: you always set the target channel
  with its public key (list them with `GET /channels`). This way, if a contact has
  conversations across **more than one channel** (multiple WhatsApp numbers),
  resolution by `phone`, `username`, or `whatsapp_user_id` already knows which one
  you mean. A key that does not exist returns `404` (`CHANNEL_NOT_FOUND`).
</Warning>

## Reading a conversation

You address a conversation with one of the references above: the public API does
not offer a listing to enumerate them. Those references come from your own
inbound —the `phone` or `whatsapp_user_id` of the contact who messaged you— or
from a `uuid` you have persisted.

With that reference, `GET /conversation` returns its state (`conversation_info`
and `contact_info`) and `GET /conversation/messages` its history. The full shape
and parameters are in [Conversation and thread](/en/action-groups/query-conversation).

## The 24h window

This is WhatsApp's core concept and it decides what you can send.

<Steps>
  <Step title="The contact messages you">
    WhatsApp opens a **24-hour window**. Each new message from the contact
    resets the timer.
  </Step>

  <Step title="Window open">
    You can send free-form messages: text, media, and quick replies.
  </Step>

  <Step title="24h pass with no reply">
    The window closes. The only allowed message is a **template** approved by
    Meta.
  </Step>

  <Step title="Sending a template reopens the window">
    You can send free-form messages again.
  </Step>
</Steps>

<Warning>
  Sending text, media, or a quick reply with the window closed fails with
  `WINDOW_CLOSED` (422). Send a template first to reopen it.
</Warning>

## Checking the window status

Before sending a free-form message, you can verify whether the window is open
with `POST /conversation/status`:

```bash theme={null}
curl -X POST "https://app.1to1.ai/api/v1/public/{slug}/conversation/status" \
  -H "Authorization: Bearer sk_1to1_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "conversation": { "phone": "+5215512345678", "channel": "ch_7A9K2M4Q" } }'
```

Response:

```jsonc theme={null}
{
  "conversation": {
    "uuid": "7b8a2c1d-3e4f-…",
    "phone": "5215512345678",
    "whatsapp_user_id": "5215512345678"
  },
  "window": {
    "status": "open",
    "expires_at": "2026-07-21T18:30:00Z",
    "hours_remaining": 18.5
  },
  "assignment": { "type": "ai_employee", "ai_employee": { "uuid": "1a2b…", "name": "Aura" } },
  "mailbox": { "uuid": "5c6d…", "name": "Sales" },
  "tags": [{ "uuid": "9e0f…", "name": "VIP", "color": "#22c55e" }],
  "inbox_status": "pending",
  "last_message": null,
  "is_tester": false,
  "tester_virtual_now": null
}
```

The response includes `conversation.uuid` (the conversation's stable key),
`window.status` (`open` or `closed`), `window.expires_at` (ISO timestamp of the
window's close; `null` if the contact never messaged), and `window.hours_remaining`,
plus the assignment, mailbox, tags, and `inbox_status` of the conversation. Use it
to decide between a free-form message and a template.

## Reading the thread

```http theme={null}
GET /conversation/messages?phone=+5215512345678&limit=50
```

Returns the conversation's messages and events. It is identified with the same
reference as the rest of the API, so **you do not need to know any uuid**.

<Note>
  The thread paginates **backwards in time**: the first call returns the most
  recent items and `next_cursor` moves towards the oldest ones.
</Note>

Every item answers four questions: who spoke (`direction` and `author`), what
happened (`event`), with what content (`content_type` and `body`), and when
(`sent_at`). Read in order, they look like the actual conversation:

```
--- July 2, 2026 ---
contact   message_received  text   "Hi, is the premium package available?"
business  message_sent      text   "Good morning! Yes, the premium package is available."
  └ AI employee: Night Sales
business  context_note             "Customer asked for a formal quote by email."
  └ CRM integration (via API)
```

And this is how it travels in the JSON:

```json theme={null}
{
  "items": [
    {
      "type": "message",
      "direction": "contact",
      "event": "message_received",
      "content_type": "text",
      "body": "Hi, is the premium package available?",
      "author": { "type": "customer", "name": "Ana Pérez" },
      "status": "delivered",
      "sent_at": "2026-07-02T14:39:00.000Z"
    },
    {
      "type": "message",
      "direction": "business",
      "event": "message_sent",
      "content_type": "text",
      "body": "Good morning! Yes, the premium package is available.",
      "author": { "type": "ai_employee", "name": "Night Sales" },
      "status": "read",
      "sent_at": "2026-07-02T14:39:12.000Z"
    }
  ],
  "next_cursor": "MjAyNi0wNy0wMlQxNDozOTowMC4wMDBafDkxODIz",
  "has_more": true
}
```

**`type`** tells the two kinds of item apart: `message` is content that
travelled over WhatsApp (it carries `content_type` and `status`), and `event` is
something that happened in the history — a context note, an updated contact
field, a conversion — with no content sent.

Items within each page are in chronological order, and `sent_at` is the key that
orders the full thread as you page backwards.

## Next steps

<CardGroup cols={2}>
  <Card title="Send messages" icon="paper-plane" href="/en/messages">
    Text, media, quick replies, and templates.
  </Card>

  <Card title="Templates" icon="newspaper" href="/en/templates">
    How to send templates when the 24h window is closed.
  </Card>
</CardGroup>
