Webhooks
Webhooks are powerful tools for reacting to events regarding your users' activity. If you have in-house systems that you would like to use to monitor new subscriptions, webhooks are the best way to do it.
This guide will walk you through setting up webhooks with OpenPay. By implementing webhooks, you can automatically respond to important events like new customers and subscription updates, ensuring a seamless experience for your users and efficient management of your business.

-
Click the Developer Icon on the right of the top navigation bar and select Webhooks
-
Click Create webhook
-
Enter your webhook endpoint URL, give it a short name, and specify which events you want the endpoint to receive. We select all events for you by default, but you can narrow it down to a specific set of events.
Each request to your webhook endpoint will be of content type application/json
with the following content schema:
{
"id": "Unique ID for this event.",
"webhook_id": "Unique ID for the webhook endpoint that this event was sent to.",
"data": "JSON-serialized data for the event."
}
The data
key is a JSON-serialized string representation of the object associated with the event. For example, if the event is a subscription creation, data
will contain a snapshot of the created subscription object as a JSON string.
Example payloadCopied!
This is an example payload for a customer update event:
{
"id": "aaaabbbb-cccc-dddd-eeee-ffffgggghhhh",
"webhook_id": "webhook_endpoint_abcdefg12345678",
"data": "{\"id\": \"event_dev_abcdefg12345678\", \"object\": \"event\", ...}"
}
The value of data
can be parsed into the following JSON object:
{
"id": "event_dev_abcdefg12345678",
"object": "event",
"created_at": "2024-06-02T00:15:10.020202-07:00",
"updated_at": "2024-06-02T00:15:10.020202-07:00",
"is_deleted": false,
"account_id": "account_dev_abcdefg12345678",
"type": "customer.updated",
"data": {
"id": "cus_dev_abcdefg12345678",
"email": "john.doe@example.ai",
"object": "customer",
"address": {
"city": "Boise",
"line1": "2512 Walnut Avenue",
"line2": "",
"line3": "",
"state": "ID",
"country": "US",
"zip_code": "83716"
},
"discount": null,
"last_name": "Doe",
"account_id": "account_dev_abcdefg12345678",
"created_at": "2024-06-02T00:14:57.575457-07:00",
"first_name": "John",
"is_deleted": false,
"updated_at": "2024-06-02T00:14:57.575457-07:00",
"balance_atom": null,
"subscriptions": []
},
"data_previous": null,
"request_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"request_idempotency_key": null,
"pending_webhooks": 1
}
The exact schema of data
depends on the type of event and matches that of the corresponding object in the API reference. For example, the customer event above contains data
which follows the customer object schema.
You have the option of specifying which object events your webhook should be notified about. These event types are declared in the type
field of the parsed data
string in the event payload. In our example in the Webhook payload format tab, it's customer.updated
.
We emit created
, updated
, and deleted
events for each of your objects, which means you will receive events such as coupon.created
and customer.updated
as these operations happen. The supported objects are:
-
account
-
api_token
-
charge
-
In addition to
created
,updated
, anddeleted
, we also emitsucceeded
,failed
,pending
andrefunded
for different statuses ofcharge
lifecycle.
-
-
coupon
-
credit_note
-
credit_note_item
-
customer
-
customer_balance_transaction
-
discount
-
invite
-
invite_item_discount
-
invoice
-
In addition to
created
,updated
, anddeleted
, we also emitupcoming
,finalized
,paid
,past_due
,voided
anduncollectible
for different statuses ofinvoice
lifecycle.
-
-
invoice_discount
-
invoice_item
-
payment_intent
-
In addition to
created
,updated
, anddeleted
, we also emitsucceeded
,processing
,requires_action
andfailed
for different statuses ofpayment_intent
lifecycle.
-
-
payment_method
-
payment_processor
-
price
-
product
-
promotion_code
-
refund
-
customer.subscription
-
In addition to
created
,updated
, anddeleted
, we also emittrial_will_end
,trialing
,activated
,past_due
,paused
,resumed
andcanceled
for different statuses ofsubscription
lifecycle. -
Note:
customer.subscription.created
will be emitted for mere creation of a subscription in incomplete state. If the intention to listen to an event is customer successfully subscribing to a product, then one should listen forcustomer.subscription.trialing
andcustomer.subscription.activated
according to respective subscription status.
-
-
subscription_item
-
user
-
user_login
-
user_record
-
user_record_summary
-
webhook_endpoint
For your security, OpenPay signs every webhook request sent to your endpoints. The signature is included in the request's signature-digest
header, and it follows the format
t=TIMESTAMP,SIGNATURE_VERSION=SIGNATURE[,SIGNATURE_VERSION=SIGNATURE_2,SIGNATURE_VERSION=SIGNATURE_3,...]
-
TIMESTAMP
is an integer representing the POSIX timestamp (seconds since epoch) of the event creation. -
SIGNATURE_VERSION
isv1
right now, but this could change in the future. -
SIGNATURE
is an HMAC SHA256 digest of the stringTIMESTAMP.DATA
-
DATA
is the value of thedata
key in the payload.
-
-
There is one signature for every available secret, but you should only need to verify at least one.
Getting the secretCopied!
The signatures are generated using automatically-generated secret keys. You can obtain the secret used to sign payloads for your webhook from the Dashboard.
-
On the Developers page (where you created your webhook), click on the webhook that you want to get the secret for.
-
On the webhook’s details page, click the three dots to the right of the webhook name. Select Show Secret from the menu that appears.
-
You can now copy the secret to clipboard.
If you need to get the secret programmatically (using something like cURL), do the following:
-
Obtain an OpenPay API token with the
ADMIN
role. (Webhook secrets can only be accessed using tokens of this role.) -
Access the webhook secret from the API:
curl -X GET \
-H 'Authorization: Bearer SECRET_KEY' \
https://connto.getopenpay.com/webhook-endpoints/webhook_endpoint_XXXXXXXXXXX/reveal_secret
# JSON response:
# {
# "id": "webhook_endpoint_XXXXXXXXXXX",
# // ...
# "secret": "whsec_XXXXXXXXXXXXXXXX"
# }
Verifying a signatureCopied!
To verify a signature, you will need to generate your own signature using the same data as above and compare the two digests.
from getopenpay.client import ApiKeys, OpenPayClient
from getopenpay.utils.webhook_utils import InvalidSignatureError
client = OpenPayClient(ApiKeys(...))
secret_key = 'whsec_XXXXXXXXXXXXXXXX'
event_data = "{\"id\": \"event_dev_abcdefg12345678\", \"object\": \"event\", ...}"
signature_digest = request.headers.get('signature-digest', None)
try:
client.webhook_utils.validate_payload(
event_data=event_data,
signature_digest=signature_digest,
secret=secret_key
)
except InvalidSignatureError: # or ValueError
# ...
import hmac
from hashlib import sha256
secret_key = 'whsec_XXXXXXXXXXXXXXXX'
content = f'{TIMESTAMP}.{DATA}'
signature = hmac.new(
secret_key.encode('utf-8'),
content.encode('utf-8'),
digestmod=sha256
).hexdigest()
expected_signature = request.headers.get('signature-digest')
is_valid = hmac.compare_digest(signature, expected_signature)
HTTPS onlyCopied!
Since events can potentially contain sensitive data, such as customer information, we require that all webhooks use SSL (i.e., use HTTPS). You will not be able to add insecure HTTP URLs as webhook endpoints.
POST requests onlyCopied!
OpenPay makes requests to webhook endpoints using the HTTP POST method. Make sure that your webhook endpoint is equipped to listen for this type of request.
Other request types like GET are currently not supported.