Easy Labs
SDKsNode.jsResources

Webhook Endpoints

Webhook Endpoints — methods, parameters, and examples for @easylabs/node.

Webhook endpoints are HTTPS URLs that receive event deliveries. Register up to 5 active endpoints per company, each subscribed to all events (["*"]) or a specific subset of EASY_EVENT_TYPES. The signing secret is returned only on creation — store it immediately.

For verifying inbound deliveries, see the top-level Webhooks page.

Methods

easy.registerWebhookEndpoint(body);                  // POST   /webhooks
easy.listWebhookEndpoints();                         // GET    /webhooks
easy.updateWebhookEndpoint(endpointId, body);        // PATCH  /webhooks/:id
easy.deleteWebhookEndpoint(endpointId);              // DELETE /webhooks/:id
easy.listWebhookDeliveries(query?);                  // GET    /webhooks/deliveries
easy.listEndpointDeliveries(endpointId, query?);     // GET    /webhooks/:id/deliveries

Register

const ep = await easy.registerWebhookEndpoint({
  url: "https://api.example.com/webhooks/easy",
  events: ["payment.created", "subscription.updated", "invoice.paid"], // or ["*"]
});

console.log(ep.data.id);
console.log(ep.data.secret); // <-- store this NOW; it's never returned again

The URL must be HTTPS and cannot resolve to private / loopback addresses. The maximum is 5 active endpoints per company.

List, update, delete

const all = await easy.listWebhookEndpoints();
await easy.updateWebhookEndpoint(ep.data.id, { active: false });
await easy.deleteWebhookEndpoint(ep.data.id);

Inspect deliveries

listWebhookDeliveries is the cross-endpoint delivery log. listEndpointDeliveries(endpointId, query) is the same shape scoped to one endpoint (no endpoint_id or include_counts parameters there).

const deliveries = await easy.listWebhookDeliveries({
  event_type: "invoice.payment_failed",
  success: false,
  created_after: "2026-04-01T00:00:00Z",
  limit: 100,
  include_counts: true,
});

console.log(deliveries.data.total, deliveries.data.event_counts, deliveries.data.failed_count);

Object shape

RegisteredWebhookEndpoint extends WebhookEndpoint:

FieldTypeNotes
idstring
urlstring
eventsstring[]["*"] or specific event types.
activeboolean
status"enabled" | "disabled" | string
consecutive_failuresnumberEndpoints auto-disable after repeated 5xx.
last_triggered_atstring | null
secretstringOnly on the create response.

WebhookDelivery: id, endpoint_id, event_type, payload (WebhookEvent), attempt, attempt_number, status_code, response_code, response_body, response_body_hash, response_time, error, success, created_at, next_retry_at.

WebhookDeliveriesListQuery: event_type?, success?, attempt?, attempt_number?, created_after?, created_before?, endpoint_id?, limit?, offset?, include_counts?.

Examples

Bootstrap a fresh endpoint at deploy time

const all = await easy.listWebhookEndpoints();
const expectedUrl = `${process.env.PUBLIC_URL}/webhooks/easy`;
const existing = all.data.find((e) => e.url === expectedUrl);

if (!existing) {
  const created = await easy.registerWebhookEndpoint({
    url: expectedUrl,
    events: ["*"],
  });
  // Persist created.data.secret in a secret manager — you can't read it back later.
  process.env.EASY_WEBHOOK_SECRET = created.data.secret;
}

Replay a failed delivery's payload locally

const failed = await easy.listEndpointDeliveries(endpointId, {
  success: false,
  limit: 1,
});
const delivery = failed.data.deliveries[0];
console.log(delivery.payload); // typed WebhookEvent

Rotate an endpoint's URL

await easy.updateWebhookEndpoint(endpointId, {
  url: "https://api.example.com/v2/webhooks/easy",
});

Rotate the signing secret

There is no rotate-secret endpoint. Delete the old endpoint and register a new one to issue a fresh secret:

const replacement = await easy.registerWebhookEndpoint({
  url: existing.url,
  events: existing.events as any,
});
await easy.deleteWebhookEndpoint(existing.id);
// store replacement.data.secret

On this page