What you'll set up
- An HTTPS endpoint that receives real-time Elido events — link created, clicked, domain verified, abuse flagged, and more.
- HMAC-SHA256 signature verification so your server can confirm every delivery is genuine.
- Automatic retries with exponential backoff, plus a delivery log for replaying failures manually.
Webhooks push events from Elido to your server as they happen. Use them instead of polling the API when you need real-time data — typical use cases include syncing link metadata to your CRM, building a custom dashboard, or kicking off a CI pipeline when a link is published.
Add a webhook endpoint
- Dashboard → Webhooks → New endpoint.
- Paste the URL we should POST to. It must be HTTPS — we reject plain HTTP at create time.
- Pick the event kinds you want. The full list is below.
- (Optional) Paste a signing secret, or let us generate one. The secret is shown once; copy it before leaving the page.
- Save. We send a test event immediately so you can confirm the endpoint is reachable.
Event catalogue
The current event kinds. Names follow <resource>.<action> and stay stable across versions.
link.created— a new short link was created.link.updated— destination, slug, expiry, or password changed.link.deleted— link was soft-deleted (still recoverable for 30 days).link.clicked.aggregated— every 60 seconds we send the click counts per link for the previous window. We don't deliver per-click events over webhooks (the volume would crush most endpoints) — use the click export for raw event data.domain.verified— a custom domain's DNS check passed.domain.tls_renewed— Caddy renewed the TLS cert for a custom domain.qr.generated— a QR code SVG/PNG was rendered.abuse.flagged— our URL scanner marked a destination as suspicious.member.invited/member.removed— workspace membership changes.
Payload shape
Every event has the same envelope:
{
"id": "evt_2c8L9N4M5",
"type": "link.created",
"created_at": "2026-05-15T09:42:11.382Z",
"workspace_id": 4123,
"data": {
"link_id": 891234,
"slug": "spring-2026",
"destination": "https://acme.com/spring-sale",
"created_by": "user_5821"
}
}
id is unique per delivery. Use it for idempotency — if you receive the same id twice (because we retried), skip the duplicate.
Verifying signatures
We sign every webhook body with HMAC-SHA256 using your endpoint's secret. The signature is in the Elido-Signature header as t=<unix_ts>,v1=<hex>.
To verify in Node:
import { createHmac } from "node:crypto";
function verify(secret: string, body: string, header: string): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${body}`)
.digest("hex");
return expected === parts.v1;
}
Retries
We retry failed deliveries (any non-2xx response, or no response within 10 seconds) with exponential backoff: 30s, 1m, 5m, 30m, 2h, 12h. After 6 failures we mark the endpoint failing and stop retrying that specific event. The endpoint stays subscribed; new events continue to attempt delivery.
Endpoint stays failing for 5 consecutive deliveries → we auto-disable it and email the workspace owner. Re-enable in the dashboard once you've fixed your server.
Delivery log
Open any webhook in the dashboard to see the last 500 deliveries, with status code, response time, and the request/response body for each. Failed deliveries can be replayed manually from this view.
Local testing
Use the Elido CLI to forward webhooks to localhost:
npx @elido/cli webhooks forward --url http://localhost:3000/webhooks
The CLI registers a temporary endpoint that lasts until you Ctrl-C, tunnels deliveries to your machine, and prints the request body for each event.
Limits
- 20 endpoints per workspace (Pro), 100 (Business).
- 10 KB max signed payload size. Larger payloads are split —
dataincludes atruncated: trueflag and a URL to fetch the full body. - 50 events per second sustained per endpoint. Bursts are queued.
Troubleshooting
Endpoint reports 401 in the delivery log. Your endpoint is verifying signatures with the wrong secret. Compare the secret in Webhooks → Endpoint → Settings against the one your server has stored.
Some events are arriving twice. Either we retried after a slow response, or your endpoint timed out without responding. Use the event id for idempotency.
link.clicked.aggregated counts don't match analytics dashboard. The dashboard is real-time (within 30s of click); the webhook is windowed at 60s. There's also a small (~1%) discrepancy because bots filtered out of the dashboard are still included in raw aggregates until the bot-filter window closes.