Webhooks
Cross-product webhook conventions for the Mono API.
A webhook is an automatic notification Mono sends to your system the moment something happens — a transfer is approved, a card is charged, a collection is credited. Instead of asking Mono repeatedly whether something changed, your server receives a single HTTPS request as soon as the event occurs.
These conventions apply to every Mono product: Banking, Core, and Bre-B Participant. Each product has its own event catalog; this page documents the shared mechanics that govern all of them.
Before you start
You will need:
- A publicly accessible HTTPS endpoint (no
localhost, no127.0.0.1). - The ability to verify an HMAC-SHA256 signature on every incoming request.
- An idempotent event handler — Mono retries on failures, so the same event may arrive more than once.
Envelope
Every webhook request body follows the same JSON envelope:
{
"event": {
"data": { ... },
"type": "event_type_or_name"
},
"timestamp": "2022-12-29T15:42:08.325158Z"
}event.type— the stable name of the event (e.g.bank_transfer_approved).event.data— the event payload; its schema depends on the event type.timestamp— ISO 8601 UTC timestamp of when the event was generated.
Signature
Each request is signed with a webhook secret you obtain from the Dashboard. The signature and the timestamp travel together in a custom header called Mono-Signature:
Mono-Signature: t=1672328528,v1=662255ca79c4b21914894d32da10189a1482cd0fee56b2765a8b472662ce55act— Unix timestamp. Use it to guard against replay attacks by rejecting requests whose timestamp falls outside your tolerance window.v1— the HMAC-SHA256 signature. Mono currently uses only versionv1.
Verifying signatures
Step 1: Extract timestamp and signature
Split Mono-Signature on , to get:
t=1672328528
v1=662255ca79c4b21914894d32da10189a1482cd0fee56b2765a8b472662ce55acStep 2: Build the signed payload string
Concatenate the timestamp, a literal ., and the raw request body:
<timestamp>.<payload_rawbody>Step 3: Compute the expected signature
Use HMAC-SHA256 with your webhook secret as the key and the signed payload string as the message:
require 'openssl'
timestamp = 1672774221
raw_payload = '{"respose_body": "example"}'
signed_payload = "#{timestamp}.#{raw_payload}"
secret = "whsec_example"
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, signed_payload)
# => 652fdc1742906b4b23ce2a5f4ac417b52c264fea0207920a5e76330a87239924Step 4: Compare and validate
Compare the computed HMAC against the v1 value using a constant-time comparison to prevent timing attacks. Then check that the difference between the current time and t is within your acceptable window (typically 5 minutes).
Retry policy
Mono retries failed requests (any non-2xx response) up to ten times with exponential backoff. After the tenth attempt, the event is not retried again.
| Attempt | Delay from previous |
|---|---|
| 1 | immediately |
| 2 | 30 seconds |
| 3 | 1.5 minutes |
| 4 | 3.5 minutes |
| 5 | 7.5 minutes |
| 6 | 15.5 minutes |
| 7 | 31.5 minutes |
| 8 | 1.06 hours |
| 9 | 2.13 hours |
| 10 | 4.26 hours |
All ten attempts complete within approximately 8.4 hours.
Money fields
Monetary amounts are always represented in the smallest currency unit (cents for COP and USD):
240000= 2,400.00160= 1.60
The exception is fx_rates, which carries the decimal exchange rate directly: "4002.24" = 4002.24.
URL restrictions
Your webhook endpoint must:
- Start with
httporhttps. - Not use
localhostor127.0.0.1as the host. - Be a valid, reachable HTTP/HTTPS URL.
Next steps
- Banking webhooks — event catalog for bank transfers and collection links.
- Core webhooks — event catalog for card transactions and ledger account transactions.
- Bre-B Participant webhooks — event catalog for Bre-B outgoing transfers and collections.