Guide

Idempotency

Atlas requires idempotency keys on all public write endpoints — events, actions, session creation, and knowledge mutations. Idempotency keys prevent duplicate processing when clients retry after network failures or timeout. Reusing the same key with the same request body returns the stored response; reusing it with a changed body returns 409 CONFLICT.

How it works

Pass any unique string as idempotency_key in the request body. Atlas stores the key alongside the computed result for 24 hours. Subsequent requests with the same key and identical body return the original result without re-executing. If the body differs, Atlas rejects the second request with 409 CONFLICT.

POST /v1/atlas/events
{
  "idempotency_key": "evt_20260521_001",
  "channel": "whatsapp",
  "customer": { "phone_number": "+6590000000" },
  "direction": "inbound",
  "content": { "type": "text", "text": "Hi, is the menu available?" }
}
Response 200: { "event_id": "evt_01HXAB", "status": "received" }
Response 409: { "error": "idempotency_key_conflict", "message": "Request body does not match stored idempotent request." }

Endpoints requiring idempotency keys

POST/v1/atlas/eventsInbound event ingestion
POST/v1/atlas/actionsSubmit reply, draft, handoff, owner assignment
POST/v1/atlas/sessionsCreate preview or sandbox session
POST/v1/atlas/knowledgeUpload URL, text, or document source
POST/v1/atlas/webhooksRegister or update webhook subscription
POST/v1/atlas/runs/{id}/approveApprove a pending agent action

Idempotency key format

Keys must be globally unique strings up to 255 characters. Mirai recommends a reverse-domain prefix plus a timestamp or UUID to avoid collisions across services:

# Recommended patterns
evt_com.yourdomain.20260521.001
act_com.yourdomain.{uuid}
session_com.yourdomain.{batch_id}.{sequence}

# These are NOT recommended (too short, high collision risk)
"1", "abc", "retry"

Retry handling in external agents

When your agent runtime receives a network timeout or a 5xx response from Atlas, retry the same request with the same idempotency key. The key must be stable across retries — store it in your own outbox or job queue before the first attempt.

async function submitActionWithRetry(payload: AtlasAction, key: string, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch("https://api.usemirai.app/v1/atlas/actions", {
      method: "POST",
      headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
      body: JSON.stringify({ ...payload, idempotency_key: key }),
    });
    if (res.ok) return await res.json();
    if (res.status === 409) throw new Error("Idempotency conflict — payload changed");
    if (res.status >= 500 && attempt < maxRetries - 1) {
      await sleep(Math.pow(2, attempt) * 1000); // exponential backoff
    }
  }
}

Storage and expiry

  • Idempotency records expire after 24 hours. After expiry, a new key with the same value is treated as a fresh request.
  • Records are scoped to the authenticated org — a key used in one tenant cannot be replayed in another.
  • Read-only endpoints (GET) do not require idempotency keys.
  • Idempotency keys must be unique per endpoint; using the same key across different endpoints is permitted.