Quickstart
Who can use this feature?
- Creating an API key requires the Pro or Plus plan.
- This page is for developers. Every step is a
curlcommand plus one endpoint on your own server.
Overview
This page walks one integration from nothing to a working event feed. By the end you will have a key that authenticates, code that reads and writes conversations, and a server endpoint that receives verified events in real time.
Plan on about 30 minutes, plus a public HTTPS endpoint for the last section.
| Base URL | https://app.chatty.net |
| Auth header | X-Api-Key: <your key> |
| Format | JSON in, JSON out (Content-Type: application/json on writes) |
Every request is scoped to your store. You never send a store ID. Your key resolves to exactly one store, and every read and write is limited to it.
Create your API key
Open the keys panel
Go to Settings → General → Manage keys. The App ID at the top of the panel identifies your store. You need it for the MCP server and the GraphQL Customer API, but not for this page.
Generate the key
Enter a name in Key name, up to 50 characters, then click Generate key. Name it after the integration that will use it, for example Order bot.
Copy it now
The full sk_ value is shown once. Chatty stores only a hash of it, so it can never be displayed again. Put it in your secrets manager before you leave the page.
You can hold up to 5 active keys per store. Create one per integration so you can revoke a single key without breaking the others.
A brand-new key needs up to 30 seconds before it is accepted. A 401 in the first few seconds after Generate key is not a bad key. Wait and try again.
Export it into your shell so the rest of the commands on this page work as written:
export CHATTY_KEY="sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Confirm the key works
Start with the lightest read on the API. GET /chat/members returns your team, changes nothing, and gives you the memberId you need later to send a message.
curl -s "https://app.chatty.net/chat/members" \
-H "X-Api-Key: $CHATTY_KEY"A working key returns a data array of members. Note the id of the member you want to reply as.
A wrong, revoked, or too-new key returns:
{ "error": { "code": "INVALID_API_KEY", "message": "API key is unknown or revoked" } }Full status codes are on Errors and rate limits.
Read conversations
List the newest open conversations
curl -s "https://app.chatty.net/chat/conversations?status=open&limit=5" \
-H "X-Api-Key: $CHATTY_KEY"The response carries data plus paging. Pass paging.nextCursor back as the cursor parameter to fetch the next page; nextCursor: null means you reached the end. Cursors are opaque, so don't parse them.
Read one conversation
Take an id from the list above.
export CONVO_ID="conversation_id"
curl -s "https://app.chatty.net/chat/conversations/$CONVO_ID" \
-H "X-Api-Key: $CHATTY_KEY"Read the thread, oldest first
curl -s "https://app.chatty.net/chat/conversations/$CONVO_ID/messages?order=asc&limit=100" \
-H "X-Api-Key: $CHATTY_KEY"Every message carries senderType: customer, agent, bot, or system. An AI reply is bot, a human on your team is agent. You will need that field again in the webhook section.
The list is not a full history. Older conversations that have not moved to the new storage are still reachable by id, and so are their messages, but they do not appear in GET /chat/conversations.
Send a message
Reply as a member from the list you fetched earlier. text is capped at 5,000 characters, and the call returns 201.
curl -s -X POST "https://app.chatty.net/chat/conversations/$CONVO_ID/messages" \
-H "X-Api-Key: $CHATTY_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Your order ships tomorrow.", "memberId": "MEMBER_ID"}'An active AI bot hands off to the human. If auto-assignment is switched on in your inbox settings, the conversation is also assigned to that member, unless the AI agent is still holding it. Delivery to Messenger, Instagram, WhatsApp, or email happens automatically.
To leave a note your customer never sees, add "isNote": true. To test without touching a real customer, use a test conversation from your own storefront.
The full endpoint list, including tags, notes, attributes, and customers, is on the Chat Conversations API page.
Receive your first event
Register your endpoint
curl -s -X POST "https://app.chatty.net/chat/webhooks" \
-H "X-Api-Key: $CHATTY_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/chatty-webhook",
"events": ["message.created"]
}'The 201 response carries a secret that starts with whsec_. Store it on your server right away: it is the only thing that verifies your deliveries. A secret you send in the request body is ignored.
Verify the signature
Every delivery carries X-Webhook-Event, X-Webhook-Timestamp, X-Webhook-Signature, X-Webhook-Event-Id, and X-Webhook-Delivery-Id.
The signature covers the timestamp and the body together, not the body alone. Sign the exact string `${timestamp};${body}`, and always use the raw, unparsed request body.
const crypto = require('crypto')
function verifyWebhook(rawBody, timestamp, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp};${rawBody}`)
.digest('hex')
const a = Buffer.from(signature)
const b = Buffer.from(expected)
if (a.length !== b.length) return false
return crypto.timingSafeEqual(a, b)
}Also reject anything whose X-Webhook-Timestamp is more than a few minutes old. The signature itself does not expire.
Answer fast, then de-duplicate
Return 2xx within 10 seconds and do the heavy work in the background. Chatty aborts a slower request and counts it as a failure, then retries at roughly 10 seconds, 1 minute, 5 minutes, 30 minutes, 2 hours, and 6 hours: up to 7 attempts across about 9 hours. After the seventh failure the subscription stops delivering every event until you re-enable it.
Retries repeat the same X-Webhook-Event-Id. Store the ids you have already processed and skip repeats.
Trigger it
Send a message from your storefront widget. Within a second or two your endpoint should receive a message.created event:
{
"eventId": "b3f1c9a0-4c2e-4f1a-9b77-2d5e8a0c1f34",
"type": "message.created",
"shopId": "your_shop_id",
"conversationId": "conversation_id",
"occurredAt": "2026-08-03T12:00:00.000Z",
"data": {
"messageId": "msg_123",
"text": "What is your return policy?",
"senderType": "customer",
"memberId": null,
"createdAt": "2026-08-03T12:00:00.000Z"
}
}Your own replies fire message.created too. If your integration listens to the event and answers through POST /chat/conversations/{id}/messages, it will answer itself and loop. Check data.senderType and act only on customer.
Where first integrations break
- The admin inbox paths are not this API. The Chatty inbox in your browser calls a near-identical set of paths under
/api/chat/..., authenticated with an admin session. AnX-Api-Keyrequest there fails. The public API carries no/apiprefix:https://app.chatty.net/chat/conversations. - Localhost cannot receive events. A registered
urlmust usehttps://and resolve to a public address, and Chatty re-checks that on every delivery. Run a tunnel such as ngrok or Cloudflare Tunnel and register the public address it gives you. - A payload above 80,000 bytes arrives without its
data. The envelope still comes through, anddatais replaced with{"truncated": true, "originalSize": <bytes>}. Read the record back over the Chat Conversations API when you see that flag. - Redirects are never followed. A
3xxis treated like a4xx: dropped, never retried. Register the final URL. - The rate limit is not a window you can keep calling through. You get 120 requests/min per key and 300 requests/min per source IP. Every request pushes the 60-second expiry back, so once you are over the limit you have to stop for a full 60 seconds. Retrying straight away keeps you blocked. Watch
X-RateLimit-Remainingon every response.
Where to go next
| Page | Use it for |
|---|---|
| Chat Conversations API | Every endpoint: tags, notes, attributes, customers, team members |
| Webhooks | All nine events, payload shapes, and subscription management |
| Authentication | What a key can do, how to rotate one, what to do if it leaks |
| Errors and rate limits | Every status code and limit in one table |
| Storefront SDK | Control the widget from your storefront. No key needed, works on any plan |
| MCP server | Let an AI assistant read and act on your inbox |
Need help?
Contact the Chatty support team from your dashboard. Include the endpoint, the timestamp of a failing request, and the returned error.code. For a failing webhook, include the X-Webhook-Delivery-Id instead.
Chatty Help Center