Aller au contenu

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

TypeWhen
session.createdSession created
session.startedCharging started
session.stoppedCharging ended
session.settledSession settled, receipt created
session.failedPayment failed, expired or cancelled
payment.authorizedAmount reserved or package paid
payment.capturedFinal amount captured
payment.releasedReservation released
payment.refundedRefund executed
device.online, device.offline, device.errorCharge point state changed
terminal.online, terminal.offlineTerminal state changed
integration.errorConnection 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.

  1. Read the raw body before parsing it as JSON.
  2. Compute HMAC_SHA256(secret, t + "." + rawBody).
  3. Compare the result with v1 in constant time.
  4. Reject requests whose t differs 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.