Errors and limits
The status codes the Walletap API answers with, the shape of an error, every error code, when to retry, and the limits on requests.
Every failed request answers with a 4xx or 5xx status and a JSON body that
says what went wrong. This page lists them all, with what to do about each.
The error body#
{
"error": "Missing permission: passes.create",
"code": "insufficient_scope"
}| Field | Present | Meaning |
|---|---|---|
error | Always | A sentence for a person. Log it; do not match on its wording |
code | On some errors | A stable identifier to branch on. See Error codes |
| Others | On some errors | Context for that error, such as reason, feature, limit or passCount |
A validation error on PATCH /pass also carries details, the list of every
problem found, and receivedData, the body as the API read it.
Status codes#
| Status | Means | What to do |
|---|---|---|
200 | Done | — |
201 | Created: a template, a file or an API key | — |
202 | Accepted: a badge change that takes effect a moment later | Read the result back shortly |
204 | Deleted. No body | — |
400 | The request is malformed or a value is invalid | Fix the field named in error |
401 | No key, or a key Walletap does not recognise | Check the key. See 401 |
402 | The account's plan or billing does not cover this | Change the plan; another key will not help. See 402 |
403 | The key is real but may not do this | Use a key with the right scope. See 403 |
404 | Nothing matches, or it belongs to another account | Check the ID |
409 | The request conflicts with the current state | See Error codes |
413 | The uploaded file is over 10 MiB | Send a smaller file |
500 | Something failed on Walletap's side | Retry with backoff |
502 | The wallet credential service behind access badges did not answer | Retry with backoff |
504 | An image URL did not answer within 10 seconds | Retry, or upload the file directly |
401 Unauthorized#
The request carried no key, or one that does not match an active key.
error | Cause |
|---|---|
API key required, Authentication required, API key is required | No key was sent |
Invalid API key | The key is mistyped, was rotated, or is disabled |
You are not authorized for this action | On the pass endpoints: the key is not recognised, or the pass belongs to another account |
unauthorized | On the access badge endpoints: no key was sent |
POST /pass, GET /pass and the notify and resend endpoints read the key only
from X-API-Key. A key sent there as Authorization: Bearer is not read, and the
request answers 401.
402 Upgrade required#
The account is authenticated and the key is allowed to make the request, but the
account's plan, trial or billing does not cover it. Every 402 carries
"code": "upgrade_required". Retrying, or using another key, gives the same
answer; the account owner has to change the plan or fix billing in
Billing.
{
"error": "Your free trial has ended.",
"code": "upgrade_required",
"reason": "trial_expired"
}On POST /pass, reason says why no more passes can be issued:
reason | Means |
|---|---|
free_cap | Every pass the free plan includes has been issued |
trial_cap | Every pass the trial includes has been issued |
trial_expired | The trial has ended |
subscription_inactive | The subscription is not active, for example after a failed payment |
active_customers_paused | New cards are paused: the account went over its monthly active-customer allowance two months running. Event tickets still issue |
Elsewhere, feature names what the plan does not include, and for an allowance
current and limit give the numbers:
feature | Answered by |
|---|---|
apiAccess | POST /api-keys: the plan has no API access |
templates | POST /templates, duplicate: the template allowance is used up |
templateType | POST /templates, duplicate, PATCH /templates/{id}: the plan does not include this type of card |
geofences | POST or PATCH /templates: more locations than the plan covers |
ticketCredits | POST /pass for an event ticket: no ticket credits are left |
See plans for what each plan includes.
403 Forbidden#
The key is real, but this request is beyond it.
error | Cause |
|---|---|
Missing permission: <scope> | The key's scopes do not include <scope>. On /pass and the notify, resend and template notification endpoints, code is insufficient_scope |
This API key is scoped and cannot perform owner actions | Managing API keys needs a Full access key. code is insufficient_scope |
Insufficient permissions to view templates | GET /templates with a key that has no template scope |
You don't have permission to access this resource… | The template, pass or file belongs to another account |
access badges not enabled | The account does not have access badges |
Access badges are created in the dashboard… | POST /templates with type: "access". code is access_badge_not_creatable_here |
To decide between 401 and 403: 401 means "wrong credential", 403 means
"right credential, not enough power". The exception is GET /pass,
GET /passes and GET /passes/search, which also answer 401 for a pass or
template that belongs to another account.
Error codes#
code | Status | Means |
|---|---|---|
upgrade_required | 402 | The plan or billing does not cover the request. See 402 |
insufficient_scope | 403 | A scoped key tried an owner action, or, on /pass and the notify, resend and template notification endpoints, the key lacks a scope |
access_badge_not_creatable_here | 403 | Access badges are designed in the dashboard |
template_has_passes | 409 | DELETE /templates/{id} on a template with issued passes. passCount says how many |
issuance_blocked | 500 | Passes cannot be issued on this account. Contact support |
no_registered_devices | 200 | POST /pass/{passId}/notify: no iPhone holds the pass, so nobody was alerted |
unchanged_message | 200 | POST /pass/{passId}/notify: the same message is already on the pass |
The access badge endpoints put their code in error itself:
error | Status | Means |
|---|---|---|
not_published | 409 | Invites need a published badge |
users_list_failed, lifecycle_failed, passes_list_failed, user_update_failed, user_delete_failed | 502 | The wallet credential service did not answer. message has the detail |
Retrying safely#
- Reads (
GET) are always safe to retry. - Field updates with
PATCH /passset values, so sending the same request twice leaves the same result. - Stamp operations are safe to retry when you send an
idempotencyKey. Walletap remembers the last 20 keys on each card. - Balance operations are not deduplicated: an
addsent twice adds twice. Read the pass before retrying, or state the new total with"operation": "set". - Issuing with
POST /passcreates a new pass, and delivers it, every time. If a request timed out, look the pass up withGET /passby itsexternalIdbefore you send it again.
For 500, 502 and 504, retry with exponential backoff: wait one second, then
two, then four.
Limits#
| What | Limit |
|---|---|
Passes in one POST /pass | 1 to 10 |
Templates per page of GET /templates | 15 by default, 100 at most |
| Stamp counts | 0 to 50 |
| Pass field values | Strings or numbers |
| Template and duplicate names | 1 to 200 characters |
Places in a template's locations | Up to 10, and no more than the plan covers |
| Image upload | 10 MiB; a URL is fetched for at most 10 seconds |
| Badge invite validity | 1 to 365 days, 30 by default |
| API key label | 60 characters, in the dashboard |
| Google Wallet notifications | Three per pass in 24 hours, set by Google |