Webhooks
Plastiq Connect uses webhooks to notify clients about asynchronous API lifecycle events. Plastiq Connect currently supports webhook events for Payments.
When and Why to Use Webhooks
Webhooks are particularly useful when an integrating partner needs to respond to an asynchronous event that occurs in the Plastiq Connect system.
For example, an integrating partner may want to be notified when a Payment
was successfully deposited into the Recipient's bank account, so that the partner's Payer can be notified.
Supported Events
Event | Event Object | Description |
---|---|---|
payment.state_change | PAYMENT | All state change events pertaining to a Payment . See Payment States for a current list of payment states. |
document.request | DOCUMENT | Webhook event to request additional information. |
payment.disbursement_information | PAYMENT | Webhook event with additional disbursement account information including routing number, account last four, and bank name. |
payment.trace_information | PAYMENT | Webhook event with additional trace information. |
How Connect Webhooks Work
Plastiq Connect will emit a webhook notification for any event that an integrating partner subscribes to as soon as it occurs in our system.
Acknowledging Requests
Given a valid and secure (HTTPS) URL, Plastiq Connect will dispatch an event via an HTTP call.
Subscribers, typically the integrating partner, will have up to 5 seconds to acknowledge that they have received the webhook payload by returning an HTTP status code of 200
.
Webhook Payload Format
All Plastiq Connect webhook events are emitted with a consistent request payload, which contains Ids and associated non-sensitive information.
Data Sensitivity
No Personal Identifiable Information (PII) or Payment Card Industry Data Security Standard (PCI DSS) information will ever be sent via Plastiq Connect to a webhook subscriber.
Sample Request Payload with Field Descriptions
{
"id": "0a176a3a-1912-4da0-88b0-131d8be60636",
"event": "payment.state_change",
"data": {
"id": "37e51171-5f17-4551-8dcd-755666ae7483",
"object": "PAYMENT",
"state": "CHARGED",
"timestamp": 1620080776056,
"payer": {
"id": "78c38cde-ab78-4741-b5e6-8164c9edd6e3"
}
}
}
{
"id": "dee9427c-3e33-490b-a9f1-9fd16b75a14f",
"event": "document.request",
"data": {
"id": "fc3d3ad3-52ae-45d7-bae0-501d112547ef",
"object": "PAYMENT",
"type": "INVOICE",
"timestamp": 1620080776056,
"payer": {
"id": "78c38cde-ab78-4741-b5e6-8164c9edd6e3"
}
}
}
{
"id": "dc99f29a-bf13-4cf1-beda-3452ac6d7781",
"event": "payment.disbursement_information",
"data": {
"id": "26df60ce-cff9-41e5-ab8f-d35976d95f68",
"object": "PAYMENT",
"disbursementInformation": {
"accountLastFour": "1234",
"routingNumber": "123456789",
"bankName": "bankName"
},
"timestamp": 1655223000158,
"payer": {
"id": "3bd80c82-643f-40ed-b262-49cbf714f232"
}
}
}
{
"id": "0a176a3a-1912-4da0-88b0-131d8be60636",
"event": "payment.trace_information",
"data": {
"id": "37e51171-5f17-4551-8dcd-755666ae7483",
"object": "PAYMENT",
"traceInformation": {
"reference": "12345678"
},
"timestamp": 1620080776056,
"payer": {
"id": "78c38cde-ab78-4741-b5e6-8164c9edd6e3"
}
}
}
Webhook payload field | Description |
---|---|
id | A unique Id (UUID v4) generated specifically for this given event, which can be used as a deduplication key. |
event | The event for which the integrating partner subscribed. See Supported Events above for a list of available event types. |
data | Data pertaining to the event. This part of the payload may vary for a given event type. |
data.id | The id of the resource object the event occurred on. |
data.object | The API resource object type for which the event occurred. |
data.state | The specific state change event that occurred for the object. |
data.timestamp | The epoch timestamp at which the event was emitted from the Plastiq system. |
data.payer.id | The id of the Payer the event was published on behalf of. |
data.disbursementInformation | The payment disbursement account information, including routing number, account last four, and bank name. |
date.traceInformation.reference | Trace information reference number for the payment. |
Retry and Backoff Strategy
If the webhook client fails to acknowledge the event within 5 seconds (maybe because their system is down or overloaded), we will retry several times with an increasing time interval between attempts.
This is done to ensure our clients receive event notifications reliably. The webhook system will attempt to retry a total of 8 times before the event is marked as failed. Plastiq Connect webhooks are built for at-least-once delivery in mind.
Subscription deactivation
If you fail to acknowledge a webhook after 8 tries, your subscription will be disabled and you will no longer receive future webhook events. This is done to ensure we do not continue to send requests to a non-responsive / inactive client.
Retry Time Intervals
Attempt | Retry delay between events |
---|---|
1 | 1 minute |
2 | 5 minutes |
3 | 15 minutes |
4 | 1 hour |
5 | 6 hours |
6 | 12 hours |
7 | 1 day |
8 | 2 days |
Jitter
All retry delays are subject to random jitter value within +/- 10% of the defined time interval. For example, for retry attempt 4, where the retry delay is 1 hour, you would receive the request between 54 and 66 minutes.
This is minor amount of randomization done to reduce congestion on your system.
Webhook Signatures and Security
How to verify your webhook payload
Plastiq Connect webhooks events are each signed with a unique shared secret
only accessible to the integrating partner. This is done to ensure integrating partners can verify Plastiq Connect-originated payloads.
As part of the webhook HTTP request sent to subscribing partners, we include the following request headers:
Header | Description |
---|---|
Plastiq-Signature | A header signed (SHA256 HMAC) with the shared secret consisting of: timestamp.request_body - where timestamp is the value of the Plastiq-Timestamp header, request_body is the webhook notification HTTP request's payload, and . is a separator between the two. |
Plastiq-Timestamp | The epoch timestamp at which the HTTP request containing the event payload was sent. This can be used to prevent replay attacks .See more about Replay Attacks here. |
In order to verify that the request was received from Plastiq, sign a string of: Plastiq-Timestamp
+ .
+ JSON representation of HTTP request payload
with the shared secret
you received when you subscribed to an event type.
Then, compare it to the Plastiq-Signature
header value. If they are the same, you can be certain the request was not tampered with.
Below are examples of how to verify a signature:
import hmac, hashlib, json
...
def is_verified_payload(payload: dict, header_signature: str, header_timestamp: str, shared_secret: str):
h = hmac.new(shared_secret.encode('utf-8'), digestmod=hashlib.sha256)
hash_string = header_timestamp + '.' + json.dumps(payload, separators=(',', ':'))
h.update(hash_string.encode('utf-8'))
signature = h.hexdigest()
return signature == header_signature
const crypto = require('crypto)
function isVerifiedPayload(sharedSecret, timestamp, payload, signature) {
const hmac = crypto
.createHmac('sha256', sharedSecret)
.update(`${timestamp}.${JSON.stringify(payload)}`)
const signedPayload = hmac.digest('hex')
return signedPayload == signature
}
void validateRequestSignature(String plastiqTimestamp, Object requestBody, String plastiqSignature) throws Exception {
// stringify body
ObjectMapper Obj = new ObjectMapper();
String bodyString = Obj.writeValueAsString(requestBody);
// generate hmac
Mac mac = Mac.getInstance(SIGNATURE_ALGORITHM);
mac.init(new SecretKeySpec(SIGNATURE_KEY.getBytes("UTF-8"), SIGNATURE_ALGORITHM));
String requestHash = Hex.encodeHexString(mac.doFinal(plastiqTimestamp.concat(".").concat(bodyString).getBytes("UTF-8")));
// confirm if request matches sent signature
if (!plastiqSignature.equals(requestHash)) {
throw new Exception("invalid signature");
}
}
Webhook Source IP Range
All webhook event notifications are sent from a defined range of static IP addresses that clients can whitelist for further security.
How to Subscribe to Webhook Events
Webhook subscriptions are currently handled on an ad-hoc basis. Reach out to us to subscribe to webhook events for your integration.
Best Practices
Acknowledge early
Sometimes, processing a webhook event can take time. Given that Plastiq Connect webhook event timeouts will be retried automatically, it is important to acknowledge receipt of an event notification as early as possible to avoid processing the same event.
Acknowledge but ignore events you don't care about
There may be events you have registered for, but simply do not need to process.
An example of such an event may be a specific Payment Status notification that does not need further action.
In these cases, it is important to still return an HTTP 200
response, but simply discard the message to prevent Plastiq Connect from unnecessarily resending you the same event.
Verify event signatures and use a whitelist
Given that webhooks, by nature, require you to expose an endpoint publicly (to some level), it is important to confirm that the event you received is from Plastiq Connect.
To further protect your system from unwanted traffic, whitelisting our domain, outbound.plastiq.com
, is suggested.
Reporting
You will be able to query all of your webhooks requests and payloads by timestamp.
Updated over 2 years ago