Skip to main content

Build workflows with webhooks

In order to notify your system of asynchronous actions within the Orb platform, Orb provides a powerful, real-time webhooks system that can issue calls to an endpoint of your choosing when certain events take place. Webhooks allow your system or integration to take actions as a result of these events.

Orb's supported webhook events can be useful for a wide variety of use cases:

  1. Resource lifecycle changes: Set up entitlements in your service as a result of object creation, such as a subscription.created event
  2. Usage patterns: Alert internal teams when a specific usage threshold is hit in a billing period, or when Orb detects an anomalous usage pattern for a customer based on historical patterns.
  3. Balance events: Send out an end-user email when a customer's pre-paid credit balance is nearing zero
info

Delivery of Resource lifecycle changes webhooks typically occurs in near real-time, though occasional delays may occur.

For alerts on usage, cost, and credit balance, we evaluate alerts (and therefore issue webhooks) several times a day, but the latency guarantee will be a function of your workload. This can be reduced with provisioning for customers on Growth and Enterprise plans.

Webhooks payloads in Orb follow the format:

{
// Unique to this WebhookEvent resource, and can be used for idempotency (process-once) purposes
"id": string,
// ISO8601 `created_at` timestamp of the `WebhookEvent` resource
"created_at": string,
// Identifies the type of webhook event being triggered
"type": string,
// Each webhook is attached to a resource, and the full resource is serialized
// in the webhook body.
"<resource_name>": object,
// Additional properties specific to this event
"properties": object,
}

Configuring webhooks

Webhooks are configured to an HTTPS URL on your backend server (note that only the HTTPS scheme is accepted for the production version of your webhook configuration).

Each webhook endpoint is associated with a secret (see the X-Orb-Signature clause below). By default, all webhook events are sent to every endpoint.

Debugging webhooks

In the Orb UI, you can test your webhook configuration to ensure that it's set up properly and ready to receive events from us. From the webhooks portal, you can select on any webhook endpoint to send a "test event." webhook-test.png

If you need to manually trigger a webhook event to be sent to your server, you can also resend it in the UI. resend-webhook.png

Webhooks retries

Orb guarantees at-least-once delivery for a single webhook event to each of your endpoint.

Orb expects a 2xx within five seconds as a successful response to a webhook request. If the request returns an error or otherwise times out, Orb will attempt to retry the event on the following retry schedule, stopping when either retries are exhausted or the request is accepted:

  • 5 seconds
  • 5 minutes
  • 30 minutes
  • 2 hours
  • 5 hours
  • 10 hours
  • 10 hours

In order for your system to handle duplicate notifications, we recommend storing the id of a webhook event temporarily in the webhook consumer and refusing to re-process an event if the same id has already been seen.

Webhook patterns

Event types follow the pattern <resource_name>.<verb>, where <resource_name> is the root object that is related to the webhook, and the <verb> is the event that has taken place (e.g. customer.created is a newly created Customer resource).

By convention, a key in the payload will match the resource_name prefix in type (e.g. if the type issubscription.action, the payload will contain the serialized subscription resource under the subscription key).

Webhook Event Types: Resource Lifecycle Changes

Orb currently issues the following webhooks for resource lifecycle changes:

customer.created

Issued when a customer resource is created.

keyschema
customerCustomer
properties{}

customer.edited

Issued when a customer is edited. The previous_attributes object will be present and populated with the previous state of any Customer attributes that have changed.

keyschema
customerCustomer
properties.previous_attributes.payment_providerstring
properties.previous_attributes.payment_provider_idstring
properties.previous_attributes.auto_collectionboolean
properties.previous_attributes.emailstring
properties.previous_attributes.namestring
properties.previous_attributes.email_deliveryboolean
properties.previous_attributes.metadataobject
properties.previous_attributes.tax_idTaxId
properties.previous_attributes.shipping_addressAddress
properties.previous_attributes.billing_addressAddress

subscription.created

Issued when a subscription resource is created.

keyschema
subscriptionSubscription
properties{}

subscription.started

Issued when a subscription begins.

keyschema
subscriptionSubscription
properties{}

subscription.fixed_fee_quantity_updated

Issued when a subscription's fixed fee quantity has been updated.

keyschema
subscriptionSubscription
properties.old_quantitynumber
properties.new_quantitynumber
properties.effective_datedate-time
properties.price_idstring

