Passmint
TemplatesDocsPricingBlog
Log inGet started
Passmint

Apple and Google Wallet passes from one API. Built for people who ship.

Product
  • Pass Designer
  • Developers
  • Distribution
  • Analytics
  • Templates
  • Pricing
Developers
  • Documentation
  • API reference
  • Node SDK
  • Webhooks
Company
  • Changelog
  • About
  • Contact
  • Support
  • Security
  • Terms
  • Privacy
Social
  • GitHub
  • X

© 2026 Passmint.

Apple Wallet & Google Wallet, one API.

Getting started

  • Overview
  • Quickstart

API

  • REST API reference
    • Authentication
    • Passes
    • Templates
    • Events
    • Webhooks
    • Errors
  • Webhooks
    • Event types
    • Creating a webhook
    • Payload format
    • Verifying signatures
    • Retries
  • Node.js SDK
    • Configuration
    • Passes
    • Templates
    • Webhook signatures
    • Errors
    • Idempotency

Open source

  • Passmint Package
REST API · v1

API reference.

Base URL: https://api.passmint.com

01

Authentication

Pass your API key as a Bearer token in the Authorization header. Test keys use the pmk_test_ prefix and live keys use pmk_live_. The mode (test or live) is determined by the key prefix. Test keys are available on every plan; live keys require a paid plan.

Authorization: Bearer pmk_test_xxx
02

Idempotency

Send an Idempotency-Key header on POST and PATCH requests to safely retry without duplicating side effects. Replays within 24 hours return the cached response. Reusing a key with a different request body returns 409.

Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7
03

Response format

Single objects include an object field identifying the type. List endpoints wrap results in a standard envelope.

// Single object
{
  "object": "pass",
  "id": "pass_xxx",
  ...
}

// List
{
  "object": "list",
  "data": [...],
  "has_more": false
}
04

Errors

Errors return a consistent JSON envelope with a type, optional code, human-readable message, and the offending param when applicable.

{
  "error": {
    "type": "invalid_request_error",
    "code": null,
    "message": "template_id is required",
    "param": "template_id"
  }
}
TypeStatusDescription
authentication_error401Invalid or missing API key
invalid_request_error400Malformed or invalid request body
authorization_error403Your plan does not include this capability
not_found_error404Resource does not exist
billing_error402Plan limit or quota exceeded
rate_limit_error429Too many requests — retry shortly
idempotency_error409Idempotency key reused with different body
api_error500Internal server error
05

Passes

Passes are the core resource — each represents a single wallet pass issued from a template.

POST/v1/passes

Issue a pass

Create a new pass from a template. Supports idempotency.

template_idstringRequired. The template to issue from.
holder_emailstringEmail address of the pass holder.
holder_namestringFull name of the pass holder.
field_valuesRecord<string, string>Key-value pairs for template fields.
metadataRecord<string, unknown>Arbitrary metadata to attach to the pass.
curl https://api.passmint.com/v1/passes \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "template_id": "tmpl_xxx",
    "holder_email": "alice@example.com",
    "holder_name": "Alice Johnson",
    "field_values": { "seatNumber": "A12" },
    "metadata": { "order_id": "ord_123" }
  }'
{
  "object": "pass",
  "id": "pass_xxx",
  "short_id": "abc123",
  "template_id": "tmpl_xxx",
  "serial_number": "...",
  "mode": "test",
  "holder_email": "alice@example.com",
  "holder_name": "Alice Johnson",
  "field_values": { "seatNumber": "A12" },
  "voided": false,
  "voided_at": null,
  "metadata": { "order_id": "ord_123" },
  "created_via_api": true,
  "url": "https://passmint.com/p/abc123",
  "download_url": "https://passmint.com/p/abc123/download",
  "created_at": "2026-04-16T12:00:00Z"
}
GET/v1/passes

List passes

Returns a paginated list of passes.

