The @open-condo/webhooks package
Webhook support is provided by the @open-condo/webhooks package — a KeystoneJS 5 plugin that can be added to any Keystone-based application in the Condo monorepo. It introduces three database-backed schemas (Webhook, WebhookSubscription, WebhookPayload) and a webHooked plugin that you attach to any model you want to observe.
Webhook
Stores the integration’s display name, the target URL, and the service user whose GraphQL session is used to fetch payload data.
WebhookSubscription
Ties a
Webhook to a specific model, the GraphQL fields to include, optional filters, and delivery state (last sync time, failure count).WebhookPayload
A single outbound HTTP request: the encrypted JSON body, signing secret, delivery status, retry metadata, and the full response from the remote server.
webHooked plugin
A one-line addition to any Keystone model schema that registers it for webhook observation and schedules delivery tasks after every
afterChange hook.How it works
- You add the
webHooked()plugin to a model (e.g.Ticket). - After each create or update on that model, an
afterChangehook fires and enqueues asendModelWebhooksbackground task. - The task queries all active
WebhookSubscriptionrecords for that model, filters changed objects by the subscription’sfiltersfield, fetches the requestedfieldsfrom GraphQL on behalf of the subscription’s user, and writes aWebhookPayloadrecord. - A separate delivery worker picks up pending
WebhookPayloadrecords and POSTs them to the target URL. - Responses, HTTP status codes, and error messages are stored on the
WebhookPayloadrecord for debugging.
Supported events
Each model registered withwebHooked() automatically emits three event types:
| Event | When it fires |
|---|---|
ModelName.created | A new record is created |
ModelName.updated | An existing record is changed |
ModelName.deleted | A record is soft-deleted |
Ticket produces Ticket.created, Ticket.updated, and Ticket.deleted.
Applications can also define additional custom event types by passing an appWebhooksEventsTypes array to getWebhookModels().
Webhook payload format
The body of each outgoing POST request is a JSON object:data item are controlled by the fields string on the WebhookSubscription (e.g. "id status updatedAt property { address }"). Only the fields listed there are fetched and forwarded.
Payloads are batched: up to maxPackSize objects (default 100) are included in a single request.
Delivery semantics
Retry with exponential back-off
Failed deliveries are retried at 0 s → 1 min → 5 min → 30 min → 2 h → 6 h → 24 h intervals.
7-day TTL
Condo stops retrying a payload after 7 days from creation (
expiresAt). Records are hard-deleted after 42 days.Automatic circuit-breaker
After 10 consecutive failures the subscription is paused. Support must manually reset
failuresCount to resume delivery.10-second timeout
Each HTTP request to the remote server must complete within 10 seconds or it is counted as a failure.
Security
Sensitive fields (payload and secret) in WebhookPayload are stored as EncryptedText using AES-256-GCM with PBKDF2-SHA512 key derivation. The encryption key material comes from the DATA_ENCRYPTION_CONFIG and DATA_ENCRYPTION_VERSION_ID environment variables — both are required for the application to start.
The secret field is an HMAC signing key. Your endpoint can use it to verify that a request genuinely originates from Condo.