Reading as an AI? Append .md to this URL for markdown — /llms.txt
Back to Inboxes

Custom Inbox

Connect your own product, app, or automation to Kronos. You send inbound customer messages to the inbox endpoint, and Kronos sends replies back to your webhook.

How to connect it

Step 1

Create the Custom Inbox

Open Kronos, go to Connect, then open the Inboxes section and choose Custom Inbox.

Step 2

Add your webhook URL

2.1. Enter the inbox name customers or your team will recognize.

2.2. Paste the HTTPS webhook URL that should receive Kronos replies.

Step 3

Choose webhook events

3.1. Keep message.created enabled for replies.

3.2. Enable agent.tool.called if your app should show tool activity.

3.3. Enable agent.reasoning.created only if you want reasoning from real reasoning models.

Step 4

Copy the credentials

4.1. Copy the inbound endpoint. Your app will send customer messages there.

4.2. Copy the webhook signing secret. It is shown once, and you can rotate it later from inbox settings.

Step 5

Send a test message

Create an API key with custom_inbox:write, send one inbound message to the endpoint, then confirm your webhook receives the reply or a webhook.test event.

What you need

  • An HTTPS webhook URL that accepts POST requests.
  • An API key from the same Kronos account as the Custom Inbox, with the custom_inbox:write scope.
  • Your own backend route that keeps the API key server-side. Do not call Kronos directly from browser code.
  • A stable user id from your system for external_user_id.
  • A stable thread id for external_conversation_id, or reuse the generated one returned by Kronos.
  • An AI agent or workflow assigned to the Custom Inbox if you expect automatic replies.

Choose a session pattern

Custom Inbox does not require a specific frontend. The important part is how your app maps its visitor, user, or thread ids to Kronos fields.

Temporary web chat

Good for landing-page chat bubbles, demos, and tests where the conversation can live only in the current browser.

  • 1. Generate a random visitor id in the browser and use it as external_user_id.
  • 2. Omit external_conversation_id on the first message, then store the value Kronos returns in localStorage or sessionStorage.
  • 3. Send browser messages to your backend, and let your backend call the Kronos inbound endpoint with the API key.
  • 4. Receive webhook replies on your backend and expose them to the browser with polling, SSE, WebSocket, or your own temporary store.

Persistent product chat

Use this for signed-in apps, support inboxes, order threads, and conversations that must survive refreshes, devices, and deploys.

  • 1. Use your database user/contact id as external_user_id.
  • 2. Use your durable ticket, conversation, cart, order, or room id as external_conversation_id.
  • 3. Store inbound and webhook messages in your database, deduping by message_id, event_id, or webhook message id.
  • 4. Hydrate the chat UI from your database and update it when new webhook events arrive.

Minimum backend flow

The API key belongs on your server. A typical web or app integration uses this flow:

  1. 1. Your frontend sends the visitor message to your backend route.
  2. 2. Your backend calls the Kronos inbound endpoint with Authorization: Bearer $KRONOS_API_KEY.
  3. 3. Your backend stores or returns the external_conversation_id so the next message stays in the same thread.
  4. 4. Kronos sends the AI reply to your webhook as an async message.created event.
  5. 5. Your webhook validates the signature, stores or emits the reply, then your UI shows it by polling, SSE, WebSocket, or your own realtime layer.

Send an inbound message

Use the inbound endpoint shown after you connect the inbox. Each inbound message needs a stable message_id so retries do not create duplicates. content accepts 1-4096 characters, and metadata can contain up to 10KB and 50 top-level keys.

bash
curl -X POST https://api.kronos.com.mx/v1/custom-inboxes/<integration_id>/messages \
  -H "Authorization: Bearer $KRONOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_user_id": "user_123",
    "external_conversation_id": "thread_abc123",
    "message_id": "msg_0001",
    "content": "Hola, quiero revisar mi pedido",
    "sender": { "name": "Maria" },
    "metadata": { "source": "mobile_app" }
  }'

To intentionally start a fresh Kronos thread even when you already have an old thread id, send "new_conversation": true. Kronos will return the new external_conversation_id; store that value if the new thread should continue.

Accepted response

