Wallet and passesTransit passes
Wallet and passes
Transit passes
Issue transit passes and record rides.
A transit pass records rides. Each ride is recorded once.
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 transit passes
/transit-passes - POSTIssue a transit pass
/transit-passes - GETRead one transit pass with its recent history
/transit-passes/{id} - POSTRecord a ride on a transit pass
/transit-passes/{id}/actions - POSTGive back one ride of a transit pass
/transit-passes/{id}/reversals - GETWhether the transit pass was handed to a wallet
/transit-passes/{id}/wallet-pass - GETThe transit pass's Apple Wallet file
/transit-passes/{id}/wallet-pass/apple - GETThe transit pass's Google Wallet save link
/transit-passes/{id}/wallet-pass/google
List transit passes
/transit-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/transit-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": "63990aad-e203-4025-97f6-8eee0dee97ad",
"success": true
}Issue a transit pass
/transit-passesScopepasses:write
Requires passes:write. Issues one transit pass from a Transit design for a trip (departsAt, Dhaka time; tripType oneWay or roundTrip). 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
designIduuidRequiredexternalCustomerIdstringdepartsAtstringRequired- The trip's local date and time (Dhaka), YYYY-MM-DDTHH:mm.
tripTypeenumRequired- One-way allows one ride, a round trip two. One of
oneWay,roundTrip. 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/transit-passes" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-H "Idempotency-Key: 5f0c2a8e-7b1d-4c3e-9a6f-0d4b8e6f99a2" \
-H "Content-Type: application/json" \
-d '{
"departsAt": "2026-11-03T08:41:12.000Z",
"designId": "b05ff791-cd85-4fe1-923f-7e3f7e9fdd25",
"sourceEventId": "order-1001",
"tripType": "oneWay"
}'{
"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": "4b5bad71-55dc-4378-8b35-55ef20e57ea9",
"success": true
}Read one transit pass with its recent history
/transit-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/transit-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": "7097f2ad-3011-4c1b-a78d-bb1aa69a56cc",
"success": true
}Record a ride on a transit pass
/transit-passes/{id}/actionsScopepasses:write
Requires passes:write. ride once per sourceEventId, through the counter's own rules. A one-way pass allows one ride and a round trip two; the next is 409 NO_RIDES_LEFT (never naming staff). Voiding a ride at the counter gives it back. 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
- 409
NO_RIDES_LEFT - 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/transit-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/actions" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-H "Idempotency-Key: 5f0c2a8e-7b1d-4c3e-9a6f-0d4b8e390a76" \
-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": "5ed745c0-3b1b-48ee-bc8c-96bc4dffa8cf",
"success": true
}Give back one ride of a transit pass
/transit-passes/{id}/reversalsScopepasses:write
Requires passes:write (whole-business keys) and an Idempotency-Key. Undoes the ride 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/transit-passes/e01b010d-2c65-48e1-83a0-acad79f8d3b4/reversals" \
-H "Authorization: Bearer $DEVELOPER_API_KEY" \
-H "Idempotency-Key: 5f0c2a8e-7b1d-4c3e-9a6f-0d4b8edee30b" \
-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": "91814ea1-75c3-4fb3-9860-3b07c1e2ead0",
"success": true
}Whether the transit pass was handed to a wallet
/transit-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/transit-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": "414b8b43-07fc-4add-84ca-00f0a0bffed5",
"success": true
}The transit pass's Apple Wallet file
/transit-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/transit-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 transit pass's Google Wallet save link
/transit-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/transit-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": "8b76805a-86e4-4d2c-88a3-fe709180cc2a",
"success": true
}