Production Message Handling
Last updated 2026-02-13
Use idempotency keys for every message write, handle async fallback (pending), and prefer webhooks for completion where possible.
Recommended pattern:
- Send message with
Idempotency-Key. - If
responded, return immediately. - If
pending, either pollGET /v1/messages/:idor await webhook delivery. - Treat duplicate idempotency calls as safe retries.
Why pending happens
You will see pending when:
- the agent runtime is cold-starting
- the runtime is busy (queueing)
- you exceed the sync wait window (defaults to ~30s on the API side)
SDK helper path
The TypeScript SDK supports both completion styles:
const result = await client.messages.sendAndWait(
{
agentId,
sessionId,
message,
metadata: {
correlationId: "req-123",
channel: {
type: "custom_ui", // or "slack" | "whatsapp_cloud"
},
},
},
{ completion: "poll" } // or { completion: "webhook" }
);completion: "poll"waits for/v1/messages/:id.completion: "webhook"returns once queued, so your app can wait on webhook callback instead.
Idempotency key strategy
Pick an idempotency key that is stable across retries for the same logical user action:
chat:${conversationId}:${clientMessageId}slack:${channelId}:${threadTs}:${eventId}whatsapp:${phoneNumberId}:${wamid}
Avoid using only timestamps if you might accidentally replay or duplicate.