Intégration
Webhooks
Have events about sessions, payments and devices sent to your system, with signature verification.
Cette page n'est pas encore disponible dans ta langue. Tu vois la version anglaise.
Webhooks report events to an HTTPS URL of your choice, for example when charging starts or a session is settled. You don't have to poll the API.
Create an endpoint
curl -X POST https://api.ladehase.com/v1/webhook_endpoints \
-H "Authorization: Bearer lh_test_…" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.ch/webhooks/ladehase", "events": ["session.settled", "device.offline"] }'
The response contains the secret for signature verification. It is shown only in this response. Without events you receive all events (["*"]).
Events
| Type | When |
|---|---|
session.created | Session created |
session.started | Charging started |
session.stopped | Charging ended |
session.settled | Session settled, receipt created |
session.failed | Payment failed, expired or cancelled |
payment.authorized | Amount reserved or package paid |
payment.captured | Final amount captured |
payment.released | Reservation released |
payment.refunded | Refund executed |
device.online, device.offline, device.error | Charge point state changed |
terminal.online, terminal.offline | Terminal state changed |
integration.error | Connection to manufacturer or payment partner disrupted |
session.updated is only distributed live to the dashboard and not sent as a webhook, because it occurs very often during charging.
Structure
POST /webhooks/ladehase HTTP/1.1
Content-Type: application/json
User-Agent: Ladehase-Webhooks/1.0
Ladehase-Event: session.settled
Ladehase-Signature: t=1792958400,v1=5f2b9c…
{
"id": "evt_…",
"object": "event",
"type": "session.settled",
"livemode": true,
"created_at": "2026-10-25T08:30:00.000Z",
"data": { "id": "ses_…", "object": "session", "state": "settled", "captured_amount": 950 }
}
Verify the signature
The Ladehase-Signature header contains a timestamp t and the signature v1. v1 is an HMAC-SHA256 over t + . + the unmodified raw request body, keyed with the endpoint secret, hex encoded.
- Read the raw body before parsing it as JSON.
- Compute
HMAC_SHA256(secret, t + "." + rawBody). - Compare the result with
v1in constant time. - Reject requests whose
tdiffers from the current time by more than 5 minutes.
With the SDK it is one line:
import { verifyWebhook } from "@ladehase/sdk";
export async function POST(request: Request) {
const body = await request.text();
const ok = await verifyWebhook(body, request.headers.get("ladehase-signature") ?? "", process.env.LADEHASE_WEBHOOK_SECRET!);
if (!ok) return new Response("Invalid signature", { status: 400 });
const event = JSON.parse(body);
// handle event …
return new Response(null, { status: 204 });
}
Without the SDK, in Node.js:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(body: string, header: string, secret: string) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!parts.v1 || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest();
const given = Buffer.from(parts.v1, "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}
Don't trust webhook content blindly. For important decisions, read the session again with GET /v1/sessions/{id}.
Response and retries
Respond within 10 seconds with a 2xx status. Otherwise Ladehase retries after 1 minute, 5 minutes, 30 minutes, 2, 6, 12, 24 and another 24 hours, about 3 days in total. Events may arrive twice, so check the id and process each event only once.
See the delivery log with GET /v1/webhook_endpoints/{id}/deliveries. Resend single events with POST /v1/events/{id}/resend.
Une erreur ? Écris à developer@ladehase.com.