Webhooks
Real-time event notifications with HMAC-SHA256 signatures, automatic retries, and delivery monitoring.
Rialto delivers real-time event notifications to your application via webhooks. Events are sent as HTTP POST requests with JSON payloads, signed with HMAC-SHA256 for verification, and automatically retried on failure.
The webhook API is served by the notifications service (/notifications prefix). The identity service publishes KYC and accreditation events; the primary issuance service publishes subscription and share events.
Setting Up Webhooks
1. Register an Endpoint
curl -X POST https://api.rialto.com/notifications/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <rialto_access_token>" \
-d '{
"url": "https://yourapp.com/webhooks/rialto",
"description": "Production webhook endpoint",
"eventTypes": ["kyc.session.approved", "subscription.created"]
}'The response includes a signing_secret. Store it securely -- it is only shown on creation and rotation.
2. Subscribe to Events
Set event types during creation (via eventTypes) or update them later:
curl -X PUT https://api.rialto.com/notifications/webhooks/<endpoint_id>/subscriptions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <rialto_access_token>" \
-d '{
"eventTypes": ["kyc.session.approved", "subscription.created", "subscription.funded"]
}'Use "*" to subscribe to all event types.
3. Test Your Endpoint
Send a test event to verify connectivity:
curl -X POST https://api.rialto.com/notifications/webhooks/<endpoint_id>/test \
-H "Authorization: Bearer <rialto_access_token>"This sends a test.ping event to your endpoint.
Event Types
KYC Events (6)
| Event | Description |
|---|---|
kyc.session.created | KYC session created for a user |
kyc.session.pii_submitted | User submitted identity information |
kyc.session.processing | Identity verification in progress |
kyc.session.approved | KYC verification passed |
kyc.session.denied | KYC verification failed |
kyc.session.requires_review | Session needs manual review |
Accreditation Events (6)
| Event | Description |
|---|---|
accreditation.session.created | Accreditation session created |
accreditation.document.uploaded | User uploaded a proof document |
accreditation.session.submitted | User submitted for review |
accreditation.session.more_info_needed | Additional documentation requested |
accreditation.session.approved | Accreditation verified |
accreditation.session.denied | Accreditation denied |
Subscription Events (10)
| Event | Description |
|---|---|
subscription.created | New subscription created |
subscription.signed | Subscription agreement signed |
subscription.funded | Payment received for subscription |
subscription.approved | Subscription approved by issuer |
subscription.rejected | Subscription rejected |
subscription.completed | Subscription fully completed |
subscription.cancelled | Subscription cancelled |
subscription.info_requested | Additional information requested from investor |
subscription.voided | Subscription agreement voided |
shares.issued | Shares issued to investor |
You can also call GET /notifications/event-types to retrieve the current list programmatically.
Note: Only events in the supported event types list are delivered to webhooks. Internal-only events are silently filtered out.
Webhook Payload Format
Every webhook delivery is an HTTP POST with this JSON body:
{
"event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event_type": "kyc.session.approved",
"source": "rialto.identity",
"org_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-03-15T10:30:00.000Z",
"version": "1",
"data": {
"session_id": "...",
"user_id": "...",
"status": "approved"
}
}Delivery Headers
| Header | Description | Example |
|---|---|---|
Content-Type | Always JSON | application/json |
X-Rialto-Signature | HMAC-SHA256 signature | sha256=a1b2c3... |
X-Rialto-Event-Type | The event type | kyc.session.approved |
X-Rialto-Event-Id | Unique event ID (UUID) | a1b2c3d4-... |
X-Rialto-Delivery-Id | Unique delivery ID (UUID) | f6e5d4c3-... |
X-Rialto-Timestamp | Event timestamp (ISO 8601) | 2026-03-15T10:30:00.000Z |
User-Agent | Rialto webhook agent | Rialto-Webhooks/1.0 |
Signature Verification
Every delivery is signed with your endpoint's signing_secret using HMAC-SHA256.
Verification Steps
- Read the raw request body (do not parse then re-stringify)
- Compute HMAC-SHA256 of the raw body using your
signing_secret - Prepend
sha256=to the hex digest - Compare with the
X-Rialto-Signatureheader using constant-time comparison
Node.js Example
import crypto from "crypto";
function verifySignature(secret, payload, signature) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(payload, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
// In your webhook handler:
app.post("/webhooks/rialto", (req, res) => {
const signature = req.headers["x-rialto-signature"];
const rawBody = req.rawBody; // ensure your framework preserves the raw body
if (!verifySignature(SIGNING_SECRET, rawBody, signature)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(rawBody);
// Process event...
res.status(200).send("OK");
});Python Example
import hmac
import hashlib
def verify_signature(secret: str, payload: bytes, signature: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode("utf-8"),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Retry Behavior
Your endpoint must return a 2xx status within 30 seconds. Non-2xx responses or timeouts trigger automatic retries.
Retry Schedule (8 attempts total)
| Attempt | Delay After Previous |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 15 minutes |
| 5 | 1 hour |
| 6 | 4 hours |
| 7 | 12 hours |
| 8 | 24 hours |
After all 8 attempts fail, the delivery is marked exhausted with no further retries.
Delivery Monitoring
Track the status of webhook deliveries:
# List deliveries (filter by endpoint, status, or event type)
curl "https://api.rialto.com/notifications/deliveries?endpoint_id=<id>&status=failed" \
-H "Authorization: Bearer <rialto_access_token>"
# Get delivery detail with all attempts
curl https://api.rialto.com/notifications/deliveries/<delivery_id> \
-H "Authorization: Bearer <rialto_access_token>"
# Manually retry a failed delivery
curl -X POST https://api.rialto.com/notifications/deliveries/<delivery_id>/retry \
-H "Authorization: Bearer <rialto_access_token>"Delivery Statuses
| Status | Description |
|---|---|
pending | Delivery queued, not yet attempted |
success | Endpoint returned 2xx |
failed | Last attempt failed, retries remaining |
exhausted | All 8 attempts failed, no more retries |
Managing Endpoints
# List your endpoints
GET /notifications/webhooks
# Get endpoint details
GET /notifications/webhooks/:id
# Update endpoint
PATCH /notifications/webhooks/:id
{ "url": "https://new-url.com/webhook", "description": "Updated" }
# Delete endpoint
DELETE /notifications/webhooks/:id
# Rotate signing secret (returns new secret once)
POST /notifications/webhooks/:id/rotate-secret
# Get current subscriptions
GET /notifications/webhooks/:id/subscriptionsBest Practices
- Respond quickly -- return 200 within a few seconds, then process events asynchronously
- Verify signatures in production using constant-time comparison
- Handle duplicates idempotently -- use
event_idto deduplicate (the same event may be delivered more than once) - Monitor failed deliveries via the deliveries API
- Rotate signing secrets periodically using the rotate endpoint
- Subscribe selectively -- only subscribe to event types you need rather than using
*