The path from a partner application to the local document management system passes through several stages. Any one of them can temporarily lose a message, which is why delivery is guaranteed at least once. To prevent this from resulting in duplicate imports, there is the
Idempotency-Key.
The promise is: A request with the same key will be executed at most once – no matter how many times it is sent.
The Key
POST /v1/dms/documents
Authorization: Bearer eyJ...
Idempotency-Key: import-schriftsatz-2026-0042-001
Content-Type: application/json
| Property | Rule |
|---|---|
| Header | Idempotency-Key |
| Allowed characters | Letters, digits, and ., _, and
-
|
| Length | 1 to 128 characters |
| Required? | No. Without a key, the call behaves like an ordinary request. |
| Recommended for | All mutating calls: import, new version, move |
A violation of the allowed character set or length is rejected with a 400 – intentionally, so that an application relying on idempotency immediately knows its key was not accepted.
A suitable value is a random identifier (such as a GUID) or a string derived from the business transaction. The only requirement is: one key per business transaction, and the same key on every retry.
What Happens on a Retry
Unlike some other APIs, the original result is not cached and not returned again. Instead, the API reports the duplicate attempt as such:
| State of the original request | Response to the retry |
|---|---|
| Still in progress |
504 IDEMPOTENCY_KEY_INFLIGHT_TIMEOUT – it did not complete in time. Try again later. |
| Completed or permanently failed |
409 IDEMPOTENCY_KEY_REUSED – the request will not be executed again. |
| Temporarily failed | The retry is executed normally. |
Important for implementation. A
409 IDEMPOTENCY_KEY_REUSEDis not a request failure. It simply means: "This request has already been processed." It does not indicate whether it succeeded. The application must determine the actual outcome on its own – for operations that return202 Accepted, via the operation status queryGET /v1/platform/operations/{operationId}; otherwise, through a domain-level check (does the document already exist in the target case file?).
Two Pitfalls to Be Aware Of
The Key Is Unaware of the Request Body
Only the key is evaluated, not the request body. If the same key is used with a different body, it is treated as a retry and rejected – the second body is not silently executed, but the mismatch is not reported as such either.
Rule: Every new request always gets a new key. Even if only one field has been corrected.
A Key Cannot Be Reused After a Correction
If an import fails due to an incorrect field and the field is corrected, that is a new request. Using the old key would cause the corrected request to be rejected as a duplicate.
Retry Strategy
Recommended behavior by HTTP status:
| Response | Retry? | Action |
|---|---|---|
2xx |
No | – |
4xx except 408, 425,
429
|
No | Fix the request first |
408 Request Timeout |
Yes | Exponential backoff: 1 s, 2 s, 4 s, 8 s, then give up |
425 Too Early |
Yes | Same as above |
429 Too Many Requests |
Yes | Honor Retry-After exactly |
502, 503, 504
|
Yes | Exponential backoff with jitter: start at 1 s, cap at 60 s, give up after approximately 10 minutes |
| No response (network error) | Yes | Same as 5xx
|
Retry-Afteralways takes precedence. If the response specifies a wait time – as it does for429and503– that value applies instead of the cap in the table above. ForTENANT_OVERLOADED, for example, this is 120 seconds.
This page is the authoritative source for retry behavior. The per-error-code recommendations in Error Codes refer back to this page; where the two appear to conflict, the recommendation for the specific error code takes precedence.
The jitter is not a minor detail – it matters: without it, all waiting applications will hammer the service at the same moment as soon as an outage ends.
The Idempotency-Key remains the same across all attempts for a given request.
The Special Case: No Response Received
This is exactly the scenario idempotency exists for: the request may have arrived and been processed, but the response was lost. The application has no way to tell the difference.
With a key, retrying is safe:
- The request did not arrive → it is executed now.
- The request arrived and is still in progress →
504 IDEMPOTENCY_KEY_INFLIGHT_TIMEOUT. - The request arrived and completed →
409 IDEMPOTENCY_KEY_REUSED, and the application determines the outcome via the operation status query.
In none of these three cases is a duplicate document created.
What Idempotency Does Not Cover
- No ordering guarantee. Two requests with different keys can be processed in any order. If ordering matters, the caller must serialize on its end or use the result of the first request as input to the second.
- No unlimited validity. See the section below – after approximately five minutes, a key expires.
- No protection against incorrect requests. Idempotency guards against duplicate execution, not against a request that is wrong at the business logic level.
How Long a Key Remains Valid
A key is retained for approximately five minutes. After that, it is no longer known to the API.
This is the most important number on this page, because the consequence is serious: A retry submitted after this deadline is processed as a brand-new request – it is no longer treated as a retry. For an import, this results in a second document being created.
This leads to two rules:
- Complete all retries for a request within a few minutes. The cap of approximately ten minutes recommended in the table above is already too long to rely on idempotency at the end of that window.
- Do not retry blindly after a long interruption. If a request is picked up again hours later – for example, after an application restart – you must first check at the business logic level whether it has already been executed. For an import, that means listing the documents in the target case file and checking.
Including a Correlation ID
Independently of the idempotency key, every request should carry its own correlation ID:
X-Correlation-Id: 9f3a2b7c4d1e5081
Allowed characters are letters, digits, and ., _,
and -, up to 64 characters. If no ID is provided or the ID is invalid, the API generates one automatically.
Where the ID appears in the response depends on the outcome:
| Outcome | Where the ID appears |
|---|---|
| Success | In the X-Correlation-Id response header |
| Error |
Only in the error response body, in the
correlationId field – the header is not present in this case |
The reason is technical: error handling rebuilds the response from scratch, discarding any headers that were already set. For integrations, this means: on error, read the ID from the response body, not the header. Relying on the header will leave you without an ID precisely when you need it most.
This ID is the thread that ties together all logs all the way into the firm. It should be recorded in the partner application's logs so it is readily available if questions arise.