Quickstart
By the end of this page you will have minted a real pass, opened it on your phone, and seen the corresponding pass.installed webhook arrive. Total time: about five minutes.
Before you start
- Create a free QairoPay account at app.qairopay.com/signup. The Free Trial tier covers 500 passes for 30 days — no card required.
- Open the dashboard and navigate to Developers → API keys. Copy your sandbox secret key. It starts with
qp_sk_sandbox_.
Step 1 — Authenticate
Set your sandbox key as an environment variable and confirm the API can see you:
export QAIROPAY_KEY="qp_sk_sandbox_..."
curl https://api.sandbox.qairopay.com/v1/me \ -H "Authorization: Bearer $QAIROPAY_KEY"import { QairoPay } from "@qairopay/sdk";
const qp = new QairoPay({ apiKey: process.env.QAIROPAY_KEY! });
const me = await qp.me.retrieve();console.log(me.tenant.name);from qairopay import QairoPay
qp = QairoPay(api_key=os.environ["QAIROPAY_KEY"])
me = qp.me.retrieve()print(me.tenant.name)import "github.com/qairopay/qairopay-go"
client := qairopay.NewClient(os.Getenv("QAIROPAY_KEY"))
me, err := client.Me.Retrieve(ctx)if err != nil { log.Fatal(err) }fmt.Println(me.Tenant.Name)require "qairopay"
QairoPay.api_key = ENV["QAIROPAY_KEY"]
me = QairoPay::Me.retrieveputs me.tenant.nameuse QairoPay\Client;
$qp = new Client(getenv("QAIROPAY_KEY"));
$me = $qp->me->retrieve();echo $me->tenant->name;Click to run the call against your sandbox tenant. Your key is stored only in this browser.
You should see your tenant name come back. If you see a 401, double-check the key prefix; if you see 403, your account is missing the developer role (ask an admin in your workspace).
Step 2 — Create a pass template
A template is the visual and behavioral mold for the passes you’ll issue. You define it once and re-use it forever.
curl https://api.sandbox.qairopay.com/v1/pass_templates \ -H "Authorization: Bearer $QAIROPAY_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "Hello Pass", "kind": "loyalty", "brand": { "background_color": "#1F7A5A", "foreground_color": "#FCFAF6" }, "fields": [ { "key": "tier", "label": "Tier", "value": "Gold" } ] }'// The SDK auto-attaches an Idempotency-Key on every write call (UUID v4).// Pass { idempotencyKey: "..." } explicitly only if you want to control it.const template = await qp.passTemplates.create({ name: "Hello Pass", kind: "loyalty", brand: { background_color: "#1F7A5A", foreground_color: "#FCFAF6" }, fields: [{ key: "tier", label: "Tier", value: "Gold" }],});console.log(template.id); // tpl_...import uuid
template = qp.pass_templates.create( { "name": "Hello Pass", "kind": "loyalty", "brand": { "background_color": "#1F7A5A", "foreground_color": "#FCFAF6", }, "fields": [{"key": "tier", "label": "Tier", "value": "Gold"}], }, idempotency_key=str(uuid.uuid4()),)print(template.id)template, err := client.PassTemplates.Create(ctx, &qairopay.PassTemplateCreateParams{ Name: "Hello Pass", Kind: "loyalty", Brand: &qairopay.Brand{ BackgroundColor: "#1F7A5A", ForegroundColor: "#FCFAF6", }, Fields: []qairopay.PassField{ {Key: "tier", Label: "Tier", Value: "Gold"}, }, IdempotencyKey: uuid.NewString(),})template = QairoPay::PassTemplate.create( { name: "Hello Pass", kind: "loyalty", brand: { background_color: "#1F7A5A", foreground_color: "#FCFAF6" }, fields: [{ key: "tier", label: "Tier", value: "Gold" }], }, idempotency_key: SecureRandom.uuid,)puts template.id$template = $qp->passTemplates->create([ "name" => "Hello Pass", "kind" => "loyalty", "brand" => [ "background_color" => "#1F7A5A", "foreground_color" => "#FCFAF6", ], "fields" => [ ["key" => "tier", "label" => "Tier", "value" => "Gold"], ],], ["idempotency_key" => bin2hex(random_bytes(16))]);echo $template->id;The response contains a template id starting with tpl_. Save it — you’ll use it next.
Step 3 — Issue a pass
curl https://api.sandbox.qairopay.com/v1/passes \ -H "Authorization: Bearer $QAIROPAY_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "template_id": "tpl_XXX", "holder": { "email": "[email protected]", "name": "Jane Doe" }, "fields": { "tier": "Platinum" } }'The response gives you a pass.id (starts with pass_) and two download URLs:
{ "id": "pass_01HZX...", "template_id": "tpl_01HZX...", "status": "issued", "download": { "apple_url": "https://passes.sandbox.qairopay.com/.../apple.pkpass", "google_url": "https://pay.google.com/gp/v/save/eyJ0eXA..." }}Open the appropriate URL on your phone:
- iOS Safari → opens Apple Wallet directly.
- Android Chrome → opens Google Wallet directly.
The pass appears with your jade-green background and “Tier: Platinum” field.
Step 4 — Watch for the webhook
When you install the pass, QairoPay sends a pass.installed event to your registered webhook URL. To watch events without a public endpoint, use the dashboard’s Webhook inspector under Developers → Webhooks.
You can also forward sandbox events to your local machine with the CLI:
npx qairopay-cli listen --forward-to http://localhost:3000/webhooksYou’ll see a JSON event arrive within a couple of seconds of installing:
{ "id": "evt_01HZX...", "type": "pass.installed", "data": { "pass": { "id": "pass_01HZX...", "platform": "apple" } }}Where to next
If something didn’t work, the error response will tell you exactly what — but Errors covers the shape, the codes, and how to recover.