Wallet and passesGeneric passes
Wallet and passes
Generic passes
Issue reusable passes and record visits.
A generic pass is a reusable pass from a Generic design. Each visit is recorded once per sourceEventId.
Reads never return a PIN, a full number or a pass link. Each issue and action carries your own sourceEventId, so it happens once.
Paths are relative to https://app.revisit.cards/v1/developer.
Endpoints
- GETList generic passs
/generic-passes - POSTIssue a generic pass
/generic-passes - GETRead one generic pass with its recent history
/generic-passes/{id} - POSTRecord a visit on a generic pass
/generic-passes/{id}/actions - POSTGive back one visit of a generic pass
/generic-passes/{id}/reversals - GETWhether the generic pass was handed to a wallet
/generic-passes/{id}/wallet-pass - GETThe generic pass's Apple Wallet file
/generic-passes/{id}/wallet-pass/apple - GETThe generic pass's Google Wallet save link
/generic-passes/{id}/wallet-pass/google
List generic passs
/generic-passesScopepasses:read
Requires passes:read. Newest first. Never returns a whole number, issue values, a name or a link. A key limited to programs or branches is 403.
Query
designIduuidexternalCustomerIdstringserialstring- The whole number (spaces and dashes ignored).
limitinteger- 1 to 100. Default
25. cursorstring- The nextCursor of the previous page.
Returns 200
itemsobject[]Requireditems[].idstringRequireditems[].familyenumRequired- One of
offer,generic,eventTicket,boardingPass,transit. items[].designIdstringRequireditems[].designNamestringRequireditems[].maskedSerialstringRequired•••• 2345.items[].reusablebooleanRequireditems[].statusenumRequired- One of
active,used. items[].tripTypeenumRequired- Transit passes only: one-way allows one ride, a round trip two. One of
oneWay,roundTrip. items[].usesintegerRequireditems[].usesAllowedinteger or nullRequired- One for an offer, ticket or boarding pass; one or two rides for transit; null for a reusable pass.
items[].validFromstring or nullRequired- The design's window (Dhaka time). Shown by the wallets; not enforced.
items[].validUntilstring or nullRequireditems[].startsAtstring or nullRequired- Event ticket start, boarding pass departure or transit trip time (Dhaka time, YYYY-MM-DDTHH:mm); null when the design has none.
items[].customerIdstring or nullRequireditems[].externalCustomerIdstring or nullRequireditems[].createdAtstringRequirednextCursorstring or nullRequired
curl "https://app.revisit.cards/v1/developer/generic-passes" \
-H "Authorization: Bearer $DEVELOPER_API_KEY"{
"data": {
"items": [
{
"createdAt": "2026-11-03T08:41:12.000Z",
"customerId": "eb83b9c0-5619-432d-b857-cef7fcf84edb",
"designId": "b05ff791-cd85-4fe1-923f-7e3f7e9fdd25",
"designName": "Mishti Ghor",
"externalCustomerId": "user-42",
"family": "offer",
"id": "d12cc02b-02fe-4a1f-83b0-5414e70fd99c",
"maskedSerial": "•••• 7310",
"reusable": false,
"startsAt": "2026-11-03T08:41:12.000Z",
"status": "active",
"tripType": "oneWay",
"uses": 3,
"usesAllowed": 20,
"validFrom": "2026-11-03T08:41:12.000Z",
"validUntil": "2026-11-03T08:41:12.000Z"
}
],
"nextCursor": "eyJvZmZzZXQiOjUwfQ"
},
"requestId": "c66bd2d6-6ee1-477e-bdce-7916130c5925",
"success": true
}Issue a generic pass
/generic-passesScopepasses:write
Requires passes:write. Issues one pass from a Generic design. sourceEventId is your id for the event: the pass exists once however often the request arrives (replacement keys included); other content under the same id is 409 SOURCE_EVENT_CONFLICT. 422 PASS_TYPE_MISMATCH for a design of another type.
Send an Idempotency-Key header. Safe to retry.
Body
designIduuidRequiredexternalCustomerIdstringholderNamestring- Shown on the pass and at the counter. Never returned.
sourceEventIdstringRequired
Returns 201
idstringRequiredfamilyenumRequired- One of
offer,generic,eventTicket,boardingPass,transit. designIdstringRequireddesignNamestringRequiredmaskedSerialstringRequired•••• 2345.reusablebooleanRequiredstatusenumRequired- One of
active,used. tripTypeenumRequired- Transit passes only: one-way allows one ride, a round trip two. One of
oneWay,roundTrip. usesintegerRequiredusesAllowedinteger or nullRequired- One for an offer, ticket or boarding pass; one or two rides for transit; null for a reusable pass.
validFromstring or nullRequired- The design's window (Dhaka time). Shown by the wallets; not enforced.
validUntilstring or nullRequiredstartsAtstring or nullRequired- Event ticket start, boarding pass departure or transit trip time (Dhaka time, YYYY-MM-DDTHH:mm); null when the design has none.
customerIdstring or nullRequiredexternalCustomerIdstring or nullRequiredcreatedAtstringRequiredduplicatebooleanRequiredsourceEventIdstringRequiredwarningsobject[]Requiredwarnings[].codestringRequiredWRONG_DAYorPAST_TIME. Never blocks.warnings[].messagestringRequired
Errors
- 409
SOURCE_EVENT_CONFLICTthis source event id was already applied with different content (another customer, program, action or amount). Nothing changed. - 422
PASS_TYPE_MISMATCH
curl -X POST "https://app.revisit.cards/v1/developer/generic-passes" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-H "Idempotency-Key: 5f0c2a8e-7b1d-4c3e-9a6f-0d4b8ec77a32" \
-H "Content-Type: application/json" \
-d '{
"designId": "b05ff791-cd85-4fe1-923f-7e3f7e9fdd25",
"sourceEventId": "order-1001"
}'{
"data": {
"createdAt": "2026-11-03T08:41:12.000Z",
"customerId": "eb83b9c0-5619-432d-b857-cef7fcf84edb",
"designId": "b05ff791-cd85-4fe1-923f-7e3f7e9fdd25",
"designName": "Mishti Ghor",
"duplicate": false,
"externalCustomerId": "user-42",
"family": "offer",
"id": "d12cc02b-02fe-4a1f-83b0-5414e70fd99c",
"maskedSerial": "•••• 7310",
"reusable": false,
"sourceEventId": "order-1001",
"startsAt": "2026-11-03T08:41:12.000Z",
"status": "active",
"tripType": "oneWay",
"uses": 3,
"usesAllowed": 20,
"validFrom": "2026-11-03T08:41:12.000Z",
"validUntil": "2026-11-03T08:41:12.000Z",
"warnings": [
{
"code": "WELCOME10",
"message": "Your 10th chai is on us this week."
}
]
},
"requestId": "ba2362a7-9fa9-42dc-a749-672b3374c630",
"success": true
}Read one generic pass with its recent history
/generic-passes/{id}Scopepasses:read
Requires passes:read. uses and status are what count now (a voided use is given back).
Path
iduuidRequired
Returns 200
idstringRequiredfamilyenumRequired- One of
offer,generic,eventTicket,boardingPass,transit. designIdstringRequireddesignNamestringRequiredmaskedSerialstringRequired•••• 2345.reusablebooleanRequiredstatusenumRequired- One of
active,used. tripTypeenumRequired- Transit passes only: one-way allows one ride, a round trip two. One of
oneWay,roundTrip. usesintegerRequiredusesAllowedinteger or nullRequired- One for an offer, ticket or boarding pass; one or two rides for transit; null for a reusable pass.
validFromstring or nullRequired- The design's window (Dhaka time). Shown by the wallets; not enforced.
validUntilstring or nullRequiredstartsAtstring or nullRequired- Event ticket start, boarding pass departure or transit trip time (Dhaka time, YYYY-MM-DDTHH:mm); null when the design has none.
customerIdstring or nullRequiredexternalCustomerIdstring or nullRequiredcreatedAtstringRequiredwarningsobject[]Required- Event tickets: what a check-in now would warn about.
warnings[].codestringRequiredWRONG_DAYorPAST_TIME. Never blocks.warnings[].messagestringRequiredhistoryobject[]Requiredhistory[].idstringRequiredhistory[].typeenumRequired- One of
redeem,visit,checkin,ride,void. history[].sourceenumRequired- One of
api,counter. history[].voidedbooleanRequiredhistory[].reversedEventIdstring or nullRequiredhistory[].createdAtstringRequired
curl "https://app.revisit.cards/v1/developer/generic-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4" \
-H "Authorization: Bearer $DEVELOPER_API_KEY"{
"data": {
"createdAt": "2026-11-03T08:41:12.000Z",
"customerId": "eb83b9c0-5619-432d-b857-cef7fcf84edb",
"designId": "b05ff791-cd85-4fe1-923f-7e3f7e9fdd25",
"designName": "Mishti Ghor",
"externalCustomerId": "user-42",
"family": "offer",
"history": [
{
"createdAt": "2026-11-03T08:41:12.000Z",
"id": "d12cc02b-02fe-4a1f-83b0-5414e70fd99c",
"reversedEventId": "70d7b7ae-0017-44ae-a9b7-ef2a373b1b5f",
"source": "api",
"type": "redeem",
"voided": false
}
],
"id": "d12cc02b-02fe-4a1f-83b0-5414e70fd99c",
"maskedSerial": "•••• 7310",
"reusable": false,
"startsAt": "2026-11-03T08:41:12.000Z",
"status": "active",
"tripType": "oneWay",
"uses": 3,
"usesAllowed": 20,
"validFrom": "2026-11-03T08:41:12.000Z",
"validUntil": "2026-11-03T08:41:12.000Z",
"warnings": [
{
"code": "WELCOME10",
"message": "Your 10th chai is on us this week."
}
]
},
"requestId": "09ccce3e-b638-4136-87cf-187fc067cdab",
"success": true
}Record a visit on a generic pass
/generic-passes/{id}/actionsScopepasses:write
Requires passes:write. visit once per sourceEventId, through the counter's own rules. A generic pass is reusable: every visit counts, none spends it. 422 UNSUPPORTED_ACTION for any other action or a status/uses/balance field. 400 BRANCH_REQUIRED when several branches are open and branchId is missing.
Send an Idempotency-Key header. Safe to retry.
Path
iduuidRequired
Body
actionenumRequiredredeemon an offer,visiton a generic pass,checkinon an event ticket or boarding pass,rideon a transit pass. One ofredeem,visit,checkin,ride.branchIduuidsourceEventIdstringRequired
Returns 200
warningsobject[]Required- Check-ins only: heads-ups that did not block the check-in.
warnings[].codestringRequiredWRONG_DAYorPAST_TIME. Never blocks.warnings[].messagestringRequiredactionenumRequired- One of
redeem,visit,checkin,ride. passIdstringRequiredeventIdstringRequiredusesintegerRequiredstatusenumRequired- One of
active,used. duplicatebooleanRequiredsourceEventIdstringRequired
Errors
- 422
UNSUPPORTED_ACTIONthe action is not one a service may take (top-up, spend, set or adjust a balance, void) or the program's card type does not take earning and redeeming from the API yet. Nothing changed. - 400
BRANCH_REQUIRED
curl -X POST "https://app.revisit.cards/v1/developer/generic-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/actions" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-H "Idempotency-Key: 5f0c2a8e-7b1d-4c3e-9a6f-0d4b8e90eb06" \
-H "Content-Type: application/json" \
-d '{
"action": "redeem",
"sourceEventId": "order-1001"
}'{
"data": {
"action": "redeem",
"duplicate": false,
"eventId": "4251c053-b8cd-48e3-885b-da303b8448cc",
"passId": "309915ad-c546-41d1-b9f6-7446b5e11be7",
"sourceEventId": "order-1001",
"status": "active",
"uses": 3,
"warnings": [
{
"code": "WELCOME10",
"message": "Your 10th chai is on us this week."
}
]
},
"requestId": "7c3a6457-9a77-422d-adc8-20f6b85cd5a7",
"success": true
}Give back one visit of a generic pass
/generic-passes/{id}/reversalsScopepasses:write
Requires passes:write (whole-business keys) and an Idempotency-Key. Undoes the visit you sent as sourceEventId, for the refund you identify with reversalId: the use is given back whole and once through the counter's own undo (a void row in the pass history, the pass is active again, one use fewer). There is no partial form: amount, quantity, currency or a state field is 422 UNSUPPORTED_ACTION. A use already undone, at the counter or by an earlier refund, is 409 REVERSAL_EXCEEDS_ORIGINAL. reversalId makes it happen once under any key (duplicate: true); the same id for other content is 409 REVERSAL_CONFLICT. An unknown use, or a pass of another family, is 404. Punchd never refunds the payment.
Send an Idempotency-Key header. Safe to retry.
Path
iduuidRequired
Body
reversalIdstringRequired- Your own id for this refund. It makes the reversal happen once, even under a new Idempotency-Key.
sourceEventIdstringRequired- The sourceEventId of the redeem, visit, check-in or ride being undone.
Returns 200
actionenumRequired- What was undone. One of
redeem,visit,checkin,ride. duplicatebooleanRequiredeventIdstringRequired- The pass history row of the undo.
passIdstringRequiredreversalIdstringRequiredsourceEventIdstringRequiredstate"complete"RequiredstatusenumRequired- One of
active,used. usesintegerRequired- Uses that count after the reversal.
Errors
- 422
UNSUPPORTED_ACTIONthe action is not one a service may take (top-up, spend, set or adjust a balance, void) or the program's card type does not take earning and redeeming from the API yet. Nothing changed. - 409
REVERSAL_EXCEEDS_ORIGINALa reversal would take back more of the earning than is left (it is already fully reversed, or the refund is larger than what remains). Nothing changed. - 409
REVERSAL_CONFLICTthis reversalId was already used for a different request (another earning, amount or customer). Nothing changed.
curl -X POST "https://app.revisit.cards/v1/developer/generic-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/reversals" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-H "Idempotency-Key: 5f0c2a8e-7b1d-4c3e-9a6f-0d4b8e3ca06e" \
-H "Content-Type: application/json" \
-d '{
"reversalId": "refund-1001",
"sourceEventId": "order-1001"
}'{
"data": {
"action": "redeem",
"duplicate": false,
"eventId": "4251c053-b8cd-48e3-885b-da303b8448cc",
"passId": "309915ad-c546-41d1-b9f6-7446b5e11be7",
"reversalId": "refund-1001",
"sourceEventId": "order-1001",
"state": "complete",
"status": "active",
"uses": 3
},
"requestId": "6efd6c16-069d-4e3e-9d4e-ec016246c485",
"success": true
}Whether the generic pass was handed to a wallet
/generic-passes/{id}/wallet-passScopepasses:read
Requires passes:read. Flags and times, never a link.
Path
iduuidRequired
Returns 200
appleobjectRequiredapple.addedAtstring or nullRequiredapple.availablebooleanRequiredgoogleobjectRequiredgoogle.addedAtstring or nullRequiredgoogle.availablebooleanRequiredenvironmentenumRequired- One of
live,test. passIdstringRequiredsimulatedbooleanRequired
curl "https://app.revisit.cards/v1/developer/generic-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/wallet-pass" \
-H "Authorization: Bearer $DEVELOPER_API_KEY"{
"data": {
"apple": {
"addedAt": "2026-11-03T08:41:12.000Z",
"available": false
},
"environment": "live",
"google": {
"addedAt": "2026-11-03T08:41:12.000Z",
"available": false
},
"passId": "309915ad-c546-41d1-b9f6-7446b5e11be7",
"simulated": false
},
"requestId": "2cb616a1-e63a-4b04-99ee-50e05873ee75",
"success": true
}The generic pass's Apple Wallet file
/generic-passes/{id}/wallet-pass/appleScopepasses:write
Requires passes:write. The .pkpass raw, no-store, never recorded. Test keys: 403 TEST_MODE_BLOCKED. 409 WALLET_NOT_CONFIGURED.
Path
iduuidRequired
Answers with a file: application/vnd.apple.pkpass.
Errors
- 403
TEST_MODE_BLOCKEDtest mode asked for a real provider (SMS, push, wallet, payment); nothing real is ever sent from a test workspace. - 409
WALLET_NOT_CONFIGUREDthe wallet asked for (Apple or Google) is not set up on this deployment. Nothing was created.
curl "https://app.revisit.cards/v1/developer/generic-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/wallet-pass/apple" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-o downloadHTTP/1.1 200
Content-Type: application/vnd.apple.pkpass
(file)The generic pass's Google Wallet save link
/generic-passes/{id}/wallet-pass/googleScopepasses:write
Requires passes:write. A bearer secret: give it to the customer, never log or store it. Not recorded anywhere.
Path
iduuidRequired
Returns 200
provider"google"RequiredsaveUrlstringRequired- A bearer secret.
curl "https://app.revisit.cards/v1/developer/generic-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/wallet-pass/google" \
-H "Authorization: Bearer $DEVELOPER_API_KEY"{
"data": {
"provider": "google",
"saveUrl": "https://pay.google.com/gp/v/save/eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJyZXZpc2l0In0"
},
"requestId": "0b951ce5-e4ec-4515-a518-053035e24141",
"success": true
}