Outbound Webhooks
Outbound Webhooks send events from Comma to a URL on your own system. Inbound Webhooks work in the opposite direction: they bring data into your archive.
Setup steps
Section titled “Setup steps”Team admins can create and manage outbound webhooks.
- Sign in to the Comma Compliance dashboard.
- Open Integrations and find the Outbound Webhooks card in the Custom connectors group.
- Click Connect or Manage, then Add Outbound Webhook.
- Enter a Name and your Outbound webhook URL.
- Create the webhook and copy the signing secret shown immediately afterward. It is shown once and is not displayed again.
Events
Section titled “Events”A webhook created in the app sends these events:
| Event type | When it is sent |
|---|---|
team_case.created | A case is opened. |
team_case.updated | A case is changed. |
Delivery format
Section titled “Delivery format”Each delivery is an HTTP POST with Content-Type: application/json and these headers:
| Header | Value |
|---|---|
X-Webhook-Comma-Compliance-Signature | Hex HMAC-SHA256 of the timestamp, a period, and the raw request body, keyed with your signing secret. |
X-Webhook-Comma-Compliance-Timestamp | The time the delivery was signed, in Unix seconds. |
X-Webhook-Comma-Compliance-Id | The event’s unique ID, equal to event_id in the body. Use it to ignore a retry you have already processed. |
Payload shape
Section titled “Payload shape”The JSON body has exactly these keys:
| Key | Value |
|---|---|
event_id | The event’s unique ID. |
event_type | team_case.created or team_case.updated. |
subject_id | The case’s ID. |
subject_type | TeamCase. |
data | The case in the same format the REST API returns. |
Example payload:
{ "event_id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "event_type": "team_case.updated", "subject_id": 123, "subject_type": "TeamCase", "data": { "id": 123 }}Verify signatures
Section titled “Verify signatures”Use your signing secret to calculate the expected signature, then compare it with the signature header using hmac.compare_digest.
In this Python example, timestamp is the timestamp header as received, received_signature is the signature header, and raw_body is the raw request body decoded as UTF-8 text without any changes. Sign over the raw body as received, not re-serialized JSON: changes to whitespace or key order change the signature.
import hashlibimport hmac
def verify_signature(secret, timestamp, raw_body, received_signature): expected_signature = hmac.new( secret.encode(), f"{timestamp}.{raw_body}".encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected_signature, received_signature)Your receiver should also reject old timestamps to block replays. Choose the timestamp tolerance for your own system. Use the event ID to ignore retries you have already processed.
Retries
Section titled “Retries”Return a 2xx response to acknowledge a delivery. A delivery that does not receive a 2xx response is retried up to 6 times over about a day, on this schedule: 15 seconds, 1 minute, 5 minutes, 15 minutes, 1 hour, and 24 hours.
Delivery history
Section titled “Delivery history”The webhook’s page lists the 20 most recent deliveries with a status of Delivered, Retrying, or Failed. Open a delivery to see its payload and attempts.
Rotate the signing secret
Section titled “Rotate the signing secret”On the webhook’s page, click Rotate Signing Secret. You may be asked to confirm your identity first. Rotation replaces the secret immediately: the old secret stops working and the new one is shown once. Update your receiver to use the new secret.