Exende Docs

Retry API v1

Retry API v1

Turn an outbound HTTPS request into a durable job with retry scheduling, recovery, status polling, attempt history, and an optional signed terminal callback.

Updated September 8, 2026

Overview

Retry schedules attempt one immediately, records execution metadata, and retries only configured temporary failures. It does not persist or return upstream response bodies. Status responses expose the request method and destination hostname, not request secrets.

Base URLhttps://retry.exende.dev
CreatePOST /v1/retries
Paid job price$0.02 USDC through x402 v2
Paid capabilityUp to 8 attempts and a 24-hour lifetime
API-key pathAccount-scoped creation with up to 20 attempts

x402 quickstart

Send an unauthenticated creation request. The first response is HTTP 402 with aPAYMENT-REQUIRED header that advertises x402 v2, exact settlement, 20,000 atomic USDC units, and Baseeip155:8453. Use the returned requirements rather than hardcoding the recipient.

bash

Initial unpaid request

curl -i -X POST \
  https://retry.exende.dev/v1/retries \
  -H "Content-Type: application/json" \
  --data '{
    "request": {
      "method": "GET",
      "url": "https://httpbin.org/status/204"
    },
    "retry": {
      "max_attempts": 8,
      "strategy": "exponential",
      "initial_delay_seconds": 5,
      "max_delay_seconds": 300
    }
  }'

json

HTTP 201 after settlement

{
  "id": "retry_0123456789abcdef0123456789abcdef",
  "status": "pending",
  "attempts": 0,
  "max_attempts": 8,
  "created_at": "2026-08-24T12:00:00.000Z",
  "next_attempt_at": "2026-08-24T12:00:00.000Z",
  "status_url": "https://retry.exende.dev/v1/retries/retry_0123456789abcdef0123456789abcdef",
  "read_token": "ex_retry_read_..."
}
Store read_token securely. It can read only that paid job's status, attempts, and callback status. A paid response also includesPAYMENT-RESPONSE andCache-Control: private, no-store.

Create a Retry job

POST https://retry.exende.dev/v1/retries

FieldRequiredBehavior
request.methodYesGET, POST, PUT, PATCH, or DELETE.
request.urlYesPublic HTTPS URL, at most 2048 bytes.
request.headersNoString values; blocked names and size limits apply.
request.bodyNoJSON-compatible value or string, up to 256 KiB; forbidden for GET.
retry.max_attemptsNoDefault 8; maximum 8 for x402 and 20 for API keys.
retry.strategyNofixed or exponential; default exponential.
retry.initial_delay_secondsNo2–3600; default 5.
retry.max_delay_secondsNo2–3600; default 300 and not below initial delay.
retry_status_codesNoExact retryable HTTP codes; at most 100.
success_codesNoExact success set; omitted means all 2xx.
callback_urlNoPublic HTTPS terminal callback URL.
callback_secretWith callbackClient-generated HMAC secret, 32–512 characters.

If the outbound body is JSON, set Content-Type inside request.headers. Exende serializes the value but does not infer the upstream content type. success_codes and retry_status_codes cannot overlap.

bash

API-key creation with idempotency and callback

curl --fail-with-body -sS \
  -X POST 'https://retry.exende.dev/v1/retries' \
  -H "Authorization: Bearer $EXENDE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: refresh-customer-42-v1' \
  --data '{
    "request": {
      "method": "POST",
      "url": "https://api.example.com/customers/42/refresh",
      "headers": {"Content-Type":"application/json"},
      "body": {"source":"agent"}
    },
    "retry": {
      "max_attempts": 5,
      "strategy": "exponential",
      "initial_delay_seconds": 5,
      "max_delay_seconds": 120
    },
    "retry_status_codes": [408,425,429,500,502,503,504],
    "callback_url": "https://agent.example.com/events/exende",
    "callback_secret": "client-generated-secret-at-least-32-characters"
  }'

Job lifecycle and status

GET /v1/retries/{id}

Authenticate with the same account API key or the matching paid-job read token.

bash

Poll job status

curl --fail-with-body -sS \
  'https://retry.exende.dev/v1/retries/retry_0123456789abcdef0123456789abcdef' \
  -H "Authorization: Bearer ex_retry_read_..."

json

Retrying job

{
  "id": "retry_0123456789abcdef0123456789abcdef",
  "status": "retrying",
  "request": {"method":"POST","destination_host":"api.example.com"},
  "attempts": 1,
  "max_attempts": 5,
  "generation": 1,
  "retry": {
    "strategy": "exponential",
    "initial_delay_seconds": 5,
    "max_delay_seconds": 120
  },
  "last_status_code": 503,
  "last_error_code": null,
  "created_at": "2026-08-24T12:00:00.000Z",
  "updated_at": "2026-08-24T12:00:01.000Z",
  "next_attempt_at": "2026-08-24T12:00:06.000Z",
  "completed_at": null,
  "expires_at": "2026-08-25T12:00:00.000Z",
  "attempts_url": "https://retry.exende.dev/v1/retries/retry_0123456789abcdef0123456789abcdef/attempts"
}
StatusMeaning
pendingCreated and awaiting its first execution claim.
runningAn outbound attempt currently owns the execution lease.
retryingA retryable failure was recorded and another attempt is scheduled.
succeededA configured success status was received.
failedA terminal result occurred or attempts were exhausted.
cancelledAn API-key client cancelled the job.
expiredThe 24-hour lifetime ended before completion.

generation is monotonic concurrency metadata. Clients should make workflow decisions from status and timestamps rather than generation.

Attempts and history

GET /v1/retries/{id}/attempts

