repurch/ docs
Dashboard ↗repurch.com ↗

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 2xx within 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.

credit

Store 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.

cash

Cash 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_order

Apply 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.

code

Save 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

http
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)

json
{
  "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

javascript
// 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

http
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

json
{
  "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.