GuidesProduction Message Handling

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:

  1. Send message with Idempotency-Key.
  2. If responded, return immediately.
  3. If pending, either poll GET /v1/messages/:id or await webhook delivery.
  4. 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:

typescript
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.