WalletPassAPI Docs

Guides

Retries and idempotency

Send a request again safely when you don't know whether the first one worked.

Networks fail. A request can time out after we've made the pass but before the answer reaches you, and then you can't tell whether to send it again: once too few and a holder has no pass, once too many and they have two. An Idempotency-Key makes the retry safe.

Send a key with every write

Make a key for each thing you mean to do, such as order-1042 for the pass for order 1042, and send it in the Idempotency-Key header. Send the same key when you retry:

curl "https://api.walletpassapi.com/v1/passes" \
  -H "Authorization: Bearer $WALLET_API_KEY" \
  -H "Idempotency-Key: order-1042" \
  -H "Content-Type: application/json" \
  -d '{
    "organizationName": "Acme Coffee",
    "logoText": "Acme"
  }'
const res = await fetch("https://api.walletpassapi.com/v1/passes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WALLET_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "order-1042",
  },
  body: JSON.stringify({
    "organizationName": "Acme Coffee",
    "logoText": "Acme"
  }),
});
const data = await res.json();
import os

import requests

res = requests.post(
    "https://api.walletpassapi.com/v1/passes",
    headers={"Authorization": f"Bearer {os.environ['WALLET_API_KEY']}", "Idempotency-Key": "order-1042"},
    json={
        "organizationName": "Acme Coffee",
        "logoText": "Acme",
    },
)
data = res.json()
<?php

$ch = curl_init("https://api.walletpassapi.com/v1/passes");
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("WALLET_API_KEY"),
        "Content-Type: application/json",
        "Idempotency-Key: order-1042",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "organizationName" => "Acme Coffee",
        "logoText" => "Acme",
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);

The first request with a key does the work and keeps its answer. A request with the same key and the same body after it does nothing again: it gets that first answer back, with the same status. So the retry of a create returns the pass that was made, not a second one; no second operation is counted, and nothing is sent to phones or webhooks twice.

A key is up to 255 characters, and belongs to your workspace: another workspace's keys never clash with yours.

Which requests take one

RequestWithout a key, a repeat…
Create a passmakes a second pass (unless its externalId is taken)
Update a passchanges nothing the second time, so it's already safe
Send a notificationshows the message twice
Revoke a passanswers alreadyDeleted: true
Delete a revoked passanswers alreadyErased: true
Send a broadcastsends it again, once the first has finished
Upload an imagestores a second copy
Create a templateanswers template_slug_taken
Save, rename or delete a templatechanges nothing more, or answers not_found once it's deleted
Update a template's passesanswers template_update_in_progress or nothing_to_update

Reads never need one.

The same key for something else

A key belongs to one request. The same key with a different body, or on a different endpoint, is refused with 409 idempotency_conflict, and nothing is done: it's almost always a bug, such as a key made from something that isn't unique. Use a new key for each new thing.

How long keys are kept

A key's answer is kept for 7 days, far longer than any retry needs. After that, the same key is a new request.

When to retry

Retry when there was no answer at all (a timeout or a dropped connection), a 5xx, or a 429 with code rate_limited (after its Retry-After). Wait longer between each try, such as 1, 2, then 4 seconds. Other errors will fail the same way again: see Errors.