Passmint
TemplatesIntegrationsDocsPricingBlog
Log inGet started
Passmint

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

Product
  • Pass Designer
  • Developers
  • Distribution
  • Analytics
  • Templates
  • Integrations
  • 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, and 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.