casset/docs
FeaturesOpen app
docs indexreference
00Overview01Thesis02Architecture03System reality04Roadmap05Investor brief06Technical brief07Full tech HTML08Casset Apps09Headless API10Playback11Audio pipeline12Commerce13Base anchoring14Hook system15Music video16Theming17Creator guide18Glossary
Headless APIDeployed · unsupported
QuickstartExamplesAPI ReferenceSDK
Advanced
React adapterStarter detailsError handlingRetries & timeoutsUse casesRequest IDs & safe loggingAPI versioningLocal sandboxLocal webhooksAvailability & limitationsFAQChangelogDesign principles
Headless API · Retries

Bound retries around reads. Different rules for webhook rehearsal.

The TypeScript client retries only idempotent GET reads under a small, configurable budget. The local webhook sandbox has a separate three-attempt policy. Do not apply one policy to the other.

Default Headless client behavior is one initial attempt plus two retries, with an 8-second timeout per attempt and a 5-second maximum delay.

Current HTTP availability

This client policy applies to the deployed public v1 GET routes and local sandbox evaluation. Public v1 is unauthenticated but unsupported for production use and may intentionally fail closed with 503 ResourceUnavailable. No uptime, availability, or production-use commitment is offered; production developer credentials remain unavailable.

HTTP client policy

Headless TypeScript client retry policy
ConditionAutomatic retry?DelayFinal error
429 with retryable: trueYesRetry-After when valid, otherwise exponentialRateLimited when attempts are exhausted
500–599 with retryable: trueYesRetry-After when valid, otherwise exponentialTyped envelope error or status fallback
429 or 500–599 with no valid error envelopeYesRetry-After when valid, otherwise exponentialStatus-mapped CassetApiError
429 or 500–599 with retryable: falseNoNoneTyped error immediately
Network or per-attempt timeout failureYesExponentialIntegrationFailure with retryable: true
External AbortSignal already aborted, or aborted during transportNo further retryNoneIntegrationFailure with retryable: false
408 Request Timeout responseNoNoneUsually IntegrationFailure; status remains 408
425 Too Early responseNoNoneUsually IntegrationFailure; status remains 425
400, 401, 402, 403, 404, or 422NoNoneMapped typed error
Malformed 2xx or request-ID mismatchNoNoneIntegrationFailure with retryable: false

408 and 425 are intentionally different

The Headless HTTP client's transient status set is exactly429 plus 500–599. A received 408 or425 is not automatically retried, even though a status fallback may classify the resulting IntegrationFailure as retryable. The local webhook sender does retry 408 and 425.

Attempts and delays

Retry configuration
OptionDefaultRangeMeaning
maxRetries20–5Retries after the first attempt. Default: at most 3 total attempts.
timeoutMs8,000 ms1–60,000 msPer-attempt timeout, not a total request deadline.
maxRetryAfterMs5,000 ms0–30,000 msCaps both Retry-After and exponential delays.

Without Retry-After, delay ismin(100 × 2^attemptIndex, maxRetryAfterMs). With defaults, the client waits 100 ms before attempt two and 200 ms before attempt three. It adds no jitter.

Retry-After

  • A non-negative finite numeric value accepted by JavaScript Number is interpreted as seconds, converted to milliseconds, rounded, and capped.
  • An HTTP date is converted to a non-negative delay and capped.
  • An invalid value is ignored and exponential delay is used.
  • The parsed bounded value is available as error.retryAfterMs after failure.

Configure the policy

const casset = createCassetClient({
  baseUrl: "http://127.0.0.1:3000",
  timeoutMs: 5_000,
  maxRetries: 1,          // two total attempts
  maxRetryAfterMs: 2_000,
})

Set maxRetries: 0 to disable automatic retries. SetmaxRetryAfterMs: 0 to remove retry waits without removing attempts. There is no separate jitter switch because the implementation adds no jitter.

Worst-case duration

Built-in fetch duration bounds
ConfigurationUpper boundComposition
Default HTTP responsesAbout 34 secondsThree 8-second attempt budgets plus two delays capped at 5 seconds.
Default network or timeout failuresAbout 24.3 secondsThree 8-second attempt budgets plus deterministic 100 ms and 200 ms waits.
Maximum accepted configurationAbout 510 secondsSix 60-second attempt budgets plus five delays capped at 30 seconds.

These bounds apply to the built-in fetch transport, which observes the per-attempt AbortSignal, plus normal scheduler overhead. A custom transport that ignores its signal can outlive timeoutMs, so the SDK cannot state a hard wall-clock bound for that transport.

Failures that never retry

  • Malformed handles, invalid configuration, and invalid request IDs fail before transport.
  • An external signal already aborted at an attempt boundary, 408, 425, 400–404, 422, and any envelope with retryable: false are terminal.
  • The built-in retry sleep is not abort-aware. If cancellation happens during that delay, the client enters the next attempt with an already-aborted signal and then fails terminally; do not treat the signal as an immediate total-deadline timer.
  • Malformed success bodies and request-ID mismatches are terminal contract failures.
  • The client exposes GET only; there is no SDK write method to retry.

Application-level recovery

The SDK has already spent its retry budget when it throws. Do not immediately wrap every call in another tight loop. For interactive UI, show a retry control after a retryable error. For a background job, add a larger, independently bounded schedule and keep the same request ID only for one SDK call—not across unrelated jobs.

Idempotency expectations

Idempotency by surface
SurfaceBehavior
Public v1 readsGET-only and naturally idempotent. They do not accept an Idempotency-Key.
SDK retriesReuse one x-request-id across every attempt of the same logical GET.
Sandbox console mutationsRequire Idempotency-Key, bounded to 128 text characters.
Same key + same action type and payloadReturns the stored result; concurrent duplicates coalesce.
Same key + different payload within one action typeReturns 409 INVALID_REQUEST.
Same key text + different action typeUses a separate action-scoped record and does not conflict.
One-time values on replayStored credential and webhook results redact plaintext tokens and signing secrets.

Local sandbox webhook policy

Sandbox rehearsal only

These delivery rules are deterministic local development behavior. Durable production webhook subscriptions, delivery, retry, replay, and audit history are unavailable.
Local sandbox webhook retry policy
OutcomeRetry?Notes
2xxNoDelivery succeeds.
408, 425, 429, or 500+YesRecorded as HTTP_TRANSIENT.
Other non-2xx, including 400–407, 409–424, and 426–499NoRecorded as HTTP_TERMINAL.
Network failureYesRecorded as NETWORK.
No response within 2 secondsYesRecorded as TIMEOUT.
  • Maximum: three total attempts.
  • Timeout: two seconds per attempt.
  • Sandbox delay after a retryable failure: 25 ms after attempt one, then 50 ms after attempt two.
  • The delay is deterministic and deliberately small for tests; it is not the HTTP client's exponential policy.

Replay and duplicate suppression

Replaying a sandbox webhook preserves the immutable event ID and exact event body, then creates a new delivery ID. The receiver validates the exact event contract before deduplication and reports whether the delivery/event pair was already received. Concurrent console replay with the same idempotency key resolves to one delivery result.

Webhook signature window

  • The HMAC input is timestamp + "." + exactBody.
  • The receiver verifies the exact bytes; adding a newline invalidates the signature.
  • Signatures outside a five-minute window are rejected.
  • The receiver reads at most 128 KiB of UTF-8 request data.
© Casset 2026
Trust & PrivacyTerms