Passes

Passes are a way to provide your users with a digital representation of a physical product. This can be a coupon, a storecard or a prepaidcard.


GET/passes

Inspect pass with email

Search for a pass using a members email address for a specific brand.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The token itself as a bearer token.

Abilities

  • Name
    passes.get
    Type
    string
    Description

    The scope required to access this endpoint.

Query parameters

  • Name
    email
    Type
    string|Required
    Description

    The email address of the member.

  • Name
    brand_id
    Type
    string|Required
    Description

    The UUID of the brand.

  • Name
    pass_code
    Type
    string|Required
    Description

    The code of the pass.

Request

GET
/passes
curl -X GET https://api.walletapp.co/passes 
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
    "brand_id": "<brand_id>",
    "email": "<email>", 
    "pass_code": "<pass_id||qr_code>"
}'

Response

{
    "success": true,
    "data": {
        "brand_id": "<brand_id>",
        "code": "<code>",
        "created_at": "<timestamp>",
        "updated_at": "<timestamp>",
        "demo": 0,
        "redeemed": 0,
        "active": 1,
        "expired": 0,
        "blocked": 0,
        "reminder_send": 0,
        "expiration_date": "<timestamp>",
        "start_date": null,
        "reminder_date": null,
        "parsed_historical": 0,
        "coupon": {
            "identifier": "<identifier>",
            "price": "<coupon shop price>",
            "points": "<coupon shop points>",
            "external_reference": "<reference>"
        }, 
        "storecard": {
            "identifier": "<identifier>",
            "points": "<points>",
            "balance": "<balance>",
        }
    }
}

GET/search/pass

Inspect passes

Search for a pass only using the pass code.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The token itself as a bearer token.

Abilities

  • Name
    search.pass
    Type
    string
    Description

    The scope required to access this endpoint.

Query parameters

  • Name
    pass_code
    Type
    string|Required
    Description

    The code of the pass.

Request

GET
/search/pass
curl -X GET https://api.walletapp.co/search/pass 
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
    "pass_code": "<pass_id||qr_code>"
}'

Response

{
    "success": true,
    "data": [
        {
            "created_at": "<timestamp>",
            "updated_at": "<timestamp>",
            "expiration_date": "<timestamp>",
            "downloaded": 1,
            "start_date": "<timestamp>",
            "batch_reference": null,
            "reminder_date": null,
            "download_lat": null,
            "user_name": "<username>",
            "member_id": "<member_id>",
            "download_long": null,
            "pass_code": "<pass_code>",
            "status": "active",
            "brand": {
                "name": "<name>",
                "identifier": "<UUID>"
            },
            "coupon": {
                "title": "<title>",
                "identifier": "<UUID>",
                "price": 0,
                "points": 0,
                "external_reference": "<external_reference>"
            }
        }
    ]
}

PUT/passes

Modify a pass

Modify the expiration date or the status of a pass.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The token itself as a bearer token.

Abilities

  • Name
    passes.get
    Type
    string
    Description

    The scope required to access this endpoint.

Query parameters

  • Name
    email
    Type
    string|Required
    Description

    The email address of the member.

  • Name
    brand_id
    Type
    string|Required
    Description

    The UUID of the brand.

  • Name
    pass_code
    Type
    string|Required
    Description

    The code of the pass.

  • Name
    expiration_date
    Type
    timestamp|Required
    Description

    The new expiration date of the pass.

  • Name
    status
    Type
    string|Required
    Description

    The new status of a pass. Can be ['active', 'expired', 'redeemed', 'blocked']

Request

PUT
/passes
curl -X PUT https://api.walletapp.co/passes 
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
    "brand_id": "<brand_id>",
    "email": "<email>", 
    "pass_code": "<pass_id||qr_code>",
    "expiration_date": "<timestamp>",
    "status": "<status>"
}'

Response

{
    "success": true,
    "message": "Pass updated successfully",
    "transaction_id": "<uuid>"
}

DELETE/passes

Remove a pass

Remove a pass from a member.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The token itself as a bearer token.

Abilities

  • Name
    passes.get
    Type
    string
    Description

    The scope required to access this endpoint.

