Track contract events and get event logs with the Circle Contracts API.
Prerequisites
Complete the Deploy contracts tutorial first.
You’ll need a deployed contract.
Set up npm scripts
If you haven’t already, add run scripts for monitoring contract events to your
package.json:
npm pkg set scripts.webhook="tsx webhook-receiver.ts"
npm pkg set scripts.import-contract="tsx --env-file=.env import-contract.ts"
npm pkg set scripts.create-monitor="tsx --env-file=.env create-monitor.ts"
npm pkg set scripts.get-event-logs="tsx --env-file=.env get-event-logs.ts"
If you completed the Deploy contracts tutorial, your project already has the
required SDKs installed. The npm scripts previously listed work with your
existing setup.
Step 1: Set up a webhook endpoint
Event monitors send real-time updates to your webhook endpoint when events
happen.
Visit webhook.site
Copy your unique webhook URL (for example, https://webhook.site/your-uuid)
Install ngrok from ngrok.com
Create a webhook receiver script:
webhook-receiver.ts
webhook_receiver.py
import express , { Request , Response } from "express" ;
const app = express ();
app . use ( express . json ());
app . post ( "/webhook" , ( req : Request , res : Response ) => {
console . log ( "Received webhook:" );
console . log ( JSON . stringify ( req . body , null , 2 ));
res . status ( 200 ). json ({ received: true });
});
const PORT = 3000 ;
app . listen ( PORT , () => {
console . log ( `Webhook receiver listening on port ${ PORT } ` );
console . log ( `Endpoint: http://localhost: ${ PORT } /webhook` );
});
Start the webhook receiver:
In a separate terminal, start ngrok:
Copy the HTTPS forwarding URL (for example,
https://abc123.ngrok-free.app/webhook)
If using ngrok for local testing, you can optionally set WEBHOOK_URL in
your .env file to store your ngrok forwarding URL.
Step 2: Register your webhook in Console
Register your webhook URL in the Developer Console:
Go to Developer Console
Navigate to Webhooks (left sidebar)
Click Add a webhook
Enter your webhook URL (from Step 1) and create the webhook
Register your webhook before creating event monitors. This allows Circle to
send notifications to your endpoint.
Step 3: Import an existing contract (optional)
If you already have a deployed contract and want to monitor its events, import
it to the Developer Console. If you deployed a contract using Circle Contracts
(for example, in the Deploy contracts
tutorial), skip this step. Your contract is already available in the Console.
import-contract.ts
import_contract.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const contractClient = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
async function importContract () {
try {
const response = await contractClient . importContract ({
blockchain: "ARC-TESTNET" ,
address: process . env . CONTRACT_ADDRESS ,
name: "MyContract" ,
});
console . log ( JSON . stringify ( response . data , null , 2 ));
} catch ( error ) {
console . error ( "Error importing contract:" , error . message );
throw error ;
}
}
importContract ();
Run the script:
If the contract is already imported, you’ll see an error: contract already exists. This means the contract is already available in the Console and you
can proceed to create an event monitor.
Step 4: Create an event monitor
Event monitors track specific contract events. They send updates to your webhook
endpoint. This example monitors Transfer events:
create-monitor.ts
create_monitor.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const contractClient = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
async function createEventMonitor () {
try {
const response = await contractClient . createEventMonitor ({
blockchain: "ARC-TESTNET" ,
contractAddress: process . env . CONTRACT_ADDRESS ,
eventSignature: "Transfer(address,address,uint256)" ,
});
console . log ( JSON . stringify ( response . data , null , 2 ));
} catch ( error ) {
console . error ( "Error creating event monitor:" , error . message );
throw error ;
}
}
createEventMonitor ();
Run the script:
Response:
{
"eventMonitor" : {
"id" : "019bf984-b4da-7026-a3d2-674ce371a933" ,
"contractName" : "TestERC20Token" ,
"contractId" : "019bf8be-7be5-7a3e-89cc-05bcd7413f20" ,
"contractAddress" : "0x281156899e5bd6fecf1c0831ee24894eeeaea2f8" ,
"blockchain" : "ARC-TESTNET" ,
"eventSignature" : "Transfer(address,address,uint256)" ,
"eventSignatureHash" : "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ,
"isEnabled" : true ,
"createDate" : "2026-01-26T08:56:22.490638Z" ,
"updateDate" : "2026-01-26T08:56:22.490638Z"
}
}
Step 5: Receive webhook notifications
When events occur, Circle sends updates to your endpoint. Here is what a
Transfer event looks like:
{
"subscriptionId" : "f0332621-a117-4b7b-bdf0-5c61a4681826" ,
"notificationId" : "5c5eea9f-398f-426f-a4a5-1bdc28b36d2c" ,
"notificationType" : "contracts.eventLog" ,
"notification" : {
"contractAddress" : "0x4abcffb90897fe7ce86ed689d1178076544a021b" ,
"blockchain" : "ARC-TESTNET" ,
"txHash" : "0xe15d6dbb50178f60930b8a3e3e775f3c022505ea2e351b6c2c2985d2405c8ebc" ,
"userOpHash" : "0x78c3e8185ff9abfc7197a8432d9b79566123616c136001e609102c97e732e55e" ,
"blockHash" : "0x0ad6bf57a110d42620defbcb9af98d6223f060de588ed96ae495ddeaf3565c8d" ,
"blockHeight" : 22807198 ,
"eventSignature" : "Transfer(address,address,uint256)" ,
"eventSignatureHash" : "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ,
"topics" : [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ,
"0x0000000000000000000000000000000000000000000000000000000000000000" ,
"0x000000000000000000000000bcf83d3b112cbf43b19904e376dd8dee01fe2758"
],
"data" : "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" ,
"firstConfirmDate" : "2026-01-21T06:53:12Z"
},
"timestamp" : "2026-01-21T06:53:13.194467201Z" ,
"version" : 2
}
Key fields:
notificationType: Always "contracts.eventLog" for event monitor webhooks
notification.eventSignature: The event that was emitted
notification.contractAddress: Address of the contract that emitted the event
notification.blockchain: The blockchain network (for example, ARC-TESTNET)
notification.txHash: Transaction hash where the event occurred
notification.userOpHash: User operation hash (for smart contract accounts)
notification.blockHash: Hash of the block containing the transaction
notification.blockHeight: Block number where the event occurred
notification.eventSignatureHash: Keccak256 hash of the event signature
notification.topics: Indexed event parameters (for example, from and to
addresses)
notification.data: Non-indexed event parameters (for example, token amount)
notification.firstConfirmDate: Timestamp when the event was first confirmed
timestamp: Timestamp when the webhook was sent
version: Webhook payload version
You can verify webhook delivery status in the Developer
Console under Contracts → Monitoring.
Step 6: Retrieve event logs
You can also query event logs with the API. This is useful for past events or if
you prefer polling.
Webhooks vs Polling : Webhooks send real-time updates (push). Polling needs
periodic API calls (pull). Use webhooks for production and polling for testing
or past queries.
get-event-logs.ts
get_event_logs.py
import { initiateSmartContractPlatformClient } from "@circle-fin/smart-contract-platform" ;
const contractClient = initiateSmartContractPlatformClient ({
apiKey: process . env . CIRCLE_API_KEY ,
entitySecret: process . env . CIRCLE_ENTITY_SECRET ,
});
async function getEventLogs () {
try {
const response = await contractClient . listEventLogs ({
contractAddress: process . env . CONTRACT_ADDRESS ,
blockchain: "ARC-TESTNET" ,
pageSize: 10 ,
});
console . log ( JSON . stringify ( response . data , null , 2 ));
} catch ( error ) {
console . error ( "Error fetching event logs:" , error . message );
throw error ;
}
}
getEventLogs ();
Run the script:
Replace CONTRACT_ADDRESS with your contract address. You can get this
address when you deploy the contract, or by listing your contracts with
listContracts().
Response:
{
"eventLogs" : [
{
"id" : "019bf987-f901-7145-9e95-55f177b05b24" ,
"subscriptionId" : "019bf984-b4da-7026-a3d2-674ce371a933" ,
"contractId" : "019bf8be-7be5-7a3e-89cc-05bcd7413f20" ,
"contractName" : "TestERC20Token" ,
"blockchain" : "ARC-TESTNET" ,
"txHash" : "0x3bfbab5d5ce0d1a5d682cbc742d3940cf59db0369d173b71ba2a3b8f43bfbcb1" ,
"logIndex" : "50" ,
"blockHash" : "0x7d12148f9331556b31f84f58a41b7ff16eaaa47940f9e86733037d7ab74d858e" ,
"blockHeight" : 23686153 ,
"contractAddress" : "0x281156899e5bd6fecf1c0831ee24894eeeaea2f8" ,
"eventSignature" : "Transfer(address,address,uint256)" ,
"eventSignatureHash" : "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ,
"topics" : [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ,
"0x0000000000000000000000000000000000000000000000000000000000000000" ,
"0x000000000000000000000000bcf83d3b112cbf43b19904e376dd8dee01fe2758"
],
"data" : "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000" ,
"decodedTopics" : null ,
"decodedData" : null ,
"userOpHash" : "0x66befac1a371fcdddf1566215e4677127e111dff9253f306f7096fed8642a208" ,
"firstConfirmDate" : "2026-01-26T08:59:55Z" ,
"createDate" : "2026-01-26T08:59:56.545962Z" ,
"updateDate" : "2026-01-26T08:59:56.545962Z"
}
]
}
You can view, update, and delete event monitors with the Circle Contracts API.
See the API
Reference
for details on managing your monitors.
Summary
After completing this tutorial, you’ve successfully:
Set up webhook endpoints using webhook.site or ngrok
Registered webhooks in the Developer Console
Created event monitors for specific contract events
Received real-time webhook updates for contract events
Retrieved past event logs with the Circle SDK