We refreshed our doc site!

Bookmarked links may have changed

Read release notes

Web3 Services

Webhooks Quickstart

This quickstart guide helps you quickly set up notifications for Programmable Wallets. Follow this to configure a subscriber endpoint that sends a notification every time the status of resource changes. 

This guide focuses on the challenge resource but applies to any resources available in Programmable Wallet Notifications.

1. Expose a Subscriber Endpoint

To receive notifications on changes in resource status, you must expose a publicly accessible subscriber endpoint on your side. The endpoint must handle both HEAD and POST requests over HTTPS.

To quickly expose an endpoint for testing, we will use webhook.site in this guide, which allows you to easily inspect, test and automate any incoming HTTPS request or e-mail directly in a web browser.

When visiting webhook.site for the first time, you should see a status message that looks like the following:

2. Subscribe to Webhook Notifications

Now that you have a publicly accessible endpoint, you need to register your endpoint as a subscriber to webhook notifications by doing the following:

  1. Click Webhooks in your Circle Developer account. 
  2. Click Add a Webhook in the upper right corner.
  3. Enter your endpoint URL from above. It will be similar to the earlier example:https://webhook.site/83fa21a0-f00a-4673-bb50-bcf62c78b1f7.
  4. Click Add Webhook.
  5. At this stage, you should receive a Hello World test notification indicating you have subscribed to your endpoint successfully:
JSON
{  
  "subscriptionId": "00000000-0000-0000-0000-000000000000",  
  "notificationId": "00000000-0000-0000-0000-000000000000",  
  "notificationType": "webhooks.test",  
  "notification": {  
    "hello": "world"  
  },  
  "timestamp": "2023-07-20T16:18:40.816010685Z",  
  "version": 2  
}

Now you have a sample local environment ready to receive transaction and challenge status change notifications. Note that in testnet and mainnet there is a limit of 20 webhook notifications each.

Customize Webhook Notifications

You can customize your webhook notifications. Customized webhook notifications allow you to listen to a restricted set of specific events, such as inbound transactions only, or a specific set of events, such as transaction or challenges, through a URL endpoint on your receiving application.

To customize your endpoints to only receive certain notifications, toggle on “Limit to specific events” when creating your webhook.

You are then able to pick the event categories that you want to listen to on your endpoint. These can be further customized at the event level under each category. This gives you the flexibility to only listen to events you care about at each endpoint, rather than overwhelming the endpoint with all notifications. This reduces the amount of data your server needs to process, making it more efficient and easier to manage. It also helps in focusing only on the relevant events, which can improve the performance and reliability of your application.

3. Initiate a Challenge Flow to Receive a Notification

To observe a notification message on the status of a challenge, initiate the creation of a wallet (for example, by following the Programmable Wallets User-Controlled Wallets Tutorial for creating a user-controlled wallet).

Once you successfully initiate a challenge payment, you should see a notification message on your local server shell that looks like the following:

Sample Challenge Notification:

JSON
{
  "subscriptionId": "a68cd974-d209-46cd-8dbf-b7a081fbd627",
  "notificationId": "eaa4a4fe-24b8-4329-a4c6-6dfd557dbcb2",
  "notificationType": "challenges.initialize",
  "notification": {
    "id": "9c0a1991-735a-5140-8996-7b95720c5e55",
    "userId": "2a054cd1-3124-4aa7-b1f0-86c4a4df995c",
    "type": "INITIALIZE",
    "status": "COMPLETE",
    "correlationIds": [
      "01890792-a199-77bc-b005-b229f81824fa"
    ],
    "errorCode": 0,
    "errorMessage": ""
  },
  "timestamp": "2023-06-29T14:33:17.785131449Z",
  "version": 2
}

4. Verify Digital Signature

  1. Every webhook notification is digitally signed by an asymmetric key. In the headers of each webhook, you can find:
    1. X-Circle-Signature: a header containing the digital signature generated by Circle
    2. X-Circle-Key-Id: a header containing the UUID you need to retrieve the relevant public key
