Skip to main content

Webhook Events

Ciyex Hub dispatches webhook events to both app vendors and the EHR instance when subscription lifecycle events occur.

Event Types​

EventDescription
subscription.createdA practice subscribed to an app
subscription.cancelledA practice cancelled their subscription
subscription.pausedA subscription was paused (e.g., payment issue)
usage.reportedUsage data was reported for a metered app

Payload Structure​

subscription.created​

{
"event": "subscription.created",
"app": {
"id": "10000000-0000-0000-0000-000000000006",
"slug": "ciyex-telehealth",
"name": "Ciyex Telehealth",
"iconUrl": "https://cdn.ciyex.com/icons/telehealth.svg",
"category": "TELEHEALTH",
"extensionPoints": ["patient-chart:action-bar"],
"smartLaunchUrl": null,
"fhirScopes": "patient/Encounter.read patient/Appointment.read"
},
"subscription": {
"id": "uuid",
"status": "active",
"orgAlias": "my-practice"
},
"timestamp": "2026-02-18T10:30:00Z"
}

subscription.cancelled​

{
"event": "subscription.cancelled",
"app": { ... },
"subscription": {
"id": "uuid",
"status": "cancelled",
"orgAlias": "my-practice"
},
"timestamp": "2026-02-18T10:30:00Z"
}

Security​

HMAC-SHA256 Signature​

All webhook payloads are signed with HMAC-SHA256. Verify the signature to ensure the payload is authentic:

Headers sent with each webhook:

HeaderDescription
X-Marketplace-EventEvent type (e.g., subscription.created)
X-Marketplace-Delivery-IdUnique delivery ID
X-Marketplace-SignatureHMAC-SHA256 signature

Verification (Java):

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.HexFormat;

String payload = request.getBody();
String signature = request.getHeader("X-Marketplace-Signature");

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
String expected = HexFormat.of().formatHex(mac.doFinal(payload.getBytes()));

if (!expected.equals(signature)) {
throw new SecurityException("Invalid webhook signature");
}

Delivery​

  • Webhooks are delivered via HTTP POST to the registered URL
  • Content type: application/json
  • Delivery is asynchronous (non-blocking)
  • Failed deliveries are retried with exponential backoff
  • Delivery attempts are logged in WebhookDeliveryLog

Setting Up Vendor Webhooks​

Register webhook endpoints through the Developer Portal:

POST /api/v1/developer/webhooks
Authorization: Bearer <token>
Content-Type: application/json

{
"url": "https://yourapp.com/webhooks/ciyex",
"secret": "your-shared-secret",
"events": ["subscription.created", "subscription.cancelled"]
}

EHR Internal Webhooks​

The marketplace automatically sends subscription events to the subscribing org's EHR instance at:

POST {practice.callbackUrl}/api/internal/marketplace-webhook

This is handled automatically — no configuration needed from the practice.