template_idstringFilter by template.
holder_emailstringFilter by holder email.
limitintegerNumber of results (default 50, max 100).
curl "https://api.passmint.com/v1/passes?template_id=tmpl_xxx&limit=10" \
  -H "Authorization: Bearer pmk_test_xxx"
{
  "object": "list",
  "data": [{ "object": "pass", "id": "pass_xxx", ... }],
  "has_more": false
}
GET/v1/passes/:id

Retrieve a pass

Returns a single pass by ID.

curl https://api.passmint.com/v1/passes/pass_xxx \
  -H "Authorization: Bearer pmk_test_xxx"
PATCH/v1/passes/:id

Update a pass

Update the field values or metadata on a pass. At least one of field_values or metadata is required. Cannot update voided passes (returns 400). Live updates are pushed to installed passes automatically. Supports idempotency.

field_valuesRecord<string, string>Updated field values.
metadataRecord<string, unknown>Updated metadata.
curl -X PATCH https://api.passmint.com/v1/passes/pass_xxx \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "field_values": { "seatNumber": "B7" } }'
DELETE/v1/passes/:id

Void a pass

Voids a pass. The pass is not deleted — it is returned with voided: true and a voided_at timestamp.

curl -X DELETE https://api.passmint.com/v1/passes/pass_xxx \
  -H "Authorization: Bearer pmk_test_xxx"
{
  "object": "pass",
  "id": "pass_xxx",
  "voided": true,
  "voided_at": "2026-04-16T14:30:00Z",
  ...
}
POST/v1/passes/:id/download-links

Create an expiring download link

Mints a time-limited link to the pass page — the expiring alternative to the permanent url on the pass object. The link works exactly like the permanent one (Apple Wallet download, Google Wallet save, QR handoff) until expires_at, after which it stops resolving and the holder is told to request a fresh one. Mint a new link per delivery; links are independent and minting one never invalidates another.

Be clear-eyed about what expiry protects: the link itself — in an email, a support thread, a log line, a forwarded message. It does not time-limit the pass. A .pkpassdownloaded before expiry is a signed file on the holder's device, barcode included, and no link expiry can reach it. To control redemption, use redemption policies and the scanner — not download links.

To make expiring links the only way in — disabling the permanent URL entirely — set require_download_link on the template.

expires_inintegerLifetime in seconds. Default 3600 (one hour), minimum 60, maximum 604800 (7 days).
curl https://api.passmint.com/v1/passes/pass_xxx/download-links \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "expires_in": 3600 }'
{
  "object": "pass.download_link",
  "pass_id": "pass_xxx",
  "url": "https://passmint.com/p/dl/pdl_mF4jK...",
  "expires_in": 3600,
  "expires_at": "2026-04-16T13:00:00Z",
  "created_at": "2026-04-16T12:00:00Z"
}

Download link object

objectstringAlways "pass.download_link".
pass_idstringThe pass this link resolves to.
urlstringThe link to hand to your customer. Contains the one-time-visible token — Passmint stores only a hash, so this is the only place the URL ever appears.
expires_inintegerLifetime in seconds, as requested.
expires_atstringISO 8601 expiry timestamp.
created_atstringISO 8601 creation timestamp.

Pass object

idstringUnique identifier (pass_xxx).
objectstringAlways "pass".
short_idstringShort URL slug.
template_idstringParent template (tmpl_xxx).
serial_numberstringUnique serial number for the pass.
modestring"test" or "live".
holder_emailstring | nullEmail of the pass holder.
holder_namestring | nullName of the pass holder.
field_valuesobjectKey-value pairs for template fields.
voidedbooleanWhether the pass has been voided.
voided_atstring | nullISO 8601 timestamp of when the pass was voided.
metadataobjectArbitrary metadata attached to the pass.
created_via_apibooleanWhether the pass was created via the API.
urlstringPublic URL for the pass landing page.
download_urlstringDirect download URL for the .pkpass file.
created_atstringISO 8601 creation timestamp.
06

Templates

Templates define the design and structure of your passes. Every pass is issued from a template.

POST/v1/templates

Create a template

Create a new pass template. Supports idempotency.

