Overview

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 webhooks settings page inside the application and apply to the entire organization. If needed, separate URLs may be provided for live and test traffic.

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.

Modern Treasury delivers webhooks exclusively over HTTPS using TLS 1.2 or greater. This protects the confidentiality of customer data by ensuring webhook payloads are encrypted 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.

IP addresses

Modern Treasury's IP addresses are documented at IP Addresses if you would like to verify the transmission IP as part of a webhook receipt solution.

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.

TopicDescription
balance_reportAny balance report lifecycle event.
connection_legal_entityAny connection legal entity lifecycle event.
decisionAny Compliance decision lifecycle event.
external_accountAny external account lifecycle event.
expected_paymentAny expected payment lifecycle event.
incoming_payment_detailAny incoming payment detail lifecycle event.
ledger_account_payoutAny ledger account payout lifecycle event.
ledger_transactionAny ledger transaction lifecycle event.
legal_entityAny legal entity lifecycle event.
paper_itemAny paper item lifecycle event.
payment_orderAny payment order lifecycle event.
returnAny return lifecycle event.
reversalAny reversal lifecycle event.
transactionAny transaction lifecycle event.
user_onboardingAny user onboarding lifecycle event.

Contents and Structure

Webhook skeleton

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
  }
}

Sample webhook headers

  • X-Event-ID is the ID of the Event object that is unique. If there are retries, each individual retry will be tied to the same Event ID. The Event ID is in the Event object in the Event Log and also in each delivery attempt. You can see this Event ID in the Event object in the Event Log and also in each delivery attempt.
  • X-Event-Time is the time of the event, resolved to a decimal fraction of seconds with a precision of 9.
  • X-Webhook-ID is a unique ID tied to this specific webhook event that will persist across retries.
  • X-Delivery-ID is an ID tied uniquely to each individual delivery attempt of this webhook. Across retries, this will change. You can see your webhook delivery attempts in your webhook endpoint pages and at the bottom of each individual Event Log.
  • X-Internal-Signature is an internal failsafe in the case that a webhook key is ever exposed to a potential attacker. You do not need to pay attention to this Signature. If that attacker were to send you an erroneous webhook event with an X-Signature that is valid (since they have the webhook key), the X-Internal-Signature is a method that Modern Treasury can verify against those spurious events.
{
  "Content-Length": "464",
  "Content-Type": "application/json",
  "X-Topic": "paper_item",
  "X-Event-ID": "f8651bc8-6862-49c1-9a1a-7ff5ea7aa228",
  "X-Event-Time": "2022-02-03T04:05:06.123456789+07:00",
  "X-Live-Mode": "true",
  "X-Signature": "62d1745dcb53d510963cfb9d3588109903a9ff2cb895ea0f59e5ed38472f6ac8",
  "X-Webhook-ID": "7b5bc00751e4c9760974ee85",
  "X-Delivery-ID": "0413352f-93df-4a4a-851b-2baaa47b4fe5",
  "X-Organization-ID": "<ORGANIZATION_ID>",
  "X-Internal-Signature": "884ee247af1d1d5b0c7122c3e69e1aadf7f83f16c992ea6c7fe557679e7753db",
  "User-Agent": "http.rb/4.2.0"
}

Sample webhook body

{
  "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"
  }
}

Troubleshooting

🚧

It is important to keep an eye on your webhook endpoint(s) to ensure they are reliably receiving and processing webhooks. By policy, Modern Treasury automatically pauses webhook endpoints when we're unable to deliver events to them for five consecutive days (excepting any days where no deliveries were attempted).

Modern Treasury keeps historical logs of all webhook delivery attempts to aid customers in troubleshooting issues with network connectivity and webhook endpoint behavior. Dashboard users can browse these logs in Developer settings, on the Webhooks page.

Modern Treasury may be configured to notify one or more user groups when a webhook endpoint exhibits a high failure rate or is wholly unresponsive. We strongly recommend that all customers that use webhooks subscribe to these notifications.