Webhook endpoints

A webhook endpoint is a URL Ripllo POSTs events to when something interesting happens in your workspace — a discount code is redeemed, a referral attribution is created, an abandoned cart is recovered. This page documents the API for managing those endpoints programmatically: registering new ones, inspecting recent deliveries, rotating secrets.

For the event payload format and per-event catalog, see Webhooks.

You don't need this resource to receive webhooks. Most integrators add endpoints in the dashboard once and never touch the API. The endpoints below exist for partners provisioning customer workspaces and infra-as-code setups.

Endpoints

Method Path Purpose
GET /api/v1/webhooks/endpoints List endpoints
POST /api/v1/webhooks/endpoints Create an endpoint
PATCH /api/v1/webhooks/endpoints/:id Update an endpoint
DELETE /api/v1/webhooks/endpoints/:id Delete an endpoint
GET /api/v1/webhooks/events List recent events

All endpoints require the admin scope on the calling key.

The webhook endpoint object

{
  "id": "whep_01HX...",
  "accountId": "acc_01HX...",
  "url": "https://api.example.com/ripllo/webhooks",
  "events": ["discount_code.redeemed", "abandoned_cart.recovered"],
  "description": "Production handler",
  "active": true,
  "secretPreview": "whsec_…cd34",
  "createdAt": "2026-05-12T10:42:00.000Z",
  "updatedAt": "2026-05-13T08:11:00.000Z"
}
Field Type Notes
id string Prefix whep_.
accountId string Owning workspace.
url string The HTTPS URL events POST to. http:// allowed only in test mode.
events string[] Event types this endpoint subscribes to. ["*"] means "everything".
description string | null Free-form label for your own bookkeeping.
active boolean Whether deliveries are attempted.
secret string The HMAC secret. Only present on create. Listing endpoints returns secretPreview (last 4 chars) instead.
secretPreview string whsec_…<last 4>. Safe to log.
createdAt, updatedAt ISO 8601

The secret is shown only on create. Every other endpoint returns it as omitted. Store it in your secret manager immediately — there's no recovery flow. To rotate, delete the endpoint and create a new one.

List endpoints

GET /api/v1/webhooks/endpoints

Returns every endpoint in the workspace, newest first. Secrets are stripped from the response (replaced with secretPreview).

{
  "data": {
    "endpoints": [
      {
        "id": "whep_01HX...",
        "url": "https://api.example.com/ripllo/webhooks",
        "events": ["*"],
        "description": "Production handler",
        "active": true,
        "secretPreview": "whsec_…cd34",
        "createdAt": "2026-05-12T10:42:00.000Z",
        "updatedAt": "2026-05-12T10:42:00.000Z"
      }
    ]
  },
  "error": null,
  "meta": { "requestId": "...", "timestamp": "..." }
}

Create an endpoint

POST /api/v1/webhooks/endpoints

Request body

Field Type Required Notes
url string (URL) yes Must start with https:// in live mode. Private IPs and localhost are rejected; use the CLI tunnel for local dev.
events string[] no Event types to subscribe to. Defaults to ["*"] for all events. Unknown event types are accepted but never fire.
description string no Free-form label, max 200 chars.

Response — 201 Created

The full endpoint object with secret, which appears nowhere else:

{
  "data": {
    "endpoint": {
      "id": "whep_01HX...",
      "url": "https://api.example.com/ripllo/webhooks",
      "events": ["discount_code.redeemed"],
      "description": "Redemption tracker",
      "active": true,
      "createdAt": "2026-05-13T10:42:00.000Z"
    },
    "secret": "whsec_<32 bytes base64url>"
  },
  "error": null,
  "meta": { "requestId": "...", "timestamp": "..." }
}
const { endpoint, secret } = await ripllo.webhookEndpoints.create({
  url: 'https://api.example.com/ripllo/webhooks',
  events: ['discount_code.redeemed', 'abandoned_cart.recovered'],
});
await secretStore.set('RIPLLO_WEBHOOK_SECRET', secret);

Update an endpoint

PATCH /api/v1/webhooks/endpoints/:id

Partial. You can change url, events, description, and active. You cannot rotate the secret in-place — delete and recreate.

Field Mutable? Notes
url yes
events yes Replaces the subscription list entirely.
description yes
active yes Set to false to pause deliveries without deleting.
secret no Delete and recreate.
await ripllo.webhookEndpoints.update('whep_01HX...', {
  active: false,
});

Delete an endpoint

DELETE /api/v1/webhooks/endpoints/:id

Hard delete. Any in-flight delivery attempts are abandoned. Audit log retains the deletion.

await ripllo.webhookEndpoints.delete('whep_01HX...');

List recent events

GET /api/v1/webhooks/events

Returns the most recent 50 events in the workspace, newest first. Each row is an event Ripllo attempted to deliver, with its envelope and delivery state.

{
  "data": {
    "events": [
      {
        "id": "evt_01HX...",
        "accountId": "acc_01HX...",
        "type": "discount_code.redeemed",
        "payload": { /* the event envelope */ },
        "status": "delivered",
        "lastAttemptAt": "2026-05-13T10:43:22.000Z",
        "attemptCount": 1,
        "createdAt": "2026-05-13T10:43:21.000Z"
      }
    ]
  },
  "error": null,
  "meta": { "requestId": "...", "timestamp": "..." }
}

Use this for debugging deliveries during integration. For production observability, consume the events directly in your handler — polling this endpoint isn't a substitute for actually receiving the webhooks.

Signature verification

Every webhook Ripllo POSTs carries a Ripllo-Signature header. The recipe to verify:

sig = "v1=" + hex(HMAC-SHA256(secret, timestamp + "." + raw-body))

Where timestamp is from the Ripllo-Signature-Timestamp header (epoch seconds).

import crypto from 'node:crypto';

function verifyWebhook(rawBody, signatureHeader, timestampHeader, secret) {
  const expected = 'v1=' + crypto
    .createHmac('sha256', secret)
    .update(timestampHeader + '.' + rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

The SDKs ship a verifyWebhook(rawBody, headers, secret) helper that handles all of this plus the 5-minute replay window. Use it.

Events

The webhook endpoints resource itself is intentionally not broadcast on the event stream — subscribing to "webhook endpoint changed" via a webhook is a circular dependency we choose not to support. Changes are visible in the audit log.

Next