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

> Set up a store from code: create it, define wallet conditions and discount tiers, verify its domain and list it in the directory agents read. No dashboard.
>
> Page: https://insumermodel.com/developers/onboarding/ · 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/) › Store Setup

# 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](https://insumermodel.com/developers/verification/) and [commerce](https://insumermodel.com/developers/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.

```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

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

#### Create

`POST /v1/merchants` with companyName and companyId

#### Verify Domain

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

#### Configure Tokens

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

#### Configure NFTs

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

#### Update Settings

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

#### 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.

| Parameter | Type | Description |
|---|---|---|
| companyName | string required | Business name. Max 100 characters. |
| companyId | string required | Unique slug. 2-50 characters, alphanumeric, dash, or underscore. |
| location | string optional | Business location. Max 200 characters. |
| allowDuplicateName | boolean optional | Set `true` to create a merchant whose `companyName` matches an existing one. Without it, a duplicate name returns 409. |

Request:

```bash
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"
  }'
```

```json
{
  "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.

| Parameter | Type | Description |
|---|---|---|
| domain | string required | Domain to verify. No protocol prefix (e.g. `acme.com` not `https://acme.com`). |

Request:

```bash
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"
  }'
```

```json
{
  "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:

```bash
curl -X PUT https://api.insumermodel.com/v1/merchants/acme-coffee/domain-verification \
  -H "x-api-key: YOUR_API_KEY"
```

Response (verified):

```json
{
  "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.

| Parameter | Type | Description |
|---|---|---|
| ownToken | object optional | Merchant'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. |
| partnerTokens | array optional | Partner tokens. Same structure as ownToken. Combined total max 8. |

Request:

```bash
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 }
      ]
    }]
  }'
```

```json
{
  "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.

| Parameter | Type | Description |
|---|---|---|
| nftCollections | array required | Array 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:

```bash
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
    }]
  }'
```

```json
{
  "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.

| Parameter | Type | Description |
|---|---|---|
| discountMode | string optional | `"highest"` (best single discount), `"stack"` (sum all qualifying discounts) or `"capped"` (sum all, then cap at `discountCap`). |
| discountCap | number optional | Maximum total discount percentage. A whole number from 1 to 100; a value with a fraction returns 400. |
| usdcPayment | object 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. |
| maxDiscountsPerWalletPerDay | integer \| null optional | The 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. |
| maxUnprovenDiscount | integer \| null optional | The 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:

```bash
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
    }
  }'
```

```json
{
  "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.

| Parameter | Type | Description |
|---|---|---|
| txHash | string required | Transfer transaction hash (USDC, USDT or BTC). |
| chainId | number \| "solana" \| "bitcoin" \| "tron" required | Chain 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). |
| amount | number required | USDC/USDT amount. Minimum 5. Not required for BTC. |
| updateWallet | boolean optional | Set 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 →](https://insumermodel.com/pricing/#usdc)

Request:

```bash
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
  }'
```

```json
{
  "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:

```bash
curl -X POST https://api.insumermodel.com/v1/merchants/acme-coffee/directory \
  -H "x-api-key: YOUR_API_KEY"
```

```json
{
  "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:

```bash
curl https://api.insumermodel.com/v1/merchants/acme-coffee/status \
  -H "x-api-key: YOUR_API_KEY"
```

```json
{
  "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" }
}
```

## From the blog

[On-Chain Verification for AI Agent Commerce: A Step-by-Step Tutorial](https://insumermodel.com/blog/on-chain-verification-ai-agent-commerce-tutorial.html) [DJD Agent Score: Architecture Pattern for Agent Trust Scoring](https://insumermodel.com/blog/djd-agent-score-insumer-api-integration.html) [AsterPay KYA: ERC-8183 Agent Trust Scoring with InsumerAPI](https://insumermodel.com/blog/asterpay-kya-erc8183-attestation-integration.html)

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

[← Commerce](https://insumermodel.com/developers/commerce/) [Next: API Reference →](https://insumermodel.com/developers/api-reference/)