namestringRequired. Display name for the template.
typestringRequired. One of: event, membership, coupon, loyalty, generic.
apple_stylestringRequired. One of: eventTicket, generic, storeCard, coupon, boardingPass.
designobjectRequired. Design configuration for the template.
require_download_linkbooleanDefault false. When true, the permanent pass URLs 404 and passes on this template are only reachable through expiring download links.
curl https://api.passmint.com/v1/templates \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "VIP Concert Pass",
    "type": "event",
    "apple_style": "eventTicket",
    "design": { ... }
  }'
{
  "object": "template",
  "id": "tmpl_xxx",
  "name": "VIP Concert Pass",
  "type": "event",
  "apple_style": "eventTicket",
  "design": { ... },
  "archived": false,
  "created_at": "2026-04-16T12:00:00Z",
  "updated_at": "2026-04-16T12:00:00Z"
}
GET/v1/templates

List templates

Returns a list of non-archived templates.

limitintegerNumber of results (default 50, max 100).
curl "https://api.passmint.com/v1/templates?limit=10" \
  -H "Authorization: Bearer pmk_test_xxx"
GET/v1/templates/:id

Retrieve a template

Returns a single template by ID, including archived templates.

curl https://api.passmint.com/v1/templates/tmpl_xxx \
  -H "Authorization: Bearer pmk_test_xxx"
PATCH/v1/templates/:id

Update a template

Update a template's name, design, or archived status. All fields are optional.

namestringUpdated display name.
designobjectUpdated design configuration.
archivedbooleanSet to true to archive the template.
require_download_linkbooleanRestrict distribution to expiring download links. Takes effect immediately for every existing pass on the template — permanent URLs already in the wild 404 while it is on.
curl -X PATCH https://api.passmint.com/v1/templates/tmpl_xxx \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Updated Template Name" }'
DELETE/v1/templates/:id

Archive a template

Archives a template. Existing passes remain valid but no new passes can be issued from an archived template.

curl -X DELETE https://api.passmint.com/v1/templates/tmpl_xxx \
  -H "Authorization: Bearer pmk_test_xxx"
{
  "id": "tmpl_xxx",
  "deleted": true
}

Template object

idstringUnique identifier (tmpl_xxx).
objectstringAlways "template".
namestringDisplay name.
typestringOne of: event, membership, coupon, loyalty, generic.
apple_stylestringOne of: eventTicket, generic, storeCard, coupon, boardingPass.
designobjectDesign configuration.
archivedbooleanWhether the template is archived.
require_download_linkbooleanWhether passes on this template are reachable only through expiring download links.
created_atstringISO 8601 creation timestamp.
updated_atstringISO 8601 last update timestamp.
07

Events

Events track the lifecycle of passes — from creation through installation and removal.

GET/v1/events

List recent events

Returns recent events across all passes.

limitintegerNumber of results (default 100, max 200).
curl "https://api.passmint.com/v1/events?limit=50" \
  -H "Authorization: Bearer pmk_test_xxx"
GET/v1/passes/:id/events

List events for a pass

Returns events for a specific pass.

limitintegerNumber of results (default 100, max 200).
curl "https://api.passmint.com/v1/passes/pass_xxx/events?limit=20" \
  -H "Authorization: Bearer pmk_test_xxx"
{
  "object": "list",
  "data": [
    {
      "object": "pass_event",
      "id": "psEvnt_xxx",
      "pass_id": "pass_xxx",
      "type": "installed",
      "metadata": {},
      "created_at": "2026-04-16T13:00:00Z"
    }
  ],
  "has_more": false
}

PassEvent object

idstringUnique identifier (psEvnt_xxx).
objectstringAlways "pass_event".
pass_idstringThe pass this event belongs to.
typestringEvent type (see below).
metadataobjectAdditional event data.
created_atstringISO 8601 timestamp.

Event types

TypeDescription
createdPass was created.
url_viewedPass landing page was viewed.
downloadedPass file was downloaded.
installedPass was added to a wallet.
updatedPass fields or metadata were updated.
removedPass was removed from a wallet.
voidedPass was voided.
08

Webhooks

Webhooks deliver real-time event notifications to your server. Passmint signs every payload so you can verify authenticity.