subscription.edited

Issued when a subscription has been edited. This will not be triggered for changes to pricing.

keyschema
subscriptionSubscription
properties.previous_attributes.metadataobject
properties.previous_attributes.default_invoice_memostring
properties.previous_attributes.auto_collectionboolean
properties.previous_attributes.net_termsnumber
properties.previous_attributes.invoicing_thresholdnumber

subscription.ended

Occurs whenever a customer’s subscription ends/lapses.

keyschema
subscriptionSubscription
properties{}

subscription.plan_changed

Issued when a subscription transitions from one plan to a different plan.

keyschema
subscriptionSubscription
properties.previous_plan_idstring

subscription.plan_version_change_scheduled

Issued when a plan version change is scheduled in the future for a subscription.

keyschema
subscriptionSubscription
properties.effective_datedate-time
properties.previous_plan_version_numbernumber
properties.new_plan_version_numbernumber

subscription.plan_version_changed

Issued when a subscription moves to a new plan version.

keyschema
subscriptionSubscription
properties.effective_datedate-time
properties.previous_plan_version_numbernumber
properties.new_plan_version_numbernumber

invoice.invoice_date_elapsed

danger

Serializing and consuming this webhook can be very expensive in the presence of a large number of subscriptions and is not enabled by default. Please reach out to the Orb team to opt-in to the webhook or discuss the intended workflow.

Issued when the invoice_date of an invoice has elapsed.

keyschema
invoiceInvoice
properties.invoice_datedate-time

invoice.issued

Issued when an invoice transitions to the "issued" state. "issued" invoices are frozen (they cannot be edited, manually or via the API). The automatically_marked_as_paid property will be true when the issued invoice's amount due is zero, which results in the invoice status being set to paid.

keyschema
invoiceInvoice
properties.automatically_marked_as_paidboolean

invoice.issue_failed

Issued when an invoice fails to issue.

keyschema
invoiceInvoice
properties.reasonstring

invoice.payment_failed

Issued when automated payment collection for an invoice fails for a configured payment gateway.

keyschema
invoiceInvoice
properties.payment_provider"stripe"
properties.payment_provider_idstring
properties.payment_provider_transaction_idstring or null

invoice.payment_processing

Issued when a payment for an invoice starts processing.

keyschema
invoiceInvoice
properties.payment_provider"stripe"
properties.payment_provider_idstring

invoice.payment_succeeded

Issued when automated payment collection for an invoice succeeds for a configured payment gateway. This webhook is not sent for $0 invoices that are automatically marked as paid upon issuance.

keyschema
invoiceInvoice
properties.payment_provider"stripe"
properties.payment_provider_idstring
properties.payment_provider_transaction_idstring

invoice.edited

Issued when a draft invoice has been edited via the webapp. The previous_attributes object will be present and populated with the previous state of any Invoice attributes that have changed.

keyschema
invoiceInvoice
properties.previous_attributes.amount_duestring
properties.previous_attributes.subtotalstring
properties.previous_attributes.totalstring
properties.previous_attributes.discountsarray of Discounts
properties.previous_attributes.minimumMinimum
properties.previous_attributes.line_itemsarray of Invoice Line Items

invoice.manually_marked_as_void

Issued when an invoice is manually marked as void.

keyschema
invoiceInvoice
properties{}

invoice.manually_marked_as_paid

Issued when an invoice is manually marked as paid.

keyschema
invoiceInvoice
properties.payment_received_datedate-time
properties.external_idstring
properties.notesstring

invoice.undo_mark_as_paid

Issued when undoing the Paid status for an invoice that was manually marked as paid.

keyschema
invoiceInvoice
properties{}

invoice.sync_succeded

Issued when an invoice successfully syncs to the invoice provider.

keyschema
invoiceInvoice
properties.payment_provider"string"
properties.payment_provider_idstring

invoice.sync_failed

Issued when an invoice fails to sync to the invoice provider.

keyschema
invoiceInvoice
properties.payment_provider"string"
properties.payment_provider_idstring

credit_note.issued

Issued when a credit note is created.

keyschema
credit_noteCredit note
properties{}

credit_note.marked_as_void

Issued when a credit note is marked as void.

keyschema
credit_noteCredit note
properties{}

resource_event.test

