Work with templates
List, read, create, change, duplicate and delete templates over the API, and upload the images they use.
Most templates are designed in the dashboard, where you can see the pass as you build it. The API does the same work from code — useful when you set up the same card for many locations, or keep a design under version control. Everything you change here shows in the dashboard designer, and the other way round.
Paid plansBefore you start#
- A key with the template scopes for what you do. Manage passes and
Read only keys can list and read templates; creating, changing and deleting
needs Full access or a key with
templates.create,templates.editortemplates.delete. See Scopes. - To design in the dashboard instead, see Create a template.
List templates#
curl "https://api.walletap.com/templates?limit=50" \
-H "X-API-Key: $WALLETAP_API_KEY"{
"object": "list",
"data": [
{ "object": "template", "id": "tmpl_123", "name": "Coffee Card", "type": "loyalty", … },
{ "object": "template", "id": "tmpl_789", "name": "Harbour Points", "type": "loyalty", … }
],
"has_more": false,
"next_cursor": null,
"total_count": 6,
"url": "/templates"
}Templates come newest first, 15 per page unless you set limit (up to 100).
While has_more is true, request the next page with
starting_after set to next_cursor.
Read a template#
curl https://api.walletap.com/templates/tmpl_123 \
-H "X-API-Key: $WALLETAP_API_KEY"| Field | What it is |
|---|---|
id, name, type | The template's identity and pass type |
pass | The design, as a JSON string: colours, images, barcode and fields |
enrollForm | The sign-up form, as a JSON string |
passPage | The pass page's design, as a JSON string |
created, updated | Epoch milliseconds |
Parse pass to read the design. Its header, rows and linkRows hold the
fields on the pass, and each field's id is the key you use for its value when
you issue a pass.
Create a template#
Only name and type are required. Walletap starts from its default design for
the type and applies whatever you send in pass on top.
curl -X POST https://api.walletap.com/templates \
-H "X-API-Key: $WALLETAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Northside Membership",
"type": "generic",
"pass": {
"backgroundColor": "#1B2A3A",
"logo": { "fileId": "file_0123456789abcdef0123456789abcdef" },
"header": { "id": "memberName", "label": "Member" },
"rows": [
[
{ "id": "plan", "label": "Plan" },
{},
{ "id": "validUntilDate", "label": "Valid until" }
]
],
"barcodeType": "qrcode"
}
}'The response is the new template, with status 201. Walletap then sets up its
Apple Wallet and Google Wallet identities in the background.
Types#
type | In the dashboard |
|---|---|
generic | Generic — membership and ID cards |
loyalty | Loyalty — points and stamp cards |
giftCard | Gift — carries a remaining balance |
eventTicket | Event — a date, venue and seat |
offer | Coupon / Offer — a headline discount |
Generic and loyalty templates start with a sign-up form asking for name, email and phone. See Sign-up page.
The design#
Common keys inside pass:
| Key | Notes |
|---|---|
backgroundColor | Hex colour. Text and label colours follow it automatically |
textColor, labelColor | Used when adjustTextColor is false |
logo, icon, hero, strip, thumbnail, background | Images. See Upload images |
header | The headline field |
rows | Front fields: a list of rows, each up to three fields. Use {} for an empty column |
linkRows | Fields on the back of the pass, one per line |
barcode | false hides the barcode. true by default |
barcodeType | qrcode, code128, pdf417 or azteccode; code39, codabar, ean13 and itf show on iPhones from iOS 27 and fall back to Code 128 before that |
useStampCollection, stampCount | Turn a loyalty card into a stamp card with this many stamps |
A field needs only an id and a label. When the id is one of the type's own
fields, such as accountName on a loyalty card, it is bound to that field; any
other id becomes a field of your own, filled from customFields when you issue
a pass. Leave out id and Walletap derives one from the label, so
"label": "Member number" gets the ID memberNumber.
See Fields and Barcodes and NFC for what each part does on the pass.
Stamp cards#
On a loyalty template, "useStampCollection": true with a stampCount makes a
stamp card. Walletap supplies a default stamp image and banner; to use your own,
upload them with the template_stamp and template_hero purposes and reference
both as stamp and hero.
Change a template#
Send only what changes:
curl -X PATCH https://api.walletap.com/templates/tmpl_123 \
-H "X-API-Key: $WALLETAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "pass": { "backgroundColor": "#3B2418" } }'Objects inside pass merge key by key, so the request above changes the colour
and nothing else. Lists — rows and linkRows — are replaced whole, so send the
complete list when you change one field in it. locations is replaced whole too,
and an empty list clears it.
Saving a template refreshes the Apple Wallet passes already issued from it. In Google Wallet, template-wide parts change straight away, and each pass's own parts, such as its colour, change the next time that pass is updated. The response is the updated template.
Duplicate a template#
curl -X POST https://api.walletap.com/templates/tmpl_123/duplicate \
-H "X-API-Key: $WALLETAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Coffee Card — Marina" }'The copy gets the source's design and settings under the new name (up to 200
characters), with its own copies of the images and its own wallet identities.
Members, passes and campaigns stay with the original. The response is the new
template, with status 201. This call needs both templates.create and
templates.duplicate.
Delete a template#
curl -X DELETE https://api.walletap.com/templates/tmpl_321 \
-H "X-API-Key: $WALLETAP_API_KEY"A template with no passes is deleted and the call answers 204. A template that
has issued passes answers 409 with "code": "template_has_passes" and the
number in passCount, and nothing is deleted.
Deleting a template that has issued passes breaks every one of them: every pass
is voided, so scanners refuse it, and it can no longer be updated or issued
again, though holders keep the card in their wallet. Only if that is what you
want, repeat the request with ?force=true. To stop issuing a card while keeping
its passes working, leave the template in place.
Upload images#
Upload an image, then reference it from a template's design by its file ID.
curl -X POST https://api.walletap.com/files \
-H "X-API-Key: $WALLETAP_API_KEY" \
-F "file=@northside-logo.png" \
-F "purpose=template_logo"Or let Walletap fetch it from a public URL:
curl -X POST https://api.walletap.com/files \
-H "X-API-Key: $WALLETAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://northside.example/logo.png", "purpose": "template_logo" }'{
"id": "file_0123456789abcdef0123456789abcdef",
"object": "file",
"url": "https://…/original.png",
"purpose": "template_logo",
"mime_type": "image/png",
"size": 48213,
"width": 1200,
"height": 375,
"variants": { "1x": "https://…", "2x": "https://…", "3x": "https://…" },
"template_id": null,
"source_url": null,
"created": 1760180400000
}Then use it in pass, in any image slot:
{ "pass": { "logo": { "fileId": "file_0123456789abcdef0123456789abcdef" } } }Walletap replaces the reference with the image's URLs when it saves the template.
Purposes#
With a purpose, Walletap resizes the image for that slot and returns each size
in variants. Without one, the original is stored as it is.
purpose | Slot | Size at 1x |
|---|---|---|
template_logo | logo | 160 × 50 |
template_icon | icon | 29 × 29 |
template_hero | hero | 1032 × 336, one size |
template_strip | strip | 320 × 123 |
template_thumbnail | thumbnail | 90 × 90 |
template_background | background | 180 × 220 |
template_stamp | stamp | 120 × 120 |
Send PNG, JPEG or WebP; SVG, AVIF, HEIC, GIF, TIFF and ICO are converted to PNG.
Files can be up to 10 MiB. A URL is fetched for at most 10 seconds, and only
from a public address. Add templateId to file the image under a template.
See Artwork for what each slot is and which pass types show it.
Logos on a solid background#
When you upload with template_logo and the logo sits on a solid background,
Walletap also makes a copy with the background removed. If the cut-out is clean,
the response is that transparent copy; the original is always kept. The
response's logo object lists the forms that exist, with their IDs.
To pick a form yourself, ask for it by the file's ID:
curl -X POST https://api.walletap.com/files/file_0123456789abcdef0123456789abcdef/logo \
-H "X-API-Key: $WALLETAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "treatment": "onecolor", "color": "#FFFFFF" }'treatment is original, transparent or onecolor, which also needs a
#RRGGBB color. The response is the file to put in pass.logo.
Read and delete files#
GET /files/{id} returns a file's details. DELETE /files/{id} deletes it and
answers 204. A template that still shows a deleted image loses it, so remove
the image from the design first.
If something goes wrong#
400 "Unsupported barcodeType…" — use one of the values in The design.
400 "Stamp collection (useStampCollection: true) requires both 'stamp' and
'hero' images…" — a stamp card needs both images. Upload them and reference
them, or leave both out on a loyalty template to get the defaults.
400 "Invalid file id", 404 "File … not found" or 403 "File … does
not belong to this account" — the fileId is mistyped, deleted, or from
another account.
402 with "code": "upgrade_required" — the account's plan does not cover
another template (feature: "templates"), this type of card
(feature: "templateType") or this many places in locations
(feature: "geofences").
403 with "code": "access_badge_not_creatable_here" — access badges are
created in the dashboard. See Access badges.
404 "Resource not found" — the template does not exist or belongs to
another account.
409 with "code": "template_has_passes" — see
Delete a template.
413 "File exceeds 10 MiB limit" — make the image smaller.
504 "Upstream timeout…" — the image URL did not answer within 10 seconds.
Upload the file instead.