Webhooks
How do I use Webhooks?
Overview
Receive real-time notifications about events in your organization using Cybrid's webhook system. Manage webhooks through the Organization API.
Configure your applications to receive events as they occur. Your back-end systems can then execute actions based on these events.
To enable webhook events, register one or more webhook endpoints. Cybrid posts JSON payloads containing a SubscriptionEvent object to your endpoints via HTTPS.
Webhooks notify you of asynchronous events: completed trades, settled transfers, and finished identity verifications.
Authenticate with the Organization API
Use the Organization API
All webhook operations are in the Organization API, not the Bank API. Use Organization API endpoints for webhook operations.
Configure Authentication Scopes
Interact with webhook subscriptions using an OAuth2 access token with organization-level scopes.
Required Scopes:
| Scope | Description |
|---|---|
subscriptions:read | List and retrieve subscription details |
subscriptions:write | Create subscriptions |
subscriptions:execute | Full subscription management |
subscription_events:read | View webhook events |
subscription_events:execute | Full event management |
Get an Organization Token
Create an Organization Application in the IDP service, then request an OAuth2 token using client credentials flow:
POST https://id.{environment}.cybrid.app/oauth/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "client_id",
"client_secret": "client_secret",
"scope": "subscriptions:execute subscription_events:read"
}{
"access_token": "access_token",
"token_type": "Bearer",
"expires_in": 3600
}Use the access token in all Organization API requests:
Authorization: Bearer {access_token}
Common Authentication Errors
- 401 Unauthorized: Invalid or expired token
- 403 Forbidden: Token lacks required scopes
- Using a bank token instead of organization token will not work
Subscribe to Webhook Events
Cybrid sends event data when activity occurs in your account. Each event creates a new
SubscriptionEvent object. A single API request may create multiple events. For example,
creating a new transfer generates transfer.storing, transfer.reviewing and
transfer.completed events.
Register webhook endpoints in your organization to enable automatic delivery of SubscriptionEvent objects via POST requests. Your app can then run back-end actions (for example, informing the customer a trade has been settled).
Trade Events
Events related to cryptocurrency trading operations:
| Event Type | Description | Use Case |
|---|---|---|
trade.storing | Creating trade | Initial state tracking |
trade.pending | Awaiting execution | Monitor pending trades |
trade.settling | Settling | Track settlement progress |
trade.completed | Completed | Final confirmation |
trade.failed | Failed | Handle failures |
trade.cancelled | Cancelled | Track cancellations |
Transfer Events
Events for money movement operations:
| Event Type | Description | Use Case |
|---|---|---|
transfer.storing | Creating transfer | Initial state tracking |
transfer.pending | Pending | Monitor pending transfers |
transfer.holding | On hold | Track held transfers |
transfer.reviewing | Under review | Compliance workflows |
transfer.completed | Completed | Final confirmation |
transfer.failed | Failed | Handle failures |
Identity Verification Events
Events for customer KYC/identity verification:
| Event Type | Description | Use Case |
|---|---|---|
identity_verification.storing | Creating verification | Initial tracking |
identity_verification.pending | In progress | Monitor progress |
identity_verification.reviewing | Requires manual review | Compliance workflows |
identity_verification.waiting | Awaiting customer action | Customer notifications |
identity_verification.expired | Expired | Re-verification needed |
identity_verification.completed | Completed | Customer onboarding |
To capture error events, listen for trade.failed, transfer.failed and/or
identity_verification.expired.
Create a Webhook Subscription
Set up your webhook endpoint
Create a webhook endpoint handler to receive event data POST requests.
Set up an HTTPS endpoint function that accepts webhook requests with a POST method. For sandbox setup you can use an HTTP endpoint.
Production webhook endpoints must use HTTPSFor production environments, your webhook URL MUST use HTTPS. HTTP URLs will be rejected with an
InvalidHttpsUrlException. For sandbox/testing, HTTP URLs are allowed for local development.
Configure your endpoint function to handle POST requests with a JSON payload consisting of a SubscriptionEvent object.
Return a successful status code (2xx) quickly, before any complex logic that could cause a timeout. For example, you must return a 200 response before updating a transfer in your system.
Endpoints must return a 2xx prior to any complex logicYour endpoint must respond with 2 seconds to avoid timeouts.
Registered webhook endpoints must be publicly accessible URLsYou can register up to 5 webhook endpoints per organization.
Register your webhook endpoint
After testing your webhook endpoint function, register the endpoint's accessible URL using the Subscription API so Cybrid knows where to deliver events.
Endpoint: POST /api/subscriptions
Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Must be webhook (only supported type) |
name | string | Yes | Descriptive name (max 128 characters) |
url | string | Yes | Your HTTPS endpoint URL |
environment | string | Yes | production or sandbox |
Request Example:
POST /api/subscriptions
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"name": "Production Trade Notifications",
"type": "webhook",
"url": "https://your-domain.com/webhooks/cybrid",
"environment": "production"
}{
"guid": "subscription_guid",
"organization_guid": "organization_guid",
"name": "Production Trade Notifications",
"url": "https://your-domain.com/webhooks/cybrid",
"environment": "production",
"type": "webhook",
"state": "storing",
"signing_key": "c3ac436b171923eb688a6bf364f89effd90427f4d58018e25558af3ecd506bc5",
"created_at": "2026-01-24T12:00:00Z"
}
Save the signing_key immediatelyNote the
signing_keyas it is only returned once and is needed to secure your webhook endpoint. The signing key is generated server-side (64-character hex string) and encrypted before storage. You cannot retrieve it later.
Subscription activationA subscription becomes active only after the endpoint is available and responds to requests.
Understand the Webhook Payload
When an event occurs, Cybrid sends an HTTP POST to your webhook URL.
Headers
Content-Type: application/json
X-Cybrid-Signature: {hmac_signature}SubscriptionEvent Object
The following is an example of a SubscriptionEvent:
{
"guid": "cf16b78e233464229c7eda5e979b25a8",
"organization_guid": "ad9007e80f35ac6d343d43496cad2744",
"bank_guid": "b3a9d531c26892954661735e1b7482b7",
"event_type": "transfer.storing",
"object_guid": "8f5ee194e5aa254feef3054bd9543939",
"environment": "sandbox"
}Payload Fields
| Field | Description |
|---|---|
guid | Unique ID for this webhook event. Can also be used as an idempotency key. |
organization_guid | Unique identifier for the organization that owns the object. This allows the same webhook endpoint to be registered with multiple organizations. |
bank_guid | Unique identifier for the bank that owns the object associated with the event. |
event_type | A tuple separated by . containing the type of the object (ie: trade, transfer or identity_verification) and the type of event generated by the Cybrid platform. |
object_guid | Identifier for the object generating the event. |
environment | production or sandbox. You may receive both sandbox and production mode event delivery requests to your endpoints. |
Verify Webhook Signatures
Every webhook payload includes an X-Cybrid-Signature header containing an HMAC signature. You
MUST verify this signature to ensure the webhook came from Cybrid.
When Cybrid sends data to registered webhook endpoints, the payload is authenticated with a
hash-based message authentication code (HMAC). The key used to create the HMAC is the
signing_key provided on subscription registration (see section above). Verify it by running the
algorithm yourself with the payload and the key to re-create the HMAC. The HMAC is created with
the HMAC_SHA256 algorithm, then encoded in hex. The HMAC is attached to the request in the
X-Cybrid-Signature header.
Signature Verification Examples
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');
const PORT = 3333;
// replace SIGNING_KEY with the key received when the webhook endpoint was created
const SIGNING_KEY = "c3ac436b171923eb688a6bf364f89effd90427f4d58018e25558af3ecd506bc5"
const ALGORITHM = 'sha256'
const SIGNATURE_HEADER = 'X-Cybrid-Signature'
const app = express();
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf;
}
}));
app.use(express.json());
app.post('/webhook', async (req, res) => {
try {
const requestSignature = req.get(SIGNATURE_HEADER);
const expectedSignature = crypto.createHmac(ALGORITHM, SIGNING_KEY)
.update(req.rawBody)
.digest('hex');
let valid_request = requestSignature === expectedSignature
// respond with 200
if (valid_request) {
res.status(200).send("OK");
console.log(`received payload ${req.body}`)
// perform actions related to the event
} else {
res.status(403).send("INVALID SIGNATURE");
}
} catch (err) {
res.status(500).send(err.toString());
}
})
app.listen(PORT, () => {
console.log(`Server running on ${PORT}`);
});
Security Best PracticeAlways use constant-time comparison when verifying signatures to prevent timing attacks.
Webhook Delivery and Retry Logic
Delivery Workflow
- Event Occurs - A trade completes, transfer succeeds, etc.
- Event Created - SubscriptionEvent record created
- Delivery Attempted - HTTP POST sent to your webhook URL
- Retry Logic - If delivery fails, exponential backoff retry
Retry Strategy
Cybrid attempts to deliver a given event to your webhook endpoint for up to 7 days with an exponential backoff.
| Attempt | Delay | Total Time |
|---|---|---|
| 1 | Immediate | 0s |
| 2 | 2x base delay | ~2s |
| 3 | 4x base delay | ~6s |
| 4 | 8x base delay | ~14s |
| 5+ | Capped at max delay | Up to 7 days |
Configuration:
- Base delay: Exponential starting point (e.g., 2 seconds)
- Max delay: Cap on exponential growth (prevents extremely long delays)
- Timeout: ~7 days total retry period
- Request timeout: 10 seconds per HTTP request
Use the SubscriptionDelivery API to view failing deliveries and when the next attempt will occur. If your endpoint subscription has been disabled or deleted when Cybrid attempts a retry, future retries of that event are prevented. If a delivery has failed, trigger a new retry cycle by creating a new delivery using SubscriptionDelivery API.
Delivery States
| State | Description |
|---|---|
storing | Initial state, delivery queued |
failing | Retries in progress (after ~10s of failures) |
completed | Successfully delivered (2xx response) |
failed | All retries exhausted or subscription deleted |
Success Criteria
A webhook delivery is considered successful if your endpoint returns:
- HTTP 2xx status code (200, 201, 202, 204, etc.)
Any other response (4xx, 5xx, timeout, network error) triggers retry logic.
Disable Behaviour
If your endpoint has not returned a successful response for multiple days (approximately 7 days),
Cybrid will automatically mark the endpoint as failed and skip future deliveries. To re-enable
the endpoint, create a new registration using the Subscription API.
Subscription State Tracking:
deliveries_failing_since: Set on first delivery failure, cleared when delivery succeeds- If failing for 7 days, subscription moves to
failedstate
Common Errors and Troubleshooting
Subscription Creation Errors
| Error | Cause | Fix |
|---|---|---|
InvalidHttpsUrlException | URL uses HTTP instead of HTTPS in production | Use HTTPS for production; HTTP allowed for sandbox only |
InvalidOrganization | Organization GUID doesn't exist or is invalid | Verify your organization GUID and authentication |
ApiNotImplemented | Webhook API not enabled for your organization | Contact Cybrid to enable webhook access |
Delivery Failures
Timeout Errors (timeout)
timeout)- Cause: Your endpoint didn't respond within 10 seconds
- Fix:
- Optimize endpoint performance
- Return 2xx immediately, process async
- Check for network issues
Unreachable Errors (unreachable)
unreachable)- Cause: DNS resolution failed, connection refused, network error
- Fix:
- Verify URL is accessible from internet
- Check firewall rules
- Validate DNS configuration
Failed After Retries
- Cause: Repeated failures for ~7 days
- Result: Subscription marked as
failed, deliveries stop - Fix:
- Resolve endpoint issues
- Delete failed subscription
- Create new subscription
Authentication Errors
Wrong Token Type
- Symptom: 403 Forbidden
- Cause: Using bank token instead of organization token
- Fix: Get organization-scoped OAuth token
Missing Scopes
- Symptom: 403 Forbidden
- Cause: Token lacks
subscriptions:executescope - Fix: Request token with proper scopes
Follow Best Practices
Endpoint Implementation
Return 2xx Quickly
@app.route('/webhooks/cybrid', methods=['POST'])
def webhook_handler():
# Verify signature first
if not verify_signature(request):
return '', 401
# Queue for async processing
queue.enqueue(process_webhook, request.json)
# Return success immediately
return '', 200Implement Idempotency
- Store
guidfrom webhook payload - Skip processing duplicate events
- Use database unique constraints
Verify Signatures
- Always validate
X-Cybrid-Signatureheader - Reject webhooks with invalid signatures
- Use constant-time comparison
Event Ordering
Cybrid may deliver events out of order from their generation sequence. For example, creating a transfer generates the following events:
transfer.storingtransfer.reviewingtransfer.completed
Your endpoint shouldn't expect delivery of these events in this order, and needs to handle delivery accordingly. You can also use the API to fetch the updated object and react according to the latest state of the object.
Handle Duplicate Events
Webhook endpoints may occasionally receive the same event more than once. Log processed event GUIDs and skip events you've already processed.
Handle Events Asynchronously
Configure your handler to process incoming events with an asynchronous queue. Processing events synchronously may cause scalability issues. Any large spike in webhook deliveries may overwhelm your endpoint hosts. Asynchronous queues allow you to process the concurrent events at a rate your system can support.
Monitoring and Alerting
Track Delivery States
- Monitor subscription_deliveries via API
- Alert on
failingorfailedstates
Monitor Endpoint Health
- Log all webhook requests
- Track response times
- Alert on elevated error rates
Set Up Alerts
- Subscription enters
failedstate deliveries_failing_sinceis set for > 1 hour- High delivery failure rates
Roll Endpoint Signing Secrets Periodically
To keep your webhook handler safe, we recommend that you roll secrets periodically, or when you suspect a compromised secret. The process of rolling a webhook signing secret consists of creating a new registration (with a new signing key), updating your webhook handler and deleting the previous registration. This process may lead to the same event being delivered multiple times but your webhook handler should handle duplicate events.
Testing
Use Sandbox Environment
- Test with
environment: sandbox - HTTP URLs allowed for local testing
Verify Retry Logic
- Temporarily break your endpoint
- Observe retry behavior
- Fix endpoint and confirm recovery
Test Event Types
- Create test trades, transfers, verifications
- Confirm correct event types received
- Validate payload structure
API Reference Summary
Subscription Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/subscriptions | Create subscription |
| GET | /api/subscriptions | List subscriptions |
| GET | /api/subscriptions/{guid} | Get subscription |
| DELETE | /api/subscriptions/{guid} | Delete subscription |
Event Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/subscription_events | List events |
| GET | /api/subscription_events/{guid} | Get event |
Delivery Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/subscription_deliveries | List deliveries |
| GET | /api/subscription_deliveries/{guid} | Get delivery |
Key Takeaways
- Use Organization API - Webhooks are in the Organization API, not the Bank API
- Get Organization Token - Use OAuth2 with
subscriptions:executescope - HTTPS Required - Production webhooks must use HTTPS URLs
- Verify Signatures - Always validate
X-Cybrid-Signatureheader with HMAC-SHA256 - Handle Retries - Return 2xx quickly; process async to avoid timeouts
- Monitor Health - Watch for
failingstates anddeliveries_failing_sincetimestamps - Be Idempotent - Use
guidto handle duplicate deliveries - Save Signing Key - The signing key is only returned once during subscription creation; store it securely
Updated 5 days ago
