Webhooks
You may wish to create endpoints to receive asynchronous events from your Prequel integration, such as transfer failures. Webhook endpoints created using the POST /webhooks
endpoint can subscribe to any of Prequel’s Event Types and configure delivery via HTTPS, Slack, or Pagerduty.
Delivery Methods
HTTP POST and GET
Prequel supports HTTPS
callbacks to your custom webhook receiver. Creating a webhook endpoint as the generic_post
type will cause payloads to be sent as a JSON payload. The generic_get
type will deliver payloads as URL parameters. The payloads are defined in Event Types.
PagerDuty & Slack
You may wish to have events sent to specific vendors for special handling of webhook requests. Currently, PagerDuty and Slack are supported, with specific payload shapes according to the vendor specifications.
Authentication
You can provide an API key for the webhook to use if the destination requires one. Additionally, Prequel signs every payload and passes the signature through the X-Prequel-Webhook-Signature
header. See Verifying Webhooks for more information on handling the signature.
Versioning
Prequel uses a top-level “version” field. A version is represented by the date it was released. Currently, all webhooks use the version 2023-10-15
.
Contents & Structure
All event types follow this structure:
Headers
Header | Description |
---|---|
Content-Type | Always application/json |
X-Prequel-Webhook-Timestamp | Timestamp generated when the event is sent. |
X-Prequel-Webhook-Signature | Signature generated by Prequel using SHA-256 and RSA PKCS1 v1.5 signature scheme. See Verifying Webhooks. |
X-Prequel-Webhook-Digest | Optional utility for signature verification. See Verifying Webhooks. |
Body
{
“type”: “resource.event”,
“version”: “XXXX-XX-XX”,
“created_at”: …, // timestamp generated when the event is created
“data”: {
// event specific
}
}
Event Types
When creating a webhook, you must specify which events it should listen to. Webhooks created before October 31 2023 will continue to receive only transfer errors.
Transfer errors
This event has type transfer.error
. It is sent whenever Prequel identifies a partial or full transfer failure. Note the following fields which do not appear on the transfer object itself:
transfer_error_type
: May be any offirst_party
,third_party
,prequel
,host
. Defaults toprequel
.first_party
indicates an error with your Prequel integration.third_party
indicates to an error caused by your customer; for example, a change in existing credentials for a destination.host
is only applicable if you are self-hosting Prequel.transfer_log
: Full log of the error.transfer_log_pretty
: Iftransfer_error_type
isthird_party
, a modified error string suitable for displaying to customers. Otherwise empty.
Example transfer error:
{
"type": "transfer.error",
"api_version": "2023-10-15",
"created_at": "2023-10-15T19:19:08+00:00",
"data": {
"error": {
"destination_id": "00000000-0000-0000-0000-000000000000",
"destination_name": "Foo Bar",
"id_in_provider_system": "acme",
"models": ["users"],
"is_full_refresh": true,
"transfer_id": "00000000-0000-0000-0000-000000000000",
"transfer_status": "PARTIAL_ERROR",
"transfer_error_type": "first_party_error",
"transfer_log": "Exception in thread \"main\" org.apache.spark.SparkException: Job aborted due to stage failure: Task 3 in stage 4.0 failed 4 times, most recent failure: Lost task 3.3 in stage 4.0 (TID 132, executor 2): java.lang.NullPointerException at org.apache.spark.sql.execution.datasources.FileScanRDD$$anon$1.next(FileScanRDD.scala)",
"transfer_log_pretty": "Error due to the heat death of the universe",
"transfer_started_at": "2023-10-15T16:19:08+00:00",
"transfer_ended_at": "2023-10-15T18:19:08+00:00",
"in_app_url": "https://app.prequel.co/export/destinations/00000000-0000-0000-0000-000000000000/transfers"
}
}
}
Other events
Aside from transfer errors, events are of the type resource.created
, resource.updated
or resource.deleted
. The resources that currently generate webhooks are sources
, destinations
, recipients
, providers
, and magic_links
.
Example event:
{
"type": "recipient.updated",
"api_version": "2023-10-15",
"created_at": "2023-10-15T19:19:08+00:00",
"data": {
"current_resource": {
"id": "00000000-0000-0000-0000-000000000000",
"name": "Acme Inc",
"products": ["billing", "invoices"],
"created_at": "2023-10-15T19:19:08+00:00",
},
"previous_resource": {
"id": "00000000-0000-0000-0000-000000000000",
"name": "Acme Inc",
"products": ["billing"],
"created_at": "2023-10-15T19:19:08+00:00",
}
}
}
Verifying Webhooks
Prequel provides a unique signature in the HTTP header of each webhook request. You can use this signature and your account’s webhook public key to verify that the data you receive is from Prequel.
Webhook Signatures
Prequel’s approach to webhook signatures is based on asymmetric cryptography. Prequel generates an RSA private and public key pair for your account’s webhook integration. After generating a webhook, Prequel digitally signs the payload with the private key. On receiving the webhook, you can use the public key to verify the authenticity and freshness of this signature. See Verifying The Signature.
Relevant headers
Header | Description |
---|---|
X-Prequel-Webhook-Timestamp | Timestamp the webhook was sent, in RFC 3339 format. |
X-Prequel-Webhook-Signature | Signature generated by Prequel using the [signing data] |
(Maybe) X-Prequel-Webhook-Digest | SHA-256 hash of the body provided by Prequel for debugging purposes |
Verify the signature
The general steps to verify a signature are outlined below.
1. Retrieve your webhook key
You can retrieve your webhook key by hitting the following API endpoint /public/signatures/webhhook-public-key
. See the API reference.
2. Reconstruct the signing data
Extract the timestamp provided in the X-Prequel-Webhook-Timestamp
header. The date is in RFC 3339 format and looks something like:
X-Prequel-Webhook-Timestamp: 2023-10-15T14:30:00Z
The signing data is in the format timestamp.body
, constructed by concatenating
- The timestamp
- The character
.
- The request body, i.e. the raw JSON payload
Finally, hash the full signing data using SHA-256
.
Warning
Prequel uses the raw body of the request to generate a signature. You should hash the raw body string (or bytes) before deserializing the JSON payload. Frameworks that parse the response body may introduce subtle changes, such as removing characters or changing key-sort order.
Validate the response hash
Prequel hashes the raw body only and provides the result in the X-Prequel-Webhook-Digest
header. You can use this to validate that you are handling the response body correctly. You should not use this to confirm the signature.
3. Confirm the signature
Once you’ve reconstructed and hashed the signing data, you can sign the data with your public key and compare the output to the signature in the X-Prequel-Webhook-Signature
header.
Prequel uses the PKCS1 v1.5 signature scheme.
4. Verify timing
A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. The provided timestamp is verified by the signature, so an attacker can’t change the timestamp without invalidating the signature. However, even if the attacker cannot alter the data, they may produce side-effects from processing the same event multiple times. We recommend defining a time window (such as 5 minutes) and rejecting events that are too old.
Updated about 1 year ago