Query parameters

  • Name
    email
    Type
    string|Required
    Description

    The email address of the member.

  • Name
    brand_id
    Type
    string|Required
    Description

    The UUID of the brand.

  • Name
    pass_code
    Type
    string|Required
    Description

    The code of the pass.

Request

DELETE
/passes
curl -X DELETE https://api.walletapp.co/passes 
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
    "brand_id": "<brand_id>",
    "email": "<email>", 
    "pass_code": "<pass_id||qr_code>"
}'

Response

{
    "success": true,
    "message": "Pass removed"
}

POST/passes

Email a pass

Send a specific coupon pass to a member.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The token itself as a bearer token.

Abilities

  • Name
    passes.get
    Type
    string
    Description

    The scope required to access this endpoint.

Body parameters

  • Name
    email
    Type
    string|Required
    Description

    The email address of the member.

  • Name
    brand_id
    Type
    string|Required
    Description

    The UUID of the brand.

  • Name
    coupon_id
    Type
    string|Required
    Description

    The id of the coupon to send.

  • Name
    status
    Type
    string|Required
    Description

    The status of the pass. Can be: ['active', 'blocked', 'expired', 'redeemed']

  • Name
    pass_title
    Type
    string
    Description

    A custom title to display on this pass.

  • Name
    expiration_date
    Type
    string
    Description

    The expiration date of the pass.

Request

POST
/passes
curl -X POST https://api.walletapp.co/passes 
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
    'email': '<email>',
    'brand_id': '<brand_id>',
    'coupon_id': '<coupon_id>',
    'expiration_date': '<expiration_date>',
    'pass_title': '<pass_title>',
    'status': 'active'
}'

Response

{
    "success": true,
    "message": "Email has been send"
}

POST/passes/url

This endpoint generates a new pass and returns the url to the download page.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The token itself as a bearer token.

Abilities

  • Name
    passes.get
    Type
    string
    Description

    The scope required to access this endpoint.

Body parameters

  • Name
    email
    Type
    string|Required
    Description

    The email address of the member.

  • Name
    brand_id
    Type
    string|Required
    Description

    The UUID of the brand.

  • Name
    coupon_id
    Type
    string|Required
    Description

    The id of the coupon to send.

  • Name
    status
    Type
    string|Required
    Description

    The status of the pass. Can be: ['active', 'blocked', 'expired', 'redeemed']

  • Name
    pass_title
    Type
    string
    Description

    A custom title to display on this pass.

  • Name
    expiration_date
    Type
    string
    Description

    The expiration date of the pass.

Request

POST
/passes/url
curl -X POST https://api.walletapp.co/passes/url 
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <your_token>"
-d '{
    'email': '<email>',
    'brand_id': '<brand_id>',
    'coupon_id': '<coupon_id>',
    'expiration_date': '<expiration_date>',
    'pass_title': '<pass_title>',
    'status': 'active'
}'

Response

{
    "success": true,
    "url": "my.walletapp.co/pass/<pass_code>"
}

POST/passes/scan/storecard

Modify points/balance

This api allows you to modify the amount of points and/or balance on a storecard. You can update points, balance, or both in a single request.

The endpoint accepts three optional fields that give the platform a richer picture of the transaction: description (what was charged), transaction_reference (idempotency key for safe retries), and vat_rate. Old integrations that don't send them continue to work unchanged — the platform falls back to a price-match heuristic against the organization's product catalogue, then to the terminal's name.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The scanner bearer token (device token). No specific abilities required.

