Webhooks
How do I use Webhooks?
Overview
Receive real-time notifications about events in your organization using Cybrid's webhook system. Manage webhook subscriptions in the Partner Portal, and retrieve events and deliveries 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
Webhook events and deliveries are in the Organization API, not the Bank API. Use Organization API endpoints to retrieve them.
Configure Authentication Scopes
Interact with webhook events using an OAuth2 access token with organization-level scopes.
Required Scopes:
| Scope | Description |
|---|---|
subscription_events:read | View webhook events |
subscription_events:execute | Full event management |
Subscription management requires the Partner PortalAPI-key tokens cannot hold the
subscriptions:read,subscriptions:writeorsubscriptions:executescopes. If you request them, they are dropped and the token is issued with the remaining scopes. Calls to/api/subscriptionsmade with an API-key token return403withmessage_code: invalid_scopes. Create, view and delete subscriptions in the Partner Portal instead. Existing subscriptions are unaffected and continue to deliver events.
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": "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 the Partner Portal 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).
Events for bank operations:
| Event Type | Description | Use Case |
|---|---|---|
bank.created | Created | New bank |
bank.updated | Updated | Configuration change |
To capture error events, listen for trade.failed, transfer.failed, plan.failed, execution.failed, external_bank_account.failed, external_bank_account.expired, 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
message_code: invalid_https_url. 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 within 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 in the Partner Portal so Cybrid knows where to deliver events. Users with the Key Admin or Founder role can manage subscriptions.
- Sign in to the Partner Portal and select a bank in the environment (
sandboxorproduction) you want to receive events for. - Open the Developers tab, then select Subscriptions.
- Click Create Webhook Subscription.
- Enter a descriptive name (max 128 characters) and your endpoint URL, then submit the form.
A subscription created in the portal receives every event type.
Save the signing key immediatelyAfter the subscription is created, the portal displays its signing key once. Copy it and store it securely; you need it to verify webhook signatures. 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.
Delete your webhook subscription
Delete a subscription from Developers → Subscriptions in the Partner Portal. Deletion is scheduled rather than immediate: the subscription's scheduled_deletion_at is set to 24 hours from the request, and a subscription.deleting event is created. Deliveries continue until that time; the subscription is then removed and a subscription.deleted event is created.
A subscription with protection_enabled: true cannot be deleted, and the Partner Portal offers no delete action for it. The field is read-only; contact [email protected] to protect a subscription or remove its protection.
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": "subscription_event_guid",
"organization_guid": "organization_guid",
"bank_guid": "bank_guid",
"event_type": "transfer.storing",
"object_guid": "object_guid",
"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');
const requestBuffer = Buffer.from(requestSignature || '', 'utf8');
const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
const valid_request = requestBuffer.length === expectedBuffer.length
&& crypto.timingSafeEqual(requestBuffer, expectedBuffer);
// 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 subscription in the Partner Portal.
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 |
|---|---|---|
invalid_https_url | URL uses HTTP instead of HTTPS in production | Use HTTPS for production; HTTP allowed for sandbox only |
unknown_organization | Organization GUID doesn't exist or is invalid | Verify your organization GUID and authentication |
not_implemented | 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 with
message_code: invalid_scopes - Cause: Token lacks the scope for the endpoint, or an API-key token was used to call
/api/subscriptions - Fix: Request a token with the required
subscription_eventsscopes; manage subscriptions in the Partner Portal
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. The previous registration keeps delivering for 24 hours after the delete request, so the same event is delivered to both endpoints in the meantime; your webhook handler should handle duplicate events.
Testing
Use Sandbox Environment
- Create test subscriptions with a sandbox bank selected in the Partner Portal
- 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
The subscription endpoints back the Partner Portal and cannot be called with an API-key token. Manage subscriptions in the Partner Portal.
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
- Manage Subscriptions in the Partner Portal - API-key tokens cannot create, view or delete subscriptions
- Use Organization API - Webhook events and deliveries are in the Organization API, not the Bank API
- 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
- Deletion is Delayed - Deleting a subscription schedules removal 24 hours out; subscribe to
subscription.storing,subscription.failed,subscription.deletingandsubscription.deletedto be told when a subscription is created, fails or is deleted
Updated 3 days ago

