Event list
The nine events sent by StampyWallet webhooks, when they are sent, and the exact content of their donnees field.
Every event has the same envelope (id, type, cree_le, commerce, see Webhooks). Only donnees changes; it is shown on its own below. Events are sent from the counter as well as from the API: a visit scanned or added through POST /v1/passages produces the same passage.cree.
| Event | Sent when |
|---|---|
client.cree | A customer joins the program (their card is created) |
carte.ajoutee | The customer saves their card in Apple Wallet or Google Wallet |
passage.cree | A visit is added: one or more stamps, or points |
passage.annule | A customer's last visit is cancelled |
recompense.atteinte | A visit crosses a tier: a reward is due (one per tier crossed) |
recompense.offerte | The reward is handed to the customer |
recompense.annulee | Handing over a reward is cancelled: it is due again |
carte_cadeau.vendue | A gift card is sold |
forfait.vendu | A prepaid package is sold |
Note : carte.ajoutee covers Apple Wallet and Google Wallet: donnees.carte.wallet is apple or google. On Apple, the event is sent for each new device (an iPhone then a watch produce two). Removing the card sends nothing.
client.cree
{
"client": {
"id": "3374a06e-9608-4376-91cb-1a3fb6721b04",
"prenom": "Camille",
"cree_le": "2026-10-22T05:52:45.664Z"
}
}telephone and email only appear if the customer gave them. prenom can be null.
carte.ajoutee
{
"client": { "id": "40de4b86-1d7a-4cc6-add3-b7a83fa43c04", "prenom": "Inès",
"telephone": "+33611223344", "email": "ines@example.com" },
"carte": { "wallet": "apple" }
}passage.cree
{
"client": { "id": "40de4b86-1d7a-4cc6-add3-b7a83fa43c04", "prenom": "Inès",
"telephone": "+33611223344", "email": "ines@example.com" },
"passage": {
"id": "226fdea1-ba59-49fb-93e6-60a14a765134",
"quantite": 1,
"montant": null,
"devise": "EUR",
"reference": "caisse-1",
"source": "scan",
"piste": "normal",
"tampons": 1,
"objectif": 2
}
}quantite: what the visit is worth (stamps, or points in points mode).montant: the receipt amount in cents if it was entered, otherwisenull.reference: the operation identifier sent by the caller (the API reference, or the counter's), otherwisenull.source:scan,manuel,api…piste:normal, orpremiumfor the second counter.tampons(orpointsin points mode) andobjectif: the counter after the visit. A visit that completes the card returns the next card's counter (often0); the reward comes in a separaterecompense.atteinte.
passage.annule
{
"client": { "id": "40de4b86-1d7a-4cc6-add3-b7a83fa43c04", "prenom": "Inès",
"telephone": "+33611223344", "email": "ines@example.com" },
"passage": {
"id": "d8f62d77-88a6-44ef-a0c3-2a7d5066b90a",
"quantite": 1,
"source": "scan",
"piste": "normal",
"tampons": 0,
"objectif": 2
},
"recompenses_retirees": 0,
"motif": "scanned twice"
}passage.id is the one from the original passage.cree, and the counter is the one after the cancellation. recompenses_retirees: the rewards this visit had unlocked and that disappear with it. motif: the reason entered at the counter, or null.
recompense.atteinte
{
"client": { "id": "40de4b86-1d7a-4cc6-add3-b7a83fa43c04", "prenom": "Inès",
"telephone": "+33611223344", "email": "ines@example.com" },
"recompense": {
"id": "504c8734-3bbd-4fe9-95f8-7a67c8239dab",
"libelle": "A free coffee",
"palier": 2,
"piste": "normal",
"expire_le": null
},
"passage_id": "8bfbdd5e-eff4-4a3b-94fe-1557ec6edce6"
}One event per tier crossed. expire_le is null if the business's rewards don't expire.
recompense.offerte
{
"client": { "id": "40de4b86-1d7a-4cc6-add3-b7a83fa43c04", "prenom": "Inès",
"telephone": "+33611223344", "email": "ines@example.com" },
"recompense": {
"id": "504c8734-3bbd-4fe9-95f8-7a67c8239dab",
"libelle": "A free coffee",
"piste": "normal",
"offerte_le": "2026-10-22T05:54:51.790Z"
}
}recompense.annulee
{
"client": { "id": "40de4b86-1d7a-4cc6-add3-b7a83fa43c04", "prenom": "Inès",
"telephone": "+33611223344", "email": "ines@example.com" },
"recompense": {
"id": "504c8734-3bbd-4fe9-95f8-7a67c8239dab",
"libelle": "A free coffee",
"piste": "normal"
},
"motif": "wrong card"
}carte_cadeau.vendue
{
"client": null,
"carte_cadeau": {
"id": "ac7e56a5-3c00-492f-971a-21ca7d675fd6",
"code": "EJ9Y-7FYN",
"sorte": "montant",
"produit": null,
"montant": 2500,
"devise": "EUR",
"pour": "Lucie",
"de": "Marc",
"canal": "comptoir",
"expire_le": "2027-04-22T05:54:51.879Z"
}
}sorte is montant or produit (then produit carries its name and montant its price). Here, 2500 cents: €25.
Warning : code is the gift card code, the one in its QR code: it is enough to redeem it at the counter. Treat it as a secret: don't write it in a shared spreadsheet or in logs.
forfait.vendu
{
"client": null,
"forfait": {
"id": "0ec29323-ed13-4e6b-9595-963601290eac",
"code": "XFF2-WTW9",
"nom": "10 coffees",
"unites": 10,
"prix": 3000,
"devise": "EUR",
"titulaire": "Paul",
"canal": "comptoir",
"expire_le": "2027-04-22T05:54:51.891Z"
}
}titulaire: the name written at the sale, or null. expire_le is null for a package with no expiry date. As with a gift card, keep code secret.
Note : personal data is limited to what the customer gave when signing up. Once received, it is your responsibility: only keep it as long as necessary.
Updated