Setup - Webhooks
Set up your environment to utilise Immutable's Blockchain Data webhooks product to be notified when a requested event occurs. Can be used to monitor updates to assets, orders, collections and tokens.
Create Webhook
Navigate to Webhooks Configuration: Log into the Immutable Hub and go to
[Your Project] > [Your Environment] > Webhooks
.Create Webhook: Click on the
Add Webhook
button to create a new webhook. A single webhook can be used to subscribe to multiple events and collections.Configure Subscription: You will be able to configure the following:
- Endpoint URL: The HTTPS URL of your endpoint for receiving events.
- Event Types: Specify the event type(s) you want to receive, or opt for all event types.
- Collection Address: The collection address(es) you wish to subscribe to, or choose to subscribe to all collections.
You can subscribe to any collection across the platform, not just the collections linked to your project.
You can find the list of available events, when they are triggered and the data they contain here.
Handle Webhook Messages
Upon creating your webhook in Immutable Hub, you will need to
- confirm your webhook according to a subscription confirmation message sent to the endpoint you've configured
- handle verification of the sender and signature of the incoming messages to secure your endpoint
- finally, handle the message
You can use Immutable Typescript SDK to do the first two of above for you. Or you can handle them manually.
- Use SDK (Node.js)
- Manually
The example below uses the webhook.handle
method from @imtbl/sdk
to automate subscription confirmation, and signature and sender verification.
Webhook package is only available in alpha version of the SDK.
It can be installed by running npm install @imtbl/sdk@alpha
.
It assumes you have a http server set up. We are using Fastify
in this example.
import { config, webhook } from '@imtbl/sdk';
import Fastify, { FastifyReply, FastifyRequest } from 'fastify';
const url = "/api/process_webhook_event" // The full url of this webhook endpoint should be entered onto the webhook configuration page on https://hub.immutable.com
fastify.post(url, async (request: FastifyRequest<any>, reply: any) => {
await webhook.handle( // this method will handle the subscription confirmation and signature & sender verification
request.body,
config.Environment.SANDBOX, // or config.Environment.PRODUCTION
{
zkevmMintRequestUpdated: (event) => {}, // this is an example of handling the zkevm_mint_request_updated event
all: (event) => { // all events will trigger this handler, even if they have a specific handler already.
switch (event.event_name) {
// see all event names here: /products/zkEVM/blockchain-data/webhooks/#event-subscriptions
case ".....arbitrary event name you want to handle.....":
console.log(event);
break;
default:
break;
}
}
}
);
reply.send({ status: "ok" });
});
Alternatively, you may be interested in functions like mintingBackend.processMint
to handle ONLY particular event (mint in this instance). Please check the quickstart guide for handling minting webhook events.
https://ngrok.com/ is a great tool to expose your local server to the internet for testing webhooks.
When using above webhook.handle
method, you will get the JSON parsed object on the "Message"
field in the below payload.
Subscription confirmation
Since we use AWS SNS to deliver your messages, you'll see a request from SNS to confirm your subscription.
You may experience some delay in receiving the confirmation request. Please give it a few minutes before you trigger a re-send from the Immutable Hub.
Please visit the URL in the SubscribeURL
field to confirm your subscription. You will be led to a page with some XML data, which you can close.
Sample Subscription Confirmation Request
{
"Type": "SubscriptionConfirmation",
"MessageId": "536d56d3-ffd3-456a-8c1d-1d4aeb5c508b",
"TopicArn" : "arn:aws:sns:us-east-2:783421985614:webhook-outbound-sandbox",
// ... other fields ...
"SubscribeURL": "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&Token=2336412f37...",
// ...
}
Sample Successful Confirmation Page
<ConfirmSubscriptionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
<ConfirmSubscriptionResult>
<SubscriptionArn>arn:aws:sns:us-east-2:362750628221:webhook-outbound-sandbox:xxx</SubscriptionArn>
</ConfirmSubscriptionResult>
<!-- other fields -->
</ConfirmSubscriptionResponse>
Please only confirm subscriptions coming from the following TopicArn
. Confirming subscriptions from any arns not in the list can expose you to malicious actors sending you fake events.
- Sandbox: "arn:aws:sns:us-east-2:783421985614:*",
- Production: "arn:aws:sns:us-east-2:362750628221:*",
Signature and sender verification
It is highly recommended that you verify the authenticity of the sender of the incoming messages to prevent spam or malicious messages.
Signature verification
AWS SNS signs every outgoing message and populates the Signature field of payload. Please follow this doc in order to perform the verification using the signature.
Sender verification
Please also perform a check that the TopicArn
on the SNS wrapper of the incoming message is from the above authorized list of arns.
Handling the message
The Message
field of the incoming message contains the event data in the form of a JSON string.
{
//"Type": "Notification",
// ... other fields ...
>>> "Message": "{"event_name":"imtbl_zkevm_activity_mint","event_id":"018b3c3d-a3aa-a4c9-4c16-dc7a2bd7d715","chain":"imtbl-zkevm-testnet","data":{"id":"018b3c3d-a377-8e68-6cab-2e8db249729a","chain":{"id":"eip155:13473","name":"imtbl-zkevm-testnet"},"details":{"to":"0x9c1634bebc88653d2aebf4c14a3031f62092b1d9","asset":{"token_id":"123123","contract_type":"erc721","contract_address":"0x43c98025464e9b326be3c3782db5867073b8e78c"},"amount":"1"},"indexed_at":"2023-10-17 06:05:54.469511","activity_type":"mint","blockchain_metadata":{"log_index":"0","block_number":"2895332","transaction_hash":"0xa58996d4250b964799ad62afa1b6d9188df1d60fad339857ccab4388a07212af","transaction_index":"0"}}}",
//"Timestamp": "2023-10-17T06:05:56.378Z",
//"SignatureVersion": "1",
// ... other fields ...
}
Inside the Message
field
Inside the Message
field is our event. It always has the following fields:
- event_name: The event that triggered the notification
- event_id: The unique identifier of the event
- chain: The Immutable rollup where the event occurred
- data: The latest state of the underlying entity
data
field
The format of the data
field will depend on the event. You can find the format of each event here.
A Complete example Webhook Message
Below is a complete example of a webhook event
{
"Type": "Notification",
"MessageId": "dca9d68e-f9b0-5b4b-aca7-8b26df34eac6",
"TopicArn": "arn:aws:sns:us-east-2:783421985614:webhook-outbound-sandbox",
"Message": "{"event_name":"imtbl_zkevm_activity_mint","event_id":"018b3c3d-a3aa-a4c9-4c16-dc7a2bd7d715","chain":"imtbl-zkevm-testnet","data":{"id":"018b3c3d-a377-8e68-6cab-2e8db249729a","chain":{"id":"eip155:13473","name":"imtbl-zkevm-testnet"},"details":{"to":"0x9c1634bebc88653d2aebf4c14a3031f62092b1d9","asset":{"token_id":"123123","contract_type":"erc721","contract_address":"0x43c98025464e9b326be3c3782db5867073b8e78c"},"amount":"1"},"indexed_at":"2023-10-17 06:05:54.469511","activity_type":"mint","blockchain_metadata":{"log_index":"0","block_number":"2895332","transaction_hash":"0xa58996d4250b964799ad62afa1b6d9188df1d60fad339857ccab4388a07212af","transaction_index":"0"}}}",
"Timestamp": "2023-10-17T06:05:56.378Z",
"SignatureVersion": "1",
"Signature": "hYHM9z0H38JA3AYO/guKTRgsxloGLx0vICVLny36gM8vfqjj1i7xzxRioWFTB+OfngE96gG+vmLmGf7W/RWkjf+dn9dKBijAtZ5CTKigkYnhmxNY1CuarAsEAzf2BlpY80fzHx+gaHaLUgaC6/DJ3ZGD1gM8SS4J5JWKWpvf7DAclGyIp8cPHCcsOcT8JBJf1Mu8Z+0sDOIhtsMD04pUn6QGAbZ82z6eG9mn1PbrocPE/8lOk8elmXdvQjVn9+FcLEIEadXG6INooJ5e5EtZuVEKXlVj9tPugwDbm/1nuWWAf2tArjGvHzIWL04IwbBMDj5Bx5cMU7ycPuLoea3ATA==",
"SigningCertURL": "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-01d088a6f77103d0fe307c0069e40ed6.pem",
"UnsubscribeURL": "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:783421985614:webhook-outbound-sandbox:b2be3be8-98de-4d84-b9b1-98d95859351d",
"MessageAttributes": {
"chain": {
"Type": "String",
"Value": "imtbl-zkevm-testnet"
},
"collection_address": {
"Type": "String",
"Value": "0x43c98025464e9b326be3c3782db5867073b8e78c"
},
"event": {
"Type": "String",
"Value": "imtbl_zkevm_activity_mint"
}
}}
Duplicate and out-of-order messages
Our message delivery system has an at-least-once delivery guarantee and does not guarantee that the messages will be delivered in order.
This means that your endpoint will need to process the messages idempotently and deal with events that may arrive out of order.
Below are a few of our recommended strategies:
Using the event_id
This event_id
field included in the payload is a ULID, which is lexicographically sortable. A newer ULID is greater than an older ULID.
Take an order with ID order123
message for example, the pseudo-code to handle this is:
1. Using the event_id, check the last_event_id of the order with id order123
2. If the last_message_id is less than (<) the event_id of the received message, process message
3. Else, skip.
In plain English, update the order only if the incoming message is newer than the last one that updated the order.
Using this strategy can save you actually calling the API to obtain the entity in cases where the messages have arrived out of order or more than once.
Using updatedAt
Alternatively, you can process the messages without discrimination and only update if it’s newer than the current state.
The pseudo-SQL:
INSERT INTO orders (order_id, status, last_message_id)
VALUES ($order.order_id, $order.status, $order.message_id)
ON CONFLICT (order_id)
DO UPDATE
set status = $order.status, updated_at = $order.updated_at
WHERE updated_at < $order.updated_at
This approach is simpler, but may be unreliable if there are multiple events in a single millisecond. This is unlikely.
Response code and retries
Please return a:
200
response code on successful processing of the message.500
response code on failure to process the message.
A 500
response code will trigger a retry of the message.
Our retry policy is exponential backoff with a maximum of 15 retries.
This gives you roughly a day of retrying at which point your message will be sent to a dead letter queue for manual inspection.
The retry policy above is the default setting. If you have special requirements, please reach out to the Immutable team directly.
Troubleshoot Webhooks
If you are having trouble with webhook setup, you can see the webhook delivery logs in the Immutable Hub.
- Navigate to Webhooks Configuration: Log into the Immutable Hub and go to
[Your Project] > [Your Environment] > Webhooks
. - View Logs: Click on the
Logs
icon to see the delivery logs of your webhooks.
Log attributes:
- Status: The status of the delivery (e.g.
SUCCESS
,FAILURE
). - MessageID: The unique identifier of the message. This ID stays the same for retries.
- Attempts: The number of times the message has been retried. First delivery attempt will have
0
attempts. - Status Code: The HTTP status code of the delivery.
- Webhook Response: The response from the endpoint.