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
secretis 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
- Webhooks reference — envelope, retries, full event catalog.
- API keys — the other half of integration plumbing.