Events
Webhooks
Webhooks notify your back-end when a Repurch event needs downstream action. Two event families ship today: redemption.created for Exchange payouts, and collection.scheduled for Clearance pickups. Both deliver to the same endpoint; route on the type field. Repurch is the B2B settlement and orchestration layer — you handle the consumer-facing payment and dispatch from your existing infrastructure.
Setting up an endpoint
Configure your HTTPS endpoint via PATCH /v1/partner/settings/integrations/webhook, or from Settings → Integrations in the partner dashboard. The endpoint must:
- Use HTTPS with a valid TLS certificate.
- Respond with
2xxwithin 5 seconds. - Tolerate duplicate deliveries — Repurch retries on timeout or 5xx.
Retries follow exponential back-off for up to 24 hours. After that, the event is parked on the dead-letter queue and surfaced in the dashboard for manual replay.
Payout choice
When a customer's old sofa sells, they pick how Repurch issues their payout. Your dashboard at Settings → Integrations controls which options are offered to your customers — most retailers enable all three.
creditStore credit (boosted)
The customer's payout is store credit at your retailer, pre-boosted by whatever multiplier your team configured (e.g. 1.10× for a 10% top-up). The credit_destination field tells your handler whether to apply it to an existing order or mint a single-use code for later.
cashCash to bank (unboosted)
Funds land in the customer's nominated bank account at the base sale price. No boost. Use this when the customer doesn't want a retention play with your brand. Your handler triggers a BACS or Stripe payout.
When customer_choice is credit, credit_destination determines where the credit lands:
existing_orderApply to existing order
credit_destination = existing_order. Credit is applied as a partial refund to the order in original_order_id (typically the new sofa they bought from you under a trade-in plan). Your handler refunds to the original payment method via your payment processor.
codeSave as a single-use code
credit_destination = code. Customer wants to spend the credit later. Your handler mints the code in your gift card or loyalty system; the code value + expires_at are pre-populated by Repurch.
redemption.created
Fired once the customer's trade-in sells and they confirm their payout choice. The payload carries everything you need to route the payout to the right downstream system.
Example delivery
POST /repurch/redemptions HTTP/1.1
Host: furnitureco.example
Content-Type: application/json
User-Agent: Repurch-Webhooks/1.0
X-Repurch-Event: redemption.created
X-Repurch-Event-Id: evt_2d8f3a17c4b9e62a
X-Repurch-Signature: t=1749548531,v1=2c0f9...Payload (apply-to-existing-order example)
{
"id": "evt_2d8f3a17c4b9e62a",
"type": "redemption.created",
"created_at": "2026-06-10T09:42:11+00:00",
"data": {
"id": "rdm_8c2f1a9d4e7b6035",
"brand_id": "furniture-co",
"customer_id": "cust_4a91c2e5",
"customer_choice": "credit",
"credit_destination": "existing_order",
"value_pence": 52800,
"value": 528.00,
"currency": "GBP",
"code": null,
"expires_at": null,
"original_order_id": "ORD-2026-0042",
"related_listing_id": 12345,
"sale_completed_at": "2026-06-09T14:00:00+00:00"
}
}Fields
id string | Unique event identifier. Use it to deduplicate retries. |
type string | Always redemption.created for this event. |
created_at string | ISO 8601 UTC timestamp the event was emitted. |
data.id string | Stable redemption ID. Reference this in any logs or downstream records so a customer-support query can be traced back. |
data.brand_id string | The brand the redemption was issued against. Matches your workspace. |
data.customer_id string | Stable Repurch customer identifier. |
data.customer_choice string | One of credit | cash. Drives the top-level payout flow. |
data.credit_destination string | null | When customer_choice is credit, one of existing_order | code. null when customer_choice is cash. |
data.value_pence integer | Payout value in pence. Pre-boosted for credit; unboosted for cash. |
data.value number | Same value as GBP decimal. Always equal to value_pence / 100. |
data.code string | null | Single-use code, present when credit_destination is code. null otherwise. |
data.expires_at string | null | ISO 8601 expiry, present when credit_destination is code. null otherwise. |
data.original_order_id string | null | Order to credit against, present when credit_destination is existing_order. null otherwise. |
data.related_listing_id integer | The Exchange listing whose sale triggered this payout. Useful for support context. |
data.sale_completed_at string | ISO 8601 UTC timestamp the underlying sale completed (delivery to the buyer). |
Handler skeleton
// Node.js / Express
//
// Repurch forwards the gross sale proceeds to your Repurch wallet
// 48h after delivery. This webhook fires alongside that settlement
// and tells your back-end which customer payout to execute from
// your end. You're the consumer-facing payment party.
app.post("/repurch/redemptions", express.json(), async (req, res) => {
const event = req.body;
if (event.type === "redemption.created") {
const { id, customer_choice, value_pence, customer_id,
credit_destination, original_order_id, code,
expires_at } = event.data;
if (customer_choice === "cash") {
// Pay the customer the unboosted sale amount via your existing
// payment processor / BACS rail.
await yourBank.payout({
customer_id,
amount_pence: value_pence,
reference: id,
});
} else if (customer_choice === "credit") {
// value_pence already includes whatever boost your team has
// configured. Where it lands depends on credit_destination.
if (credit_destination === "existing_order") {
// Partial refund to the original payment method for the
// order the customer already placed with you.
await stripe.refunds.create({
payment_intent: getPaymentIntentForOrder(original_order_id),
amount: value_pence,
reason: "requested_by_customer",
metadata: { repurch_redemption_id: id },
});
} else {
// Mint a single-use code in your gift card / loyalty system.
await giftCards.create({
code,
discount_pence: value_pence,
customer_id,
expires_at,
single_use: true,
});
}
}
}
// Acknowledge within 5 seconds. Repurch retries 5xx and timeouts
// with exponential back-off for up to 24 hours.
res.status(200).json({ ok: true });
});collection.scheduled
Fired when a Clearance sale's collection date is confirmed in the partner dashboard. Your logistics provider integration uses the payload to dispatch a pickup from the chosen warehouse to the buyer. The buyer's delivery window is included so route planners can slot the delivery leg into the same vehicle run.
Example delivery
POST /repurch/collections HTTP/1.1
Host: furnitureco.example
Content-Type: application/json
User-Agent: Repurch-Webhooks/1.0
X-Repurch-Event: collection.scheduled
X-Repurch-Event-Id: evt_a8b14e932cd0f76e
X-Repurch-Signature: t=1749728922,v1=8d4a1...Payload
{
"id": "evt_a8b14e932cd0f76e",
"type": "collection.scheduled",
"created_at": "2026-06-12T11:08:42+00:00",
"data": {
"id": "col_5b209a17e4f6c81d",
"brand_id": "furniture-co",
"order_id": 12345,
"order_reference": "#12345",
"product_id": 67890,
"scheduled_for": "2026-06-15",
"scheduled_at": "2026-06-12T11:08:42+00:00",
"warehouse_id": "f3c1a4b5-9d22-4e6f-8a01-7b2d4e8c9f10",
"warehouse_label": "Nottingham hub",
"buyer_window_start": "2026-06-12",
"buyer_window_end": "2026-06-22"
}
}Fields
id string | Unique event identifier. Use it to deduplicate retries. |
type string | Always collection.scheduled for this event. |
created_at string | ISO 8601 UTC timestamp the event was emitted. |
data.id string | Stable collection ID. Reference in your dispatch records for support traceability. |
data.brand_id string | The brand the collection belongs to. Matches your workspace. |
data.order_id integer | WooCommerce-side order ID for the sold item. |
data.order_reference string | Display-friendly order reference, e.g. #12345. |
data.product_id integer | Product ID of the line item being collected. |
data.scheduled_for string | The confirmed collection date (YYYY-MM-DD). Always inside the buyer's delivery window. |
data.scheduled_at string | ISO 8601 UTC timestamp the retailer pressed Schedule. |
data.warehouse_id string | null | The collection-source warehouse UUID. null if the brand has no warehouses configured. |
data.warehouse_label string | Display label for the warehouse, e.g. 'Nottingham hub'. Empty when no warehouse is assigned. |
data.buyer_window_start string | Start of the buyer's 10-day delivery window (YYYY-MM-DD). Useful for the delivery leg. |
data.buyer_window_end string | End of the buyer's 10-day delivery window (YYYY-MM-DD). |
Delivery semantics are identical to redemption.created: same endpoint (configured at Settings → Integrations), same HMAC signature scheme, same retry policy. Route on the type field.
Pull-based alternative
If you'd rather poll than receive webhooks, the same data is available via GET /v1/redemptions?since=<timestamp>. That endpoint returns the same payload shape under the data envelope. Most partners pick one or the other — you can run both during cutover.
Signature verification
Every delivery carries an X-Repurch-Signature header in the Stripe-compatible t=<timestamp>,v1=<hmac> format. The HMAC is SHA-256 over <timestamp>.<raw body> using your endpoint's signing secret as the key.
Signature verification ships in a future iteration. Until then, treat the webhook URL itself as the shared secret: use a long unguessable path component and reject requests to any other path.