Docs

Errors & rate limits

Every error uses the same JSON envelope, and every response tells you how much rate-limit budget remains.

Error envelope

Non-2xx responses carry an HTTP status and a body with a stable machine-readable code, a human message, optional details and a request_id to quote to support.

422 Unprocessable Entity
{
  "error": {
    "code": "validation_failed",
    "message": "Request validation failed",
    "details": {
      "to": "must be a valid phone number in E.164 format",
      "text.body": "must be at most 4096 characters"
    },
    "request_id": "8c3f2a91b7d84e0f9a1c2b3d4e5f6a7b"
  }
}

Error codes

StatusCodeMeaningWhat to do
401unauthorizedMissing, invalid, expired or revoked keyCheck the Authorization header
401mfa_requiredDashboard session needs a second factorDashboard only
402plan_limitA plan limit was reached (messages, contacts…)Upgrade or wait for the next period
403forbiddenKey lacks the permission or IP not allowedGrant the permission
404not_foundResource doesn’t exist in this organizationCheck the id
409conflictState conflict, or idempotency key reused with a different bodyRe-read state; use a new key
422validation_failedBody failed validation; see detailsFix the fields listed in details
429rate_limitedToo many requestsWait for Retry-After seconds
502meta_errorWhatsApp (Meta) rejected or failed the calldetails include Meta’s code
500internalUnexpected server errorRetry with backoff

Rate limits

Each API key has a request budget per second based on your plan (Starter 20, Growth 100, Business 300 rps). Every response includes the standard headers:

HeaderDescription
RateLimit-LimitRequests allowed in the current window
RateLimit-RemainingRequests left in the current window
RateLimit-ResetSeconds until the budget refills
Retry-AfterOn 429 only: seconds to wait before retrying

Message sending is additionally paced per WhatsApp number according to Meta's throughput tier. That pacing happens inside Engage's queue: you receive 202 immediately and messages are delivered as fast as WhatsApp allows.

Retrying safely

Retry 429, 500, 502 and network errors with exponential backoff and jitter. Always send an Idempotency-Key so a retried POST never creates a duplicate.

async function engage(path, body, key = crypto.randomUUID()) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`https://api.engage.swastik.ai/v1${path}`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.ENGAGE_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": key, // same key on every retry
      },
      body: JSON.stringify(body),
    });
    if (res.ok) return res.json();
    if (![429, 500, 502, 503].includes(res.status)) {
      const { error } = await res.json();
      throw new Error(`${error.code}: ${error.message}`);
    }
    const wait = Number(res.headers.get("Retry-After")) * 1000 || 2 ** attempt * 500;
    await new Promise((r) => setTimeout(r, wait + Math.random() * 250));
  }
  throw new Error("Engage request failed after retries");
}