Documentation
cardano.delivery pushes signed HTTP notifications to your endpoint when events are confirmed on Cardano mainnet. Free for everyone.
Getting started
- Create an account and sign in.
- Click New webhook, pick a trigger type, set your target URL and how many confirmations to wait for.
- Optionally add conditions to filter events - all conditions must match.
- Save. Deliveries start with the next matching confirmed event (usually within a block or two, ~20–40 seconds).
Webhook types
| Type | Fires | Payload contains |
|---|---|---|
transaction | for each matching transaction in a confirmed block | transaction details, inputs/outputs (UTxOs) |
block | once per confirmed block | block header: height, hash, slot, pool, fees, … |
delegation | for each matching stake delegation | transaction + delegation certificates and pool info |
epoch | at each epoch transition | previous and current epoch numbers |
proposal | when a new governance action is submitted on-chain | full proposal detail (type, deposit, expiration, …) + the submitting transaction |
vote | when a vote is cast on a recent governance action | the vote (voter, role, yes/no/abstain) + the proposal it belongs to |
pool | per confirmed block containing pool registrations, updates or retirements | the transaction + pool certificates |
withdrawal | per confirmed block containing reward withdrawals | the transaction + withdrawals (stake address, amount) |
mint | per confirmed block containing asset mints or burns | the transaction + minted/burned assets (unit, policy, quantity, action) |
script | per confirmed block containing Plutus script executions | the transaction + redeemers (script hash, purpose, execution units) |
drep | when a DRep registers (any DRep), or when a watched DRep deregisters or votes | action (registered/deregistered/voted), the DRep (id, voting power, status), and the vote + proposal id for voted |
account | when a watched stake account changes (balance, rewards, delegation) | previous and current account state |
proposal, vote and drep
events are delivered on detection (typically within a minute of on-chain inclusion) and have
no confirmation setting; votes are watched on the most recent open proposals until their
voting period expires. account webhooks require a stakeAddress =
condition and are polled every ~5 minutes. drep registrations cover every DRep;
deregistered and voted actions require a drepId =
condition (the DRep is then watched every ~5 minutes). All other types follow your chosen
confirmation depth like blocks and transactions.
Confirmations
Each webhook waits for the number of confirmations you choose (1–10) before firing. One confirmation means the event fires as soon as the block after it is minted; ten gives you near-certain finality at the cost of a few minutes of latency. If the chain rolls back before your confirmation depth is reached, the event is not delivered.
Conditions
Conditions filter which events fire your webhook. All conditions must match (logical AND). A webhook without conditions fires for every event of its type.
| Condition | Applies to | Operators | Example value |
|---|---|---|---|
recipient | transaction | = != | addr1… or stake1… |
sender | transaction | = != | addr1… or stake1… |
quantity | transaction | < <= = > >= != | 1000000 (lovelace) |
policyId | transaction | = != | 56-char hex policy ID |
fingerprint | transaction | = != | asset1… |
assetHex | transaction | = != | policy ID + hex asset name |
poolId | block, delegation | = != | pool1… |
txCount size height totalFees totalOutput | block | < <= = > >= != | 500 |
epoch | epoch | < <= = > >= != | 600 |
governanceType | proposal | = != | treasury_withdrawals, parameter_change, hard_fork_initiation, info_action, no_confidence, new_committee, new_constitution |
voterRole | vote | = != | drep, spo, constitutional_committee |
voter | vote | = != | drep1… / pool1… |
vote | vote | = != | yes, no, abstain |
govActionId | vote | = != | gov_action1… |
action | pool, mint, drep | = != | update/retire · mint/burn · registered/deregistered/voted |
stakeAddress | withdrawal, account | = != | stake1… (account requires =) |
amount | withdrawal | < <= = > >= != | lovelace |
assetHex | mint | = != | policy ID + hex asset name |
quantity | mint | < <= = > >= != | absolute amount |
scriptHash purpose | script | = != | hex hash · spend/mint/cert/reward |
unitMem unitSteps | script | < <= = > >= != | execution budget |
drepId | drep | = != | drep1… |
jsonPath | all types | < <= = > >= != | selector $.tx.hash, value to compare |
jsonPath conditions evaluate a JSONPath
selector against the event payload and compare the result to your value - an escape hatch for
anything the built-in conditions don't cover.
Delivery payload
Your endpoint receives an HTTP POST with a JSON body:
{
"id": "47668401-c3a8-42c3-93ed-6c0e1abcf1c0", // unique event id (stable across retries)
"webhook_id": "b592db93-4bb9-4bd0-a196-…", // which webhook fired
"created": 1754255963, // unix timestamp of the event
"api_version": 1,
"type": "block", // transaction | block | delegation | epoch
"payload": { /* event data for the type */ }
}
Requests are sent with User-Agent: cardano-delivery and time out after 10 seconds.
Event ids are deterministic - if you ever receive a duplicate, deduplicate by id.
Verifying signatures
Every delivery includes a Delivery-Signature header so you can verify the request
came from cardano.delivery and was not tampered with:
Delivery-Signature: t=1754255965,v1=f4d1ede9…
t is a unix timestamp, v1 is
HMAC-SHA256(secret, `${t}.${rawRequestBody}`) in hex, using your webhook's secret
(shown in the dashboard under Secret).
Node.js example
import { createHmac, timingSafeEqual } from 'node:crypto'; const verify = (rawBody, signatureHeader, secret) => { const parts = Object.fromEntries( signatureHeader.split(',').map(kv => kv.split('=')), ); const expected = createHmac('sha256', secret) .update(`${parts.t}.${rawBody}`) .digest('hex'); // reject stale timestamps to prevent replay (e.g. older than 10 minutes) if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 600) return false; return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)); };
Retries & history
- Failed deliveries (network errors and 5xx responses) are retried a few times with backoff; respond with any 2xx to acknowledge.
- The last deliveries for each webhook - with status codes and errors - are visible under History in the dashboard.
- Disabling or deleting a webhook stops deliveries within ~10 seconds.
- Webhooks whose endpoint keeps failing are disabled automatically: more than 50 delivery attempts in 48 hours with under 10% success. Fix your endpoint and flip the webhook back on in the dashboard - editing or re-enabling resets the measurement window.
Use cases
- Monitoring All Cardano
Governance Votes on Telegram - a full step-by-step guide:
votewebhook, a signature-verifying Cloudflare Worker relay, and a Telegram bot. The same relay pattern works for wallet activity (twotransactionwebhooks with a stake addressrecipient/sendercondition). - Delegator protection: a
poolwebhook with your pool id andaction = retire, pointed at a Slack/Discord incoming webhook relay. - NFT drop monitor: a
mintwebhook withpolicyId =your collection - every mint and burn, live. - Agent subscriptions: an AI agent creates an ephemeral inbox webhook mid-task and polls for the result - no account, no endpoint.
Limits
| Price | Free |
| Network | Cardano mainnet |
| Webhooks per account | 5 |
| Conditions per webhook | 10 |
| Confirmations | 1–10 |
| Delivery timeout | 10 s |
| Target URLs | public http(s) endpoints only - private and internal addresses are rejected |
FAQ
Which networks are supported?
Cardano mainnet. Preprod/preview support may come later if there's demand.
Can I get the same event twice?
Under rare failure conditions a delivery can be repeated. Event ids are deterministic, so deduplicating by id makes processing idempotent.
What happens during a chain rollback?
Events are only delivered once your chosen confirmation depth is reached. Higher confirmations (e.g. 5–10) make rollback delivery practically impossible.
Can AI agents use this without an account?
Yes. One unauthenticated POST creates a self-expiring webhook (up to 24h), and inbox mode lets the agent poll matched events instead of hosting an endpoint. See the ephemeral API and llms.txt.
Need help?
Write to support@cardano.delivery.
Is there an API?
Yes - manage webhooks programmatically with API keys. See the API reference.