Skip to main content

Webhooks

Odus webhooks allow you to receive real-time notifications about events that occur in your Odus account. This enables you to build integrations that can react to events such as successful payments, recurring payments, subscription updates, and more.

It is important to understand that webhook delivery guarantees are as follows:

  • Webhooks are delivered at least once, meaning that you may receive duplicate events. Use Event IDs to deduplicate events on your end.
  • Webhooks are sent in order of event creation, however, it doesn't mean that they will be delivered in the same order, as they may be processed in parallel and there's network latency involved.
  • Webhooks are sent asynchronously, meaning that you may receive events after a delay. The delay is usually less than 5 seconds, but it can be longer in some cases.

Webhook payload

The webhook payload is a JSON object that contains the following fields:

  • eventId: A unique identifier for the event.
  • eventType: The type of the event, e.g. payment.created, payment.updated, etc.
  • profile: The webhook subscriber profile that the webhook is sent to.
  • timestamp: The timestamp of the event creation in ISO 8601 format.
  • data: The event data, which is a JSON object that contains the event-specific data. The structure of this object depends on the eventType and represents the event details.

Data format follows the return type of "Find One" API endpoints, including sensitive fields. For example, for payment.created event, the data field will contain the payment object outlined in the GET /payments/:id endpoint documentation.

Example webhook payload
{
"eventId": "evt_abc",
"eventType": "payment.created",
"profile": "whs_xyz",
"timestamp": "2023-10-01T12:00:00Z",
"data": {
// Payment object data
}
}

Receiving webhooks

To start receiving webhooks, you first need to create a webhook subscriber and save the generated shared secret. This secret is used to sign the webhook payloads, allowing you to verify that the payloads are sent by Odus.

Once you have created a webhook subscriber, saved the shared secret, and exposed a publicly accessible URL to receive webhooks, you will now receive webhooks for all events that you have subscribed to.

Validating authenticity

Each webhooks comes with an HMAC in the X-Webhook-HMAC header, which is a SHA256 hash of the payload using the shared secret. To validate the authenticity of the webhook, you should:

  1. Retrieve the X-Webhook-HMAC header from the incoming request.
  2. Compute the HMAC of the request body using the shared secret and the SHA256 algorithm.
  3. Compare the computed HMAC with the X-Webhook-HMAC header value.
Example of validating webhook authenticity
import crypto from 'node:crypto';

const sharedSecret = 'your_shared_secret';
const hmac = req.headers['x-webhook-hmac'];
const computedHmac = crypto.createHmac('sha256', sharedSecret).update(req.body).digest('hex');

if (hmac !== computedHmac) {
// Request did not come from Odus, reject.
return res.status(401).send();
}

// Request is valid, process the webhook.

Responding to webhooks

When you receive a webhook, you should respond with a 2xx status code to acknowledge that you have received the webhook. If you do not respond with a 2xx status code, the webhook will be retried.

If you receive a webhook that you cannot process, you should respond with a 4xx or 5xx status code to indicate that there was an error.

important

Each webhook will be attempted up to 10 times over a period of 3 days with an exponential backoff strategy. If the webhook fails to deliver after 10 attempts, it will be marked as failed and will not be retried again.

Long-running webhook handlers

If your webhook handler takes a long time to process the event, you should respond with a 202 Accepted status code as soon as you start processing the event. This will acknowledge the receipt of the webhook and prevent it from being retried.

Webhooks are considered as failed if they exceed the timeout of 15 seconds. If you need more time to process the event, you can use a background job or a message queue to handle the event asynchronously.

Best Practices

  • Security: Always validate the incoming requests using the shared secret to ensure that the requests are coming from Odus.
  • Least Privilege: Only subscribe to the events that you need. This reduces the amount of data you receive and helps you focus on the events that are relevant to your application.
  • Idempotency: Design your webhook handlers to be idempotent, meaning that processing the same event multiple times will not have unintended side effects. Use the eventId to deduplicate events if necessary.
  • Logging: Implement logging for your webhook handlers to track incoming events and any errors that occur during processing. This will help you troubleshoot issues.

Supported Events

Event TypeDescription
customer.createdTriggered when a new customer is created
customer.updatedTriggered when customer information is updated
blacklist.addedTriggered when a customer is added to the blacklist
blacklist.removedTriggered when a customer is removed from the blacklist
subscription.activeTriggered when a subscription becomes active
subscription.cancelledTriggered when a subscription is cancelled
subscription.updatedTriggered when subscription details are updated
payment.createdTriggered when a new payment is created
payment.succeededTriggered when a payment succeeds
payment.attempt_failedTriggered when a payment attempt fails
payment.requires_actionTriggered when a payment requires additional action from the customer
payment.updatedTriggered when payment information is updated
payment.cancelledTriggered when a payment is cancelled
invoice.createdTriggered when a new invoice is created
invoice.paidTriggered when an invoice is paid
invoice.voidedTriggered when an invoice is voided
invoice.updatedTriggered when invoice information is updated