Skip to content

    Arabic or English · ↑↓ to move · Enter to open · Esc to close

    العربية
    On this page

    Idempotency

    Retry safely: the Idempotency-Key header makes sure a send, campaign or OTP runs at most once.

    Networks fail. If a request times out you can’t know whether the message left. Send Idempotency-Key with every write and retry with the same key: Mursilat returns the first answer instead of doing the work again.

    http
    Idempotency-Key: order-1042-shipped
    SituationAnswer
    First requestAnswerRuns normally.
    Same key + same body (within 24 h)AnswerThe stored answer, plus Idempotent-Replayed: true. Nothing is sent again.
    Same key + different bodyAnswer409 IDEMPOTENCY_KEY_REUSED — use a new key for a new request.
    Same key while the first is still runningAnswer409 IDEMPOTENCY_IN_PROGRESS — wait a moment and retry.
    Missing on a send / campaign / OTPAnswer400 IDEMPOTENCY_KEY_REQUIRED.
    Longer than 191 or not visible ASCIIAnswer400 IDEMPOTENCY_KEY_INVALID.

    Where it is required

    Required on POST /v2/messages, POST /v2/campaigns and POST /v2/otp. Optional but accepted on every other write. Keys are scoped to the API key that used them: another key, even of the same account, never replays an answer.

    Choosing keys

    • Derive the key from your own business event so retries naturally reuse it: order-1042-shipped, otp-login-<sessionId>.
    • Don’t use a random key per attempt — that defeats the purpose.
    • Replays return ids and statuses only (never message text, captions or URLs).

    A safe retry loop

    Node.js
    async function sendWithRetry(body, key, attempts = 4) {
      for (let i = 0; i < attempts; i++) {
        const res = await fetch('https://mursilat.com/api/v2/messages', {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${process.env.WASLA_API_KEY}`,
            'Idempotency-Key': key, // the SAME key on every attempt
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(body),
        }).catch(() => null); // network error → retry
    
        if (res && res.ok) return res.json();
        if (res && res.status < 500 && res.status !== 409 && res.status !== 429) {
          throw new Error((await res.json()).error.code); // won't succeed on retry
        }
        const wait = Number(res?.headers.get('Retry-After')) || 2 ** i;
        await new Promise((r) => setTimeout(r, wait * 1000));
      }
      throw new Error('gave up');
    }