The official Node.js SDK.
Typed, tested, ESM + CJS. Idempotency, retries, and webhook signature verification — all built in.
Install
npm install @passmint/nodeInitialize
import { Passmint } from "@passmint/node"
const passmint = new Passmint({
apiKey: process.env.PASSMINT_API_KEY!,
})Configuration
The constructor accepts several options to control SDK behaviour:
const passmint = new Passmint({
apiKey: process.env.PASSMINT_API_KEY!,
baseUrl: "https://api.passmint.com", // default
timeoutMs: 30_000, // default
maxRetries: 3, // default
})apiKey— required. Your secret API key.baseUrl— override the API host, useful for proxies or self-hosted instances.timeoutMs— per-request timeout in milliseconds.maxRetries— automatic retries on network errors and 5xx responses.
You can check the environment at runtime with passmint.mode, which returns "test" or "live" based on the key prefix.
Passes
// Create a pass
const pass = await passmint.passes.create({
templateId: "tmpl_xxx",
holderEmail: "alice@example.com",
fieldValues: { seatNumber: "A12" },
}, { idempotencyKey: "purchase_42" })
// Retrieve a single pass
const fetched = await passmint.passes.retrieve(pass.id)
// List passes with optional filters
const list = await passmint.passes.list({ limit: 100 })
// Update a pass — pushes live updates to installed passes
const updated = await passmint.passes.update(pass.id, {
fieldValues: { seatNumber: "B7" },
metadata: { upgraded: true },
})
// Void a pass
await passmint.passes.void(pass.id)
// Fetch pass events
const events = await passmint.passes.events(pass.id)update() accepts fieldValues and/or metadata — at least one is required. Voided passes cannot be updated.
Templates
// Create a template
const tpl = await passmint.templates.create({
name: "VIP Membership",
type: "membership",
appleStyle: "storeCard",
design: { /* your design config */ },
starterTemplateId: "starter_xxx", // optional
})
// Retrieve a template
const fetched = await passmint.templates.retrieve("tmpl_xxx")
// List all templates
const templates = await passmint.templates.list()
// Update a template
const updated = await passmint.templates.update("tmpl_xxx", {
name: "Premium Membership",
design: { /* updated design */ },
archived: false,
})
// Archive a template
const result = await passmint.templates.archive("tmpl_xxx")
// { id: "tmpl_xxx", deleted: true }The type parameter accepts "event", "membership", "coupon", "loyalty", or "generic".
The appleStyle parameter accepts "eventTicket", "generic", "storeCard", "coupon", or "boardingPass".
Webhook signatures
Verify webhook signatures with the SDK helper. Timing-safe compare and a tolerance window — Stripe-style.
const event = passmint.webhooks.constructEvent(
rawBody,
request.headers["passmint-signature"]!,
process.env.PASSMINT_WEBHOOK_SECRET!,
300, // tolerance in seconds (default: 300)
)payload— the raw request body as a string or Buffer.signature— the value of thepassmint-signatureheader.secret— your webhook signing secret.toleranceSeconds— optional. Defaults to 300 seconds (5 minutes). Rejects events with timestamps outside the window.
Throws PassmintError if the signature is invalid or the timestamp is outside the tolerance window.
Errors
The SDK throws typed errors so you can branch cleanly:
PassmintError— base class for all SDK errors.PassmintAPIError— non-2xx response. Includesstatus,type,code, andparamproperties.PassmintAuthError— thrown on 401/403 responses (bad, expired, or revoked API key).PassmintRateLimitError— includes aretryAfterSecondsproperty with the number of seconds to wait before retrying.
import {
PassmintError,
PassmintAPIError,
PassmintAuthError,
PassmintRateLimitError,
} from "@passmint/node"
try {
await passmint.passes.create({ /* ... */ })
} catch (err) {
if (err instanceof PassmintRateLimitError) {
console.log(`Retry after ${err.retryAfterSeconds}s`)
} else if (err instanceof PassmintAuthError) {
console.log("Check your API key")
} else if (err instanceof PassmintAPIError) {
console.log(err.status, err.code, err.param)
}
}Idempotency
Both create() and update()methods auto-generate an idempotency key if you don't provide one. You can pass your own for deterministic replay:
await passmint.passes.create(
{ templateId: "tmpl_xxx", holderEmail: "alice@example.com" },
{ idempotencyKey: "purchase_42" },
)
await passmint.passes.update(
"pass_xxx",
{ fieldValues: { seatNumber: "C3" } },
{ idempotencyKey: "upgrade_42" },
)- Keys are scoped per organization.
- Keys expire after 24 hours.
- Replayed requests return the original response without re-executing the operation.