Issued via the Orb UI to test if your webhook configuration is set up properly and ready to receive events from us.

keyschema
messagestring

Webhook Event Types: Usage Patterns and Balance Events

Orb currently issues the following usage patterns and balance events webhooks:

subscription.usage_exceeded

Issued when a billable metric in a subscription exceeds a pre-configured quantity threshold.

keyschema
subscriptionSubscription
properties.billable_metric_idstring
properties.timeframe_startdate-time
properties.timeframe_enddate-time
properties.quantity_thresholdnumber

subscription.cost_exceeded

Issued when a subscription's accrued spend for a month exceeds a pre-configured currency amount.

info

Orb will not evaluate alerts on cost that are configured with a threshold below the total of a plan's fixed costs with a prioritized cadence during billing period rollovers. These alerts can be expected to fire near the first actual usage event received for the new billing period and will subsequently be evaluated several times a day. Please note, prepurchased credits are not included in the spend calculation, and only actual spend will be considered.

keyschema
subscriptionSubscription
properties.timeframe_startdate-time
properties.timeframe_enddate-time
properties.amount_thresholdnumber

customer.credit_balance_depleted

Issued when a customer's prepaid credit balance is depleted.

keyschema
customerCustomer
properties.pricing_unit.namestring
properties.pricing_unit.symbolstring
properties.pricing_unit.display_namestring

customer.credit_balance_dropped

Issued when a customer's prepaid credit balance is depleted to a configured threshold.

keyschema
customerCustomer
properties.balance_thresholdstring
properties.pricing_unit.namestring
properties.pricing_unit.symbolstring
properties.pricing_unit.display_namestring

customer.credit_balance_recovered

Issued when a customer's prepaid credit balance is replenished after depletion.

keyschema
customerCustomer
properties.pricing_unit.namestring
properties.pricing_unit.symbolstring
properties.pricing_unit.display_namestring

Webhooks security

Each webhook event sent by Orb also includes two specific headers in the request:

  1. X-Orb-Timestamp: This header represents the time that the webhook was sent from Orb. You can check this timestamp and compare it to a configured threshold in your system, in order to prevent processing of the webhook event. This can be a mitigation against replay attacks where webhook events are re-sent, causing downstream systems to behave unexpectedly.
  2. X-Orb-Signature: The signature header (formatted as v1=<signature>) is a security header which can be used to confirm that the webhook event originated from Orb. We highly recommend verifying the signature, and only processing events where the header matches the signature you generate. Since each webhook endpoint is associated with a secret, your backend server should re-compute the signature by preparing the payload and subsequently computing an HMAC with the SHA 256 function.
    • The payload for the HMAC is the literal "v1:" followed by the ISO format X-Orb-Timestamp and finally the literal : followed by the event message body itself.
    • You should use the configured secret for the endpoint URL as the signing secret for the HMAC.
    • When comparing to your generated HMAC, you can either prefix the literal v1= to compare with X-Orb-Signature header directly, or extract the element after v1= in X-Orb-Signature to check against your generated value.

Webhooks verification

Orb's SDKs provide verification methods for your webhook events, available in Python, Node, and Go.

Here's an example verification snippet implemented as a Flask endpoint:

from flask import Flask, request
import hmac
from hashlib import sha256

app = Flask(__name__)

# Example Using Flask
@app.route('/webhook-handler', methods=['POST'])
def handle_webhook():
# parse webhook JSON
webhook_event = request.json

if webhook_event is None:
# could not parse request
return ('Unable to parse notification.', 400)

expected_signature = request.headers["X-Orb-Signature"]
iso_format_delivery_time = request.headers["X-Orb-Timestamp"]

secret_string = "<insert your secret string here>"
secret = bytes(secret_string, "utf-8")

# Prefix the beginning of the signature with `v1:<isoformat timestamp>`
prefix = "v1:{0}:".format(iso_format_delivery_time)

# Convert to bytes and append the body of the request
message = prefix.encode("utf-8") + request.data

# Generate an encode message based on the secret and the message string with sha256.
hmac_object = hmac.new(secret, message, sha256)
created_signature = "v1=" + hmac_object.hexdigest()

if not expected_signature == created_signature:
return ('Unable to verify webhook signature.', 400)

# Handle based on webhook_event.type
return ('Successfully processed webhook!', 200)