Store Setup API

Create merchants, verify domain ownership, configure discount tiers, buy credits with USDC, and publish to the directory. All via API. No dashboard needed.

Configures merchants for the verification and commerce endpoints.

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

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

View on Smithery · GitHub repo

What this does

Register merchants, prove domain ownership, set up token and NFT discount tiers, and make them discoverable in the public directory. Designed for AI agents that onboard businesses autonomously.

Zero to first discount

  1. Get a free API key (use the form below), or, if you’re an AI agent, buy a key autonomously with USDC via POST /v1/keys/buy (no email needed)
  2. Test an attestation: POST /v1/attest with any wallet and condition
  3. Create a merchant: POST /v1/merchants with your company name
  4. Configure token tiers: PUT /v1/merchants/:id/tokens with at least one tier
  5. Generate a discount: POST /v1/acp/discount with a wallet that qualifies

Domain verification and directory publishing are optional. You can generate discounts as soon as tokens are configured.

Onboarding flow

1

Create

POST /v1/merchants with companyName and companyId

2

Verify Domain

POST then PUT /v1/merchants/:id/domain-verification

3

Configure Tokens

PUT /v1/merchants/:id/tokens with tiers

4

Configure NFTs

PUT /v1/merchants/:id/nfts (optional)

5

Update Settings

PUT /v1/merchants/:id/settings (discountMode, cap, USDC)

6

Publish

POST /v1/merchants/:id/directory

Endpoint reference

POST /v1/merchants

Create Merchant

Creates a new merchant account and enables API access immediately. The API key that creates it owns it and pays one credit for each discount code or NFC tap that carries a discount (a 0% result is free) and for every in-store scan; the store has no balance of its own. credits in the response is the owner key's current balance. A key can own a limited number of merchants; past the limit, creating one returns 429.

ParameterTypeDescription
companyNamestring requiredBusiness name. Max 100 characters.
companyIdstring requiredUnique slug. 2-50 characters, alphanumeric, dash, or underscore.
locationstring optionalBusiness location. Max 200 characters.
allowDuplicateNameboolean optionalSet true to create a merchant whose companyName matches an existing one. Without it, a duplicate name returns 409.
Request
curl -X POST https://api.insumermodel.com/v1/merchants \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "companyName": "Acme Coffee",
    "companyId": "acme-coffee",
    "location": "New York, NY"
  }'
