Skip to content
campaiDeveloper
campai.com

A failed request needs a decision: fix the request, fix access, wait and retry a read, or reconcile a possibly completed write. Retrying every error can create duplicate contacts, duplicate invoices, or repeated external actions.

The examples in Make your first API request make one attempt. Add retries only around operations you have confirmed are safe to repeat.

What you’ll have at the end

A bounded retry for a known read and a separate recovery path for writes with uncertain outcomes.

  1. Classify failure
  2. Retry eligible read
  3. Stop at limit
  4. Reconcile uncertain write
Before you start
  • A working read request
  • An operation log with HTTP status and attempt count
On this page
Failure Default response
400 Inspect authentication or input validation. Correct the request before repeating it.
401 Check whether the credential was supplied and is accepted.
403 Check organization, scope, action, and mandate restrictions. Retrying unchanged access does not help.
404 Check the operation path, record ID, environment, and accessible scope.
409 Inspect the conflict and current state before deciding whether another operation is appropriate.
429 Respect a server-provided retry delay when available; reduce concurrency.
500 Investigate the error and request contract. Do not assume every server error is transient.
502, 503, 504 A read can usually be retried with a small bounded backoff. A write requires reconciliation.
Network failure or timeout The client may not know whether the server received or completed the operation.

The exact error message matters. For example, an invalid X-API-Key returns 400 in the current implementation; it is not always a malformed business payload. Keep credentials out of diagnostics.

Replace YOUR_API_KEY directly in your chosen example. Each version calls only GET /apiKey/info, makes at most three attempts, and waits one second, then two seconds between attempts. The cURL version uses Bash shell syntax.

Example

for attempt in 1 2 3; do
  reply=$(curl --silent --show-error --max-time 15 \
    --header 'X-API-Key: YOUR_API_KEY' \
    --write-out '\n%{http_code}' \
    'https://cloud.campai.com/api/apiKey/info')
  transport=$?
  status=${reply##*$'\n'}
  body=${reply%$'\n'*}
  if [ "$transport" -eq 0 ]; then
    case "$status" in
      2??) printf '%s\n' "$body"; break ;;
    esac
  fi
  case "$status" in
    000|502|503|504)
      if [ "$attempt" -lt 3 ]; then
        sleep "$attempt"
        continue
      fi ;;
  esac
  printf 'Request failed: HTTP %s, cURL exit %s\n' "$status" "$transport" >&2
  exit 1
done

These examples retry 502, 503, 504, and transport failures. Other HTTP errors stop immediately. For multiple workers, add jitter and a shared concurrency limit. A 429 response needs separate Retry-After handling; these examples do not retry it automatically.

Apply this policy only to a known read. HTTP method alone is not enough: List contacts uses POST but reads data. A contact-creation POST has different retry consequences.

Suppose a create request times out after the server commits the new record. Sending the same create request again can create a second record. A client timeout does not cancel or roll back a committed server operation.

For each write, persist a local operation identifier, its input fingerprint, and its state before sending. After success, store the returned campai _id. If the outcome is unknown, mark the operation uncertain and reconcile against the intended record before retrying.

Do not add an arbitrary Idempotency-Key header and assume campai deduplicates it. Use such a mechanism only where the specific operation documents and implements it. Otherwise, your integration needs a durable ledger, record matching, and an explicit handling path for uncertainty.

For invoice creation, Create invoices in batches separates draft creation and verification from issuing or sending documents. Never use an automatic create retry as a substitute for checking the previous invoice result.

Start with sequential writes and a small number of concurrent reads. Keep each page or work item independently recoverable. When a batch fails halfway through, resume from recorded outcomes rather than rerunning the entire input file blindly.

Re-fetch current state before applying a stale update. A successful retry of an old request can still overwrite a newer user change if your integration has not defined field ownership and conflict handling.

Log the operation name, attempt number, duration, status, and a local correlation identifier. Keep sensitive bodies and server details in restricted diagnostics only when necessary.

Exercise a successful first attempt, a 503 followed by success, repeated 503 failures, an immediate 403, and a transport timeout. Confirm the request count and the final reported outcome. For writes, simulate a lost response after a successful server commit and verify that reconciliation prevents a second create.

Use Track changes to the API contract when failures begin after a release. Retrying an obsolete payload will not make it conform to a changed schema.

Check your result

403 response
Fail immediately instead of retrying.
Temporary 503 then success
Return the success within the attempt limit.
Write timeout
Keep the outcome uncertain until reconciled.
Continue with Track changes to the API contract