Google Sheet Extension
PDF to Pass
Image to Pass
A loyalty pass is a Dynamic Pass with a progress counter — a stamp card ("buy 5, get 1 free") or a points counter. This endpoint creates the loyalty template, which behaves like a dynamic pass template plus a loyaltyProgressData block describing the counter.
Once the template exists, you manage members with the exact same Manage Users endpoints as dynamic passes: Add User, List Users, Update User and Delete User. The loyalty-specific part is just this template and the Progress Update endpoint.
On creation, one image per count value (0 … maxStampCount) is rendered server-side from your counter config and stored on the template — you do not send countBasedImageUrls. For large maximums this is batched and may take a little time to finish.
All API requests must include your API key in the request headers:
"apikey": "your-api-key-here"
The body is a pass template (same design fields as the Dynamic Pass template — see Create Template for the shared fields and {placeholder} syntax) with two loyalty additions: cardSubType and loyaltyProgressData.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | A descriptive name for the loyalty template. |
| cardSubType | string | Yes | Must be "dynamic_loyalty_stamp_card" to mark this template as a loyalty pass. |
| loyaltyProgressData | object | Yes | The counter configuration. See the breakdown below. |
| groupId | string | No | Existing user group to attach. If omitted, a group is auto-created (look it up via List User Groups). |
| ...templateData | object | No | Standard pass design fields ( cardTitle, header, subheader, hexBackgroundColor, textModulesData, barcodeValue, …) — identical to the Dynamic Pass template. Any string may contain {placeholder} keys filled per customer. |
This block is what makes the pass a loyalty card, and it comes in two shapes — pick one with loyaltyType and send the matching counter object:
stampCounter.textCounter.| Name | Type | Required | Description |
|---|---|---|---|
| loyaltyType | "STAMP" | "TEXT" | Yes | STAMP renders a row of stamp icons; TEXT renders a "current / max" text counter. |
| stampCounter | object | Conditional | Required when loyaltyType is STAMP. |
| maxStampCount | number | Yes | Stamps needed to complete the card (the reward threshold). |
| initialStampCount | number | No | Stamps a new member starts with. Defaults to 0. |
| stampIconName | string | Yes | Which icon fills each stamp slot. One of the built-in names in the Stamp icons table below (default coffeeIcon2). |
| customStampIcon | string | No | URL of your own hosted icon to use instead of a built-in one. When set, it overrides stampIconName. |
| stampBannerBgColor | string | Yes | Hex background colour of the stamp banner. |
| stampBannerIconBgColor | string | Yes | Hex background colour behind each stamp icon. |
| textCounter | object | Conditional | Required when loyaltyType is TEXT. |
| maxProgress | number | Yes | Target value that completes the card. |
| initialProgress | number | No | Starting value for a new member. Defaults to 0. |
| textCounterFormat | string | Yes | Display format, e.g. {current}/{max}. |
| textCounterFontSize | number | No | Font size of the counter text. |
| textColor | string | Yes | Hex colour of the counter text. |
| textBackgroundColor | string | Yes | Hex background colour behind the counter. |
| heroImageUrl | string | No | Optional background image for the counter banner. |
For STAMP cards, stampIconName chooses the icon drawn in each stamp slot. Use one of these built-in names:
coffeeIcon2 — Coffee cup (default)coffeeIcon3 — Coffee cup (alternate style)burgerIcon1 — Burgerdonut1 — DonuticeCream1 — Ice creampizza1 — Pizza slicesandwich1 — SandwichstampIconName (or customStampIcon).customStampIcon to a hosted image URL — it replaces the built-in stampIconName for every slot (except the gift reward).The two loyalty types take different bodies. Switch tabs to see a complete, copy-pasteable request for each:
curl -X POST 'https://app.addtowallet.co/api/v2/loyaltyPass/template' \
-H 'apikey: your-api-key-here' \
-H 'Content-Type: application/json' \
-d '{
"name": "Cafe Muse Loyalty Card",
"cardSubType": "dynamic_loyalty_stamp_card",
"groupId": "66b6005bb7bddce8f05a3392",
"cardTitle": "Cafe Muse",
"hexBackgroundColor": "#45290d",
"appleFontColor": "#ffffff",
"logoUrl": "https://s3.amazonaws.com/i.addtowallet.co/assets/cafe-muse-logo.png",
"barcodeType": "QR_CODE",
"barcodeValue": "{_customerId}",
"textModulesData": [
{
"id": "r1start",
"header": "Member",
"body": "{name}"
}
],
"header": "FREE COFFEE",
"subheader": "REWARD ON 5TH VISIT",
"loyaltyProgressData": {
"loyaltyType": "STAMP",
"stampCounter": {
"maxStampCount": 5,
"initialStampCount": 0,
"stampIconName": "coffeeIcon2",
"stampBannerBgColor": "#45290d",
"stampBannerIconBgColor": "#ffffff"
}
}
}'On success the API returns:
dynamicPassId when checking members in.{
"dynamicPassId": "686d0d92c2f0bc5cdf25c834",
"message": "Template created successfully"
}Add members to the template's group with Add User, then record visits with Progress Update and review history via the Activity Log.
A loyalty template is stored as a dynamic pass template, so you manage its lifecycle with the same template endpoints — there is no separate loyalty update/delete:
loyaltyProgressData block is included).cardSubType, the full loyaltyProgressData (including countBasedImageUrls). Omitting them turns the card back into a plain pass.maxStampCount, stampIconName or the banner colours won't re-render the artwork — create a new template for those changes.| Error Code | Description |
|---|---|
| 400 | Bad Request - Missing required fields or invalid loyaltyProgressData. |
| 401 | Unauthorized - Missing or invalid API key. |
| 429 | Too Many Requests - API rate limit exceeded. Please wait before making additional requests. |
| 500 | Internal Server Error - Template or counter-image generation failed. Please contact support. |