Webhooks
Receive real-time notifications when statements are generated or spend thresholds are crossed.
Overview
Webhooks send HTTP POST requests to your endpoint when events occur. Configure them in Settings > Webhooks in the dashboard.
Event types
| Event type | Trigger |
|---|---|
statement.generated | A usage statement is generated for a customer (manual or scheduled). |
usage.threshold_reached | A customer's spend crosses the threshold configured in statement settings. |
These are the only two webhook events currently supported.
Payload format
Every webhook POST includes:
Content-Type: application/jsonX-Dexcost-Signatureheader (HMAC-SHA256 of the body using your webhook secret)
statement.generated
Fires when a usage statement is created — either manually via the API or automatically on your configured billing cycle.
{
"event_type": "statement.generated",
"workspace_id": "ws-abc123",
"statement_id": "stmt-xyz789",
"customer_id": "acme-corp",
"period_start": "2026-03-01",
"period_end": "2026-04-01",
"summary": {
"total_cost_usd": "470.25",
"total_tasks": 1234,
"margin_pct": "52.5"
},
"fetch_url": "/v1/api/statements/stmt-xyz789",
"timestamp": "2026-04-01T00:05:00Z"
}Use fetch_url to retrieve the full statement with line items, per-service breakdowns, and margin data.
usage.threshold_reached
Fires when a customer's running spend in the current period crosses the threshold you configured in Settings > Statements > Spend Threshold.
{
"event_type": "usage.threshold_reached",
"workspace_id": "ws-abc123",
"customer_id": "acme-corp",
"current_spend_usd": "512.30",
"threshold_usd": "500.00",
"period": "monthly",
"period_start": "2026-04-01",
"timestamp": "2026-04-15T14:30:00Z"
}Signature verification
Every request includes an X-Dexcost-Signature header. Verify it to ensure the webhook came from dexcost.
The signature is HMAC-SHA256(raw_request_body, your_webhook_secret) encoded as hex.
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Retry behavior
If your endpoint returns a non-2xx status code, dexcost retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 10 seconds |
| 2nd retry | 60 seconds |
| 3rd retry | 5 minutes |
After 3 failed attempts, the delivery is marked as failed. You can view delivery history in Settings > Webhooks.
Slack integration
If your webhook URL points to a Slack incoming webhook (hooks.slack.com), dexcost automatically formats the payload as a Slack Block Kit message instead of raw JSON.
Example Slack message for statement.generated:
📊 Statement Generated
Customer: acme-corp
Period: Mar 1 — Apr 1, 2026
Total cost: $470.25 | Tasks: 1,234