Webhooks push real-time event notifications to your server whenever a settlement changes state. This is the recommended way to track settlement progress instead of polling.
Setting up a webhook endpoint
1. Register the endpoint
Create a webhook endpoint in the KeyStone Dashboard under Settings > Webhooks. You’ll need to provide:
- URL - Your HTTPS endpoint that will receive webhook deliveries
- Event types - Which events to subscribe to (see patterns below)
The webhook secret is displayed once at creation time. Store it securely - you’ll need it to verify webhook signatures.
You can also register endpoints via the API:
curl -X POST https://api.keystoneos.xyz/v1/platforms/me/webhooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-platform.com/webhooks/keystone",
"event_types": ["settlement.*"]
}'
2. Event type patterns
| Pattern | Matches |
|---|
* | All events |
settlement.* | All settlement events |
settlement.state.finalized | Only finalization events |
settlement.confirmation_required | Counterparty confirmation requests |
Every webhook delivery includes:
{
"event": "settlement.state.compliance_cleared",
"data": {
"settlement_id": "stl-...",
"state": "COMPLIANCE_CLEARED",
"previous_state": "COMPLIANCE_CHECKING",
"settlement_type": "single_platform",
"timestamp": "2026-03-16T12:00:00Z"
}
}
Verifying signatures
Every webhook delivery includes an X-Keystone-Signature header containing an HMAC-SHA256 hex digest of the request body.
import hashlib
import hmac
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
Always use constant-time comparison to prevent timing attacks. Never use === or == for signature verification.
Secret rotation
Rotate your webhook secret in the KeyStone Dashboard under Settings > Webhooks by clicking Rotate Secret on the endpoint, or via the API:
curl -X POST https://api.keystoneos.xyz/v1/platforms/me/webhooks/$WEBHOOK_ID/rotate-secret \
-H "Authorization: Bearer $TOKEN"
During the 24-hour grace period:
- Deliveries include both
X-Keystone-Signature (new secret) and X-Keystone-Signature-Previous (old secret)
- Your server should verify against both headers
- After 24 hours, only the new secret is used
Testing
Send a test ping from the KeyStone Dashboard under Settings > Webhooks, or via the API:
curl -X POST https://api.keystoneos.xyz/v1/platforms/me/webhooks/$WEBHOOK_ID/test \
-H "Authorization: Bearer $TOKEN"
Best practices
- Return 200 quickly - Process webhooks asynchronously. Return a 200 status immediately and handle the event in a background job.
- Handle duplicates - Webhooks may be delivered more than once. Use the event ID or settlement state to make your handler idempotent.
- Verify signatures - Always verify the HMAC signature before processing. Reject unsigned or invalid requests.
- Monitor delivery logs - Check delivery logs in the KeyStone Dashboard under Settings > Webhooks (click an endpoint to see its delivery history), or via the API:
curl https://api.keystoneos.xyz/v1/platforms/me/webhooks/$WEBHOOK_ID/delivery-logs \
-H "Authorization: Bearer $TOKEN"
Retry policy
Failed deliveries (non-2xx response or timeout) are retried with exponential backoff. After all retries are exhausted, the delivery is marked as failed in the delivery log.