ActionBoxDOCS

Idempotency and Dedupe

Prevent duplicate requests while keeping repeated occurrences visible to reviewers.

ActionBox has three related protections with different jobs:

MechanismProtectsWhat repeats do
Idempotency-Key headerTransport retries of a create or outcome requestReturns the original result for the same logical request
dedupe_key body fieldRepeated occurrences of the same open workflowUpdates one open Action and increments occurrence_count
action_version + fingerprintDecisions made from a reviewed snapshotRejects a stale decision with 409 ACTION_CHANGED

Do not use one as a substitute for another.

Protect Action creation

Send a stable, unique key for the logical operation:

curl -fsS -X POST https://api.actionbox.cloud/v1/actions \
  -H "Authorization: Bearer $ACTIONBOX_SOURCE_KEY" \
  -H "Idempotency-Key: deploy-build-42" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Approve the production deploy",
    "dedupe_key": "production-deploy-build-42",
    "options": [
      {"id": "approve", "label": "Approve"},
      {"id": "reject", "label": "Reject"}
    ]
  }'

The key is scoped to the authenticated Source and its live/test environment. ActionBox retains it for 24 hours. During that window:

  • the same key with the same logical request returns the original Action and idempotent_replay: true;
  • the same key with changed request content returns 409 IDEMPOTENCY_KEY_REUSED;
  • a key longer than 255 characters returns INVALID_IDEMPOTENCY_KEY;
  • an empty key is treated as no idempotency key and does not deduplicate a create request.

The key is not global: another Source, or the other environment of the same Source, has its own boundary. Never derive a key from a secret, and never reuse one for an unrelated operation.

Persist the key before sending

Generate or choose the key before the first network attempt and persist it with the worker's job/run record. If the response is lost, retry the exact request with that same key instead of creating a new key.

Merge repeated open occurrences

dedupe_key is scoped to the Source and environment. When a request arrives with a matching open Action, ActionBox updates that Action, increments its occurrence_count, and records an update event. It does not queue a terminal webhook merely because the occurrence was deduped.

If the matching Action is already resolved, cancelled, or expired, the next request creates a new Action. Dedupe therefore keeps an active incident visible without hiding a later occurrence after the first one is complete.

Use a stable workflow identity for dedupe_key, such as a deployment target and release line. Include a run or attempt identity in Idempotency-Key when each attempt must be retried independently.

Bind a human decision to what was reviewed

Every Action response includes action_version and a canonical SHA-256 fingerprint. A material update—including a dedupe update—advances the version and changes the fingerprint. Send both values from the snapshot that the human reviewed:

{
  "action_version": 2,
  "fingerprint": "sha256:<64 hex characters>",
  "option_id": "approve"
}

If the Action changed after it was displayed, the API returns 409 ACTION_CHANGED with the current binding. Re-read the Action and require the caller to review the new content. This is intentionally fail-closed.

SDK behavior

The SDKs generate an idempotency key automatically when creating an Action, or accept one explicitly when a job needs a stable key:

action = client.create(
    title="Approve the production deploy",
    options=[{"id": "approve", "label": "Approve"}],
    idempotency_key=f"deploy-{build_id}",
)
const action = await client.create(
  { title: "Approve the production deploy", options: [{ id: "approve", label: "Approve" }] },
  `deploy-${buildId}`,
);

Outcome helpers use a deterministic outcome-{action_id} key so an exact transport retry is safe. A different second outcome is rejected because the execution record is immutable.

Next: Errors and safe retries, Decisions and outcomes, or Testing with sandbox keys.

On this page