eSimerge

Webhooks

eSimerge POSTs a signed payload to your endpoint whenever something relevant happens — no polling required.

Register an endpoint

From the partner portal → DevelopersWebhooks tab → New endpoint. Enter a public HTTPS URL that accepts POST. The signing secret is shown once — store it.

Available events

EventDescription
order.issued
Order accepted and eSIM provisioned successfully.
order.failed
Provisioning failed (insufficient balance, plan unavailable, etc.).
esim.activated
Customer installed and activated the eSIM on their device.
esim.expired
Plan validity has elapsed.
esim.usage.threshold
Usage crossed 50% / 80% / 100% of the plan's data.
order.topup.completed
A top-up (extra data / extra days) was successfully applied to an existing eSIM.
order.topup.failed
A top-up could not be applied. Payload includes code and reason.

Request shape

Every delivery includes:

POST /your/webhook/url
X-eSimerge-Signature: t=1748275200,v1=<hex_hmac_sha256>
X-eSimerge-Event: esim.activated
X-eSimerge-Delivery: dlv_AbCdEf...
Content-Type: application/json

{
  "id": "evt_AbCdEf...",
  "type": "esim.activated",
  "created_at": "2026-05-26T12:00:00Z",
  "data": {
    "esim_id": "esm_esim_...",
    "order_id": "esm_order_...",
    "iccid": "8988..."
  }
}

Verify the signature

Rebuild the string `${t}.${rawBody}`, compute HMAC-SHA256 with the signing secret, and compare it to v1 using a timing-safe compare. Reject the request if t is older than 5 minutes (replay protection).

import { createHmac, timingSafeEqual } from "crypto";

export function verifyeSimergeSignature(secret, header, rawBody) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const ts = parts.t;
  if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Retries

A delivery is considered successful when your endpoint returns 2xx within 60 seconds. On failure we retry the same delivery on this schedule:

  • +30 seconds
  • +60 seconds
  • +3 minutes
  • +5 minutes
  • +15 minutes

If the delivery still fails after those 5 retries, the endpoint is blocked for 1 minute — no new deliveries are sent during the block. After 5 consecutive blocks the endpoint is fully disabled and must be re-enabled from the API page in the partner dashboard (use the status switch on the endpoint row). Every attempt is recorded in the Delivery log with status code and latency.

Security tips

  • Always verify the signature — never trust X-eSimerge-Event alone.
  • Return 200 immediately and process in the background — don't slow down delivery.
  • Use the event id as an idempotency key to ignore duplicate deliveries.
  • Log X-eSimerge-Delivery to attach when contacting support.