Each webhook notification contains the following headers:
Header parameter | Description | Format |
---|---|---|
Revolut-Request-Timestamp | UNIX timestamp of the webhook event. Example value: 1715269527223 . | String |
Revolut-Signature | Signature of the request payload. Contains the current version of the signature-generating algorithm and the hexadecimal-encoded signature. Example value: v1=d913b5305c6b975e50ed7c5dbb5826d31d39a488f47dc0a935692b7558eb6b23 . | String |
To ensure that a webhook request originates from Ramp and not a third party, we recommend that you verify the request's signature using a signing secret.
Ramp uses the HMAC SHA-256 algorithm to sign its webhooks. The secret is generated on webhook creation. After you've created a webhook, you can retrieve the signing secret when you retrieve a specific webhook's details.
You can mitigate replay attacks in your webhooks by applying timestamp validation.
For each webhook event, Ramp sends a Revolut-Request-Timestamp
header with the exact date-time when it was delivered.
To validate the event, make sure that the Revolut-Request-Timestamp
date-time is within a 5-minute time tolerance of the current universal time (UTC).
Follow these steps to verify the signature for the webhook's request payload.
To compute payload_to_sign
, concatenate the following data, separating each item with a full stop (.
):
v1
)Revolut-Request-Timestamp
headerpayload_to_sign = {version}.{Revolut-Request-Timestamp}.{raw-payload}
An example of payload_to_sign
might look like this:
v1.1715269527223.{"order_id":"19218d6e-5f55-4a0d-b7c5-6e333881c1c9","wallet":"0x96e2B7Bf479f84e7A0a94f0620290B7D3E08f5EF","event":"ORDER_CREATED"}
The signature is sensitive to any modifications, meaning even a small change in the body will result in a completely different signature. Therefore, it is crucial not to alter the body, especially before the verification.
To compute the expected signature, you need to concatenate the version of the signature-generating algorithm (v1
) with the hash-based message authentication code (HMAC).
Separate them with the equals character (=
).
To compute the HMAC, use the SHA256 hash function and:
You can use the following Python implementation for reference:
import hmac
import hashlib
signing_secret = 'wsk_8fT55z3C5hCr41l6B0b057D85s2043x4' #Obtained on webhook creation/details retrieval
raw_payload = '{"order_id":"19218d6e-5f55-4a0d-b7c5-6e333881c1c9","wallet":"0x96e2B7Bf479f84e7A0a94f0620290B7D3E08f5EF","event":"ORDER_CREATED"}'
timestamp = '1715269527223'
payload_to_sign = 'v1.' + timestamp + '.' + raw_payload #Prepared in Step 1
signature = 'v1=' + hmac.new(bytes(signing_secret , 'utf-8'), msg = bytes(payload_to_sign , 'utf-8'), digestmod = hashlib.sha256).hexdigest()
print(signature)
Once you've computed the expected signature, compare it with the signature obtained in the Revolut-Signature
header of the webhook notification.
The computed signature must match exactly the signature sent in that header.