API Reference
CRM Integration
💬Get free consultation

CRM integration example

ⓘ

Who can use this feature?

  • An admin on Pro or Plus creates a key with conversations:write and customers:read.
  • AI assistance also needs enabled AI and available usage. Keep credentials and webhook secrets on your server.

This workflow mirrors customer messages into your CRM, lets a teammate reply, and gives that teammate a separate AI conversation for drafting follow-ups. The webhook consumer does not automatically call AI or send every answer back to the customer.

1. Register events

curl -s -X POST "https://app.chatty.net/chat/webhooks" \
  -H "X-Api-Key: $CHATTY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://crm.example.com/chatty/events","events":["message.created","customer.updated","conversation.updated"]}'

For a new URL, save the 201 response's data.id and data.secret. An existing URL returns 200 without the secret. Recover from a lost secret with Rotate a signing secret, not by listing subscriptions.

For each delivery:

  1. Verify the timestamp and signature against the raw body, as shown in Webhooks.
  2. Store eventId with a uniqueness constraint and enqueue the work durably. A duplicate must not create a second CRM message or reply.
  3. Return 2xx within 10 seconds. Process the saved event after acknowledgement.
  4. For message.created, mirror customer messages and ignore agent, bot, and system when deciding whether to reply. Never use your own outbound replies as a new request.
  5. For customer.updated, merge the supplied contact fields by data.customerId. This event has conversationId: null; tags-only and note-only changes do not trigger it.
  6. For conversation.updated, update the email or phone you hold for that conversation from data.detectedEmail and data.detectedPhone. data.changed says which one changed; a null value means it was cleared. Use this to link an anonymous chat to a CRM contact once the customer shares an email.

Reconcile periodically through the read API because webhook delivery order and completeness are not guaranteed. After an outage, list the subscription's failed deliveries and redeliver them; Chatty keeps payloads for 30 days.

2. Reply from the CRM

Resolve the teammate's memberId with GET /chat/members. Use the original message event's conversation ID to send the reply:

curl -s -X POST "https://app.chatty.net/chat/conversations/conv_123/messages" \
  -H "X-Api-Key: $CHATTY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"memberId":"member_123","text":"Thanks for getting in touch. I can help with that."}'

Success is 201 with data containing the saved message. Keep its returned ID in the CRM. A 409 CHANNEL_UNAVAILABLE needs a channel/contact check, not an immediate retry. Other errors use the Chat API error table.

Sending this reply emits another message.created, with senderType: "agent". The filter above prevents a loop. There is no write idempotency key: if the network times out, inspect messages before sending again.

3. Ask AI for a draft

Use the Public API base URL for /ai/ask. This starts a separate saved AI session:

curl -s -X POST "https://app.chatty.net/v1/ai/ask" \
  -H "X-Api-Key: $CHATTY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Explain the return policy so I can answer a customer."}'

On 200, show data.answer to the teammate and save data.conversationId as the CRM thread's AI session ID. It is separate from conv_123, the original livechat conversation ID. AI turns also create messages in Chatty; exclude your integration's AI sessions from customer-facing automation.

4. Continue the AI session

Replace ai_conv_123 with the actual ID from step 3:

curl -s -X POST "https://app.chatty.net/v1/ai/ask" \
  -H "X-Api-Key: $CHATTY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"conversationId":"ai_conv_123","message":"What if the customer bought the item on sale?"}'

Persist the returned ID after every success. When the teammate accepts the answer, send it with the message endpoint from step 2, using the original livechat ID. /ai/ask saves the draft exchange; it does not deliver the answer to that livechat for you.

5. Handle quota and disabled AI

Read the HTTP status and error.code. A 403 AI_DISABLED and a 403 FORBIDDEN need different actions.

function nextAction(status, body) {
  if (status === 402 && body.error?.code === 'AI_USAGE_LIMIT_REACHED') return 'manual-reply'
  if (status === 403 && body.error?.code === 'AI_DISABLED') return 'enable-ai'
  if (status === 403 && body.error?.code === 'FORBIDDEN') return 'fix-key-permissions'
  if (status === 429) return 'wait-with-backoff'
  if (status === 404) return 'check-ai-session'
  if (status >= 400) return 'review-error'
  return 'show-draft'
}

For manual-reply, keep the CRM thread open for your team; do not keep consuming retries. For enable-ai, direct an admin to enable AI. Keep the existing session ID so you can resume it once the store can use AI again. Do not automatically repeat a timed-out AI request: it may already have saved a turn.

The complete request, response and error contract is on Ask AI.

Extend the CRM workflow

For a new email thread, use Public conversations; sending an existing-thread reply is still a separate action. The same reference covers conversation-owned uploads and file attachments. Use Customers for profile creation, tags, memory changes, and the two scopes required for customer deletion. Do not treat conversation deletion or customer deletion as an ordinary retryable update.