Wat je instelt
- Een endpoint dat Elido-gebeurtenissen ontvangt, zoals
link.created,link.updated,link.deleted,member.invitedendomain.verified. - HMAC-SHA256-verificatie van de
X-Elido-Signature-header, zodat je server kan bevestigen dat elke bezorging van Elido afkomstig is. - Drie bezorgingspogingen per gebeurtenis, een bezorgingslogboek met een handmatige Retry-knop en geheimrotatie met een overlap van 7 dagen.
Webhooks sturen gebeurtenissen vanuit Elido naar je server zodra ze plaatsvinden, zodat je de API niet hoeft te pollen. Typische toepassingen zijn het synchroniseren van linkmetadata met een CRM, een interne index van links actueel houden of een taak starten wanneer een link wordt gepubliceerd.
Webhooks omvatten wijzigingen aan links, domeinen, leden en de werkruimte. Ze bevatten geen individuele kliks. Gebruik voor klikgegevens de klikexport of een gebeurtenisintegratie zoals Mixpanel. Webhookgebeurtenissen per klik zijn gepland.
Een webhookendpoint toevoegen
- Ga naar Dashboard → Webhooks.
- Voer de URL in waarnaar we moeten POST'en en eventueel een beschrijving.
- Kies het endpointtype: Events (je kiest gebeurtenistypes) of SIEM (firehose) (elke gebeurtenis in de werkruimte).
- Vink voor een Events-endpoint de gewenste gebeurtenistypes aan. De
link.*-gebeurtenissen zijn standaard aangevinkt. - Klik op Add endpoint. Het ondertekeningsgeheim wordt één keer weergegeven, direct onder het formulier. Kopieer het nu: Elido toont het niet opnieuw.
De URL moet HTTPS zijn. Gewone http://-URL's worden geweigerd wanneer je het endpoint opslaat, behalve localhost en 127.0.0.1, die toegestaan blijven voor lokale ontwikkeling.
Je kunt een endpoint vanuit de lijst pauzeren en hervatten. Een gepauzeerd endpoint krijgt geen nieuwe bezorgingen en alles wat er nog voor in de wachtrij staat, wacht totdat je het hervat.
Wie webhooks kan beheren
Iedereen in de werkruimte kan endpoints en het bezorgingslogboek bekijken. Voor het aanmaken, bewerken, pauzeren, verwijderen en roteren van het geheim en het opnieuw proberen van een bezorging is de machtiging workspace.edit nodig. De ingebouwde rollen Admin en Owner hebben die machtiging. Een API-sleutel kan dezelfde webhookendpoints van de API aanroepen als de rol van die sleutel over die machtiging beschikt.
Endpointtypes
- Events (
event): ontvangt alleen de gebeurtenistypes waarop je bent geabonneerd. Ondertekend. - SIEM (
siem): ontvangt elke gebeurtenis die voor de werkruimte wordt gepubliceerd, inclusiefaudit.eventvoor elk auditlogboekitem. Ondertekend. Bedoeld voor logpijplijnen en beveiligingstools. - Discord, Telegram en Sentry: chat- en waarschuwingskanalen die hetzelfde bezorgingssysteem hergebruiken, maar de eigen berichtindeling van de leverancier versturen. Ze authenticeren via de URL of het token van de leverancier en bevatten daarom geen signatureheaders. Zie Discord, Telegram en Sentry.
Alles hieronder over signatures en geheimen is alleen van toepassing op Events- en SIEM-endpoints.
Gebeurteniscatalogus
Namen volgen <resource>.<action>. Het dashboardformulier biedt de meest voorkomende opties; via de API kun je een Events-endpoint abonneren op elk type in deze lijst, en een SIEM-endpoint ontvangt ze allemaal.
In de dashboardkiezer:
link.created: er is een korte link aangemaakt, inclusief links die in bulk zijn aangemaakt.link.updated: een link is gewijzigd (bestemming, titel, tags, vervaldatum enzovoort), hersteld, van status veranderd of door moderatie van veiligheidsstatus veranderd.link.deleted: een link is soft-deleted.workspace.updated: de werkruimte-instellingen zijn gewijzigd.workspace.created: wordt gepubliceerd wanneer een werkruimte wordt aangemaakt. Omdat er geen endpoint kan bestaan in een werkruimte voordat die is aangemaakt, ontvang je deze normaal gesproken niet.member.invited: iemand is lid geworden van de werkruimte via rechtstreeks toevoegen, het accepteren van een uitnodiging of SCIM-provisioning.member.removed: een lid is handmatig of via SCIM verwijderd.audit.event: één gebeurtenis per auditlogboekitem.
Beschikbaar via de API of een SIEM-endpoint:
link.expired: een link heeft zijn vervaldatum bereikt.link.cap_reached: een link heeft zijn kliklimiet bereikt.databevatcapenclicks.link.broken: Elido's link-healthcheck heeft vastgesteld dat de bestemming niet werkt.databevatstatus_code,error_messageenfirst_seen_at.link.encrypted_createdenlink.encryption_rotated: voor versleutelde links.domain.verified: een aangepast domein heeft de DNS-controle doorstaan.domain.ssl_failed: de DNS-records van een aangepast domein waren 24 uur nadat het was toegevoegd nog steeds niet gevonden. Ondanks de naam gaat dit over DNS-verificatie, niet over certificaatvernieuwing.member.role_changed: de rol van een lid is gewijzigd.databevatold_role,new_roleenchanged_by_user_id.invitation.createdeninvitation.accepted.- Factureringgebeurtenissen:
subscription.*(bijvoorbeeldsubscription.activated,subscription.renewed,subscription.canceled) enusage.threshold_80,usage.threshold_100.
Gepland, vandaag nog niet verstuurd: link.clicked.aggregated (klikgegevens per link per venster), gebeurtenissen per klik, conversion.recorded, domain.tls_renewed, qr.generated en een aparte abuse.flagged-gebeurtenis. Een moderatiebesluit over een link komt momenteel binnen als link.updated met de nieuwe abuse_status.
Payloadstructuur
Elke ondertekende bezorging is een POST met Content-Type: application/json, User-Agent: Elido-Webhooks/1.0 en deze envelop:
{
"type": "link.created",
"workspace_id": 4123,
"timestamp": "2026-09-22T09:42:11Z",
"data": {
"id": 891234,
"workspace_id": 4123,
"domain_id": 12,
"slug": "spring-2026",
"destination_url": "https://acme.com/spring-sale",
"title": "Spring sale",
"tags": ["email"],
"status": "active",
"expires_at": null,
"created_by_user_id": 5821,
"created_at": "2026-09-22T09:42:11Z"
}
}
typeis het gebeurtenistype. Het staat ook in deX-Webhook-Event-header.timestampis het moment waarop de gebeurtenis plaatsvond, in UTC.datahangt af van de gebeurtenis. Voorlink.*-gebeurtenissen is het het volledige linkobject (het bovenstaande voorbeeld is ingekort). Voor andere gebeurtenissen is het een klein object met de ID's en velden die in de catalogus staan.- Geheimen verlaten Elido nooit: velden zoals de wachtwoordhash van een link of een uitnodigingstoken worden vóór bezorging uit
dataverwijderd.
Er is nog geen id-veld voor de gebeurtenis in de body; een ID per gebeurtenis is gepland. Gebruik voor idempotentie de X-Webhook-Delivery-header, die hieronder wordt beschreven.
Headers en signatures
Elke bezorging aan een Events- of SIEM-endpoint bevat:
| Header | Waarde |
|---|---|
X-Elido-Signature | v1= gevolgd door de hex HMAC-SHA256-signature |
X-Webhook-Signature | Dezelfde waarde als X-Elido-Signature, behouden voor oudere integraties |
X-Webhook-Timestamp | Unix-tijd in seconden waarop dit verzoek is ondertekend |
X-Webhook-Event | Het gebeurtenistype, bijvoorbeeld link.created |
X-Webhook-Delivery | De bezorgings-ID. Deze blijft gelijk bij retries van dezelfde bezorging |
X-Elido-Signature-Previous | Alleen tijdens geheimrotatie: dezelfde signature, gemaakt met de oude sleutel |
De signature wordt als volgt berekend:
v1=hex( HMAC-SHA256( secret, "{X-Webhook-Timestamp}.{raw request body}" ) )
De sleutel is je volledige geheim, inclusief het voorvoegsel whsec_, en wordt ongewijzigd gebruikt. Onderteken de onbewerkte bytes die je hebt ontvangen, niet een opnieuw geserialiseerd JSON-object, en vergelijk in constante tijd. Een kort Node-voorbeeld (Express):
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const SECRET = process.env.ELIDO_WEBHOOK_SECRET; // full whsec_... value
function same(a, b = "") {
const x = Buffer.from(a);
const y = Buffer.from(b);
return x.length === y.length && timingSafeEqual(x, y);
}
const app = express();
app.post(
"/webhooks/elido",
express.raw({ type: "application/json" }),
(req, res) => {
const ts = req.get("X-Webhook-Timestamp") ?? "";
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
return res.status(400).send("stale timestamp");
}
const expected =
"v1=" +
createHmac("sha256", SECRET)
.update(`${ts}.`)
.update(req.body)
.digest("hex");
const ok = [
req.get("X-Elido-Signature"),
req.get("X-Elido-Signature-Previous"),
].some((sig) => same(expected, sig));
if (!ok) return res.status(401).send("bad signature");
const event = JSON.parse(req.body.toString("utf8"));
// queue event for processing, then answer quickly
res.sendStatus(200);
},
);
Zie voor versies in Python en Go, een testvector en de veelgemaakte fouten hoe je webhooksignatures verifieert.
Het ondertekeningsgeheim roteren
Open het endpoint in Dashboard → Webhooks en klik op Rotate secret. Het nieuwe whsec_-geheim wordt één keer weergegeven en Elido ondertekent vanaf dat moment ermee.
De oude sleutel blijft geldig gedurende een respijtperiode van 7 dagen. Tijdens die periode bevat elke bezorging twee signatures: X-Elido-Signature met de nieuwe sleutel en X-Elido-Signature-Previous met de oude, zodat een ontvanger die nog niet is overgeschakeld kan blijven verifiëren. Accepteer beide headers terwijl je het nieuwe geheim uitrolt.
Endpoints die vóór 22 september 2026 zijn aangemaakt vormen een uitzondering: hun eerste rotatie levert geen X-Elido-Signature-Previous op, waardoor het oorspronkelijke geheim meteen niet meer werkt. Implementeer het nieuwe geheim direct na die eerste rotatie op je server. Endpoints die sindsdien zijn aangemaakt, ondertekenen vanaf de eerste rotatie met beide geheimen. Vanaf de tweede rotatie geldt de overlap van 7 dagen.
Retries
Een bezorging mislukt bij elke niet-2xx-respons, een netwerkfout of wanneer er binnen 10 seconden geen antwoord komt. Elke bezorging krijgt in totaal 3 pogingen:
- De eerste poging, enkele seconden na de gebeurtenis.
- Een retry ongeveer 5 minuten na de eerste mislukking.
- Een laatste retry ongeveer 15 minuten na de tweede mislukking.
Als de derde poging mislukt, wordt de bezorging gemarkeerd als failed en niet automatisch opnieuw geprobeerd. Het endpoint blijft actief en nieuwe gebeurtenissen blijven worden bezorgd. Je endpoint kan dezelfde bezorging meer dan één keer zien (bijvoorbeeld wanneer het een verzoek verwerkte maar pas na de timeout van 10 seconden antwoordde), dus dedupliceer op X-Webhook-Delivery.
Bezorgingslogboek
Open een endpoint in Dashboard → Webhooks om de 100 meest recente bezorgingen te bekijken. Voor elke bezorging zie je het gebeurtenistype, de status (pending, delivered of failed), de HTTP-status die je server retourneerde, de responstijd, het aantal gebruikte pogingen van de 3, wanneer de bezorging is aangemaakt en voltooid, het tijdstip van de volgende retry voor bezorgingen met status pending, de laatste fout en de volledige payload die is verstuurd.
Elke delivered- of failed-bezorging heeft een Retry-knop. Hiermee wordt dezelfde bezorging opnieuw in de wachtrij gezet (met dezelfde X-Webhook-Delivery-ID en een nieuwe signature) met een nieuw budget van 3 pogingen. Via de API doorloopt GET /v1/workspaces/{workspace_id}/webhooks/{id}/deliveries oudere bezorgingen met limit (maximaal 100) en offset, en doet POST .../deliveries/{delivery_id}/retry hetzelfde als de knop. Als je een bezorging niet zelf kunt replayen, kan Elido Support dat voor je doen.
Het vastleggen van de responsebody van je server in het logboek is gepland.
Lokaal testen
Een CLI-opdracht die webhooks naar localhost doorstuurt (npx @elido/cli webhooks forward) is gepland. Stel tot die tijd je lokale server beschikbaar via een tunnel zoals ngrok of Cloudflare Tunnel, voeg de HTTPS-URL van de tunnel toe als endpoint en verwijder die wanneer je klaar bent.
Limieten
- 10 seconden om op je endpoint te reageren.
- 3 bezorgingspogingen per gebeurtenis, plus handmatige retries.
- De API van het bezorgingslogboek retourneert maximaal 100 bezorgingen per pagina.
Probleemoplossing
Signature komt nooit overeen. De gebruikelijke oorzaken zijn: je framework heeft de JSON geparsed en opnieuw geserialiseerd voordat je die hashtte (hash de onbewerkte body), je hebt het voorvoegsel whsec_ van de sleutel verwijderd of je ondertekent met de timestamp uit de body in plaats van met de X-Webhook-Timestamp-header. De post over signatureverificatie bevat een testvector om je code mee te controleren.
Signature kwam na een rotatie niet meer overeen. Je server gebruikt nog het oude geheim. Implementeer het nieuwe geheim. Bij een endpoint dat vóór 22 september 2026 is aangemaakt, is er bij de eerste rotatie geen X-Elido-Signature-Previous om op terug te vallen.
Sommige gebeurtenissen komen twee keer aan. Elido heeft het opnieuw geprobeerd omdat je endpoint met een fout antwoordde of langer dan 10 seconden nodig had. Dedupliceer op X-Webhook-Delivery en antwoord met 2xx voordat je langzaam werk uitvoert.
Een gebeurtenistype komt nooit aan. Controleer of het endpoint erop is geabonneerd, niet is gepauzeerd en of het type in de bovenstaande catalogus staat. Types met de markering gepland worden nog niet verstuurd.