---
title: "Create a new merchant"
method: POST
path: "/v1/merchants"
tags: ["Merchant Setup"]
---

# Create a new merchant

`POST /v1/merchants`

## Request

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

Create a merchant programmatically. The API key that creates the merchant owns it and pays for it: each discount code or NFC tap at the store that carries a discount uses one regular credit from that key (a 0% result is free), and so does every in-store scan. The store has no credit balance of its own. A key can create a limited number of merchants (429 past it).

## Request body

- object
  - `companyName` string, required: Display name
  - `companyId` string, required: Unique ID (alphanumeric, dash, underscore)
  - `location` string: City or region
  - `externalId` string: Optional partner handle for this merchant (e.g. a token collection address). Makes the call an idempotent upsert and requires domain verification before the merchant can publish to the directory.
  - `allowDuplicateName` boolean: Set true to allow creating a merchant whose companyName matches an existing merchant. Without it, a duplicate name is rejected with 409.

## Response `200`

Merchant already exists and is managed by this same API key — returned as an idempotent upsert (the existing merchant) instead of a 409.

- object
  - `ok` true
  - `data` object
    - `id` string
    - `companyName` string
    - `credits` integer
    - `apiAccessEnabled` boolean
    - `alreadyExists` true
  - `meta` Meta
    - `version` string
    - `timestamp` string, date-time

## Other responses

- `201`: Merchant created
- `400`: Validation error
- `401`: Invalid or missing API key
- `409`: Merchant ID already exists and is owned by a different API key, or a merchant with the same companyName already exists (set allowDuplicateName: true to override)
- `429`: A key can create a limited number of merchants (429 past it), or daily rate limit exceeded
- `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)