Response
{
  "ok": true,
  "data": {
    "id": "acme-coffee",
    "companyName": "Acme Coffee",
    "credits": 250,
    "apiAccessEnabled": true
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
POST /v1/merchants/:id/domain-verification

Request Verification Token

Generates a verification token and returns instructions for DNS, meta tag, and file-based domain verification.

ParameterTypeDescription
domainstring requiredDomain to verify. No protocol prefix (e.g. acme.com not https://acme.com).
Request
curl -X POST https://api.insumermodel.com/v1/merchants/acme-coffee/domain-verification \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "domain": "acme.com"
  }'
Response
{
  "ok": true,
  "data": {
    "domain": "acme.com",
    "token": "abc123xyz",
    "methods": {
      "dns": {
        "type": "TXT",
        "host": "acme.com",
        "value": "abc123xyz",
        "instructions": "Add a TXT record..."
      },
      "meta": {
        "tag": "<meta name=\"insumer-verification\" content=\"abc123xyz\">",
        "instructions": "Add this meta tag inside <head>"
      },
      "file": {
        "path": "/insumer-verify.txt",
        "content": "abc123xyz",
        "instructions": "Host file at /insumer-verify.txt"
      }
    }
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
PUT /v1/merchants/:id/domain-verification

Trigger Verification

Checks whether the verification token is present at the domain. Rate limited per merchant (the 429 says when to retry).

No request body. Send an empty PUT.

Request
curl -X PUT https://api.insumermodel.com/v1/merchants/acme-coffee/domain-verification \
  -H "x-api-key: YOUR_API_KEY"
Response (verified)
{
  "ok": true,
  "data": {
    "verified": true,
    "domain": "acme.com",
    "method": "dns"
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}

// Response (not verified):
{
  "ok": true,
  "data": {
    "verified": false,
    "domain": "acme.com",
    "message": "Verification token not found...",
    "attemptsRemaining": 4
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
PUT /v1/merchants/:id/tokens

Configure Tokens

Sets the merchant's own token and partner token discount tiers. Maximum 8 tokens total. Tiers must be in ascending threshold order with discounts between 1% and 50%. A discount is a whole number: a value with a fraction, such as 10.5, returns 400.

ParameterTypeDescription
ownTokenobject optionalMerchant's own token: symbol, chainId (number, "solana", or "xrpl"), contractAddress, decimals, tiers array of {name, threshold, discount}. decimals is required (0 to 18) but is not used when a discount is evaluated: the token’s own decimals are read from the chain. For Solana, contractAddress is "native" for SOL or the mint address (base58, 32 to 44 characters). For XRPL trust line tokens, include currency and use the issuer r-address as contractAddress; the address checksum is verified. Enter the currency code exactly as the issuer created it: a 3-character code, a token name of 1 to 20 printable ASCII characters, or a 40-character hex code. Codes are case-sensitive and stored as entered. For native XRP, use contractAddress: "native" and leave currency out. An address or currency that fails these checks returns 400. To switch the own token off, send null or {"enabled": false}; enabled must be a boolean, and any other value returns 400.
partnerTokensarray optionalPartner tokens. Same structure as ownToken. Combined total max 8.
Request
curl -X PUT https://api.insumermodel.com/v1/merchants/acme-coffee/tokens \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "ownToken": {
      "symbol": "ACME",
      "chainId": 1,
      "contractAddress": "0x1234...abcd",
      "decimals": 18,
      "tiers": [
        { "name": "Bronze", "threshold": 100, "discount": 5 },
        { "name": "Silver", "threshold": 500, "discount": 10 },
        { "name": "Gold", "threshold": 1000, "discount": 15 }
      ]
    },
    "partnerTokens": [{
      "symbol": "USDC",
      "chainId": 1,
      "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "decimals": 6,
      "tiers": [
        { "name": "Holder", "threshold": 100, "discount": 3 }
      ]
    }]
  }'
Response
{
  "ok": true,
  "data": {
    "ownToken": {
      "symbol": "ACME",
      "tiersConfigured": 3
    },
    "partnerTokens": [
      { "symbol": "USDC", "tiersConfigured": 1 }
    ],
    "totalTokens": 2,
    "maxTokens": 8
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
PUT /v1/merchants/:id/nfts

Configure NFTs

Sets NFT collection discounts. Maximum 4 collections with discounts between 1% and 50%, or recognition only (no discount). A discount is a whole number: a value with a fraction, such as 10.5, returns 400.

ParameterTypeDescription
nftCollectionsarray requiredArray of {name, contractAddress, chainId, discount}. Max 4 collections; name max 50 characters. Optional benefitType: "discount" (default) or "recognition" (holders are recognized, no discount; discount is then not required). Optional enabled: a boolean; only false switches a collection off. chainId accepts a number, "solana", or "xrpl". For Solana NFTs, use the mint address as contractAddress. For XRPL NFTs, use the issuer r-address as contractAddress (the address checksum is verified) and optionally include taxon, an integer from 0 to 4294967295. A malformed address or taxon returns 400.
Request
curl -X PUT https://api.insumermodel.com/v1/merchants/acme-coffee/nfts \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "nftCollections": [{
      "name": "Acme Members",
      "contractAddress": "0x5678...efgh",
      "chainId": 1,
      "discount": 20
    }]
  }'
Response
{
  "ok": true,
  "data": {
    "nftCollections": [
      {
        "name": "Acme Members",
        "chainId": 1,
        "discount": 20
      }
    ],
    "totalCollections": 1,
    "maxCollections": 4
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
PUT /v1/merchants/:id/settings

Update Settings

Updates merchant discount mode, cap, limits for unproven wallets, and USDC payment configuration. Send at least one parameter.

ParameterTypeDescription
discountModestring optional"highest" (best single discount), "stack" (sum all qualifying discounts) or "capped" (sum all, then cap at discountCap).
discountCapnumber optionalMaximum total discount percentage. A whole number from 1 to 100; a value with a fraction returns 400.
usdcPaymentobject optional{enabled, evmAddress, solanaAddress, preferredChainId}. To switch it on, include at least one of evmAddress or solanaAddress; send null or {"enabled": false} to switch it off.
maxDiscountsPerWalletPerDayinteger | null optionalThe store’s limit on discount codes per UTC day for a wallet sent without a walletProof, a whole number from 1 to 100; null removes the limit. A proven wallet is not limited.
maxUnprovenDiscountinteger | null optionalThe most a wallet sent without a walletProof can get, a whole number from 0 to 100 (0 = no discount); null gives it the same as a proven wallet. When this lowers a discount, the response carries discountIfProven.
Request
curl -X PUT https://api.insumermodel.com/v1/merchants/acme-coffee/settings \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "discountMode": "highest",
    "discountCap": 25,
    "usdcPayment": {
      "enabled": true,
      "evmAddress": "0x47aD...14E9",
      "preferredChainId": 8453
    }
  }'
Response
{
  "ok": true,
  "data": {
    "discountMode": "highest",
    "discountCap": 25,
    "maxDiscountsPerWalletPerDay": null,
    "maxUnprovenDiscount": null,
    "usdcPayment": {
      "enabled": true
    }
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
POST /v1/merchants/:id/credits

Buy Credits with USDC, USDT or BTC

Kept for compatibility. Submit a USDC, USDT or BTC transaction hash to buy credits into the regular balance of the API key that owns the merchant. Flat $0.04 per credit (25 credits per $1) at every purchase size on this endpoint. POST /v1/credits/buy is the normal way to top up and carries the volume tiers. Funds sent on unsupported chains cannot be recovered. Non-refundable.

ParameterTypeDescription
txHashstring requiredTransfer transaction hash (USDC, USDT or BTC).
chainIdnumber | "solana" | "bitcoin" | "tron" requiredChain ID (e.g. 1, 8453) or "solana", "bitcoin", "tron". Supported: ETH, Base, Polygon, Arbitrum, Optimism, BNB, Avalanche (USDC or USDT), Solana (USDC or USDT), Tron (USDT-TRC20), Bitcoin (BTC).
amountnumber requiredUSDC/USDT amount. Minimum 5. Not required for BTC.
updateWalletboolean optionalSet to true to change the registered sender wallet.

Sender verification: The first purchase registers the sender wallet to the API key. Subsequent purchases must come from the same sender. To change the registered wallet, include "updateWallet": true: the verified transfer proves ownership.

See platform wallet addresses and supported chains →

Request
curl -X POST https://api.insumermodel.com/v1/merchants/acme-coffee/credits \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "txHash": "0xabc...def",
    "chainId": 8453,
    "amount": 10
  }'
Response
{
  "ok": true,
  "data": {
    "creditsAdded": 250,
    "totalCredits": 350,
    "usdcPaid": "10",
    "chainName": "Base"
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
POST /v1/merchants/:id/directory

Publish to Directory

Publishes the merchant to the public directory, making it discoverable by wallets and agents. No request body needed.

Request
curl -X POST https://api.insumermodel.com/v1/merchants/acme-coffee/directory \
  -H "x-api-key: YOUR_API_KEY"
Response
{
  "ok": true,
  "data": {
    "id": "acme-coffee",
    "published": true,
    "tokensListed": 2,
    "nftCollectionsListed": 1
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}
GET /v1/merchants/:id/status

Get Merchant Status

Returns the full private details of a merchant, including the owner key's credit balance, directory listing status, token configuration, and discount settings.

Request
curl https://api.insumermodel.com/v1/merchants/acme-coffee/status \
  -H "x-api-key: YOUR_API_KEY"
Response
{
  "ok": true,
  "data": {
    "id": "acme-coffee",
    "companyName": "Acme Coffee",
    "location": "New York, NY",
    "credits": 350,
    "apiAccessEnabled": true,
    "createdAt": "2026-02-20T10:00:00.000Z",
    "discountMode": "highest",
    "discountCap": 25,
    "maxDiscountsPerWalletPerDay": null,
    "maxUnprovenDiscount": null,
    "listedInDirectory": true,
    "ownToken": {
      "symbol": "ACME",
      "chainId": 1,
      "tiers": [/* ... */]
    },
    "partnerTokens": [/* ... */],
    "nftCollections": [/* ... */],
    "usdcPayment": { "enabled": true },
    "verification": { "status": "verified", "domain": "acme.com" }
  },
  "meta": { "version": "1.0", "timestamp": "2026-02-27T12:00:00.000Z" }
}

See how this endpoint fits the full API → API Topology