---
title: "ACP-format discount eligibility check"
method: POST
path: "/v1/acp/discount"
tags: ["Agent Commerce Protocols"]
---

# ACP-format discount eligibility check

`POST /v1/acp/discount`

## Request

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

ACP discount check. Same verification as POST /v1/verify, wrapped in OpenAI/Stripe Agentic Commerce Protocol format (coupon objects, allocations, applied/rejected arrays). A code that carries a discount costs 1 regular credit from the API key that owns the store; a 0% result is free. A caller using another key is not charged. Source: "acp".

## Request body

- object: At least one of `wallet` (EVM), `solanaWallet` or `xrplWallet` is required; a request with none is a 400.
  - `merchantId` string, required: Merchant identifier
  - `wallet` string: EVM wallet address (0x...)
  - `solanaWallet` string: Solana wallet address
  - `xrplWallet` string: XRPL wallet address (classic r-address). The base58 checksum is verified; an address that fails it returns 400.
  - `walletProof` WalletProof: Optional proof that the caller controls the EVM wallet named in `wallet`: an EIP-4361 (Sign-In with Ethereum) message signed by that wallet (EIP-191; smart-contract wallets are not accepted yet). The message must use domain `api.insumermodel.com` or the merchant's own website (a wallet asked to sign in the store's page expects the page's own site), the wallet as its address, URI `https://api.insumermodel.com/v1/merchants/{merchantId}`, an Issued At within the last 5 minutes, and a nonce not used before. A proven wallet gets the full discount its holdings earn (under the store's tiers and cap), without the daily limit for unproven wallets. A proof object without a string `message` and `signature` is a 400; a proof that does not verify is a 401, never a quiet fallback to the unproven terms. Neither uses a credit. Send it with `wallet` only: a request with a proof and `solanaWallet` or `xrplWallet` is a 400.
    - `message` string, required: The EIP-4361 message, exactly as signed.
    - `signature` string, required: The wallet's signature over the message (0x hex).
  - `items` object[]: Optional line items. Supply these (or subtotal) to receive an applied discount entry: ACP's applied_discount requires an integer amount in minor units, and coupon.percent_off does not substitute for it. Allocations always sum exactly to the discount amount.
    - `path` string: JSONPath reference to the line item
    - `amount` number: Item price in minor units (cents for USD); a fraction is truncated
  - `subtotal` number: Optional order-level base in minor units, for callers that do not send line items. Ignored when items[] is supplied. With neither, the response carries no applied entry; the verified discount is still returned in the verification object.

## Response `200`

ACP-format discount response

- object
  - `ok` true
  - `data` object
    - `protocol` 'acp'
    - `version` string: ACP release this payload targets
    - `discounts` object
      - `codes` string[]: Echo of discount codes submitted by the caller. This endpoint accepts none, so this is always empty. The INSR redemption code is returned in the verification object.
      - `applied` object[]
        - `id` string
        - `coupon` object
          - `id` string
          - `name` string
          - `percent_off` integer
        - `amount` integer: Discount total in minor units. Never null — the entry is omitted entirely when no monetary base was supplied.
        - `automatic` true: Always true. The discount follows from wallet contents rather than a code anyone entered, so it is an automatic discount and the entry carries no code field.
        - `method` string
        - `priority` integer
        - `allocations` object[]
          - `path` string
          - `amount` integer
        - `start` string, date-time
        - `end` string, date-time
      - `rejected` object[]
        - `code` string
        - `reason` string
    - `verification` object
      - `code` string
      - `expiresAt` string, date-time
      - `breakdown` DiscountBreakdown[]
        - `symbol` string
        - `tier` string
        - `discount` integer
        - `chain` string: Chain name (present only in GET /v1/discount/check responses, omitted in /v1/verify, /v1/acp/discount, /v1/ucp/discount). A token configured with alsoOn and held on more than one of its networks lists them, comma-separated ("Ethereum, Arbitrum").
      - `sig` string
      - `kid` string: Always insumer-attest-v1. Commerce discount responses sign under the v1 scheme regardless of the API key's signing generation; resolve the public key from the JWKS by this kid.
      - `walletProven` boolean: True when the request carried a valid walletProof. Not part of the signed result.
      - `discountIfProven` integer: Present only when the store limits unproven wallets and a walletProof would get this wallet more.
  - `meta` object
    - `creditsRemaining` integer: Credits left on the caller's API key after this call. Never another key's balance.
    - `creditsCharged` integer: 1 when the caller's key owns the store and the code carries a discount (the caller's key paid); otherwise 0. A code at a store another key owns is paid by that key, which this field never shows.
    - `version` string
    - `timestamp` string, date-time

## Other responses

- `400`: Validation error
- `401`: Invalid or missing API key, or a walletProof that fails (wrong wallet, store, domain, expired, or nonce already used). No credit is used.
- `402`: The API key that owns this store has no credits remaining. Its owner can buy more via POST /v1/credits/buy. Returned only when the result carries a discount. No credit is used and no code is created.
- `403`: Merchant has not enabled third-party app access, or the store has no active owner API key ("This store has no active owner API key, so it cannot issue codes or record scans and taps.")
- `404`: Merchant not found
- `409`: The store's recognition is paused by its owner: "This store's recognition is paused." No credit is used and no code is created.
- `429`: Too many requests. Either the key's daily limit (the response carries Retry-After and the X-RateLimit headers), or this wallet, sent without a walletProof, has reached the store's own limit on discount codes per day (maxDiscountsPerWalletPerDay in merchant settings; no credit used, no code created; resets at 00:00 UTC). The limit does not apply to a proven wallet. Paid discount codes requested with a key other than the store's owner are subject to an hourly limit (429; the message says when to try again): "The hourly limit on discount codes has been reached. Try again in N minutes." No credit is used and no code is created.
- `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.
- `503`: Data source unavailable — retryable after a short delay. No discount signed, no credits charged. A verification that could not be signed or stored is also a 503, in the standard error envelope: no credit used, no code created, equally retryable.

---

[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)
