Integration Matrix — what works where
Document marker: CLAIMFUL_INTEGRATION_MATRIX_SENTINEL
Claimful reaches your store through several channels — the embedded browser widget, the REST API, the PHP and TypeScript SDKs, and the merchant dashboard. Not every capability is available on every channel, and that is deliberate: the publishable widget key is public by design, so it can only ever quote, confirm, and decline an offer. This page is the honest map of which capability is reachable on which channel, plus where the offer can appear across real storefronts.
If you just want to ship, start with the Quickstart. This page is the reference you come back to when you are deciding which surface to build against.
1. Capability × channel
Read a cell as: can I do this thing on this channel today?
- Yes — reachable and correct on this channel.
- Server only — reachable, but only with a secret
mch_key (never in a browser). - By design: no — the channel deliberately cannot do this. It is a security boundary, not a missing feature.
- Dashboard — done in the merchant dashboard, not programmatically.
- Not applicable — the capability is not a function of this channel.
| Capability | Widget (wk_) | REST API | PHP SDK | TypeScript SDK | Dashboard |
| --- | --- | --- | --- | --- | --- |
| Quote an offer | Yes | Yes | offers.quote() | offers.quote() | Not applicable |
| Confirm a protection plan | Yes | Yes | offers.confirm() | offers.confirm() | Not applicable |
| Decline an offer | Yes | Yes | offers.decline() | offers.decline() | Not applicable |
| Void a confirmed plan | By design: no | Server only | offers.void() | offers.void() | Not applicable |
| Report a merchant-issued refund | By design: no | Server only | refunds.create(orderId, …) | refunds.create(orderId, …) | Dashboard |
| List refund requests | By design: no | Server only | claims.list() | claims.list() | Dashboard |
| List protected purchases | By design: no | Server only | purchases.list() | purchases.list() | Dashboard |
| Manage webhook endpoints (create / read / update / delete / rotate secret) | By design: no | Server only | Raw transport | Raw transport | Dashboard |
| Verify a webhook signature | Not applicable | Local check | webhooks.verifyWebhook() | webhooks.verifyWebhook() | Not applicable |
| Manage API keys (mint / rotate / revoke) | By design: no | Not yet | Not yet | Not yet | Dashboard |
| Choose test vs live mode | Key-selected | Key-selected | Key-selected | Key-selected | Chosen at key mint |
Notes on the honest cases
- Void, refund, refund-request list, protected-purchases list — "By design: no"
on the widget. The widget's publishable
wk_key lives in your page's HTML, so it is restricted to the quote/confirm/decline offer flow. Anything that moves money after the sale, or reads back your account data, requires a secretmch_key from your server. This is enforced at a single chokepoint, not by omission. - Report a merchant-issued refund is keyed by
orderId. Both SDKs callPOST /orders/{orderId}/refundwith a matchingrefunds.create(orderId, …)signature. The protected order moves tomerchant_refunded, the plan is voided, and Claimful credits your fee share on the next settlement statement — no second consumer refund is issued. - Webhook endpoints — "Raw transport" in the SDKs. The SDKs ship a
first-class signature verifier (
webhooks.verifyWebhook(rawBody, header, secret)), but they do not wrap endpoint create/update/delete/rotate. Use the SDK's generic HTTP methods, or call the REST routes directly. Over REST the create response reveals the signing secret exactly once; a headless integrator can also mint a fresh secret once viaPOST /v1/webhooks/endpoints/{id}/secret/rotate. Editing an endpoint URL or its subscribed events is aPATCHand does not rotate the secret. - Manage API keys — "Not yet". Programmatic key rotation/revocation is not exposed on the REST API or SDKs today; mint and rotate keys in the dashboard. Self-service key management is being designed alongside per-event keys.
- Test vs live is a property of the key, not a toggle. A
wk_test_…/mch_test_…key targets the test environment; the_live_variants target production. There is no switch to flip at request time.
See the TypeScript SDK reference and the PHP SDK reference for exact method signatures, and the Widget Events reference for the frozen browser event shapes.
2. Embeddability — where the offer can appear
The widget is a single <claimful-widget> custom element plus one script tag.
The table below records how it behaves inside the hostile variety of real
merchant pages. Every row is exercised hermetically in the widget
embed-compatibility suite, so these are guarantees, not guesses.
Across every row, two rules always hold: the single-use quote token never leaves the browser closure (never in the DOM, storage, a URL, or an order attribute), and a blocked or absent widget never blocks your checkout — it fails open and simply shows no offer.
| Host page scenario | Result | What you get | | --- | --- | --- | | Plain HTML (the canonical snippet) | Works | Boots to the offer with styles and lifecycle events | | Single-page app that re-mounts on client navigation | Works | Re-registers cleanly — exactly one quote per mount, no duplicate calls | | Strict CSP with a per-request script nonce | Works | Runtime runs and styles apply with no CSP violations (allowlist the widget + API hosts) | | Consent manager that injects the runtime seconds later | Works | Still boots when the script arrives after the page | | Tag manager (async) injecting the snippet | Works | Element present before the script still boots once the script lands | | Ad-blocker blocks the runtime entirely | Degrades safely | Silent absence — no broken layout, no console error, checkout proceeds | | Dark host page | Works | The widget stays visually isolated in its own closed Shadow DOM (light-only by design) and still boots |
If a widget embed looks like it is doing nothing, check the browser console for a
[claimful-widget] diagnostic line and see
Widget Troubleshooting.
CSP essentials
A strict CSP (the browser's script + network allowlist) must admit the widget's script and network hosts, or the widget fails closed:
script-src https://widget.claimful.ai(or a per-request'nonce-…')connect-src https://widget.claimful.ai https://api.claimful.ai
The two directives above are the essentials. If your CSP also enforces nonces or
SRI hashes, apply them to the widget script tag; the widget renders into a closed
Shadow DOM, so allow its inline style-src as well.
Browser support
Browsers with Custom Elements support (all current Chrome, Edge, Firefox, and
Safari) register <claimful-widget> directly. Browsers without Custom
Elements fall back automatically to a hosted <iframe> bridged over a strict,
origin-checked postMessage channel — the integration snippet is identical
either way; you never choose the transport.
3. Platform surfaces (Shopify)
A commerce platform is not one surface — a storefront page, a hosted checkout, and your server webhook each confirm protection differently, and they do not share one mechanism. Shopify is the worked example; the same shape applies to other carts.
Since 2024, Shopify checkout enforces a strict CSP that blocks arbitrary third-party scripts, so the script embed cannot run in checkout. Use the native checkout extension to show the offer and confirm it server-side from the order webhook.
| Surface | Renders on | Offer display | Confirms via | Key | Reliability |
| --- | --- | --- | --- | --- | --- |
| Theme script embed | Online-store pages (product, cart) | <claimful-widget> | Buyer confirm inside the widget | wk_ | Works on storefront pages; blocked in checkout |
| Checkout UI extension | Checkout page | Native Shopify block (quote fetched with the wk_ key) | Records opt-in as a checkout attribute; no browser confirm | wk_ | Offer is reliable; confirm is delegated to the webhook below |
| Order-webhook server confirm | Your server (orders/create) | None (server-side) | Headless quote + confirm, keyed by quoteId | mch_ | Reliable — the money-affecting path, and the fallback for any surface above |
The order-webhook path is the dependable one: verify the webhook HMAC, then call
headless.quote + headless.confirm with your secret mch_ key. It needs no
quote token in the browser, so it holds even when checkout blocks scripts. The
PHP and TypeScript SDK references carry the exact
server-side method signatures for this path. WooCommerce, Magento, and
custom/headless carts follow the same three-surface pattern.
Related pages
- Quickstart — zero to your first protected purchase.
- TypeScript SDK reference · PHP SDK reference — exact method signatures for every "Yes" cell above.
- Widget Events — the frozen browser event shapes.
- Widget Troubleshooting — decoding a silent embed.