Attempt outcomes are success, http_failure, timeout, network_error, blocked_redirect, or cancelled. Entries include timing, status/error metadata, and the scheduled delay without persisting upstream response bodies.

json

Attempt history response

{
  "retry_id": "retry_0123456789abcdef0123456789abcdef",
  "count": 2,
  "attempts": [
    {
      "attempt_number": 1,
      "outcome": "http_failure",
      "status_code": 503,
      "error_code": null,
      "duration_ms": 241,
      "retry_scheduled": true,
      "retry_delay_seconds": 5,
      "retry_after_seconds": null,
      "created_at": "2026-08-24T12:00:01.000Z"
    },
    {
      "attempt_number": 2,
      "outcome": "success",
      "status_code": 204,
      "error_code": null,
      "duration_ms": 198,
      "retry_scheduled": false,
      "retry_delay_seconds": null,
      "retry_after_seconds": null,
      "created_at": "2026-08-24T12:00:07.000Z"
    }
  ]
}

Optional terminal callback

Configure callback_url and callback_secret at creation. Retry schedules one independent delivery workflow for retry.succeeded, retry.failed, or retry.cancelled. Expired jobs do not emit a callback in v1.

http

Callback request headers

Content-Type: application/json
X-Exende-Event: retry.succeeded
X-Exende-Delivery: <stable-delivery-id>
X-Exende-Timestamp: <unix-seconds>
X-Exende-Signature: <hmac-sha256-signature>

json

Signed callback payload

{
  "event": "retry.succeeded",
  "retry_id": "retry_0123456789abcdef0123456789abcdef",
  "status": "succeeded",
  "attempts": 2,
  "max_attempts": 5,
  "last_status_code": 204,
  "last_error_code": null,
  "completed_at": "2026-08-24T12:02:00.000Z"
}

Delivery is at-least-once. Any 2xx response succeeds. Network errors, timeouts, and 408, 425, 429, 500, 502, 503, or 504 are retried up to 8 times within 24 hours. Retries keep the same X-Exende-Delivery value and use a fresh timestamp and signature. Verify HMAC against the exact raw body and deduplicate only after your business operation succeeds.

GET /v1/retries/{id}/callback

This protected endpoint returns safe delivery metadata only. It never exposes callback_url, callback_secret, raw payload, lease data, or internal generation.

Retry policies and idempotency

Retry behavior

Default retryable statuses are 408, 425, 429, 500, 502, 503, and 504. Timeouts and network errors are retryable. Attempt one runs immediately. Fixed delay starts at the configured initial delay; exponential delay doubles by attempt, is capped by max_delay_seconds, and is jittered. A valid Retry-After is a minimum delay capped by the configured maximum.

API-key idempotency

Idempotency-Key is optional, 1–255 characters, scoped to account plus key, and retained for 24 hours. An equivalent replay returns the original 201 response with Idempotency-Replayed: true; a different request returns 409 IDEMPOTENCY_KEY_CONFLICT.

x402 replay safety

Paid clients must include the x402 v2 payment-identifier extension. Reuse one identifier only for retries of the same logical paid creation. Exende binds it to the endpoint, payment requirements, and full request. This prevents duplicate Exende jobs but does not make a non-idempotent upstream operation exactly-once.

API-key-only operations

EndpointBehavior
POST /v1/retries/{id}/cancelCancels a non-terminal account-owned job. A request already sent upstream cannot be recalled.
GET /v1/usage?from=&to=Account-scoped outbound attempt usage. from is inclusive, to exclusive, and the maximum span is 90 days.

Paid x402 jobs are covered by the fixed job price and do not appear as API-key billable attempts. A paid-job read token cannot cancel a job or read aggregate usage.

Errors, security, and limits

Clients should branch on error.code rather than message. Retry accepts public HTTPS destinations only, blocks IP literals and private/internal/Exende hosts, revalidates up to three same-origin redirects, and applies a 20-second timeout across the complete chain.

HTTPError code
400INVALID_REQUEST
400INVALID_URL
400URL_NOT_ALLOWED
400INVALID_HEADERS
400TOO_MANY_ATTEMPTS
400INVALID_IDEMPOTENCY_KEY
400INVALID_USAGE_RANGE
400PAYMENT_IDENTIFIER_REQUIRED
401MISSING_AUTHORIZATION
401INVALID_AUTHORIZATION
401INVALID_API_KEY
401INVALID_STATUS_TOKEN
402PAYMENT_REQUIRED
402PAYMENT_SETTLEMENT_FAILED
404RETRY_NOT_FOUND
404NOT_FOUND
409RETRY_ALREADY_COMPLETED
409IDEMPOTENCY_KEY_CONFLICT
409PAYMENT_IDENTIFIER_CONFLICT
409PAYMENT_IN_PROGRESS
413PAYLOAD_TOO_LARGE
500INTERNAL_ERROR
503PAYMENT_RECONCILIATION_PENDING

json

Error response

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Human-readable explanation."
  }
}

Production limits

API version1.0.0
Paid job price$0.02 USDC
Paid maximum attempts8
API-key maximum attempts20
Maximum job lifetime24 hours
Default policy8 attempts, exponential, 5s initial, 300s maximum
Retry delay range2–3600 seconds
Outbound timeout20 seconds across the redirect chain
RedirectsUp to 3, same origin only
Outbound body256 KiB maximum
Creation JSON320 KiB maximum
NetworkBase Mainnet · eip155:8453
RetentionApproximately 30 days for terminal jobs and attempt/callback records; approximately 90 days for usage events.

OpenAPI and Bazaar discovery

The static OpenAPI 3.1 specification describes both creation paths and every public Retry endpoint. The paid POST resource is also currently discoverable through Coinbase x402 Bazaar with the exact $0.02 Base USDC requirement.