Webhooks

You can configure a single webhook URL to be notified about events from Pier. All events go to the same URL and are distinguished by an event type and code. If you configure the webhook multiple times, the URL will be overwritten and all events will only be delivered to the most recently configured endpoint URL. In addition, deleting the webhook URL will clear it and no events will be delivered, even if the webhook was configured multiple times.

Setup webhook endpoint

Configuration Create Endpoint

Delete webhook endpoint

Configuration Delete Endpoint

Webhooks include a JWT in the pier-webhook-verification header. The JWT payload contains a sha256 signature for the webhook body in the request_body_sha256 field.

To ensure the payload is sourced from Pier:

  1. The JWT should be verified using a library for your stack and the Pier public key (see /configuration/webhook-verification). You should replace \n with newlines in the public key before using it (e.g. .replace(/\\n/g, "\n")).
  2. Extract the payload from the JWT.
  3. Extract the SHA256 signature from the JWT payload request_body_sha256 field.
  4. Compute the SHA256 signature of the webhook payload.
  5. Ensure that the computed SHA256 and the SHA256 from the JWT match.

Take care not to process the webhook body prior to computing the signature, otherwise you may get a false negative when comparing signatures.

Webhook Object

  • type: High-level webhook event type.
  • code: Detailed event type.
  • env: The Pier environment from which the event was sent (see above).
  • url: The url the webhook was sent to.
  • timestamp: The time at which the event occurred.
  • identifiers: Event-specific identity of the object acted upon in the event.
  • details: Event-specific supplemental details about the event.

More details about identifiers and details for each event can be found in the detailed documentation for each event type/code below.

Generic webhook object example

1{
2 "type": "example",
3 "code": "example_code",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-15T22:00:28.785Z",
7 "identifiers": {
8 "object_id": "obj_db2f7ce6eba24268a04633e4c560ba44"
9 },
10 "details": "Additional details about the event"
11}

Note that the type, code, identifiers, and details fields depicted here are generic and don’t reflect actual values you will see. Concrete examples for each event type are shown below.

Webhook event types

At a high level, Pier events fall into four types as shown:

TypeDescription
PaymentA payment event occurred
DisbursementA facility disbursement occurred
StatementA new account statement was generated
KYC InquiryA KYC inquiry result was returned

In addition to an event type, each event has a more detailed code as summarized in the list below. More detailed descriptions of each event code and the data returned are included in the following section.

Webhook codes

The following webhook codes provide more detail about the nature of the event. Additional info about each code is provided in the detailed webhook examples below:

  • transaction_succeeded
  • transaction_failed
  • transaction_initiated
  • statement_ready
  • autopay_scheduled
  • payment_due
  • payment_late_5_days
  • payment_late_10_days
  • payment_late_15_days
  • payment_late_30_days
  • payment_late_45_days
  • kyc_approved
  • kyc_declined

Identifiers

Certain event types also include identifiers for relevant Pier entities when appropriate:

Webhook typeIdentifiers
Disbursementfacility_id and disbursement_id
Paymentfacility_id and payment_id
Statementfacility_id and statement_id
KYC Inquiryconsumer_id

Detailed webhook examples

Disbursements

Disbursements (and payments) have three events in their lifecycle that trigger webhooks:

  • transaction_initiated
    • indicates that the disbursement has been sent to the ACH network
  • transaction_succeeded
    • indicates that the disbursement has successfully settled.
  • transaction_failed
    • indicates that the disbursement has failed, and likely has a return code.

Disbursement webhook examples

  • transaction_initiated
1{
2 "type": "disbursement",
3 "code": "transaction_initiated",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-15T21:39:27.051Z",
7 "identifiers": {
8 "disbursement_id": "dsb_0d22b88519554e8b8df69436f14180d4",
9 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
10 }
11
12}
  • transaction_succeeded
1{
2 "type": "disbursement",
3 "code": "transaction_succeeded",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-18T00:24:36.535Z",
7 "identifiers": {
8 "disbursement_id": "dsb_0d22b88519554e8b8df69436f14180d4",
9 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
10 }
11}
  • transaction_failed
1{
2 "type": "disbursement",
3 "code": "transaction_failed",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-18T00:27:10.928Z",
7 "identifiers": {
8 "disbursement_id": "dsb_0d22b88519554e8b8df69436f14180d4",
9 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
10 },
11 "details": "ACH transfer return code R01: Insufficient Funds"
12}

Payments

Payments have the same events as disbursements. The only difference in these cases are the type field which will be payment instead of disbursement in the examples above. In addition, payments also have a number of autopay and past due events:

  • autopay_scheduled
    • indicates that an autopay has been scheduled for the next payment due. Implies payment_due event will not be sent.
  • payment_due
    • indicates that a payment is due. Implies autopay_scheduled event will not be sent.
  • payment_late_5_days
  • payment_late_10_days
  • payment_late_15_days
  • payment_late_30_days
  • payment_late_45_days
    • indicates that a payment is late by the number of days specified in the event code.

Payment webhook examples

  • autopay_scheduled
1{
2 "type": "payment",
3 "code": "autopay_scheduled",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-18T00:27:10.928Z",
7 "identifiers": {
8 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
9 }
10}
  • payment_due
1{
2 "type": "payment",
3 "code": "payment_due",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-18T00:27:10.928Z",
7 "identifiers": {
8 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
9 }
10}
  • payment_late_5_days
  • payment_late_10_days
  • payment_late_15_days
  • payment_late_30_days
  • payment_late_45_days
1{
2 "type": "payment",
3 "code": "payment_late_5_days",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-18T00:27:10.928Z",
7 "identifiers": {
8 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
9 }
10}

Statements

When a statement is ready, a statement_ready webhook is sent. The statement_id and facility_id are included in the identifiers field.

1{
2 "type": "statement",
3 "code": "statement_ready",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-15T21:39:27.051Z",
7 "identifiers": {
8 "statement_id": "stmt_0d22b88519554e8b8df69436f14180d4",
9 "facility_id": "fac_207b5820591743fab5994f5b5b2b475e"
10 }
11}

KYC Inquiries

KYC inquiry webhook examples are shown below. They only vary in the result indicated in the codes kyc_approved for a successful KYC check and kyc_declined for a KYC check that resulted in a failure. Both always include the consumer_id of the borrower for whom the KYC check was performed in the identifiers field.

  • Approved
1{
2 "type": "kyc_inquiry_result",
3 "code": "kyc_approved",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-15T21:39:27.051Z",
7 "identifiers": {
8 "consumer_id": "bor_0ec20e551975485ca49701bb622c6edf"
9 }
10}
  • Declined
1{
2 "type": "kyc_inquiry_result",
3 "code": "kyc_declined",
4 "env": "sandbox",
5 "url": "https://example.com/webhook",
6 "timestamp": "2024-10-15T21:39:27.051Z",
7 "identifiers": {
8 "consumer_id": "bor_0ec20e551975485ca49701bb622c6edf"
9 }
10}

See the KYC Guide for more information about this feature.

Webhook retries

If the webhook endpoint does not respond with a 2xx status code, Pier will retry sending the webhook up to 5 times with an exponential backoff. The first retry will be around 10 seconds later, the second after around 2 minutes, the third after around 15-20 minutes, the fourth after 2-3 hours, and the fifth after approximately 1 day. If the webhook still fails after 5 retries, the webhook will be marked as failed and no further retries will be attempted.