Browse documentation
Concept All API surfaces

Idempotency and retries

Retry Hodoflow operations safely by separating JSON:API response replay, webhook delivery deduplication, read retries, and permanent failures.

View as Markdown

Hodoflow has two distinct duplicate-suppression contracts and ordinary retry guidance. Do not reuse one surface's behavior as a promise for another.

JSON:API unsafe writes

Send a unique Idempotency-Key on POST, PUT, PATCH, or DELETE requests under /api/json. The key is scoped by tenant and service-account principal. The request fingerprint includes method, path, and raw body.

Idempotency-Key: dashboard-create-20260731-001
  • First request: reserves the key and runs normally.
  • Same key and same request: replays the stored response with idempotency-replayed: true.
  • Same key and different request: returns 409 idempotency_key_reused.
  • Concurrent duplicate while the first is running: returns 409 idempotency_key_in_progress.
  • A 5xx response is not retained, so a transient failure remains retryable.
  • If the key cannot be claimed at all, the request is refused with 503 idempotency_unavailable and nothing is written. Retry with the same key.

A reservation left behind by a request that never finished is released after five minutes, so an interrupted call cannot strand its key for the full retention window.

Completed responses are retained for 24 hours by default; deployments can configure the window. Generate keys from a stable business operation ID, not a random value on every retry. Never put a credential or tenant secret in a key.

Webhook delivery deduplication

Webhook ingestion uses a stable sender delivery identifier from Idempotency-Key or a supported provider header. It retains accepted delivery identity for seven days. A duplicate receives 202 with {"status":"duplicate"} and no new run ID.

When no stable header exists, Hodoflow falls back to a time-bucketed hash of the trigger token and raw body. That protects short retries but is not a substitute for a provider delivery ID. See Retries and troubleshooting.

Retry matrix

Result Retry? Action
Network timeout before any response Yes Retry with the same idempotency/delivery key
429 Yes Wait the Retry-After duration, then retry
500 or 503 Usually Exponential backoff with jitter and a bounded attempt/time budget
401 Once after exchange Discard cached token, exchange, retry once; then alert
403 No Correct scopes or run-as permissions
404 No unchanged Correct path/ID or object access
409 idempotency conflict No Reconcile key reuse; do not generate a new key blindly
413 or 422 No unchanged Correct body size or validation failure

Reads are naturally repeatable, but still need bounded backoff. GraphQL factQuery is read-only and does not use JSON:API response replay. A GraphQL validation or complexity error is permanent until the document or variables change.

Operational rules

  • Cap attempts and total elapsed time.
  • Add jitter so many workers do not retry together.
  • Respect the credential-wide rate budget.
  • Preserve method, path, body bytes, and idempotency key across retries.
  • Record x-request-id, status, attempt, and non-secret operation identity.
  • Never log bearer tokens, client secrets, trigger URLs, or sensitive bodies.

Practice the contract in Perform an idempotent write.

Related documentation