Webhooks
Receive real-time event notifications the moment a credential attestation is issued or revoked. Webhooks integrate ~alter into your event pipeline, a downstream service, or a custom workflow without polling.
Subscription management
Use the following endpoints to create, update, list, and delete webhook subscriptions. All endpoints require JWT authentication with the org_admin role.
| Method | Path | Auth |
|---|---|---|
| POST | /webhooks/Register a webhook | jwt |
| GET | /webhooks/List webhook subscriptions | jwt |
| GET | /webhooks/{subscription_id}Get webhook details | jwt |
| PATCH | /webhooks/{subscription_id}Update a webhook | jwt |
| DELETE | /webhooks/{subscription_id}Delete a webhook | jwt |
| GET | /webhooks/{subscription_id}/deliveriesList webhook deliveries | jwt |
| POST | /webhooks/{subscription_id}/testSend test webhook | jwt |
| GET | /webhooks/eventsList supported event types | jwt |
Event types
Subscribe to any combination of the following event types when registering a webhook. Events marked Live emit today; those marked Planned can be registered now and will emit as the matching surface ships.
| Event | Description | Status |
|---|---|---|
attestation.issued | A verifiable credential has been issued against an identity | Live |
attestation.revoked | A previously issued credential has been revoked | Live |
match.proposed | A match has been proposed between an identity and a collective | Planned |
match.accepted | A proposed match has been accepted | Planned |
match.declined | A proposed match has been declined | Planned |
placement.created | A new placement record has been created | Planned |
placement.updated | An existing placement record has changed state | Planned |
Delivery format
Webhook events are delivered as POST requests to your registered HTTPS endpoint. Each delivery includes three custom headers for verification and deduplication:
| Header | Description |
|---|---|
X-Alter-Signature | HMAC-SHA256 signature over the canonical JSON serialisation of the payload (sorted keys, compact separators) using your webhook secret |
X-Alter-Delivery | Unique delivery UUID for idempotency and deduplication |
X-Alter-Event | The event type (e.g. attestation.issued) |
Example payload
A typical attestation.issued webhook delivery:
POST /webhooks/alter HTTP/1.1
Host: ats.example.com
Content-Type: application/json
X-Alter-Event: attestation.issued
X-Alter-Delivery: d4e5f6a7-b8c9-0123-4567-890abcdef012
X-Alter-Signature: sha256=a1b2c3d4e5f6...Signature verification
Always verify the X-Alter-Signature header to confirm the request originated from ~alter. The signature is an HMAC-SHA256 over the canonical JSON serialisation of the payload (sorted keys, compact separators), signed with the webhook secret you received when creating the subscription. Parse the request body and re-serialise it canonically before computing the HMAC.
import hmac
import hashlib
import json
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
"""Verify ~alter webhook signature."""
# Re-serialise canonically: sorted keys, compact separators
canonical = json.dumps(
json.loads(payload),
separators=(",", ":"),
sort_keys=True,
)
expected = hmac.new(
secret.encode("utf-8"),
canonical.encode("utf-8"),
hashlib.sha256
).hexdigest()
received = signature.removeprefix("sha256=")
return hmac.compare_digest(expected, received)
# In your webhook handler:
is_valid = verify_webhook(
payload=request.body,
signature=request.headers["X-Alter-Signature"],
secret="whsec_your_webhook_secret" # pragma: allowlist secret
)
if not is_valid:
return Response(status_code=401)Retry policy
If your endpoint returns a non-2xx status code or fails to respond within 30 seconds, ~alter retries the delivery with exponential backoff:
| Attempt | Delay | Notes |
|---|---|---|
| 1st retry | 1 minute | After initial failure |
| 2nd retry | 5 minutes | Exponential backoff |
| 3rd retry | 15 minutes | Exponential backoff |
| 4th retry | 1 hour | Exponential backoff |
| 5th retry | 2 hours | Final attempt |
After five failed attempts, the delivery is marked as failed. A subscription that records ten consecutive failures is automatically deactivated; re-enable it once your endpoint is healthy again. Use the GET /webhooks/{subscription_id}/deliveries endpoint to inspect delivery history and diagnose failures.
Best practices
- •Always verify signatures. Never process a webhook without validating the
X-Alter-Signatureheader. - •Respond quickly. Return a 2xx within 10 seconds (the server allows 30 seconds; 10 is a reliability target). Queue heavy processing for async execution.
- •Handle duplicates. Use the
X-Alter-Deliveryheader for idempotency. Retries may deliver the same event multiple times. - •Use the test endpoint. Send a synthetic event via
POST /webhooks/{id}/testto verify your handler before going live.