- the widget has a v2 subscriber token for this customer (below), and
- the project has a webhook URL in Integration → Webhooks.
data-cuprice-manage-url, as before.
Each customer gets offers at most once per cooldown (6 months by default). Customers on yearly billing are only offered the cheaper plan. Nothing is offered while an accepted discount or pause is still running.
1. Pass a v2 subscriber token
The v2 token tells Cuprice who the customer is, which plan they are on and how they pay, signed with your project’s SDK secret so it can’t be edited in the browser. Mint it on your server and put it in the widget container.
Pass a v2 token only for a subscription that is active and not already set to cancel. You know that; Cuprice doesn’t.
Node.js
POST /api/v1/projects/:slug/sdk with { "subscriberId", "plan", "interval", "amount" } returns a subscriberTokenV2 valid for 24 hours.
2. Add your webhook URL
In the project sidebar, open Integration → Webhooks and save anhttps:// URL. Cuprice creates a signing secret (whsec_…) on the first save. Copy it to your server. Send test event sends a retention.test event and shows the result.
Addresses on private or local networks are refused.
3. Handle the webhook
Cuprice sends aPOST with a JSON body:
discount, downgrade and pause is set, matching kind:
A test event has the same envelope, with
"type": "retention.test" and "data": { "test": true }.
Headers:
Verify the signature
Check the signature against the raw request body, before parsing it, and reject anything more than 5 minutes old.Node.js
Reply
Cuprice waits up to 5 seconds for your reply, and the customer sees the result right away:
If applying the offer takes longer than 5 seconds, answer
202 right away and confirm through the API when you’re done.
Retries and idempotency
Every attempt for an event carries the sameid. Store the ids you have applied and ignore repeats.
A notice that wasn’t received is retried about 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours later: 6 attempts in all. If every attempt fails, the project owner gets one email, and Integration → Webhooks lists the delivery with a Resend button that sends the same event again.
If the project’s last 3 accepted offers all failed to deliver, the widget stops showing offers until a delivery or a test event succeeds.
Confirm later, or catch up
Confirm an offer you answered with “received”, or correct an earlier answer. The latest answer counts. Use an API key with write access.status is applied or rejected, with an optional note of up to 500 characters. Sending the same answer again changes nothing ("changed": false).
List the offers still waiting for your answer, with the same fields as the webhook’s data:
partner_status can also be applied or rejected.
The JavaScript SDK’s older cancel flow (
/api/rescue/trigger, /api/rescue/outcome and /api/rescue/pause) still works, but it only records what the customer picked. Use the billing widget with a v2 token for offers that apply.