TypeScript SDK Reference
Authoritative reference for the official Claimful TypeScript SDK
(@claimful/sdk, source at packages/sdk-ts). Pair this document with the
SDK semantic-versioning covenant — breaking-change rules and the
90-day deprecation-overlap window live there.
Document marker: CLAIMFUL_SDK_TS_SENTINEL
1. Install
pnpm add @claimful/sdk
Requirements:
- Node
>=22(matchesengines.nodeinpackage.json). - A
fetchimplementation (Node 22 ships a global; you may inject your own). - A merchant secret API key for server-side calls (
mch_live_.../mch_test_...). The browser widget uses a separate publishablewk_key — never send a secretmch_key from the browser.
Dual ESM + CJS with TypeScript declarations.
2. Client construction
import { Claimful } from '@claimful/sdk';
const client = new Claimful({
apiKey: process.env.CLAIMFUL_API_KEY!,
baseUrl: 'https://api.claimful.ai/api/v1', // optional, default shown
fetch: globalThis.fetch, // optional override
retry: { // optional, defaults shown
baseDelayMs: 250,
jitter: true,
maxAttempts: 3,
multiplier: 2,
retryAfterCeilingSeconds: 600,
},
});
ClaimfulClient is an alias of Claimful. Construction throws
ClaimfulValidationError for an empty apiKey or a missing fetch
implementation.
Retries fire on HTTP 408, 429, 500, 502, 503, 504. Retry-After is
honoured up to retryAfterCeilingSeconds.
Idempotency contract (Table 34 / ADR 0018):
- Keys and their cached responses are valid for 24 hours. Within that window, resending the same key replays the original response with no side-effects.
- Reusing the same key with a different request body returns
409 idempotency_conflict. Generate a new key for a distinct request. - A missing or invalid key returns
400 idempotency_key_required. The SDK auto-injects UUIDv4 keys so raw-HTTP callers are the primary audience for this error.
3. Public methods
client.offers
| Method | HTTP |
| --- | --- |
| offers.quote(input, options?) | POST /offers/quote (returns QuoteResponse) |
| offers.confirm(input, options?) | POST /offers/confirm |
| offers.decline(input, options?) | POST /offers/decline |
| offers.void(input, options?) | POST /offers/void |
interface QuoteRequest {
currency: 'USD' | string;
customerEmail?: string;
orderAmountCents: number;
orderId: string;
protectionProgramId: string;
}
interface QuoteResponse {
expiresAt: string;
fee: number;
quoteId: string;
}
interface QuoteConfirmRequest {
quoteId: string;
quoteToken: string;
}
client.claims
| Method | HTTP |
| --- | --- |
| claims.list(params?) | GET /merchant/claims (optional limit, status) |
The bare GET /claims path is portal/consumer-side only; the merchant-key claim
list lives under /merchant/claims (FER-189).
client.refunds
| Method | HTTP |
| --- | --- |
| refunds.create(orderId, payload?, options?) | POST /orders/{orderId}/refund |
The refund is keyed by orderId in the path; the payload bag is reserved for
future API versions.
client.webhooks
Two aliases of the top-level verifyWebhook export — client.webhooks.verify
and client.webhooks.verifyWebhook.
client.headless
Tier-1.5 helpers (ADR 0055 proposed). Body is canonicalised via
canonicalStringify() so the wire bytes match the PHP server-side
App\Headless\CanonicalJson::stringify AND the PHP SDK byte-for-byte.
| Method | HTTP | Response type |
| --- | --- | --- |
| headless.quote(input, options?) | POST /headless/quote | HeadlessQuoteResponse |
| headless.confirm(input, options?) | POST /headless/confirm | HeadlessProtectedPurchaseResponse |
| headless.decline(input, options?) | POST /headless/decline | HeadlessDeclineResponse |
The exported constant HEADLESS_DISCLOSURE_PHRASE holds the byte-string
"This is not insurance". The server response disclosure_text always equals
this constant. Use it only for byte-equality assertions — do not use it
as visible disclosure copy. Any consumer-facing UI must render the full
two-sentence phrase verbatim:
This is not insurance. This is a contractual refund guarantee provided by Claimful Inc.
verifyWebhook(rawBody, header, secret, toleranceSeconds?)
Top-level export. Returns a discriminated union:
type WebhookVerificationResult =
| { verified: true; timestamp: number }
| {
verified: false;
reason:
| 'header_invalid'
| 'signature_mismatch'
| 'timestamp_invalid'
| 'timestamp_outside_tolerance';
};
HMAC-SHA-256 via crypto.subtle.sign, constant-time hex comparison.
canonicalStringify(value)
Lower-level helper for callers that want byte-identical canonical JSON without going through the headless helpers.
4. Error types
| Class | Extends | Thrown when |
| --- | --- | --- |
| ClaimfulValidationError | Error | Construction or arg validation. |
| ClaimfulHttpError | Error | Non-2xx response (not 429). Fields: httpStatus, body, requestId, idempotencyKey. |
| ClaimfulRateLimitError | ClaimfulHttpError | HTTP 429. Adds retryAfter. |
5. End-to-end example
import {
Claimful,
ClaimfulHttpError,
ClaimfulRateLimitError,
HEADLESS_DISCLOSURE_PHRASE,
verifyWebhook,
} from '@claimful/sdk';
const claimful = new Claimful({ apiKey: process.env.CLAIMFUL_API_KEY! });
const offer = await claimful.offers.quote({
currency: 'USD',
orderAmountCents: 12_900,
orderId: 'order_1234',
protectionProgramId: 'pp_abc',
});
try {
await claimful.offers.confirm(
{ quoteId: offer.quoteId, quoteToken: '<server-token>' },
{ idempotencyKey: 'example-order-1234' },
);
} catch (err) {
if (err instanceof ClaimfulRateLimitError) {
await new Promise((r) => setTimeout(r, (err.retryAfter ?? 5) * 1000));
} else if (err instanceof ClaimfulHttpError) {
console.error('http', err.httpStatus, 'request', err.requestId);
throw err;
}
}
const headless = await claimful.headless.quote({
orderAmountCents: 12_900,
protectionProgramId: 'pp_abc',
});
if (headless.disclosure_text !== HEADLESS_DISCLOSURE_PHRASE) {
throw new Error('disclosure drift');
}
const result = await verifyWebhook(
rawBody,
request.headers['x-claimful-signature']!,
process.env.CLAIMFUL_WEBHOOK_SECRET!,
);
6. Confirmation email requirements [iii-3-8]
After a consumer opts in at checkout, your order confirmation email must include the following four items (contractual requirement — Merchant Agreement §3.2):
-
Protection reference number — the
CLM-YYYY-XXXXXXXXvalue returned in the confirm response (Crockford Base32 format, regex^CLM-\d{4}-[0-9A-HJKMNP-TV-Z]{8}$). -
"Not insurance" disclosure — include this exact phrase verbatim:
This is not insurance. This is a contractual refund guarantee provided by Claimful Inc.
-
48-hour free-look cancellation notice — inform the consumer they may request a full fee refund within 48 hours of purchase by visiting
claimful.claimswith their protection reference number. -
Covered-reasons summary or link — either list the 12 covered reasons or link to the consumer's coverage details page at
claimful.claims.
Note: The claimful.claims coverage deep-link is delivered by Claimful
directly via its own welcome email (welcome_protected). Your confirmation email
does not need to reproduce the tokenized link — a plain claimful.claims
reference is sufficient.
Sample template
Subject: Your order is confirmed + purchase protection is active
Hi [Customer Name],
Thank you for your order. You've added Claimful purchase protection
(reference: CLM-2026-7H2KQ9PM).
If you cannot attend for a covered reason, request a refund at claimful.claims
within 30 days of your event.
Free-look cancellation: You may cancel this protection for a full fee refund
within 48 hours of purchase at claimful.claims.
Disclosure: This is not insurance. This is a contractual refund guarantee
provided by Claimful Inc.
7. Cross-references
- SDK semver covenant
packages/sdk-ts/CHANGELOG.md- PHP SDK reference