Authentication
Every request carries an API key as a bearer token. Create keys from Settings → Developers in your dashboard; the full key is shown once, on creation, and stored only as a hash.
curl https://wivly.me/api/v1/passes \
-H "Authorization: Bearer wivly_..." \
-H "Content-Type: application/json" \
-d '{"templateId":"tpl_123","customer":{"name":"Marco"}}'Key scopes
Every key carries exactly one scope. Pick the narrowest that does the job.
FULLISSUE_ONLYREAD_ONLYRate limits
60 requests per minute per key. Over that, the API returns 429 and the request is not processed — retry after the next minute boundary rather than immediately.
Endpoints
All paths are relative to https://wivly.me/api/v1 and return JSON.
/passesIssue a pass
Scopes: FULL, ISSUE_ONLY
/passes/{serial}Read pass state
Scopes: Any
/passes/{serial}Stamp or redeem
Scopes: FULL
/passes/{serial}/notifySend a lock-screen message
Scopes: FULL
/eventsExport analytics events
Scopes: Any
/analytics/venuesCross-venue rollups
Scopes: Any
/webhooksList webhooks
Scopes: Any
/webhooksRegister a webhook
Scopes: FULL
/webhooks/{id}Delete a webhook
Scopes: FULL
Webhooks
Register an endpoint and Wivly posts to it when something happens. Deliveries are retried on failure.
Events
pass.addedpass.removedpass.scannedreward.unlockedreward.redeemedprepaid.soldprepaid.debitedprepaid.low_balanceprepaid.spentVerifying a delivery
Every request carries an x-wivly-signature header: HMAC-SHA256 of the raw request body, keyed with your webhook secret. Compare it against the raw bytes before parsing, and use a constant-time comparison.
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const received = req.headers["x-wivly-signature"];
const ok =
received.length === expected.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(received));Idempotency
Each delivery has a stable id in the body, alongside apiVersion "2026-01-01". A retry reuses the id, so store it and ignore ids you have already handled.
Errors
Errors return a JSON body with an error message, and a code where one is useful.
401403404429OpenAPI
The machine-readable spec, for Postman, Swagger, Stoplight or code generation. Public and unauthenticated: it describes the surface, not any data.
Open the OpenAPI spec →