Body parameters

  • Name
    points
    Type
    numeric|Optional
    Description

    A postive or negative number to add or subtract from the current points balance. At least one of points or balance must be provided.

  • Name
    balance
    Type
    numeric|Optional
    Description

    A postive or negative number to add or subtract from the current monetary balance. At least one of points or balance must be provided.

  • Name
    organization_identifier
    Type
    string|Required
    Description

    The identifier of the organization.

  • Name
    pass_code
    Type
    string|Required
    Description

    The pass code of the pass to modify.

  • Name
    external_reference
    Type
    string|Optional
    Description

    The external reference of the transaction. Echoed back as transaction_id in the response.

  • Name
    description
    Type
    string|Optional
    Description

    Human-readable description of what was charged (e.g. "Latte macchiato", "Tier 1 punch"). Persisted to the storecard's transaction history and surfaced on the per-pass view in the dashboard. Maximum 191 characters. When omitted, the platform infers a description by matching the transaction amount against the organization's products, falling back to the terminal's name.

  • Name
    transaction_reference
    Type
    string|Optional
    Description

    Idempotency key. If a previous request for the same pass and storecard was POSTed with the same transaction_reference, this request is treated as a retry: the balance and points are not changed a second time, no extra history row is written, and the response's was_duplicate field will be true. Recommended for any device that retries on transient failures. Maximum 64 characters. When omitted, the platform generates a UUID server-side and returns it in the response so it can be reused for follow-up retries.

  • Name
    vat_rate
    Type
    integer|Optional
    Description

    VAT rate that applies to this line, expressed as a whole percentage between 0 and 21. Stored alongside the transaction for use by the platform's invoicing flows. Defaults apply when omitted.

Product-restricted storecards

Some storecards (typically postpaid / subscription / punch card categories) are configured by the organization to accept only a specific subset of products. When such a card is scanned, the platform tries to resolve the transaction to one of the allowed products using, in order: the request's description, the terminal's configured product, and a price-match against the catalogue.

If the resolved product is on the allowed list, the request proceeds as normal. Otherwise the request is refused with a 422 response — see the Restriction denied example on the right. Top-up scans (a positive balance value) always bypass the restriction.

Request

POST
/passes/scan/storecard
curl -X POST https://api.walletapp.co/passes/scan/storecard
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer <scanner_device_token>"
-d "{
    'points': 1,
    'balance': 5.50,
    'organization_identifier': '<organization_identifier>',
    'pass_code': '<pass_code>',
    'external_reference': '<external_reference>',
    'description': 'Latte macchiato',
    'transaction_reference': '<idempotency_key>',
    'vat_rate': 21
}"

Response

{
    "success": true,
    "message": "Pass updated",
    "transaction_id": "<transaction uuid>",
    "transaction_reference": "<idempotency_key>",
    "was_duplicate": false,
    "points": 11,
    "balance": 25.50
}

Response - Duplicate retry

{
    "success": true,
    "message": "Pass updated",
    "transaction_id": "<transaction uuid>",
    "transaction_reference": "<idempotency_key>",
    "was_duplicate": true
}

Error - Restriction denied (product not allowed)

{
    "message": "Product \"Latte macchiato\" is not allowed on this storecard. The card is restricted to: Espresso, Cappuccino.",
    "error_code": "product_not_allowed",
    "allowed_products": ["Espresso", "Cappuccino"],
    "resolved_product_id": 42,
    "resolved_description": "Latte macchiato"
}

Error - Restriction denied (cannot validate)

{
    "message": "This card is restricted to specific products. The device did not provide enough information to validate the scan — set a description or transaction_reference on the request.",
    "error_code": "cannot_validate_restriction",
    "allowed_products": ["Espresso", "Cappuccino"],
    "resolved_product_id": null,
    "resolved_description": "Charged at: Counter-01"
}

GET/passes/storecard/topup-products

List top-up products

Some storecards are linked to an external card system that owns the balance — currently Totalwash (Technodata) washcards. Those systems do not accept a free amount: a top-up is always the purchase of one of their top-up products (for example "€25 credit, get €27.50"). This endpoint returns the products available for a pass, so your kiosk or POS can let the customer choose one.

The response's provider tells you which system the card is linked to. Storecards that are not linked to such a system return 422 with error_code: "no_provider" — use Modify points/balance for those.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The scanner bearer token (device token) with the storecard.topup ability.

Query parameters

  • Name
    organization_identifier
    Type
    string|Required
    Description

    The identifier of the organization.

  • Name
    pass_code
    Type
    string|Required
    Description

    The pass code (QR value) of the storecard pass. The pass must belong to the brand of the device's location.

