BUILD ON IT / ELPIS
Webhooks
Signed delivery of treasury and settlement events, with retries and SSRF protection.
How delivery works
- Register an HTTPS endpoint in the console; it receives a JSON POST per event.
- Every delivery is signed, so a receiver can refuse a forged one. The secret is shown once at creation.
- Failures are retried with backoff, and each attempt is recorded as a delivery row.
- Delivery is asynchronous — it never delays or changes a treasury decision.
Verifying a delivery
header
Elpis-Signature: t=<unix>,v1=<hmac-sha256(t + "." + body)>Compute the HMAC over timestamp + "." + rawBody, compare in constant time, and reject a timestamp outside your tolerance window to prevent replay. The bundled SDK exports a verifier:
sdk
import { verifyWebhookSignature } from "@devilabs/elpis";
const ok = await verifyWebhookSignature({
secret, header: req.headers["elpis-signature"], body: rawBody,
});Use the raw body. Re-serialising JSON changes the bytes and breaks the signature. The Python SDK ships the same verifier as
verify_webhook_signature(body, header, secret).Events
| Event | Fires when |
|---|---|
| PAYMENT_PROPOSED | An intent is created. |
| POLICY_CHECKED | All controls have produced facts. |
| PAYMENT_STAGED | Escrow opened; the window started. |
| PAYMENT_CANCELLED | Recalled inside the window; escrow refunded. |
| PAYMENT_EXECUTED | The transaction was submitted. |
| PAYMENT_VERIFIED | The chain confirms the transfer. |
| PAYMENT_FINALIZED | Settled and irreversible. |
| ACTION_BLOCKED | A blocking rule refused it. |
| ACTION_FROZEN / ACTION_UNFROZEN | A freeze was applied or lifted. |
| ACTION_ESCALATED | A person was asked to decide. |
| ACTION_FAILED / ACTION_ROLLED_BACK | Execution failed, or was unwound. |
| APPROVAL_REQUESTED / GRANTED / REJECTED | Approval lifecycle. |
| RECIPIENT_CHANGED | A recipient wallet change was detected. |
| RECONCILIATION_INCIDENT | A books/chain discrepancy was found. |
| COMPENSATION_CREATED / SETTLED | A compensating action was raised or paid. |
| SUPERVISOR_REVIEW | A supervisor verdict was recorded. |
| JOB_COMMITTED | A settlement job committed its condition. |
| EVIDENCE_RECEIVED | Evidence was recorded against a job. |
| SETTLED_RELEASE / SETTLED_REFUND | A settlement decided — released, or refunded. |
| HELD | A settlement held; no money moved. |
| DEADLINE_PASSED | A job's deadline passed unsettled and it was refunded. |
| DISPUTE_OPENED | A dispute was opened; settlement is frozen. |
| WITNESS_UNRESPONSIVE | A witness has not responded. |
SSRF protection
Endpoint registration is a server-side request primitive, so it is fenced:
- Only
httpsis allowed. - Private, loopback and link-local addresses are refused — including alternate encodings (integer, hex, octal, IPv4-mapped IPv6) that bypass naive string checks.
- The resolved address is re-checked at delivery time, so DNS rebinding cannot turn a valid host into a private target after registration.