<!-- Markdown copy of https://insumermodel.com/developers/commerce/ for agents. -->

> For developers building AI shopping agents: check a wallet's discount at a store through OpenAI ACP or Google UCP and get a signed code the store can validate.
>
> Page: https://insumermodel.com/developers/commerce/ · API: https://api.insumermodel.com · OpenAPI: https://insumermodel.com/openapi.json · Index for agents: https://insumermodel.com/llms.txt

[Developers](https://insumermodel.com/developers/) [API Map](https://insumermodel.com/workbench/) / AI Agent Commerce

# AI Agent Commerce

Return discount data in OpenAI ACP or Google UCP format so AI shopping agents can apply token-holder discounts natively. Same on-chain verification, standard protocol output.

Combines [signed attestation](https://insumermodel.com/developers/verification/) with protocol-native discount output.

Auth: `X-API-Key`. Wallet-signed auth (`Authorization: Wallet`) is not accepted on the commerce endpoints; it covers `/v1/attest`, `/v1/trust`, `/v1/trust/batch`, `/v1/credits`, and `/v1/credits/buy`. [See Authentication →](https://insumermodel.com/developers/api-reference/#auth)

**Building this in Claude Code?** Install the wallet auth skill and Claude will write correct, signature-verifying integration code on the first try.

```bash
git clone https://github.com/insumerapi/insumer-skill.git ~/.claude/skills/insumer
```

[View on Smithery](https://smithery.ai/skills/douglasborthwick/insumer-skill) · [GitHub repo](https://github.com/insumerapi/insumer-skill)

## What this does

Generate discount codes from on-chain token holdings in standard commerce protocol formats. ACP-compatible agents get Stripe coupon objects. UCP-compatible agents get Google commerce extensions. Every response is ECDSA-signed with the same key as [POST /v1/attest](https://insumermodel.com/developers/verification/), published at [JWKS](https://insumermodel.com/.well-known/jwks.json). The signed payload differs from an attestation's, so it is not checked by the same flow: verify it against JWKS directly, or validate the code through `GET /v1/codes/{code}`.

A code that carries a discount costs one credit ($0.04 at the base rate), paid by **the API key that owns the store** (the key that created it); a 0% result is free. A caller using another key is not charged, and the store has no balance of its own. If you call with the owner key, your key pays, and `meta.creditsCharged` shows 1. 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). Stores run through a licensed platform such as Skye Meta are covered by that platform's license.

## Three-step flow

### Verify holdings

`POST /v1/acp/discount` or `POST /v1/ucp/discount` with wallet address and merchantId. The API checks on-chain balances against the merchant's configured tiers.

### Get discount code

Response includes an `INSR-XXXXX` discount code wrapped in the protocol-native format. ACP returns Stripe coupon objects. UCP returns Google commerce extensions.

### Validate at checkout

Merchant backend calls `GET /v1/codes/{code}` to confirm the code is valid, unexpired, and unused before applying the discount.

POST /v1/acp/discount

## ACP Discount

Verifies token holdings and returns the result in OpenAI ACP format. Returns Stripe-style coupon objects with applied and rejected arrays. A code that carries a discount costs 1 credit, paid by the API key that owns the store; a 0% result is free. Response is ECDSA-signed.

### Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `merchantId` | string | Required | Merchant ID |
| `wallet` | string | Required* | EVM wallet address (0x...). Required unless solanaWallet or xrplWallet is provided. |
| `solanaWallet` | string | Optional | Solana wallet address. Use instead of wallet for Solana-based merchants. |
| `xrplWallet` | string | Optional | XRPL wallet address (r-address). Use for XRP Ledger-based merchants. The checksum is verified; an address that fails it returns 400. |
| `walletProof` | object | Optional | Proof that the caller controls `wallet`: `{message, signature}`, an EIP-4361 (Sign-In with Ethereum) message signed by that wallet within the last 5 minutes, with domain `api.insumermodel.com` or the store's own website, URI `https://api.insumermodel.com/v1/merchants/{merchantId}` and an unused nonce. A proven wallet gets the full discount its holdings earn, without the store's limits for unproven wallets. A proof object without a string `message` and `signature` returns 400; a proof that does not verify returns 401. Neither uses a credit. EVM only: send it with `wallet`, not with `solanaWallet` or `xrplWallet`. |
| `items` | array | Optional | Cart items with `path` and `amount` (minor units). Supply `items` or `subtotal` to receive an applied discount: with neither, `applied` is empty even for a qualifying wallet, and the discount reaches you through `verification`. |
| `subtotal` | number | Optional | Order-level base in minor units (a fraction is truncated), for callers that do not send line items. Ignored when `items` is supplied. |

### Request

```bash
curl -X POST \
  https://api.insumermodel.com/v1/acp/discount \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "merchantId": "merchant_abc123",
    "wallet": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "items": [
      { "path": "$.line_items[0]", "amount": 12000 }
    ]
  }'
```

`merchant_abc123` is a placeholder: use the ID of a merchant you created (see [Merchant Onboarding](https://insumermodel.com/developers/onboarding/)). An unknown `merchantId` returns `404 Merchant not found`.

### Response

200 OK:

```json
{
  "ok": true,
  "data": {
    "protocol": "acp",
    "version": "2026-04-17",
    "discounts": {
      "codes": [],
      "applied": [
        {
          "id": "INSR-A7B3K",
          "coupon": {
            "id": "insumer-insr-a7b3k",
            "name": "Token Holder Discount (15% off)",
            "percent_off": 15
          },
          "amount": 1800,
          "automatic": true,
          "method": "across",
          "priority": 10,
          "allocations": [
            { "path": "$.line_items[0]", "amount": 1800 }
          ],
          "start": "2026-02-27T12:00:00Z",
          "end": "2026-02-27T12:30:00Z"
        }
      ],
      "rejected": []
    },
    "verification": {
      "code": "INSR-A7B3K",
      "expiresAt": "2026-02-27T12:30:00Z",
      "breakdown": [{ "symbol": "UNI", "tier": "Gold", "discount": 15 }],
      "sig": "MEUCIQD...",
      "kid": "insumer-attest-v1",
      "walletProven": false
    }
  },
  "meta": { "creditsRemaining": 47, "creditsCharged": 0, "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
```

### No qualifying tier

When the wallet holds none of the merchant's configured tokens, the response still returns 200. The `codes`, `applied` and `rejected` arrays are all empty: the discount is automatic, so an ineligible wallet is simply one the automatic discount did not apply to, not a rejected code. A verification code is still generated and signed. When a read does not complete, the answer is 503 `rpc_failure`, never a discount of zero: retry. A frozen XRPL trust line earns no tier.

200 OK (no match):

```json
{
  "ok": true,
  "data": {
    "protocol": "acp",
    "version": "2026-04-17",
    "discounts": {
      "codes": [],
      "applied": [],
      "rejected": []
    },
    "verification": {
      "code": "INSR-K9F2L",
      "expiresAt": "2026-02-27T12:30:01Z",
      "breakdown": [],
      "sig": "MEUCIQD...",
      "kid": "insumer-attest-v1",
      "walletProven": false
    }
  },
  "meta": { "creditsRemaining": 47, "creditsCharged": 0, "version": "1.0", "timestamp": "2026-02-27T12:00:01.000Z" }
}
```

The response is still ECDSA-signed regardless of eligibility. The signed payload includes `verified: false` so the attestation itself is verifiable proof the check was performed.

POST /v1/ucp/discount

## UCP Discount

Same on-chain verification, Google UCP format. Uses a title string instead of Stripe coupon objects and includes a `extension` field for protocol identification.

### Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `merchantId` | string | Required | Merchant ID |
| `wallet` | string | Required* | EVM wallet address (0x...). Required unless solanaWallet or xrplWallet is provided. |
| `solanaWallet` | string | Optional | Solana wallet address. Use instead of wallet for Solana-based merchants. |
| `xrplWallet` | string | Optional | XRPL wallet address (r-address). Use for XRP Ledger-based merchants. The checksum is verified; an address that fails it returns 400. |
| `walletProof` | object | Optional | Proof that the caller controls `wallet`: `{message, signature}`, an EIP-4361 (Sign-In with Ethereum) message signed by that wallet within the last 5 minutes, with domain `api.insumermodel.com` or the store's own website, URI `https://api.insumermodel.com/v1/merchants/{merchantId}` and an unused nonce. A proven wallet gets the full discount its holdings earn, without the store's limits for unproven wallets. A proof object without a string `message` and `signature` returns 400; a proof that does not verify returns 401. Neither uses a credit. EVM only: send it with `wallet`, not with `solanaWallet` or `xrplWallet`. |
| `items` | array | Optional | Cart items with `path` and `amount` (minor units). Supply `items` or `subtotal` to receive an applied discount: with neither, `applied` is empty even for a qualifying wallet, and the discount reaches you through `verification`. |
| `subtotal` | number | Optional | Order-level base in minor units (a fraction is truncated), for callers that do not send line items. Ignored when `items` is supplied. |

### Request

```bash
curl -X POST \
  https://api.insumermodel.com/v1/ucp/discount \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "merchantId": "merchant_abc123",
    "wallet": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "items": [
      { "path": "$.line_items[0]", "amount": 12000 }
    ]
  }'
```

`merchant_abc123` is a placeholder: use the ID of a merchant you created (see [Merchant Onboarding](https://insumermodel.com/developers/onboarding/)). An unknown `merchantId` returns `404 Merchant not found`.

### Response

200 OK:

```json
{
  "ok": true,
  "data": {
    "protocol": "ucp",
    "version": "2026-08-25",
    "extension": "dev.ucp.shopping.discount",
    "discounts": {
      "codes": [],
      "applied": [
        {
          "title": "Token Holder Discount \u2014 15% Off",
          "amount": 1800,
          "automatic": true,
          "method": "across",
          "priority": 10,
          "allocations": [
            { "path": "$.line_items[0]", "amount": 1800 }
          ]
        }
      ]
    },
    "verification": {
      "code": "INSR-M9X2P",
      "expiresAt": "2026-02-27T12:30:00Z",
      "sig": "MEUCIQD...",
      "kid": "insumer-attest-v1",
      "walletProven": false
    }
  },
  "meta": { "creditsRemaining": 47, "creditsCharged": 0, "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
```

GET /v1/codes/{code}

## Validate Code

Public endpoint. No API key required. Validate an `INSR-XXXXX` discount code. Returns valid or invalid status with discount percentage, merchant, and expiration details.

### Path Parameter

| Parameter | Type | Required | Description |
|---|---|---|---|
| `code` | string | Required | Discount code in `INSR-XXXXX` format |

### Request

```bash
curl https://api.insumermodel.com/v1/codes/INSR-A7B3K
```

### Response (valid)

200 OK:

```json
{
  "ok": true,
  "data": {
    "valid": true,
    "code": "INSR-A7B3K",
    "merchantId": "merchant_abc123",
    "discountPercent": 15,
    "expiresAt": "2026-02-27T12:30:00Z",
    "createdAt": "2026-02-27T12:00:00Z",
    "walletProven": false
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:05.000Z" }
}
```

### Response (invalid)

200 OK:

```json
{
  "ok": true,
  "data": {
    "valid": false,
    "code": "INSR-A7B3K",
    "reason": "expired"
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:35:00.000Z" }
}
```

Possible `reason` values: `not_found`, `expired`, `already_used`

## ACP vs UCP

| Feature | ACP | UCP |
|---|---|---|
| Protocol | OpenAI | Google |
| Format | Stripe coupon objects | Title + extension |
| Version | `2026-04-17` | `2026-08-25` |
| Discount shape | `coupon.percent_off` | `title` string |
| Start/end times | Yes | No |
| Extension field | No | `dev.ucp.shopping.discount` |

**Both share:** same on-chain verification, same ECDSA signature, same INSR-XXXXX discount codes, same cost (1 credit from the store's owner key when the code carries a discount).

### Reference integration

AsterPay KYA uses `POST /v1/attest` for ERC-8183 agent trust scoring with 4 conditions in a single call. DJD Agent Score integrates wallet auth into its agent trust scoring architecture in the Coinbase x402 ecosystem. Fork either as a starting point for ACP or UCP commerce integration.

[DJD on GitHub](https://github.com/jacobsd32-cpu/djdagentscore) [AsterPay on GitHub](https://github.com/AsterPay/erc8183-kya-hook)

## Related articles

[ACP and UCP: AI Agent Commerce Protocols Explained](https://insumermodel.com/blog/acp-ucp-ai-agent-commerce-protocols.html) [ACP Discount Endpoint Integration Guide](https://insumermodel.com/blog/acp-discount-endpoint-integration-guide.html) [Wallet Auth for AI Agents: Python + LangChain Tutorial](https://insumermodel.com/blog/wallet-authentication-ai-agents.html)

See how this endpoint fits the full API → [API Topology](https://insumermodel.com/workbench/)

[← Compliance](https://insumermodel.com/developers/compliance/) [Next: Merchant Onboarding →](https://insumermodel.com/developers/onboarding/)
