Online store — Passtastic API
Passtastic
No account yet? Sign up free

Online store

Enroll a shopper on checkout, award points when their order is paid, and keep their wallet pass in sync with refunds — four calls into your existing order flow.

Enroll at checkout or sign-up

Call this from your checkout's order-confirmation step, or your account sign-up flow. It is safe to call on every order — a returning shopper is matched by email rather than duplicated (see result/matchedBy below).

Send firstName/lastName, or a single name — we split it at the first space.

bash
curl -X POST https://api.passtastic.io/api/v1/customers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalCustomerId": "shop_customer_9001",
    "cardId": "64f1c2a9b8e4a2d1c0a1b2c3",
    "firstName": "Jamie",
    "lastName": "Cole",
    "email": "jamie@example.com"
  }'
json
{
  "passUserId": "66a1e2f3c9d4b5a6f7081920",
  "externalCustomerId": "shop_customer_9001",
  "cardId": "64f1c2a9b8e4a2d1c0a1b2c3",
  "result": "created",
  "matchedBy": null,
  "linked": true,
  "installUrl": "https://passtastic.io/get-pass/64f1c2a9b8e4a2d1c0a1b2c3?code=8K3F2Q",
  "status": "pending_install"
}

Put installUrl in the order-confirmation email — opening it on a phone adds the card to Apple or Google Wallet. Nothing to build on your side; the pass itself is served by Passtastic.

Let Passtastic email the card

Don't want to build that email yourself? Set sendCardEmail: true on the same enroll call and Passtastic sends "Your {business} card — add it to your wallet in one tap", with Apple Wallet and Google Wallet buttons, right after the customer is created.

It only fires on result: "created" — never on "matched". That is what makes it safe to leave wired into a flow that calls enroll on every order: a returning shopper is matched, not re-emailed, even though your integration enrolls them again on every purchase.

bash
curl -X POST https://api.passtastic.io/api/v1/customers \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalCustomerId": "shop_customer_9001",
    "cardId": "64f1c2a9b8e4a2d1c0a1b2c3",
    "firstName": "Jamie",
    "lastName": "Cole",
    "email": "jamie@example.com",
    "sendCardEmail": true
  }'
json
{
  "passUserId": "66a1e2f3c9d4b5a6f7081920",
  "externalCustomerId": "shop_customer_9001",
  "cardId": "64f1c2a9b8e4a2d1c0a1b2c3",
  "result": "created",
  "matchedBy": null,
  "linked": true,
  "installUrl": "https://passtastic.io/get-pass/64f1c2a9b8e4a2d1c0a1b2c3?code=8K3F2Q",
  "status": "pending_install",
  "cardEmail": "sent"
}

Send notifyEmail instead when the address to email differs from email — it is used only to send this one message, never stored on the customer and never used for matching, and it still carries the same unsubscribe link every card email does. Store-credit cards are supported; a Stripe-sold paid card is not, and comes back as cardEmailReason: "card_not_supported" instead of failing the enrollment. See CRM and customer data for the full cardEmail/cardEmailReason list, including "opted_out" and "recently_sent".

No-code tools (Zapier, Make, etc.): skip the separate "send an email" step after your enroll action — set sendCardEmail to true right on the enroll step itself. It also accepts the string "true"/"false" in any casing, since that's what these tools send instead of a real boolean — and it works the same with a test key, which sends a real email rather than a sandboxed one.

Earn when an order is paid

Trigger this from your payment-succeeded webhook, not from checkout submission — an order can still fail payment or be abandoned between the two. Use from: "spend" for the order total, and set Idempotency-Key to the order id so a retried payment webhook never double-awards the same order.

bash
curl -X POST https://api.passtastic.io/api/v1/customers/email:jamie@example.com/earn \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-58210" \
  -d '{
    "type": "points",
    "from": "spend",
    "spend": { "amount": 64.00, "currency": "EUR" },
    "note": "Order #58210"
  }'

Refunds

A refund is a negative balanceCorrection on adjust — use the order id for Idempotency-Key (a distinct key from the earn call above, since they're different writes) so a retried refund webhook can never withdraw twice. balanceCorrection must be non-zero — a partial refund is the actual points/stamps that order earned, negated, not the full order value.

bash
curl -X POST https://api.passtastic.io/api/v1/customers/email:jamie@example.com/adjust \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-58210-refund" \
  -d '{
    "balanceCorrection": -64,
    "note": "Refund for order #58210"
  }'

Show the balance in the account page

Read the same customer back to show their stamps, points, or level next to their order history.

bash
curl https://api.passtastic.io/api/v1/customers/email:jamie@example.com \
  -H "X-Api-Key: YOUR_API_KEY"
json
{
  "externalCustomerId": "shop_customer_9001",
  "passUserId": "66a1e2f3c9d4b5a6f7081920",
  "cardId": "64f1c2a9b8e4a2d1c0a1b2c3",
  "name": "Jamie Cole",
  "firstName": "Jamie",
  "lastName": "Cole",
  "cardType": "point_card",
  "balance": { "points": 245 },
  "installed": true,
  "installUrl": "https://passtastic.io/get-pass/64f1c2a9b8e4a2d1c0a1b2c3"
}