POST/v1/webhooks

Create a webhook

Register a new webhook endpoint. The response includes the secret field — this is the only time the secret is returned. Store it securely. Supports idempotency.

urlstringRequired. The URL to deliver events to.
eventsstring[]Required. Non-empty array of event types to subscribe to.
descriptionstringOptional description for the webhook.
enabledbooleanWhether the webhook is active (default true).

Valid event types: pass.issued, pass.add_intent, pass.added_to_wallet, pass.update_pushed, pass.update_delivered, pass.removed, pass.voided, pass.redeemed (fires when a pass is scanned and accepted at a scanner device), or * for all events.

curl https://api.passmint.com/v1/webhooks \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/passmint",
    "events": ["pass.issued", "pass.added_to_wallet"],
    "description": "Production webhook"
  }'
{
  "object": "webhook",
  "id": "whk_xxx",
  "url": "https://example.com/webhooks/passmint",
  "events": ["pass.issued", "pass.added_to_wallet"],
  "description": "Production webhook",
  "enabled": true,
  "secret": "whsec_xxx",
  "created_at": "2026-04-16T12:00:00Z"
}
GET/v1/webhooks

List webhooks

Returns all webhooks for your account.

curl https://api.passmint.com/v1/webhooks \
  -H "Authorization: Bearer pmk_test_xxx"
GET/v1/webhooks/:id

Retrieve a webhook

Returns a single webhook by ID. The secret field is not included in retrieval responses.

curl https://api.passmint.com/v1/webhooks/whk_xxx \
  -H "Authorization: Bearer pmk_test_xxx"
PATCH/v1/webhooks/:id

Update a webhook

Update a webhook's URL, subscribed events, description, or enabled status. All fields are optional.

urlstringUpdated delivery URL.
eventsstring[]Updated event subscriptions.
descriptionstringUpdated description.
enabledbooleanEnable or disable the webhook.
curl -X PATCH https://api.passmint.com/v1/webhooks/whk_xxx \
  -H "Authorization: Bearer pmk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "events": ["*"], "description": "All events" }'
DELETE/v1/webhooks/:id

Delete a webhook

Permanently deletes a webhook and its delivery history. This is a hard delete and cannot be undone.

curl -X DELETE https://api.passmint.com/v1/webhooks/whk_xxx \
  -H "Authorization: Bearer pmk_test_xxx"
{
  "id": "whk_xxx",
  "deleted": true
}
GET/v1/webhooks/:id/deliveries

List recent deliveries

Returns the last 100 delivery attempts for a webhook.

curl https://api.passmint.com/v1/webhooks/whk_xxx/deliveries \
  -H "Authorization: Bearer pmk_test_xxx"
{
  "object": "list",
  "data": [
    {
      "object": "webhook_delivery",
      "id": "whd_xxx",
      "webhook_id": "whk_xxx",
      "event_type": "pass.issued",
      "status": "delivered",
      "attempts": 1,
      "last_attempt_at": "2026-04-16T12:01:00Z",
      "next_attempt_at": null,
      "response_status": 200,
      "response_body": "ok",
      "created_at": "2026-04-16T12:00:00Z"
    }
  ],
  "has_more": false
}

Webhook object

idstringUnique identifier (whk_xxx).
objectstringAlways "webhook".
urlstringDelivery URL.
eventsstring[]Subscribed event types.
descriptionstring | nullOptional description.
enabledbooleanWhether the webhook is active.
created_atstringISO 8601 creation timestamp.

WebhookDelivery object

idstringUnique identifier (whd_xxx).
objectstringAlways "webhook_delivery".
webhook_idstringParent webhook (whk_xxx).
event_typestringThe event type that triggered delivery.
statusstringOne of: pending, delivered, failed, dead.
attemptsintegerNumber of delivery attempts.
last_attempt_atstring | nullISO 8601 timestamp of last attempt.
next_attempt_atstring | nullISO 8601 timestamp of next retry.
response_statusinteger | nullHTTP status from your server.
response_bodystring | nullResponse body from your server.
created_atstringISO 8601 creation timestamp.