Skip to main content

Idempotency

Use this page when you need the exact create retry contract for the Public API.

What It Is

Idempotency-Key identifies one logical create request for POST /v1/bulkfill/export-jobs within a fixed 24-hour window.

X-Request-Id is separate:

  • Idempotency-Key = logical create replay identity
  • X-Request-Id = request tracing and support correlation

If you omit X-Request-Id, Doqlo generates one and returns it in the response header. Webhook payloads also include both values:

  • idempotency_key
  • request_id

Header Rules

Create requests must send:

  • Idempotency-Key

Create requests may also send:

  • X-Request-Id

Idempotency-Key validation:

  • exactly one value
  • case-sensitive
  • allowed characters: A-Z, a-z, 0-9, ., _, :, -
  • length: 1..128

Recommended values:

  • UUID v4
  • another high-entropy random string

Core Behavior

After auth, security checks, and create-side rate limiting succeed:

  • same Idempotency-Key + same effective body returns the same logical job_id
  • same Idempotency-Key + different effective body returns 409 IDEMPOTENCY_KEY_PAYLOAD_MISMATCH
  • same Idempotency-Key after 24 hours is treated as a new request

Front-door protections run first. A retry can still return 429 before Doqlo reaches replay lookup.

Example: First Create With Both Headers

curl -X POST https://api.doqlo.com/v1/bulkfill/export-jobs \
-H "Authorization: Bearer $DOQLO_BF_EXPORT_API_KEY" \
-H "Idempotency-Key: 8a3f0c08-5a6f-4f49-a502-c8b8c47a6d8f" \
-H "X-Request-Id: support-create-001" \
-F "[email protected];type=application/pdf" \
-F "[email protected];type=application/octet-stream" \
-F 'rows_json=[{"column_0":"Alice Nguyen"}]' \
-F "max_failed_row_percent=5"

Example response headers:

HTTP/1.1 202 Accepted
X-Request-Id: support-create-001

Example response body:

{
"job_id": "<job_id>",
"status": "queued",
"created_at": "<timestamp>"
}

Example: First Create With Only Idempotency-Key

curl -X POST https://api.doqlo.com/v1/bulkfill/export-jobs \
-H "Authorization: Bearer $DOQLO_BF_EXPORT_API_KEY" \
-H "Idempotency-Key: e3f00f71-2d08-4a9f-bbb3-28b8d7ab1b76" \
-F "[email protected];type=application/pdf" \
-F "[email protected];type=application/octet-stream" \
-F 'rows_json=[{"column_0":"Alice Nguyen"}]' \
-F "max_failed_row_percent=5"

Example response headers:

HTTP/1.1 200 OK
X-Request-Id: trace-7f1ab0a9-bdc6-4d5b-a87f-3b1c4e96d0e9

The generated X-Request-Id is the trace request_id you will also see in logs, admin investigation, and webhook payloads.

Example: Same Key, Same Body Replay

Retry the same logical create with the same Idempotency-Key and the same effective request body:

{
"job_id": "<same_job_id>",
"status": "processing",
"created_at": "<timestamp>",
"started_at": "<timestamp>"
}

The replay can return the current job state:

  • queued
  • processing
  • completed
  • failed

Example: Same Key, Different Body Conflict

If you reuse the same Idempotency-Key for a different effective create body:

{
"code": "IDEMPOTENCY_KEY_PAYLOAD_MISMATCH",
"message": "This Idempotency-Key has already been used for a different export request."
}

Example: Retry That Hits Front-Door 429 First

Doqlo evaluates create-side rate limits and safeguards before replay lookup. That means a retry can still return 429:

{
"code": "RATE_LIMITED",
"message": "Too many requests. Retry later."
}

Honor both:

  • Retry-After
  • retry_after_seconds when present

Example: Webhook Payload With Both Identifiers

{
"event": "bulkfill.export_job.completed",
"event_id": "<event_id>",
"occurred_at": "<timestamp>",
"job_id": "<job_id>",
"status": "completed",
"idempotency_key": "8a3f0c08-5a6f-4f49-a502-c8b8c47a6d8f",
"request_id": "support-create-001",
"reused_result": false,
"delivery_id": "<delivery_id>",
"download_url": "/v1/bulkfill/export-jobs/<job_id>/download",
"download_expires_at": "<timestamp>"
}

request_id is the originating request trace:

  • client-supplied X-Request-Id when you sent one
  • server-generated trace ID when you did not

Best Practices

  • Generate a new Idempotency-Key for each new logical create request.
  • Reuse the same Idempotency-Key only when retrying that exact logical create.
  • Do not reuse a key for a different payload inside the 24-hour window.
  • Keep X-Request-Id for tracing, support tickets, and correlation.
  • Store both job_id and request_id from the first response when you need support/debug context.