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 identityX-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_keyrequest_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 logicaljob_id - same
Idempotency-Key+ different effective body returns409 IDEMPOTENCY_KEY_PAYLOAD_MISMATCH - same
Idempotency-Keyafter 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:
queuedprocessingcompletedfailed
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-Afterretry_after_secondswhen 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-Idwhen you sent one - server-generated trace ID when you did not
Best Practices
- Generate a new
Idempotency-Keyfor each new logical create request. - Reuse the same
Idempotency-Keyonly when retrying that exact logical create. - Do not reuse a key for a different payload inside the 24-hour window.
- Keep
X-Request-Idfor tracing, support tickets, and correlation. - Store both
job_idandrequest_idfrom the first response when you need support/debug context.