본문으로 건너뛰기

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

  1. Navigate to Webhooks Configuration: Log into the Immutable Hub and go to [Your Project] > [Your Environment] > Webhooks.

  2. 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.

  3. 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.

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: https://docs.immutable.com/docs/zkevm/products/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.

{
//"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.