Webhooks
The Equa platform uses webhooks for both receiving third-party billing events and (proposed) sending outbound notifications when resources change. This page documents both systems.Inbound Webhooks (Chargify)
The Equa API receives webhooks from Chargify (Maxio) to keep billing and subscription data in sync.
Source: equa-server/modules/api/src/endpoints/billing-endpoints.ts
Events Handled
How It Works
- An event occurs in Chargify (e.g., subscription state change)
- Chargify sends an HTTP POST to the configured webhook URL on the Equa API
- The billing module processes the event and updates the local database
- Subscription features and limits are recalculated for the affected organization
Subscription Synchronization
The billing system automatically syncs member/shareholder counts with Chargify whenever cap table changes occur. This ensures billing stays aligned with actual usage:- When a shareholding is created or deleted, the shareholder count is updated via
appendShareholderCountUpdateViaShareholding(Source:captable-endpoints.ts, line 138) - When a holding (option grant) is created or deleted, the member count is updated via
updateChargifyMemberCount(Source:captable-endpoints.ts, line 139) - These count changes trigger Chargify component quantity updates
Webhook Security
Webhook payloads from Chargify are validated server-side to ensure they originate from Chargify. Only events from authenticated Chargify accounts tied to the configured Chargify API credentials are processed.Configuring Chargify Webhooks
Webhook URLs are configured in the Chargify dashboard and correspond to the billing endpoints on the Equa API server. These are internal server-to-server integrations and are not exposed for external consumption. If you need to integrate with Equa billing events in your application, use the Billing Endpoints to query subscription and transaction data instead.Outbound Webhooks (Proposed)
The outbound webhook system is a proposed design specification. It is not yet
implemented in the Equa API. This documentation describes the planned architecture
for when the feature is built. All endpoints, payloads, and behaviors described in
this section are subject to change.
Overview
The proposed outbound webhook system allows integrators to receive real-time HTTP notifications when resources change within the Equa platform. The design follows the Stripe webhook model — a widely adopted pattern for financial SaaS APIs. Key capabilities:- Event-driven: Receive HTTP POST callbacks when cap table, organization, document, or billing events occur
- Secure: HMAC-SHA256 signatures on every payload for verification and replay protection
- Reliable: Automatic retries with exponential backoff over a 26-hour window
- Filterable: Subscribe to specific event types per webhook endpoint
- Idempotent: Unique event IDs enable safe deduplication by consumers
Event Types
Events use{resource}.{action} dot-notation. Subscribe to all events or filter by specific types.
Cap Table Events
These events fire when equity instruments and related records change within an organization’s cap table.Organization Events
These events fire when organization-level resources change.Document Events
These events fire when documents and folders are managed within an organization.Subscription Events
These events fire for billing lifecycle changes. They complement the inbound Chargify webhooks by providing a normalized outbound notification format.Event Type Summary
Payload Format
Every webhook delivery uses a consistent JSON envelope:Envelope Fields
Example Payloads
shareholding.created — New shares issued:
member.updated — Member role changed:
holding.converted — Convertible note converted to equity:
payment.failed — Payment failure notification:
Delivery and Retries
Transport
- Webhooks are delivered as HTTP POST requests to the registered endpoint URL
- The request body is JSON (
Content-Type: application/json) - The registered URL must be HTTPS in production environments
- Each delivery includes the
Equa-Signatureheader for verification (see Signature Verification)
Timeout
Each delivery attempt has a 30-second timeout. If the endpoint does not respond with an HTTP2xx status code within 30 seconds, the attempt is considered failed.
Retry Schedule
Failed deliveries are retried with exponential backoff:
After 6 total attempts (1 initial + 5 retries) over approximately 26 hours, the event is marked as failed. Webhook endpoints that consistently fail will be automatically disabled after 3 consecutive days of failures.
Idempotency
Every event has a uniqueid field (e.g., evt_2f8a3b1c4d5e6f7890abcdef). The same event may be delivered more than once due to retries or network issues. Consumers must store processed event IDs and skip duplicates:
Ordering
Events are delivered in best-effort chronological order based on thecreated timestamp. However, due to retry scheduling and network variability, events may arrive out of order. Consumers should:
- Use the
createdtimestamp to determine actual event order - Handle out-of-order delivery gracefully (e.g., ignore a
shareholding.updatedthat arrives before the correspondingshareholding.created) - Fetch the current resource state via the API if ordering is critical
Signature Verification
Every webhook delivery includes anEqua-Signature header that allows the consumer to verify the payload originated from Equa and has not been tampered with.
Header Format
Verification Steps
- Extract the
t(timestamp) andv1(signature) values from theEqua-Signatureheader - Construct the signed payload string:
{timestamp}.{raw_request_body}(thetvalue, a literal., and the raw JSON body — do not parse and re-serialize) - Compute the expected signature:
HMAC-SHA256(signing_secret, signed_payload)and hex-encode the result - Compare the computed signature with the
v1value using a constant-time comparison - Check the timestamp is within 5 minutes of the current time to prevent replay attacks
Signing Secret
Each registered webhook endpoint receives a unique signing secret (prefixedwhsec_) at creation time. The secret is displayed once and should be stored securely. If compromised, delete the webhook and create a new one.
Timestamp Tolerance
Webhook Management Endpoints
These endpoints are proposed and not yet implemented. They describe the planned API
for managing outbound webhook registrations.
Register a Webhook
canEditOrganization
Request Body:
Example Request:
201 Created):
List Webhooks
canViewOrganization
Response (200 OK):
The
secret field is never returned in list or get responses. It is only available
at creation time.Delete a Webhook
canEditOrganization
Response (204 No Content): Empty body.
Send a Test Event
canEditOrganization
Request Body:
Response (
200 OK):
Example Integration
The following Node.js/Express example shows how to receive, verify, and process Equa webhook events.Key implementation notes:
- Use
express.raw()(notexpress.json()) so you receive the raw bytes for signature verification. Parsing and re-serializing JSON may alter whitespace, breaking the signature. - Always use
crypto.timingSafeEqual()for signature comparison to prevent timing attacks. - Return
200immediately and queue heavy processing for async workers. The 30-second delivery timeout is strict. - Store processed event IDs in a persistent store (database, Redis) with a TTL of at least 48 hours to handle retry deduplication across the full retry window.
Best Practices
- Use HTTPS endpoints. Webhook payloads may contain sensitive financial data (share counts, member details, payment information). Always use TLS-encrypted endpoints.
-
Respond quickly. Return a
2xxresponse within a few seconds. Offload processing to a background queue (e.g., Bull, SQS, Pub/Sub) to avoid timeouts. - Implement idempotency. Due to retries, you may receive the same event more than once. Track processed event IDs and skip duplicates.
-
Handle out-of-order events. Use the
createdtimestamp and the current resource state (via API calls) rather than assuming delivery order matches event order. -
Monitor delivery health. Use the List Webhooks endpoint to check
lastDeliverystatus. Set up alerts for consecutive failures. - Rotate secrets periodically. Delete and recreate webhook registrations to rotate signing secrets. Update your verification code before deleting the old webhook.
- Filter events at registration. Subscribe only to the event types you need. This reduces delivery volume and the surface area for processing errors.
- Log raw payloads. Store the raw JSON body and signature header for debugging. This helps diagnose verification failures and unexpected payload formats.