Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.varmo.fi/llms.txt

Use this file to discover all available pages before exploring further.

Instead of polling the Status API on a schedule, you can register a webhook endpoint with Varmo to receive delivery status-change events as they happen. When a card’s status transitions — for example, from in_transit to out_for_delivery — Varmo sends an HTTP POST request to your endpoint with the full updated payload. This removes polling overhead from your infrastructure and ensures your application always reacts to status changes in real time.

Configure your webhook endpoint

Register your webhook URL through the Varmo dashboard under Settings → Webhooks. Your endpoint must meet the following requirements before Varmo will deliver events to it:
  • Accept POST requests with a Content-Type: application/json body
  • Return HTTP 200 within 5 seconds of receiving the request
  • Validate the X-Varmo-Signature header on every inbound request (see Validate webhook signatures)
When a status change occurs, Varmo sends a payload with this structure:
webhook payload
{
  "event": "status.updated",
  "timestamp": "2026-05-03T09:15:00Z",
  "data": {
    "id": "12b986fd-8f73-4a55-b1b1-1b3f203c7522",
    "status": "out_for_delivery",
    "prediction": {
      "delivery_window": { "min": "2026-05-03", "max": "2026-05-04" },
      "confidence_level": "High"
    },
    "ui_suggestion": {
      "locale": "en-US",
      "recommended_message": "Your card is out for delivery today.",
      "recommended_action": "None"
    }
  }
}
The data object matches the response shape of GET /v1/status/{id}, so you can use the same parsing logic for both polling and webhook-driven flows.

Validate webhook signatures

Every request Varmo sends to your endpoint includes an X-Varmo-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook secret. Validate this signature before processing any payload to confirm the request originated from Varmo.
Always validate X-Varmo-Signature before processing webhook payloads. Skipping this check exposes your endpoint to spoofed events that could trigger unintended activation flows or misleading status updates for your cardholders.
Retrieve your webhook secret from the Varmo dashboard under Settings → Webhooks. Then implement signature validation as follows:
const crypto = require("crypto");

function verifyVarmoSignature(rawBody, signatureHeader, webhookSecret) {
  const expected = crypto
    .createHmac("sha256", webhookSecret)
    .update(rawBody)
    .digest("hex");

  const received = signatureHeader ?? "";

  // Use timingSafeEqual to prevent timing attacks
  const expectedBuffer = Buffer.from(expected, "utf8");
  const receivedBuffer = Buffer.from(received, "utf8");

  if (expectedBuffer.length !== receivedBuffer.length) {
    return false;
  }

  return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
}

// Express example
app.post("/webhooks/varmo", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-varmo-signature"];
  const isValid = verifyVarmoSignature(req.body, signature, process.env.VARMO_WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body);
  handleVarmoEvent(event);

  res.sendStatus(200);
});
Always compute the HMAC over the raw request body bytes before any JSON parsing. Parsing the body first and re-serializing it may alter whitespace or key ordering, causing signature verification to fail even for legitimate requests.

Webhook event types

EventDescription
status.updatedFired whenever the card’s delivery status transitions to a new value (for example, dispatchedin_transit).
delivery.predictedFired when Varmo calculates a new or revised delivery window prediction for the dispatch.
delivery.completedFired when the card’s status reaches delivered, indicating the card has arrived at its destination.
delivery.exceptionFired when Varmo detects a delivery exception, such as a failed delivery attempt or a postal delay.
Subscribe only to the event types your application needs. Filtering at the subscription level reduces traffic to your endpoint and simplifies your event handler logic.

Retry behavior

If your endpoint returns a non-2xx status code or does not respond within 5 seconds, Varmo marks the delivery attempt as failed and schedules a retry using exponential backoff. Varmo retries up to 5 times over a 24-hour window before marking the event as permanently undeliverable. Retry schedule after the initial failure:
AttemptDelay after previous attempt
11 minute
25 minutes
330 minutes
42 hours
58 hours
To avoid processing the same event more than once, treat webhook delivery as at-least-once. Use data.id combined with event and timestamp as a deduplication key and check against a short-lived store (such as Redis) before updating your application state.