---
title: "Verify a scanned pass and compute the discount"
method: POST
path: "/v1/pass/verify"
tags: ["Point of Sale"]
---

# Verify a scanned pass and compute the discount

`POST /v1/pass/verify`

## Request

- Base URL: `https://api.insumermodel.com`
- URL: `https://api.insumermodel.com/v1/pass/verify`
- Auth: API key in header `X-API-Key`

Verify a scanned pass and compute the discount. X-API-Key required (metered on the partner key). All cryptography stays server-side: InsumerAPI verifies the ECDSA signature on the scanned QR, enforces 65-second freshness, then computes the discount against the merchant config. The partner's scanner only displays the result. A bad signature is rejected with no discount. There is deliberately no companion "sign" endpoint — passes are issued by InsumerAPI's own pass surface; signing is the primitive and is never exposed as an API call.

## Request body

- object
  - `merchantId` string, required
  - `qr` object, required: The scanned pass object exactly as encoded, including its `sig`. The signed bytes are the QR object with `sig` removed, re-serialized (`sig` is always the last key).
    - `w` string: Truncated wallet address.
    - `t` string: Encoded token balances.
    - `nft` string: Encoded NFT holdings.
    - `ts` integer: Issue timestamp in ms (65-second freshness window).
    - `n` string: Nonce.
    - `sig` string, required: Base64 P1363 ECDSA signature.

## Response `200`

Pass verified

- object
  - `ok` true
  - `data` object
    - `recognized` boolean
    - `matched` object[]: Which configured assets the pass holds. A superset of breakdown: recognition entries carry no discount.
      - `symbol` string: Token symbol, or collection name for NFTs
      - `kind` 'token' | 'nft'
    - `configured` integer: How many assets this merchant recognizes at all. Distinguishes "nothing configured yet" from "pass not recognized".
    - `discount` integer
    - `breakdown` object[]
      - `symbol` string
      - `tier` string
      - `discount` integer
    - `merchantName` string, nullable: The merchant's display name, or null when none is configured.
    - `paused` boolean: Present and true when the store's owner has paused its recognition: recognized is false, discount is 0, and configured still counts what the store recognizes. Show the store as paused, not as unconfigured.
  - `meta` Meta
    - `version` string
    - `timestamp` string, date-time

## Other responses

- `400`: Missing merchantId, or qr/sig
- `401`: Invalid signature or expired pass
- `404`: Merchant not found
- `429`: Rate limit exceeded. API-key requests count against the key's daily limit; every authenticated response carries X-RateLimit-Limit and X-RateLimit-Remaining headers, and a 429 additionally carries Retry-After (seconds until the window resets).
- `500`: Unexpected server error. Safe to retry; no credits are charged on a 500. Router endpoints return the standard error envelope; standalone endpoints (the ones whose 200 is a flat object) return a flat {"error": "..."} object.

---

[API](https://skmtc.dev/insumermodel/apis/insumerapi.md) · [All operations](https://skmtc.dev/insumermodel/apis/insumerapi/llms.txt) · [OpenAPI document](https://skmtc.dev/insumermodel/apis/insumerapi/revisions/91f56fe9e3d8?raw)