Text
# Headers
`X-Circle-Key-Id: “879dc113-5ca4-4ff7-a6b7-54652083fcf8”`  
`X-Circle-Signature: “MEYCIQCA9EvPbdEJiy7Cw0eY+KQZA/oFi5ZEInPs8CYpyaJexgIhAKtRNnDz9QRQmFKx8QFrvawp+8b9Bs2dQ03xD+XaWVDE”`
  1. Using the X-Circle-Key-Id value, query the following endpoint to retrieve the public key and algorithm used to sign the message: GET /v2/notifications/publicKey/{keyId)
Bash
# Replace ${YOUR_API_KEY} with your API key
# Replace ${PUBLIC_KEY_ID} with your public key id
curl --request GET \
     --url 'https://api.circle.com/v2/notifications/publicKey/${PUBLIC_KEY_ID)' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer $ENV_API_KEY:ID:SECRET$' \

If successful, you will receive a response with the following shape

Response Body

JSON
{  
  "data": {  
    "id": "879dc113-5ca4-4ff7-a6b7-54652083fcf8",  
    "algorithm": "ECDSA_SHA_256",  
    "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESl76SZPBJemW0mJNN4KTvYkLT8bOT4UGhFhzNk3fJqf6iuPlLQLq533FelXwczJbjg2U1PHTvQTK7qOQnDL2Tg==",  
    "createDate": "2023-06-28T21:47:35.107250Z"  
  }
}
  1. Use the public key and algorithm to verify the integrity of the webhook’s payload against the signature found in X-Circle-Signature.

Note: Please make sure the webhook notification is properly formatted JSON string prior to verifying it

Response Body

JSON
{
  "subscriptionId": "890a8cae-46bd-40ad-bd7f-2e49b8dea9da",
  "notificationId": "0bdd3e4b-5070-4530-8ed4-2e2e4c9fd2f0",
  "notificationType": "webhooks.test",
  "notification": {
    "hello": "world"
  },
  "timestamp": "2023-07-12T04: 02: 28.555562821Z",
  "version": 2
}

The following code sample demonstrates how to verify the X-Circle-Signature:

import base64
import json

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec

# Load the public key from the base64 encoded string
public_key_base64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESl76SZPBJemW0mJNN4KTvYkLT8bOT4UGhFhzNk3fJqf6iuPlLQLq533FelXwczJbjg2U1PHTvQTK7qOQnDL2Tg=="
public_key_bytes = base64.b64decode(public_key_base64)
public_key = serialization.load_der_public_key(public_key_bytes)

# Load the signature you want to verify
signature_base64 = "MEQCIBlJPX7t0FDOcozsRK6qIQwik5Fq6mhAtCSSgIB/yQO7AiB9U5lVpdufKvPhk3cz4TH2f5MP7ArnmPRBmhPztpsIFQ=="
signature_bytes = base64.b64decode(signature_base64)

# Load and format the message you want to verify
message = "{\n\"subscriptionId\":\"00000000-0000-0000-0000-000000000000\",\"notificationId\":\"00000000-0000-0000-0000-000000000000\",\"notificationType\":\"webhooks.test\",\"notification\":{\"hello\":\"world\"},\"timestamp\":\"2024-01-26T18:22:19.779834211Z\",\"version\":2}"
json_data = json.loads(message)
formatted_json = json.dumps(json_data, separators=(',', ':'))

# Verify the signature
try:
    public_key.verify(
        signature_bytes,
        formatted_json.encode(),
        ec.ECDSA(hashes.SHA256())
    )
    print("Signature is valid.")
except InvalidSignature:
    print("Signature is invalid.")

5. Confirm IP Accesslist

To ensure the security of your integration, only trust Circle webhook notifications from the following IP addresses.

Text
54.243.112.156  
100.24.191.35  
54.165.52.248  
54.87.106.46

WHAT'S NEXT Congratulations, you have received your first notification for a Programmable Wallets challenge!

Did this page help you?
© 2023-2024 Circle Technology Services, LLC. All rights reserved.