Idempotency

The Sent API v3 supports idempotency for safely retrying requests without accidentally performing the same operation twice. With an idempotency key, the API guarantees at-most-once execution for a mutation: duplicate requests with the same key return the original response instead of executing again.

For retry loops and client implementations that use this contract, see How to retry Sent API requests safely.


How It Works

  1. The client generates a unique key for each distinct operation
  2. The client sends it in the Idempotency-Key header
  3. The API caches the successful response for 24 hours
  4. Duplicate requests with the same key return the cached response
POST /v3/messages
Idempotency-Key: msg_send_abc123
Content-Type: application/json

{
  "to": ["+1234567890"],
  "template": {
    "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "parameters": {
      "customer_name": "John"
    }
  }
}

Supported Requests

Idempotency applies to every POST, PUT, and PATCH request to a /v3 endpoint that carries the Idempotency-Key header.

ConditionBehavior
POST, PUT, or PATCH to /v3/* with Idempotency-KeyIdempotency processing applies
POST, PUT, or PATCH without Idempotency-Key, or with an empty oneRequest executes normally; the header is optional
POST, PUT, or PATCH with a key that doesn't meet the format requirementsThe key is ignored and the request executes normally, without idempotency
GET or DELETE requestThe Idempotency-Key header is ignored

Idempotency Key Format

Requirements

  • Length: 1-255 characters
  • Characters: Letters, digits, hyphens (-), and underscores (_)
  • Scope: Per customer account
  • Expiration: 24 hours

A key that is longer than 255 characters or contains any other character is not rejected. The API ignores it and executes the request without idempotency protection, so a retry with the same key executes again. Validate keys on the client side if you depend on deduplication.

Valid Examples

req-abc123
send_msg_001
webhook-retry-1
invoice-payment-2024-001
create-contact-john-doe

Invalid Examples

The API ignores these keys and executes the request without idempotency:

req abc 123       # Contains spaces
create@contact    # Contains special character @
send.msg.001      # Contains periods

Response Caching

RuleBehavior
Cached responsesOnly successful (2xx) responses are cached
Error responsesNot cached, so a retry with the same key executes the request again
Cache lifetime24 hours from the original response
Response size limitResponses larger than 5 MB are not cached; duplicates execute again
Replayed contentThe original status code and body, byte for byte

A cached response is replayed only to the same request. The API stores a fingerprint of the original request's method, path (including the query string), and body alongside the response. A request that reuses the key within 24 hours with a different method, path, query string, or body is neither replayed nor executed: it's rejected with 422 Unprocessable Entity and error code CONFLICT_005. The body is compared byte for byte, so a semantically identical payload serialized differently (for example, with keys in a different order) counts as a different request. After the 24-hour lifetime the key expires, and a request that reuses it executes as a new operation.

{
  "success": false,
  "status": 422,
  "error": {
    "code": "CONFLICT_005",
    "message": "This Idempotency-Key was already used for a different request. Send a new key for a new operation, or repeat the original request unchanged to receive its response.",
    "doc_url": "https://docs.sent.dm/reference/api/idempotency"
  },
  "meta": { ... }
}

Never reuse a key for a different operation. Generate a new key for each new operation, and resend the original request unchanged when retrying.

If the cache backing idempotency is unavailable, the API rejects idempotent requests with 503 Service Unavailable and error code SERVICE_001 rather than risking a duplicate execution.


Concurrent Requests

When a duplicate request arrives while the original request with the same key is still executing:

  • The duplicate waits up to 5 seconds for the original to complete, then returns the cached response. If the duplicate's method, path, or body differs from the original's, it's rejected with 422 and CONFLICT_005 instead.
  • If the original has not completed within that window, the duplicate is rejected with 409 Conflict and error code CONFLICT_001. Retrying with the same key after the original completes returns the cached response.
{
  "success": false,
  "status": 409,
  "error": {
    "code": "CONFLICT_001",
    "message": "A request with this Idempotency-Key is currently being processed. Please retry shortly.",
    "doc_url": "https://docs.sent.dm/reference/api/idempotency"
  },
  "meta": { ... }
}

Response Headers

Idempotent-Replayed

When a cached response is returned, the Idempotent-Replayed: true header is included:

HTTP/1.1 201 Created
Idempotent-Replayed: true
X-Original-Request-Id: req_original_abc123
X-Request-Id: req_replay_def456
Content-Type: application/json

{
  "success": true,
  "data": { ... },
  "error": null,
  "meta": { ... }
}

Header Reference

HeaderDescription
Idempotent-Replayedtrue if this is a cached response
X-Original-Request-IdRequest ID of the original request
X-Request-IdRequest ID of the current request

Idempotency vs Sandbox Mode

Both features help with safe API usage, but serve different purposes:

FeaturePurposeSide EffectsResponse
Sandbox ModeValidate requestsNone (validation only)Fake/sample data
IdempotencyPrevent duplicatesOnly on first requestReal/cached data

The two features can be combined in one request: a sandbox: true payload sent with an Idempotency-Key is validated without side effects, and its response is cached and replayed like any other. Refer to the Sandbox Mode reference for the sandbox contract.


On this page