Developers
The OnBio API and webhooks
Read the sales, contacts and invoices of your store from your own software and get every change as soon as it happens.
- Base URL
https://api.onbio.es/public/v1- Authentication
- Send the key in the Authorization: Bearer ob_live_… header or in X-API-Key. Keys are created in Integrations in the panel, with read or read and write permission.
- Quota
- 120 requests per minute per store, adding up all its keys, Zapier and Make included. Beyond that, the answer is 429 with the Retry-After header.
Webhook events
Each delivery is a POST with the event in JSON. If the destination does not answer with 2xx within 10 s, it is retried up to 8 times with waits growing from 10 s to 3600 s; after 10 failures in a row the webhook is turned off.
| Event | Resource | When it arrives |
|---|---|---|
sale.created | sale | A buyer has paid an order. |
refund.created | refund | A sale has been refunded, in full or in part. |
subscription.created | subscription | A buyer has started a subscription. |
subscription.cancelled | subscription | A subscription has ended. |
lead.created | lead | A visitor has downloaded a lead magnet. |
entitlement.granted | entitlement | A buyer has received access to a product or got it back. |
entitlement.revoked | entitlement | Access to a product has been suspended or withdrawn. |
invoice.created | invoice | An invoice of the creator has been issued. |
Verify the signature
Each delivery carries X-Onbio-Timestamp and X-Onbio-Signature, the hexadecimal HMAC-SHA256 of “timestamp.body” with the whsec_ secret of the webhook. Compute the signature over the body exactly as it arrives, before parsing it as JSON. A timestamp more than 300 s away from your clock is rejected even if the signature matches.
import crypto from "node:crypto";
export function verifyOnbioSignature(rawBody, headers, secret, nowSeconds = Math.floor(Date.now() / 1000)) {
const timestamp = headers["x-onbio-timestamp"];
const signature = headers["x-onbio-signature"];
if (!timestamp || !signature || Math.abs(nowSeconds - Number(timestamp)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
return signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}API reference
Every route of /public/v1 with its parameters, responses and examples, read from the OpenAPI document the API publishes.