Webhooks
Webhooks allow AlchemOS Positive to push real-time event notifications to your application instead of requiring you to poll the API.
Supported events
| Event | Triggered when |
|---|---|
emission.created |
New emission entry created |
emission.updated |
Emission entry edited |
emission.approved |
Entry moved to Approved state |
emission.locked |
Entry locked (reporting period closed) |
report.generated |
Report generation job completed |
offset.purchased |
Credit purchase order confirmed |
offset.retired |
Registry confirms a retirement |
certificate.issued |
Certificate approved and issued |
certificate.revoked |
Certificate revoked |
target.milestone.achieved |
Milestone year passed; target met |
target.milestone.missed |
Milestone year passed; target missed |
user.invited |
New team member invited |
user.deactivated |
Team member deactivated |
Creating a webhook endpoint
- Go to Settings → Webhooks → Add Endpoint
- Enter your endpoint URL (must be HTTPS)
- Select the events to subscribe to
- Optionally add a description
- Click Save
AlchemOS Positive will send a test ping event immediately to confirm reachability.
Creating a webhook via API
POST /api/v1/settings/webhooks
{
"url": "https://yourapp.com/webhooks/alchemos",
"events": ["emission.approved", "certificate.issued", "offset.retired"],
"description": "ERP integration listener"
}
Response (201)
{
"data": {
"id": "wh_abc123",
"url": "https://yourapp.com/webhooks/alchemos",
"signingSecret": "whsec_xxxxxxxxxxxxxxxxxxxx",
"events": ["emission.approved", "certificate.issued", "offset.retired"],
"status": "active"
}
}
Save the
signingSecret— it is shown only once and is used to verify webhook authenticity.
Webhook payload structure
{
"id": "evt_xyz789",
"type": "emission.approved",
"createdAt": "2024-11-01T09:05:00Z",
"organisationId": "org_001",
"data": {
"id": "em_abc123",
"scope": 1,
"tco2e": 0.273,
"state": "approved"
}
}
The data object mirrors the corresponding REST API response for that resource type.
Verifying webhook signatures
Every webhook request includes an Alchemos-Signature header. Verify it to ensure the request came from AlchemOS Positive and was not tampered with.
Alchemos-Signature: t=1730000000,v1=abc123def456...
Verification algorithm
- Extract
t(timestamp) andv1(signature) from the header - Construct the signed payload:
{t}.{raw_request_body} - Compute HMAC-SHA256 using your
signingSecret - Compare the result to
v1
TypeScript example
import crypto from 'crypto';
function verifyWebhook(payload: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
const signedPayload = `${parts.t}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Reject requests where:
- Signature verification fails
- The
ttimestamp is more than 5 minutes in the past (replay attack protection)
Retries and delivery guarantees
AlchemOS Positive delivers webhooks at least once. If your endpoint returns a non-2xx response or times out (10-second timeout), retries occur:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 8 hours |
After 5 failed retries, the event is marked failed and no further retries occur. Review failed events in Settings → Webhooks → → Delivery Log.
Testing webhooks
Use Settings → Webhooks → → Send Test Event to send a sample payload for any event type. Useful for testing your receiver locally.
For local development, use a tunnel tool such as ngrok or Cloudflare Tunnel to expose your local server.
Managing webhooks
| Endpoint | Description |
|---|---|
GET /api/v1/settings/webhooks |
List all endpoints |
GET /api/v1/settings/webhooks/{id} |
Get endpoint details |
PATCH /api/v1/settings/webhooks/{id} |
Update URL or events |
DELETE /api/v1/settings/webhooks/{id} |
Delete endpoint |
GET /api/v1/settings/webhooks/{id}/deliveries |
List delivery history |
POST /api/v1/settings/webhooks/{id}/test |
Send test event |