Idempotency
Every analysis request requires an Idempotency-Key. One key represents one logical message submission.
Key contract
Use 8–128 ASCII characters matching:
^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$
A UUID with an integration prefix is suitable:
mailgw-0b8c29c8-491a-4a8d-a832-58e5417e065f
The key is not the API credential. Do not include an address, subject, message ID, or other personal data in it, and do not reuse it indefinitely.
Observable behavior
- Create a new key for every new logical submission.
- Read the EML bytes once and retain that immutable buffer for all attempts.
- Reuse the same key and exactly the same bytes after a network failure, client timeout, or retryable response.
- The same scoped key and same bytes can replay the stored public response for 24 hours.
- The same key with different bytes returns
409 idempotency_conflict; do not retry that combination. - A completed replay does not account the same operation twice.
- Alternating response versions
1.0and1.1keeps the same operation identity: use the sameIdempotency-Key, credential and EML bytes within the replay window. - A replay reuses the original result. It does not call the provider again or complete a previously partial inspection.
- A client timeout does not prove that Hermes did not execute the operation.
503 analysis_outcome_unknownmeans Hermes cannot safely confirm the original outcome. Keep the same key; never create another key automatically to bypass it.
Idempotency is scoped to the authenticated credential's organization, customer, and environment. Callers never select that scope in the request.
For new analyses, the evidence explanation is frozen with the original result and
accounting. If a retained historical result has useful metadata but no summary,
the first explicit 1.1 request can freeze a historical projection once; concurrent
readers receive that same snapshot. A later metadata enrichment does not silently
rebuild it. If support is missing or uninterpretable, the API says so.
After the replay window expires, another POST may create a new operation and consume another unit. Never use an expired POST as a historical lookup. Retrying must remain an explicit integration decision under the existing bounded policy; requesting a more detailed response is not permission to create a new analysis.
Retry decisions
| Outcome | Retry? | Same key and bytes? | Delay | If it persists |
|---|---|---|---|---|
| Network error or client timeout | Yes, bounded | Yes | Exponential backoff with jitter | Stop and contact support; explain that no terminal response arrived |
408 request_body_timeout | Yes, bounded | Yes | Exponential backoff with jitter | Check upload/network behavior, then provide request_id |
409 idempotency_conflict | No | Not applicable | None | Recover the original bytes or start a genuinely new operation with a new key |
429 | Yes, bounded | Yes | Use Retry-After when present; otherwise backoff with jitter | Reduce concurrency and provide request_id |
500 internal_error | Yes, bounded | Yes | Backoff with jitter | Stop; provide request_id without assuming another retry will succeed |
503 idempotency_in_progress | Yes, bounded | Yes | Use Retry-After | Stop and provide request_id |
503 analysis_outcome_unknown | Yes, later and bounded | Yes | Use Retry-After when present | Do not create a new key; contact support |
Other 503 | Yes, bounded | Yes | Use Retry-After when present; otherwise backoff with jitter | Stop and provide request_id |
Other 4xx | No automatic retry | Preserve for diagnosis | Correct the request or authorization first | Contact support if the correction is unclear |
Hermes sends Retry-After only when it can calculate a useful interval. Its absence is not permission to retry immediately.
The 24-hour replay window supports recovery; it is not long-term result storage. Persist the key, terminal status, analysis ID, and request ID in your own authorized system.