The endpoint returns once the message is accepted. Agent replies are generated asynchronously and sent separately to your webhook. A successful accepted response means Kronos stored the inbound message; it does not mean the AI reply has already been delivered. New accepted messages return HTTP 202. Idempotent replays may return HTTP 200 with X-Idempotent-Replay: true.

json
{
  "status": "accepted",
  "integration_id": "4ad91e83-7d48-4371-9f2f-4505c7912c2e",
  "conversation_id": "b8e547b4-3837-49d2-9f4b-6c2e9d01fd7d",
  "external_conversation_id": "thread_abc123",
  "inbound_message_id": "99618f3e-4f7a-4e17-896e-250f6a92db31",
  "idempotent_replay": false,
  "created_at": "2026-07-06T09:12:27.852Z"
}

Receive replies by webhook

Your webhook receives one POST per enabled event. For normal replies, expect one message.created event per message bubble. Validate the signature against the exact raw request body before trusting the event.

http
X-Kronos-Event-Id: <uuid>
X-Kronos-Timestamp: <unix_seconds>
X-Kronos-Signature: v1=<hex_hmac_sha256(timestamp + "." + raw_body)>
json
{
  "event_id": "5bdbb9f1-21e6-4f90-984f-67172d0e7e9e",
  "event_type": "message.created",
  "created_at": "2026-07-06T09:12:32.102Z",
  "data": {
    "integration_id": "4ad91e83-7d48-4371-9f2f-4505c7912c2e",
    "conversation_id": "b8e547b4-3837-49d2-9f4b-6c2e9d01fd7d",
    "external_conversation_id": "thread_abc123",
    "external_user_id": "user_123",
    "message": {
      "id": "0f6c052c-4c95-43ab-8d5a-569b7a7184e7",
      "sender": "agent",
      "content": "Claro, lo reviso contigo.",
      "created_at": "2026-07-06T09:12:32.101Z"
    }
  }
}

Your receiver should return any 2xx status quickly. Store the event_idor webhook message id before updating your UI so duplicate deliveries or manual tests do not create duplicate bubbles.

Events you can enable

message.created

Sent for each outbound reply bubble.

agent.reasoning.created

Sent only when the selected model is a real reasoning model.

agent.tool.called

Sent when the agent uses a tool. The payload includes the user-facing tool name.

webhook.test

Sent when you click Test webhook from the inbox settings page.

Quick checklist

  • 1. Create the Custom Inbox and copy the inbound endpoint.
  • 2. Store the signing secret somewhere safe.
  • 3. Create an API key with custom_inbox:write from the same Kronos account that owns this Custom Inbox.
  • 4. Assign an AI agent or workflow to the inbox if you expect automatic replies.
  • 5. Send one test message to the inbound endpoint from your backend, not from browser code.
  • 6. Click Test webhook in inbox settings and confirm your receiver gets the event.
  • 7. Confirm your UI can show an async message.created webhook reply for the same external_conversation_id.
See the API reference

Connection FAQ

What if I close the setup before copying the signing secret?

Open the inbox settings and rotate the secret. Copy the new value and update your webhook receiver.

What should I use as the conversation id?

Use your own stable thread id as external_conversation_id. If you do not have one, omit it on the first message and reuse the value Kronos returns.

Can I build a landing-page chat without storing conversations forever?

Yes. Generate browser-scoped ids and keep the returned external_conversation_idin local or session storage. For production-grade persistence across devices or deploys, store the ids and webhook messages in your backend database.

Why did the API accept my message but no reply appeared?

Check that the Custom Inbox is assigned to an AI agent or workflow, that message.createdis enabled, and that your webhook receiver returns a quick 2xx response after validating the signature.

Why do I get integration_not_found or HTTP 404?

The API key is valid, but it cannot access that Custom Inbox. Use the inbound endpoint and an API key from the same Kronos account, and make sure the key includes custom_inbox:write.

How do I confirm the webhook is working?

Click Test webhook from the inbox settings page and check that your receiver gets a webhook.test event.

For AI agents and LLMs: every docs page has a clean markdown twin — append .md to its URL. Curated index: /llms.txt. Open semantic search, no auth: POST https://api.kronos.com.mx/v1/docs/search.
Custom Inbox - Kronos AI Docs | Kronos AI