Product fields

  • Name
    productcode
    Type
    string
    Description

    The code to send to Top up with a product.

  • Name
    description
    Type
    string
    Description

    Display name of the product, as configured in the external system.

  • Name
    price
    Type
    numeric
    Description

    What the customer pays (in euros). Collecting this payment is up to your integration.

  • Name
    amount
    Type
    numeric
    Description

    The credit added to the card. Can be higher than price for bonus products.

Request

GET
/passes/storecard/topup-products
curl -G https://api.walletapp.co/passes/storecard/topup-products \
-H "Accept: application/json" \
-H "Authorization: Bearer <scanner_device_token>" \
-d organization_identifier=<organization_identifier> \
-d pass_code=<pass_code>

Response

{
    "success": true,
    "provider": "totalwash",
    "products": [
        { "productcode": "TW25", "description": "Tegoed 25", "price": 25.00, "amount": 27.50 },
        { "productcode": "TW50", "description": "Tegoed 50", "price": 50.00, "amount": 57.50 }
    ]
}

Error - Not linked to an external system

{
    "success": false,
    "error_code": "no_provider",
    "message": "This storecard is not linked to an external card system that supports product top-ups. Use POST /passes/scan/storecard to change its balance."
}

POST/passes/storecard/topup

Top up with a product

Buys one top-up product on the external card system for this pass and mirrors the credit into Walnut: the storecard balance, the transaction history (type topup, with your device as the location) and the customer's Wallet pass are updated. Price and credited amount are always taken from the external system's own product list — you only send the productcode.

Call this after you have collected the customer's payment. The request makes two or three calls to the external system; use a client timeout of at least 15 seconds.

Retries

Send a transaction_reference and reuse it when you retry. A repeat of a top-up that already succeeded returns 200 with was_duplicate: true and does not buy a second product. While the first request is still running, a concurrent retry gets 409 (error_code: "in_progress") — wait and retry. A failed top-up (any non-2xx response) has not credited the card and may be retried with the same reference.

Required headers

  • Name
    Authorization
    Type
    string
    Description

    The scanner bearer token (device token) with the storecard.topup ability.

Body parameters

  • Name
    organization_identifier
    Type
    string|Required
    Description

    The identifier of the organization.

  • Name
    pass_code
    Type
    string|Required
    Description

    The pass code (QR value) of the storecard pass.

  • Name
    productcode
    Type
    string|Required
    Description

    A productcode from List top-up products.

  • Name
    transaction_reference
    Type
    string|Optional
    Description

    Idempotency key, maximum 64 characters. Strongly recommended.

Error codes

  • Name
    403 · unauthorized / device_token_required
    Type
    error
    Description

    The token is not a scanner device token, lacks the storecard.topup ability, or belongs to another organization.

  • Name
    404 · pass_not_found
    Type
    error
    Description

    No storecard pass with this code for the device's brand.

  • Name
    409 · in_progress
    Type
    error
    Description

    A request with the same transaction_reference is still being processed.

  • Name
    422 · no_provider / no_product / no_card / no_email
    Type
    error
    Description

    The card is not linked to an external system, the product is not available for this card, or the card/member is incomplete. Blocked or disabled passes are also refused with 422.

  • Name
    502 · api_error
    Type
    error
    Description

    The external system refused or failed. Nothing was credited.

Request

POST
/passes/storecard/topup
curl -X POST https://api.walletapp.co/passes/storecard/topup \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer <scanner_device_token>" \
-d '{
    "organization_identifier": "<organization_identifier>",
    "pass_code": "<pass_code>",
    "productcode": "TW25",
    "transaction_reference": "<idempotency_key>"
}'

Response

{
    "success": true,
    "provider": "totalwash",
    "message": "Top-up successful.",
    "was_duplicate": false,
    "transaction_reference": "<idempotency_key>",
    "balance": 37.50,
    "previous_balance": 10.00,
    "product": { "productcode": "TW25", "description": "Tegoed 25", "price": 25.00, "amount": 27.50 }
}

Response - Duplicate retry

{
    "success": true,
    "provider": "totalwash",
    "message": "Top-up already applied.",
    "was_duplicate": true,
    "transaction_reference": "<idempotency_key>",
    "balance": 37.50
}

Error - Product not available

{
    "success": false,
    "error_code": "no_product",
    "message": "This top-up product is not available for this card."
}