Global Ledger v1 Webhooks — Client Integration Guide
This guide shows how to receive Global Ledger events (address score updates and
alerts) on your own HTTPS endpoint using the v1 Integration API. It assumes you
already have an API key, an API secret, and a callback URL. All requests go to
your blockchain subdomain, for example https://btc.glprotocol.com/integration.
Purpose and mental model
Webhook delivery uses two building blocks:
-
A webhook endpoint is a callback destination: a URL plus an enabled state, identified by a
webhookIdyou choose. -
A webhook binding selects which events are delivered to that destination. A binding is an event route: a
type, an optionalsubType, and an optionalcoin.
One endpoint may have any number of bindings. Create the endpoint once, then
attach one binding per event route you want to receive. Bindings are
persistent by default; a one-shot binding (isOneShot: true) is removed
after its first successful delivery and is meant only for temporary callbacks.
Quick start
-
Create (or update) a webhook endpoint.
PUTrequests must be signed — see Authentication and signing.curl -X PUT "https://btc.glprotocol.com/integration/webhooks/{WEBHOOK_ID}" \ -H "Authorization: {API_KEY}" \ -H "X-Signature: {SIGNATURE}" \ -H "Content-Type: application/json" \ -d '{"url": "{CALLBACK_URL}", "enabled": true}'A new endpoint returns
201with the stored object; an update returns200. -
Create a binding for the events you want. For address score updates:
curl -X POST "https://btc.glprotocol.com/integration/webhooks/{WEBHOOK_ID}/bindings" \ -H "Authorization: {API_KEY}" \ -H "X-Signature: {SIGNATURE}" \ -H "Content-Type: application/json" \ -d '{"type": "monitoring.score", "coin": "btc"}'The response is
201with the created binding, including itsid:{ "id": "{BINDING_ID}", "webhookId": "{WEBHOOK_ID}", "type": "monitoring.score", "coin": "btc", "isOneShot": false, "createdAt": "2026-07-21T12:00:00.000Z" }Note the two identifiers:
webhookIdis the endpoint id you chose in step 1;idis the binding id generated by the API. You need the bindingidto delete that binding later. -
Perform the prerequisite product action. Score updates are produced only for addresses that are monitored for your account, so make sure the address is in monitoring (see Address score flow). Alerts require an alert configured in your Global Ledger account.
-
Receive and verify the callback on
{CALLBACK_URL}(see Authentication and signing for verification). -
List and remove bindings when routes change:
curl "https://btc.glprotocol.com/integration/webhooks/{WEBHOOK_ID}/bindings" \ -H "Authorization: {API_KEY}" curl -X DELETE "https://btc.glprotocol.com/integration/webhooks/{WEBHOOK_ID}/bindings/{BINDING_ID}" \ -H "Authorization: {API_KEY}"
Event selection
type |
subType |
coin |
Delivered when |
|---|---|---|---|
monitoring.score |
none (must be omitted) | optional | The score of a monitored address is updated |
alert |
monitoring |
optional | A configured alert produces a new alert event |
alert |
risk-report |
optional | A configured risk-report alert produces a new alert event |
alert |
behavior-alert |
optional | A behavior alert produces a new alert event |
Only the values above are accepted. monitoring.score takes no subType;
sending one is rejected. Omit subType or coin when you do not need them —
never send null, "null", or an empty string. On a blockchain-specific
subdomain, an explicit coin must match that blockchain.
Address score flow
-
Keep a persistent
monitoring.scorebinding on your endpoint (quick start step 2). -
Make sure the address is monitored for your account through the v1 monitoring lifecycle. Creating a binding does not add any address to monitoring; the binding only routes events. The compatibility endpoint
PUT /integration/webhooks/{WEBHOOK_ID}/subscription/score/address(deprecated, see Deprecated legacy flow) adds addresses to monitoring for the webhook’s account. -
When a monitored address’s score changes, the update is delivered to every enabled endpoint of yours whose binding matches the event route.
Alert flow
-
Create a persistent
alertbinding with one supportedsubType, for example risk-report alerts:curl -X POST "https://btc.glprotocol.com/integration/webhooks/{WEBHOOK_ID}/bindings" \ -H "Authorization: {API_KEY}" \ -H "X-Signature: {SIGNATURE}" \ -H "Content-Type: application/json" \ -d '{"type": "alert", "subType": "risk-report", "coin": "btc"}' -
Alerts are configured in your Global Ledger account; the binding only routes the resulting events. An alert callback is delivered when an alert produces a new alert event — repeated recalculations of an event that was already delivered are not sent again.
Authentication and signing
Authenticate every webhook API request with your API key passed as the
Authorization header value, without a Bearer prefix. This is the
recommended authorization method for all webhook operations, and every
example in this guide uses it.
On top of that, there are two separate signature operations. Both use the same formula, with your API secret as the HMAC key:
signature = base64( HMAC-SHA256( secret = {API_SECRET},
input = "{API_KEY}" + "." + <compact JSON body> ) )
Signing your API requests. POST and PUT requests require the
X-Signature header computed over the exact JSON body you send. GET and
DELETE requests need only the Authorization header. In JavaScript:
const crypto = require("crypto");
const body = JSON.stringify({ type: "monitoring.score", coin: "btc" });
const signature = crypto
.createHmac("sha256", API_SECRET)
.update(`${API_KEY}.${body}`)
.digest("base64");
// send `body` as the request body and `signature` as X-Signature
Verifying incoming callbacks. Each callback is an HTTPS POST to your
callback URL with these headers:
Authorization: {API_KEY}
X-SIGNATURE: <base64 HMAC of "{API_KEY}." + callback body>
X-GL-WEBHOOK-ID: {WEBHOOK_ID}
X-GL-WEBHOOK-TYPE: monitoring.score
X-GL-WEBHOOK-COIN: btc
X-GL-WEBHOOK-SUBTYPE is present when the event route has a subtype;
X-GL-WEBHOOK-COIN is present when the event has a coin. Recompute the HMAC
over {API_KEY}. plus the received body (compact JSON, exactly as delivered
— in JavaScript, JSON.stringify(JSON.parse(rawBody)) yields the same
string) and compare it with X-SIGNATURE.
A monitoring.score callback body is an array of score updates:
[
{
"address": "{ADDRESS}",
"score": 42.7,
"timestamp": "2026-07-21T12:34:56.000Z",
"coin": "btc"
}
]
An alert callback body contains the new alert events:
{
"subjects": [
{
"subjectId": "664f73107e88eb9ebaafb5d4",
"alertId": "664f73107e88eb9ebaafb5d5",
"title": "High risk exposure",
"priority": 2,
"address": "{ADDRESS}",
"directTx": ""
}
]
}
Additional fields may be present in either body; use the route headers, not the body shape, to dispatch events.
Lifecycle operations
-
GET /integration/webhooks/{WEBHOOK_ID}/bindingslists bindings for one endpoint;GET /integration/webhook-bindingslists bindings across all your endpoints. Both accepttype,subType,coin, andisOneShotquery filters. -
DELETE /integration/webhooks/{WEBHOOK_ID}/bindings/{BINDING_ID}removes one binding by id — the recommended way to remove a route. -
Advanced:
POST .../bindings/delete-by-selector(per endpoint or account-wide under/integration/webhook-bindings/delete-by-selector) deletes bindings matching a route selector in bulk. By default the match is exact: an omittedsubTypeorcoinmatches only bindings without that field. Crossing those dimensions requires explicitincludeSubTypes/includeCoinsflags plusconfirm: true. Prefer deletion by binding id unless you really need bulk cleanup. -
Deleting a webhook endpoint (
DELETE /integration/webhooks/{WEBHOOK_ID}) also removes its bindings.
Deprecated legacy flow
Two older operations remain for compatibility. They are deprecated and will be removed in a future release:
-
POST /integration/score/address— one-shot score delivery and legacysubscribe: truemonitoring setup. -
PUT /integration/webhooks/{WEBHOOK_ID}/subscription/score/address— adds addresses to (or removes them from) monitoring for a webhook.
New integrations should use a webhook endpoint plus bindings for all event
routing. The legacy events field on the webhook endpoint is stored and
returned for compatibility but is ignored for routing.
Troubleshooting checklist
-
The endpoint is
enabled: true(checkGET /integration/webhooks/{WEBHOOK_ID}). -
A binding exists whose
type/subType/coinmatch the expected event (check the binding list and theX-GL-WEBHOOK-*headers of any callback). -
Optional fields are omitted, not sent as
null,"null", or"". -
For score events, the address is actually monitored for your account — creating a binding alone does not start monitoring.
-
Signature verification uses the exact delivered body representation (compact JSON) with the input
{API_KEY}.<body>and your API secret as the HMAC key. -
One-shot bindings disappear after their first delivery — recreate them if you expected repeated callbacks.