Chatonio

This is taking longer than usual.

Chatonio

Webhooks: setup & events

July 7, 2026 65 viewsChatonio API

Webhooks

Webhooks let Chatonio notify your server when something happens in your project — the most common use is being told when a customer sends a new message. Chatonio sends a signed POST with a JSON body to a URL you register.

You do not need an API key to use webhooks. Add and manage endpoints on your project’s Developer settings page (you can also manage them through the API).

Setting up an endpoint

  • Open Project settings → Developer → Webhooks and click Add webhook.
  • Enter your HTTPS URL and tick the events you want.
  • Copy the signing secret shown once on creation — you need it to verify deliveries (see Verifying webhooks & reliability).

Your endpoint should respond with a 2xx status quickly. Do the real work asynchronously.

Allowlisting our IP

Every webhook delivery leaves Chatonio from one fixed address:

  • 64.7.198.218

If your endpoint sits behind a firewall, a WAF, or an IP allowlist, add that address. It is stable, and any change will be announced here in advance.

An IP allowlist is not a substitute for checking the signature. It tells you a request arrived from our network, not that we sent it — only the Chatonio-Signature header proves that. See Verifying webhooks & reliability.

Events

  • message.created — a message was added to a conversation (any sender except internal notes). Filter on data.message.sender_type == "CLIENT" for “new customer message”.
  • conversation.created — a new conversation was created.
  • conversation.status_changed — a conversation changed status.

Payload shape

Every delivery has the same envelope: a unique id, the event type, an api_version, a created timestamp, and a data object specific to the event.

message.created

{
  "id": "d5e1a220-4f77-4c33-a1b2-c3d4e5f60718",
  "type": "message.created",
  "api_version": "v1",
  "created": "2026-07-01T10:30:00+00:00",
  "data": {
    "message": {
      "id": "a1000000-0000-4000-8000-000000000001",
      "conversation_id": "3f2b1c9a-7d4e-4b21-9c33-0a1b2c3d4e5f",
      "sender_type": "CLIENT",
      "content": "Where is my order?",
      "is_internal_note": false,
      "thread_id": null,
      "created_at": "2026-07-01T10:30:00+00:00"
    },
    "conversation": {
      "id": "3f2b1c9a-7d4e-4b21-9c33-0a1b2c3d4e5f",
      "status": "NEW",
      "channel_id": "9a1c5f30-2b6d-4c71-8e42-1a2b3c4d5e6f",
      "channel_type": "WEB"
    }
  }
}

conversation.created

{
  "id": "d5e1a220-4f77-4c33-a1b2-c3d4e5f60719",
  "type": "conversation.created",
  "api_version": "v1",
  "created": "2026-07-01T10:29:00+00:00",
  "data": {
    "conversation": {
      "id": "3f2b1c9a-7d4e-4b21-9c33-0a1b2c3d4e5f",
      "status": "NEW",
      "subject": "",
      "source": "CHANNEL",
      "channel_id": "9a1c5f30-2b6d-4c71-8e42-1a2b3c4d5e6f",
      "channel_type": "WEB",
      "ticket_number": null,
      "created_at": "2026-07-01T10:29:00+00:00"
    }
  }
}

conversation.assigned

A conversation was assigned to an operator, or unassigned. One event covers both directions: on an unassign, operator is null. It fires however the change happened — an operator claiming it in the dashboard, automatic routing, or your own API call.

{
  "id": "9a3f7c11-2b60-4d5e-8f09-1a2b3c4d5e6f",
  "type": "conversation.assigned",
  "api_version": "v1",
  "created": "2026-07-01T10:46:00+00:00",
  "data": {
    "conversation": {
      "id": "3f2b1c9a-7d4e-4b21-9c33-0a1b2c3d4e5f",
      "status": "PROCESSING"
    },
    "operator": { "user_id": "7c1e5a02-9b43-4f18-8a77-2d6e0f4b9c81" }
  }
}

conversation.status_changed

{
  "id": "d5e1a220-4f77-4c33-a1b2-c3d4e5f60720",
  "type": "conversation.status_changed",
  "api_version": "v1",
  "created": "2026-07-01T10:45:00+00:00",
  "data": {
    "conversation": {
      "id": "3f2b1c9a-7d4e-4b21-9c33-0a1b2c3d4e5f",
      "status": "RESOLVED",
      "previous_status": "PROCESSING"
    }
  }
}

message.created fires for every customer, operator, AI and system message delivered in a conversation — internal operator notes are never sent to webhooks, so is_internal_note is always false in deliveries. Use sender_type to keep only what you care about.

Was this article helpful?