Wakato Pass
Space MCP tools in the Wakato Pass scope (pass). Reach these with a wks_live_ key over space-mcp or space-api.
Tools in the pass scope, callable over space-mcp (tools/call) or space-api (REST) with a wks_live_* key that grants pass.
adjust_pass_balance
Scope: pass:write
Add or deduct on a customer's card. balance: 'points' (points or stamps, the default) or 'gift_value' (money — load it with a positive delta, spend with a negative one). Target by install_id, or by template_id + contact_email/contact_id. Never below zero. Lands in the pass ledger like every other change; a points change fires 'balance reaches' automations.
| Parameter | Type | Required | Description |
|---|---|---|---|
install_id | string | ||
template_id | string | ||
contact_email | string | ||
contact_id | string | ||
delta | number | yes | |
balance | points | gift_value | Which balance (default points — points or stamps). | |
reason | string |
cancel_pass_entry
Scope: pass:write
Take back an entry a holder has (entry_id from list_pass_holders).
| Parameter | Type | Required | Description |
|---|---|---|---|
entry_id | string | yes |
create_pass_template
Scope: pass:write
Create a Wakato Pass — ONE card a customer adds to their account via the returned /card/<code> link (or is given with grant_pass_card, or by an automation's enroll_in_pass step). Say what it does — any combination, changeable later with update_pass_template: earn (points or stamps), membership, gift_value, next_booking — or nothing yet (a name and a description is a pass; what it unlocks can come after). Membership tiers each have their own length in config.tier_days {tier: days, 0 = never ends}; perks every holder has are config.perks [{kind:'discount', name, style:'percent'|'amount', value, applies_to} | {kind:'content', name, body}]; buttons every holder can tap (on the card page and in Apple/Google Wallet, ≤6) are config.buttons [{kind:'url'|'booking'|'listing'|'phone'|'whatsapp'|'email', label, value}] — value = the https address, a booking page id, a listing id, a phone number with country code or an email; an entry with on_join:true is given to every new holder. A salon with points, a membership and gift value is one card, not three. A pass needs no community. Earn rules: earn_per_scan awards on a staff scan at the till (a door never earns); for anything else (e.g. +10 points per booking) create an automation with the adjust_pass_balance action. Starts as draft — set status:'active' (or update later) to make it installable.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | e.g. "Bella's Coffee Club". |
approve_holders | boolean | A card someone signs up for waits until the business approves it (review_pass_holder). | |
min_age | number,null | Sign-up refused under this age, read from the birthday the form asks (ask for the birthday in join_ask). null = none. | |
max_holders | number,null | Sign-up closes once this many hold the pass. null = no limit. | |
earn | points | stamps | none | What the card earns: points (a balance spent on rewards), stamps (toward one reward, e.g. 10 coffees), or none. Never both. | |
membership | boolean | A tier and a valid-until date, checked at the door (config: tiers[], default_tier, validity_days). | |
gift_value | boolean | Money on the card — its OWN balance, never mixed with points; loaded with adjust_pass_balance {balance:'gift_value'}, spent at the till by amount (config: currency). | |
next_booking | boolean | Show the holder's next appointment with a Book button (config: booking_page_id). | |
holder_mode | card_only | account | How people get the card: card_only (default for new passes — a name and an email or phone, no Wakato account, kept in their phone's Apple/Google Wallet; nothing mentions Wakato) or account (they sign in / sign up; the card lives in their Wakato Pass too). | |
headline | earn | membership | gift_value | Which one is the big number on the card when it does several. Defaults to earn, then membership, then gift value. | |
description | string | ||
config | object | Settings for what the card does: earn points {points_label, welcome_bonus, points_per_currency, multiplier_windows}; earn stamps {stamps_required}; membership {tiers[], default_tier, validity_days}; gift value {currency}; birthday {birthday_bonus}; points that expire {points_expire_days}; the phone's lock screen near the business {lock_screen_places: [{label, address, latitude, longitude}] — up to 10, Apple Wallet shows the card when the holder is near one}. | |
rewards | object[] | [{title, cost, method, kind?, amount?}] — method 'staff_scan' (redeemed at the counter) or 'self_code' (customer self-redeems); kind 'free' (default), 'money_off' (amount off the bill) or 'gift_value' (amount put on the card — needs gift_value). | |
join_ask | object | What sign-up asks: { photo, phone, email, birthday } each 'required' | 'optional' | 'off', plus photo_approval (a manager checks each photo before the card works). Name is always asked; email and phone can't both be off (when both are optional, one of them is needed). On update, send only what changes. A required photo makes a membership that can't be lent — staff see it big at every scan. | |
entries | object[] | What the card lets its holder INTO — replaces the whole list. Each: { key?, kind: 'appointment' (their next booking on a booking page: target_type 'booking_page', target_id) | 'event' (tickets) | 'classes' (a pack that counts down) | 'pass' (day/week pass) | 'place' (a room, locker, parking) | 'other', name, types?: [{name, people}] (e.g. General, VIP, Table of 4), people (per entry), uses (visits; null = unlimited, 10 = a pack of ten), reentry: 'none'|'same_day'|'always', valid: {mode:'none'|'dates'|'days'|'hours', from, to, days, open:'HH:MM', close:'HH:MM'} }. Give a holder an entry with give_pass_entry; staff let people in at an Entry place. | |
brand | object | { business_name, bg_color, text_color, logo_url } for the card face. business_name is what the holder sees as the name of the business — set it whenever the space is not named after the business, or the card prints the space name. | |
earn_per_scan | number | Points/stamps per staff scan at the till (default 1 when the card earns, 0 otherwise). | |
scan_cooldown_minutes | number | Minimum minutes between scans of the same card (default 1). | |
join_fields | object[] | Questions asked before someone gets the card (name and email already come from their Wakato account). [{key, label, type, required, options?, maps_to?}] — type: text|textarea|email|tel|number|date|url|select|radio|checkboxes|checkbox; options[] for the choice types; maps_to writes the answer onto the contact (phone|company|location|website|line_of_work|birthday — a date question mapped to birthday powers the birthday bonus), otherwise it lands in the contact's custom fields. A required question parks a granted card as 'pending' until the holder answers. | |
allow_new_contacts | boolean | false = invite-only (installer must already be a contact). | |
status | draft | active |
get_pass_card_design
Scope: pass:read
How a pass's card looks in Apple and Google Wallet: its design (colours, typed name or logo, the banner's layers, the scan code, what the front says), a link to the banner as it looks now, the designs you can start from, and how every part works. Read it before changing the look.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes |
get_pass_template
Scope: pass:read
Get one pass template with its config, rewards, active holder count, and the public install link (/card/<code>) customers use to add the card.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes |
give_pass_entry
Scope: pass:write
Give a card holder an ENTRY: a ticket (type_key: one of the entry's types, e.g. vip or table_of_4), a class pack, a day pass, a place. entry_key names one of the pass's entries (get_pass_template shows them). Target by install_id, or template_id + contact_email/contact_id. people/uses/valid_from/valid_until override the entry's settings for this one.
| Parameter | Type | Required | Description |
|---|---|---|---|
install_id | string | ||
template_id | string | ||
contact_email | string | ||
contact_id | string | ||
entry_key | string | yes | |
type_key | string | ||
people | number | ||
uses | number,null | Visits; null = unlimited. | |
valid_from | string | ISO date-time. | |
valid_until | string | ISO date-time. |
grant_pass_card
Scope: pass:write
Give someone a card — the way to enroll a person who did not scan the sign-up code themselves. On a card_only pass (holder_mode) the card is made and its link sent by email, or by a text from the space's number when there is only a phone (billed like any text); they keep it in their phone's wallet, no account needed → outcome card_sent | card_sent_again (installed/already_held when that email is already a Wakato account). On an account pass it is an offer: a Wakato account gets it as PENDING to accept; anyone else is emailed a sign-up link → pending | installed | already_held | invited | invite_already_sent. The pass must be ACTIVE.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes | |
email | string | Who to give the card to (required on an account pass). | |
phone | string | Card-only passes: a phone with its country code (+1 646 555 0142), used when there is no email. | |
name | string | Optional — used when creating a contact for them. |
list_pass_holders
Scope: pass:read
List customers holding cards in this space (joined with their contact name/email), with balance (points or stamps), stored_value (gift value), tier, validity, and their answers to the pass's joining questions. Optional template_id filter.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | ||
limit | number | ||
offset | number |
list_pass_places
Scope: pass:read
The places this space scans Wakato Pass cards at (the front counter, a door, a festival gate) and, for each, what staff see (show, in order), the stop signs (red = don't let in, amber = look twice), what staff and managers may do (limits: points_max, gift_max, override, undo_minutes) and the briefing on the scanner.
list_pass_templates
Scope: pass:read
List the space's Wakato Pass templates — the cards customers add to their account (loyalty points, stamp cards, memberships, gift value, next booking; one card can do several). Each carries earn, has_membership, has_stored_value (gift value), has_booking and headline. Call before create_pass_template to reuse an existing one.
| Parameter | Type | Required | Description |
|---|---|---|---|
status | draft | active | archived | ||
limit | number |
review_pass_holder
Scope: pass:write
Approve or refuse a holder waiting on a pass that approves each one (approve_holders on; list_pass_holders shows approval 'waiting'). A waiting card doesn't work at the scan until approved.
| Parameter | Type | Required | Description |
|---|---|---|---|
install_id | string | yes | |
approve | boolean | yes |
review_pass_photo
Scope: pass:write
Approve or refuse the photo on a card, when the pass has photos checked before the card works (list_pass_holders shows photo_status 'pending'). Refusing needs a note — the holder reads it and sends a new one.
| Parameter | Type | Required | Description |
|---|---|---|---|
install_id | string | yes | |
approve | boolean | yes | |
note | string |
save_pass_place
Scope: pass:write
Create or change a place the space scans cards at (place_id to change one). { name, mode: 'till' (counter: points, rewards, gift value) | 'door' (membership check) | 'entry' (let in and count on an entry: entry_key names the pass's entry) | 'event' (a community event: event_id), show: [{key, on}] in order (photo, entry, tags, answers, visits, note, membership, points, gift, rewards, next_booking, birthday), stop_signs: [{key, on, age?}] (already_in, not_now, used_up, no_entry, membership_ended, frozen, photo_pending, under_age) plus your own [{key:'custom_1', on, custom:{level:'red'|'amber', title, when:{kind:'tag', tag} | {kind:'answer', question, equals?} | {kind:'gift_below', amount} | {kind:'points_above', amount}}}], limits: {staff:{override, points_max, gift_max, undo_minutes}, managers:{...}}, briefing, buttons: [{key, label, who:'everyone'|'managers', when:'always'|'stop'|'clear', ask}] — the owner's OWN buttons under every scan answer here (e.g. 'Ask for help'; up to 6; ask = a line staff type first, or null). What a button does is automations on the trigger pass_scan_button_pressed filtered by button_key; a button nothing listens to is not shown. Keep each existing button's key when you change it. }. Anything left out gets the place's defaults — except buttons, which stay as they are when left out.
| Parameter | Type | Required | Description |
|---|---|---|---|
place_id | string | ||
name | string | ||
mode | till | door | entry | event | ||
entry_key | string | ||
event_id | string | ||
template_ids | string[] | Only these passes are scanned here (empty = every pass of the space). | |
show | object[] | ||
stop_signs | object[] | ||
limits | object | ||
briefing | string | ||
buttons | object[] | The owner's own scanner buttons here (replaces the list when given). |
sell_pass_gift_card
Scope: pass:write
Sell the pass's gift value as a gift card of one amount (at least 5): makes a LIVE Commerce listing of the shop run from this space and the automation that, when it is paid, gives the buyer the card and puts the amount on it. stop_product_id takes one off sale instead. The pass needs gift_value on and the space a Commerce account.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes | |
amount | number | ||
stop_product_id | string |
sell_pass_unlock
Scope: pass:write
Sell a membership tier or an entry (a ticket, a class pack, a day pass…) straight from a pass: makes a LIVE Commerce listing of the shop run from this space and the automations that give it to the buyer — the card too if they don't hold it. unlock = {kind:'tier', tier} (one of config.tiers) or {kind:'entry', entry_key, type_key?} (one of the pass's entries). billing one_time | monthly | yearly (monthly/yearly only for a membership: renewals extend it, the subscription ending ends it; on_fail 'now' also ends it the moment a payment fails, 'period' (default) lets the paid time run out). stop_product_id takes one off sale instead. What is on sale is in the pass's config.sales.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes | |
unlock | object | ||
price | number | ||
billing | one_time | monthly | yearly | ||
on_fail | period | now | ||
stop_product_id | string |
set_pass_card_design
Scope: pass:write
Design how a pass's card looks in Apple and Google Wallet: start from one of Wakato's designs (start_from: wellness, coffee, membership, gift, ticket, salon, neon, barber, bakery, yoga, florist, carwash, winebar, playcentre, cowork, surf, pets, books, blank — the business's name and initials go on it), then change what the business wants: colours, the typed name or an uploaded logo, a background photo (photo_url; null removes it), the banner's layers (shapes, patterns, stamps, words, pictures), the scan code (wallet's own, branded in the business's colours, or none), and what the front says (top, big, under, appointmentFirst). Each part sent is merged over the current design; send design to replace it whole. Every card already in a wallet redraws. Answers with banner_url — look at it to check your work. get_pass_card_design explains every part.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes | |
start_from | string | A design key from get_pass_card_design's designs_to_start_from. | |
style | card | ticket | ticket = Apple's ticket shape (a notch at the top, a shorter banner). | |
colors | object | { bg, fg, label } as #hex: the card, its words, its small labels. | |
logo | object | { kind: 'word'|'image', text, sub, font, size, spacing, italic, mark, url } — the typed name (font: playfair|jost|cormorant|bebas|dmserif|inter) or an uploaded logo (https url). | |
photo_url | string,null | An https link to a photo that fills the banner (the shop, the food, the place). null removes it. | |
layers | object[] | Replaces the banner's layers (first is the background, type fill). See get_pass_card_design how_it_works. | |
code | object | { mode: 'wallet'|'branded'|'none', dot: 'round'|'soft'|'square', dotColor (dark), eyeColor, tile (light), mark, x, y, size }. Switching to branded places it where there is room. | |
front | object | { top, big, under: [a, b, c], appointmentFirst } — front fact keys: earn, name, name_tier, membership, gift, next_reward, next_visit, entry, perk, card_number, expires. | |
design | object | A whole design, replacing the current one. |
set_pass_note
Scope: pass:write
Pin the team's note on a card holder (staff see it at the scan, e.g. “Prefers the window table”). An empty note removes it.
| Parameter | Type | Required | Description |
|---|---|---|---|
install_id | string | yes | |
note | string | yes |
start_pass_grant_automation
Scope: pass:write
Grant something on a pass when something happens: makes ONE automation of the space, switched OFF, with the trigger and the steps filled in (Give Pass Card, then what it gives). unlock = {kind:'pass'} (the card itself), {kind:'tier', tier}, {kind:'entry', entry_key, type_key?}, {kind:'points'} or {kind:'gift_value'}. trigger_type one of booking_submission, form_submission, community_member_joined, commerce_order_paid, tag_added, pass_balance_reached, pass_scanned. Finish the trigger's settings (which form, which tag…) with the automation tools, then switch it on.
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes | |
unlock | object | yes | |
trigger_type | string | yes |
update_pass_template
Scope: pass:write
Edit a pass: what it does (earn points/stamps/none, membership, gift_value, next_booking, headline — send only what changes; switching points<->stamps is refused while anyone holds a balance, and gift_value can't be switched off while anyone holds money; switching anything else off hides nothing people hold for good), rules (earn_per_scan, scan_cooldown_minutes, config), rewards (replaces the whole array), join_fields (replaces the whole array), brand, description, or status (draft/active/archived — archived cards stop scanning).
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | yes | |
name | string | ||
earn | points | stamps | none | What the card earns: points (a balance spent on rewards), stamps (toward one reward, e.g. 10 coffees), or none. Never both. | |
membership | boolean | A tier and a valid-until date, checked at the door (config: tiers[], default_tier, validity_days). | |
gift_value | boolean | Money on the card — its OWN balance, never mixed with points; loaded with adjust_pass_balance {balance:'gift_value'}, spent at the till by amount (config: currency). | |
next_booking | boolean | Show the holder's next appointment with a Book button (config: booking_page_id). | |
holder_mode | card_only | account | How people get the card: card_only (default for new passes — a name and an email or phone, no Wakato account, kept in their phone's Apple/Google Wallet; nothing mentions Wakato) or account (they sign in / sign up; the card lives in their Wakato Pass too). | |
headline | earn | membership | gift_value | Which one is the big number on the card when it does several. Defaults to earn, then membership, then gift value. | |
description | string | ||
config | object | Merged into the current settings: send only the keys that change, a key as null to clear it. Keys as in create_pass (incl. lock_screen_places). | |
rewards | object[] | Replaces the whole array. Each: { id?, title, cost, method: 'staff_scan'|'self_code', description?, kind?: 'free' (default — something handed over) | 'money_off' (amount taken off the bill) | 'gift_value' (amount put on the card's gift balance — needs gift_value on), amount? }. description is what the HOLDER reads when they open the reward — where to go, what to ask for, an address, anything they need to actually use it. | |
join_ask | object | What sign-up asks: { photo, phone, email, birthday } each 'required' | 'optional' | 'off', plus photo_approval (a manager checks each photo before the card works). Name is always asked; email and phone can't both be off (when both are optional, one of them is needed). On update, send only what changes. A required photo makes a membership that can't be lent — staff see it big at every scan. | |
entries | object[] | What the card lets its holder INTO — replaces the whole list. Each: { key?, kind: 'appointment' (their next booking on a booking page: target_type 'booking_page', target_id) | 'event' (tickets) | 'classes' (a pack that counts down) | 'pass' (day/week pass) | 'place' (a room, locker, parking) | 'other', name, types?: [{name, people}] (e.g. General, VIP, Table of 4), people (per entry), uses (visits; null = unlimited, 10 = a pack of ten), reentry: 'none'|'same_day'|'always', valid: {mode:'none'|'dates'|'days'|'hours', from, to, days, open:'HH:MM', close:'HH:MM'} }. Give a holder an entry with give_pass_entry; staff let people in at an Entry place. | |
brand | object | { business_name, bg_color, text_color, logo_url }. Merged into what is there: send only what changes. The card's full look (template, banner, photo, branded code, what the front says) is set with set_pass_card_design. | |
earn_per_scan | number | ||
scan_cooldown_minutes | number | ||
allow_new_contacts | boolean | ||
approve_holders | boolean | A card someone signs up for waits until the business approves it (review_pass_holder). | |
min_age | number,null | Sign-up refused under this age, read from the birthday the form asks (ask for the birthday in join_ask). null = none. | |
max_holders | number,null | Sign-up closes once this many hold the pass. null = no limit. | |
join_fields | object[] | Questions asked before someone gets the card (name and email already come from their Wakato account). [{key, label, type, required, options?, maps_to?}] — type: text|textarea|email|tel|number|date|url|select|radio|checkboxes|checkbox; options[] for the choice types; maps_to writes the answer onto the contact (phone|company|location|website|line_of_work|birthday — a date question mapped to birthday powers the birthday bonus), otherwise it lands in the contact's custom fields. A required question parks a granted card as 'pending' until the holder answers. | |
status | draft | active | archived |