Retries and idempotency
createRetryingFetch() retries a request after a network error, a timeout, 408, 429 or a 5xx, with exponential backoff, and waits as long as Retry-After asks. Every POST and DELETE gets an Idempotency-Key, the same on each retry, so a retried request never runs twice.
Every method also takes RequestOptions as its last argument: your own idempotencyKey, extra headers (such as MoreVoice-Version) and an abort signal.
// Retries on network errors, timeouts, 408, 429 and 5xx, honouring Retry-After.// POST and DELETE carry an Idempotency-Key, so a retry never runs twice.import { createClient, createResources, createRetryingFetch } from "@morevoice/sdk";
const mv = createResources({ client: createClient({ baseUrl: "https://api.morevoice.ai/v1", auth: process.env.MOREVOICE_API_KEY, fetch: createRetryingFetch({ maxRetries: 3, onRetry: (info) => console.warn(`Retry ${info.attempt} in ${info.delayMs} ms: ${info.method} ${info.url}`), }), }),});
export default mv;Exported from @morevoice/sdk (source: packages/sdk-ts/src/lib/request.ts).
IDEMPOTENCY_HEADER
Section titled “IDEMPOTENCY_HEADER”const IDEMPOTENCY_HEADER = "Idempotency-Key";DEFAULT_MAX_RETRIES
Section titled “DEFAULT_MAX_RETRIES”const DEFAULT_MAX_RETRIES = 2;RetryInfo
Section titled “RetryInfo”interface RetryInfo { /** 1 for the first retry. */ attempt: number; /** How long it waits before this retry. */ delayMs: number; method: string; url: string; /** The status that caused the retry, when there was a response. */ status?: number; /** The network error or timeout that caused the retry. */ error?: unknown; idempotencyKey?: string;}RetryingFetchOptions
Section titled “RetryingFetchOptions”interface RetryingFetchOptions { /** The underlying fetch (default: the global one, resolved per request). */ fetch?: FetchLike; /** Retries after the first attempt (default 2; 0 disables). */ maxRetries?: number; /** Per-attempt time limit until the response headers arrive; a timeout is retried like a network error. */ timeoutMs?: number; /** First backoff (default 500 ms), doubled per retry. */ initialDelayMs?: number; /** Backoff ceiling (default 8 s). */ maxDelayMs?: number; /** Longest Retry-After / RateLimit wait honoured (default 60 s); a longer one returns the response instead. */ maxRetryAfterMs?: number; /** Idempotency-Key generator (default uuidv7). */ idempotencyKey?: () => string; /** Observe retries (logging, metrics). */ onRetry?: (info: RetryInfo) => void; /** Jitter source (tests). */ random?: () => number;}uuidv7()
Section titled “uuidv7()”RFC 9562 UUIDv7: 48-bit Unix-ms timestamp + 74 random bits, so keys sort by creation time.
function uuidv7(now: number = Date.now()): string;isRetryableResponse()
Section titled “isRetryableResponse()”Whether a response is worth retrying (doc 07 §7.1; WS05 v1 idempotency and rate limits).
function isRetryableResponse(response: Response): Promise<boolean>;createRetryingFetch()
Section titled “createRetryingFetch()”A fetch with idempotency keys and retries, for the generated client. Same signature as fetch; resolves with the
final response (successful or not, the generated client maps errors) or rejects with the last network error.
function createRetryingFetch(options: RetryingFetchOptions = {}): FetchLike;FetchLike
Section titled “FetchLike”The fetch signature the helpers call: the global fetch, or any compatible implementation (undici, a mock…).
type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;rateLimitResetMs()
Section titled “rateLimitResetMs()”Milliseconds until the exhausted rate-limit window resets, from the RateLimit headers: the longest t among
policies with nothing remaining (or, when none says so, the longest t). Undefined when absent.
function rateLimitResetMs(headers: HeaderGetter): number | undefined;retryAfterMs()
Section titled “retryAfterMs()”Milliseconds from Retry-After, or undefined when absent / unparseable.
function retryAfterMs(headers: HeaderGetter, now: number = Date.now()): number | undefined;Exported from @morevoice/sdk (source: packages/sdk-ts/src/facade-runtime.ts).
RequestOptions
Section titled “RequestOptions”Per-request options every facade method accepts as its last argument.
interface RequestOptions { /** * Sent as `Idempotency-Key` (POST and DELETE). Reuse it to retry a request safely for 24 hours: a retried * `calls.create` never dials twice. When omitted, the client's retry layer generates one per call (WS06-W1-07). */ idempotencyKey?: string; /** Extra request headers, e.g. `MoreVoice-Version` or an operation's own header parameters. */ headers?: Record<string, string>; /** Aborts the request (and, for lists and streams, stops fetching further pages or events). */ signal?: AbortSignal;}