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

# Quickstart

> The full conversation loop in six requests

All requests carry your [service-account token](/documentation/s2s/authentication) and ideally a pinned `Gleap-Version`. Send an `Idempotency-Key` (any unique string, e.g. a UUID) on POSTs you might retry.

## 1. Upsert the contact

Contacts are addressed by **your** user identifier and deduplicate with SDK-created contacts on the same id.

```bash theme={null}
curl -X PUT https://api.gleap.io/v3/s2s/contacts/AZ-4711 \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "name": "Aynur M.", "email": "aynur@example.az", "language": "az",
        "customData": { "tier": "gold" } }'
```

The response includes `unread` — the contact's total unread conversation count for your badge.

## 2. Open a conversation

The first message rides along. `workflowId` starts a workflow exactly like the SDK's `startConversation`; `agentId` engages an AI agent directly (the two are mutually exclusive). `attributes` values land as ticket attributes — define an attribute in **Settings → Data attributes** with shared visibility **disabled** and it is visible to your agents but never to the customer (the AI-handover-summary pattern):

```bash theme={null}
curl -X POST https://api.gleap.io/v3/s2s/contacts/AZ-4711/conversations \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9f1c2e4a-4b0e-4c1a-9d5e-000000000001" \
  -d '{
    "title": "Card blocked after travel",
    "message": "My card was blocked while I was abroad.",
    "workflowId": "<workflow id from the dashboard>",
    "attributes": { "aiSummary": "Customer travelled, card auto-blocked. AI could not lift the block." },
    "source": "your-app"
  }'
```

```json theme={null}
{ "id": "cnv_…", "number": 1042, "status": "OPEN", "closed": false,
  "workflowActive": true, "contact": { "userId": "AZ-4711" }, "…": "…" }
```

<Note>
  A workflow's first step lands asynchronously (\~1 second). React to the `workflow.step.presented` event, or fetch `GET /v3/s2s/conversations/{id}/workflow`.
</Note>

## 3. Send and read messages

```bash theme={null}
curl -X POST https://api.gleap.io/v3/s2s/conversations/cnv_…/messages \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "text": "I still cannot pay with the card." }'
```

Read oldest → newest, cursor-paginated (max 100 per page). Pass `after` = the last message id you already have:

```bash theme={null}
curl "https://api.gleap.io/v3/s2s/conversations/cnv_…/messages?after=msg_…&limit=100" \
  -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{ "items": [ { "id": "msg_…", "author": { "type": "agent", "name": "Aynur" },
               "text": "We have lifted the block.", "createdAt": "…" } ],
  "has_more": false, "next_after": null }
```

`author.type` is `contact`, `agent`, `bot` or `system`. Internal team notes never appear on this surface.

## 4. Mark read

Reading messages never changes unread state — send the explicit receipt when your UI displays the conversation:

```bash theme={null}
curl -X POST https://api.gleap.io/v3/s2s/conversations/cnv_…/read \
  -H "Authorization: Bearer $TOKEN"
```

## 5. List the contact's conversations

Most recent activity first; bot-only conversations without human interaction are hidden, exactly like the widget's list:

```bash theme={null}
curl "https://api.gleap.io/v3/s2s/contacts/AZ-4711/conversations?limit=50" \
  -H "Authorization: Bearer $TOKEN"
```

## 6. Close / reopen

```bash theme={null}
curl -X POST https://api.gleap.io/v3/s2s/conversations/cnv_…/close -H "Authorization: Bearer $TOKEN"
```

While closed, customer messages are rejected with problem code `conversation_closed`; `POST …/reopen` re-enables them. Your support team closing a conversation in the dashboard behaves identically and emits the same `conversation.closed` event.

## Uploads & voice notes

```bash theme={null}
curl -X POST https://api.gleap.io/v3/s2s/uploads -H "Authorization: Bearer $TOKEN" \
  -F "file=@receipt.png"
# → { "url": "…", "name": "receipt.png", "size": 18342, "contentType": "image/png" }

curl -X POST https://api.gleap.io/v3/s2s/uploads/audio -H "Authorization: Bearer $TOKEN" \
  -F "file=@note.webm"
# → { "url": "…", "durationMs": 7430 }   (transcoded to MP3 server-side)
```

Attach the returned descriptor to a message's `attachments`. Max 25 MB per file.

<Warning>
  File URLs are **signed and expire** (about 1 day in API responses, about 30 days inside webhook payloads) and their signatures rotate. Never persist or deduplicate by URL — store the message id and re-fetch the message when you need a fresh link.
</Warning>
