A large number Modern Treasury's integrations are asynchronous in nature and do not take effect immediately. Primarily this is due to how the underlying systems operate (e.g. file-based messaging over FTP) but it also allows Modern Treasury to efficiently batch its communications to the bank. Fewer files often translates into lower costs and less operational overhead.
To make dealing with these systems easier, the Modern Treasury API is built around the idea of sending important updates over HTTP webhooks. These endpoints can be configured through the settings page inside the application and apply to the entire organization. If needed, separate URLs may be provided for live and test traffic.
Some resources allow the ability to pass a custom webhook_url
during creation. This will override the organization-level settings for the created instance.
Technical Details
All endpoints must be configured to receive a HTTP POST with a JSON payload. The webhook system will wait a maximum 5 seconds for a 2XX HTTP status code in order to mark the payload as delivered. If the endpoint takes longer to respond or an error is returned, the webhook will be re-enqueued for delivery at a later time subject to an exponential backoff.
All endpoints are strongly encouraged to use HTTPS as this protects the payload in-transit.
Authentication
To verify that a webhook was actually sent by Modern Treasury, every payload is signed with a signature that is passed through as the HTTP header X-Signature
. The signature is Hex encoded and can be replicated by applying HMAC-SHA-256 to the body of the webhook with your specific webhook key, which can be found in your webhook settings page.
Please contact support if your webhook key is accidentally made public. We will rotate the key and coordinate the change with you.
Webhook Idempotency
Each webhook has a unique ID. This is passed through as the HTTP header X-Webhook-ID
. You can save these IDs as you process webhooks to ensure each webhook is only processed once. If a webhook is sent multiple times, its ID will remain the same between requests.
Live vs Test
When making API calls that invoke webhooks, the endpoint URL will depend on whether the live or test key was used. If you want to use the same URL for both live and test webhooks, you can still differentiate using the HTTP header X-Live-Mode
which is true
for live traffic and false
for test traffic.
Topics
Each webhook payload contains a topic that describes which category the event belongs to. This is passed through as the HTTP header X-Topic
.
Topic | Description |
---|---|
balance_report | Any balance report lifecycle event. |
external_account | Any external account lifecycle event. |
expected_payment | Any expected payment lifecycle event. |
paper_item | Any paper item lifecycle event. |
payment_order | Any payment order lifecycle event. |
return | Any return lifecycle event. |
transaction | Any transaction lifecycle event |
Contents and Structure
All Modern Treasury webhooks have the same top level structure.
event
specifies what happened to the object.data
contains the updated object that changed.
{
"event": "event_type",
"data": {
// serialized object
}
}
{
"content-length": "464",
"x-topic": "paper_item",
"x-signature": "62d1745dcb53d510963cfb9d3588109903a9ff2cb895ea0f59e5ed38472f6ac8",
"x-delivery-id": "bee2db7f-5794-47cd-9d98-84453baa5117",
"content-type": "application/json",
"x-live-mode": "true",
"x-internal-signature": "884ee247af1d1d5b0c7122c3e69e1aadf7f83f16c992ea6c7fe557679e7753db",
"x-organization-id": "<ORGANIZATION_ID>",
"x-webhook-id": "7b5bc00751e4c9760974ee85",
"user-agent": "http.rb/4.2.0"
}
{
"event": "created",
"data": {
"account_number": "222222223",
"amount": 9900,
"check_number": "11",
"created_at": "2019-12-12T22:34:59Z",
"currency": "USD",
"deposit_date": "2019-12-12",
"id": "9e5c22be-2145-4da4-963f-b0434765d18f",
"lockbox_number": "12345",
"memo_field": "Christmas Tip",
"object": "paper_item",
"remitter_name": "Cristina Angela",
"routing_number": "021000021",
"status": "pending",
"transaction_id": null,
"transaction_line_item_id": null,
"updated_at": "2019-12-12T22:34:59Z"
}
}