Docs menu

Reference

REST API

Customers

List trade customers, read one customer's discounts, agreed prices and buyers, and create or update customers.

A customer is a trade account: a business you sell to, with an account code, an optional account discount, and the people (buyers) who can sign in to order for it.

The customer objectLink to this section#

json
{
  "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
  "accountCode": "OAK01",
  "name": "Oakfield Joinery",
  "legalName": "Oakfield Joinery Ltd",
  "email": "accounts@oakfieldjoinery.co.uk",
  "discountBp": 500,
  "active": true,
  "newsOptOut": false,
  "createdAt": "2026-07-02T10:12:44.000Z"
}
FieldTypeDescription
idstringCustomer id.
accountCodestringYour account code. Unique in the workspace.
namestringTrading name.
legalNamestring or nullRegistered name, when different.
emailstring or nullMain contact email, lowercased.
discountBpinteger or nullThe customer’s own discount on everything, in basis points. null for none.
activebooleanInactive customers can’t sign in or order.
newsOptOutbooleanThe customer turned off offers and news emails. Price-change emails still go.
createdAtstringWhen the customer was added.

Anywhere the API takes a customer, you can use the id or the accountCode.

List customersLink to this section#

GET/api/v1/customers

Every customer, sorted by name.

NameTypeRequiredDescription
limitintegerno1 to 100. Default 100.
offsetintegernoRows to skip. Default 0.
bash
curl "https://tradecatalog.app/api/v1/customers?limit=100" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
js
const res = await fetch('https://tradecatalog.app/api/v1/customers?limit=100', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: customers, nextOffset } = await res.json()
json
{
  "data": [
    {
      "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
      "accountCode": "OAK01",
      "name": "Oakfield Joinery",
      "legalName": "Oakfield Joinery Ltd",
      "email": "accounts@oakfieldjoinery.co.uk",
      "discountBp": 500,
      "active": true,
      "newsOptOut": false,
      "createdAt": "2026-07-02T10:12:44.000Z"
    }
  ],
  "nextOffset": null
}

Get one customerLink to this section#

GET/api/v1/customers/{id}

The customer plus everything that affects their prices, and who can sign in for them.

NameInTypeRequiredDescription
idpathstringyesCustomer id or account code.
bash
curl https://tradecatalog.app/api/v1/customers/OAK01 \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
js
const res = await fetch('https://tradecatalog.app/api/v1/customers/OAK01', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: customer } = await res.json()
json
{
  "data": {
    "id": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
    "accountCode": "OAK01",
    "name": "Oakfield Joinery",
    "legalName": "Oakfield Joinery Ltd",
    "email": "accounts@oakfieldjoinery.co.uk",
    "discountBp": 500,
    "active": true,
    "newsOptOut": false,
    "createdAt": "2026-07-02T10:12:44.000Z",
    "discountRules": [
      {
        "id": "e1f2a3b4-5c6d-4e7f-8a9b-0c1d2e3f4a5b",
        "scope": "brand",
        "discountBp": 1500,
        "target": "Northgate"
      },
      {
        "id": "f2a3b4c5-6d7e-4f80-9a1b-2c3d4e5f6a7b",
        "scope": "category",
        "discountBp": 1250,
        "target": "Hinges"
      }
    ],
    "agreedPrices": [
      {
        "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "price": 1450,
        "note": "Contract price to March 2027",
        "sku": "HX-8512",
        "name": "Hex bolt M12 x 50 zinc"
      }
    ],
    "buyers": [{ "name": "Sam Patel", "email": "sam@oakfieldjoinery.co.uk" }],
    "pendingInvitations": [{ "email": "orders@oakfieldjoinery.co.uk" }]
  },
  "currency": "GBP"
}
FieldDescription
discountRulesCategory and brand discounts. scope is category or brand; target is its name.
agreedPricesAgreed (fixed) prices valid now, per SKU, in minor units.
buyersPeople who can sign in and order for this customer.
pendingInvitationsInvitations sent but not yet accepted, and not expired.

See how prices are worked out for how these combine.

Create or update customersLink to this section#

PUT/api/v1/customerswrite key

Send up to 100 customers per call. Each row is matched to an existing customer like this:

  1. If accountCode is set, by account code. No match means a new customer with that code.
  2. Otherwise, if email is set, by email. No match means a new customer.
  3. Otherwise by name, ignoring case.

New customers without an account code get one made from their name.

BodyLink to this section#

FieldTypeRequiredDescription
customersarrayyes1 to 100 customers. Account codes must be unique within the call.
customers[].accountCodestring or nullyesUp to 50 characters. null to match by email or name.
customers[].namestringyesTrading name, up to 160 characters.
customers[].emailstring or nullyesMain contact email. null clears it.
customers[].discountBpinteger or nullyesAccount discount, 0 to 10,000. null clears it.

Every field is required, and written as sent: null clears email and discountBp. That way a missing field in your code can’t silently leave an old discount in place.

Updating a customer doesn’t email anyone or invite buyers. Invite buyers from the customer’s page in the app. Registered name, active status, discount rules and agreed prices are changed in the app too.

bash
curl -X PUT https://tradecatalog.app/api/v1/customers \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customers": [
      {
        "accountCode": "OAK01",
        "name": "Oakfield Joinery",
        "email": "accounts@oakfieldjoinery.co.uk",
        "discountBp": 500
      },
      {
        "accountCode": "BRK07",
        "name": "Brookside Builders",
        "email": null,
        "discountBp": null
      }
    ]
  }'
js
const res = await fetch('https://tradecatalog.app/api/v1/customers', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ customers }),
})
const { data } = await res.json()
console.log(`${data.created} new, ${data.updated} updated`)
json
{
  "data": { "created": 1, "updated": 1 }
}

The change appears in the activity log as the key’s name.