Commerce Catalyst Self-Service User API

This API lets a retailer create and manage the people who sign in to its Commerce Catalyst brand portal, and the brands each of them can work on, from its own systems. You will learn:

  1. How the API is set up.
  2. How errors, ids, lists and times work.
  3. How to create and manage users.
  4. How to create and manage brands and AdButler advertisers.
  5. How to handle common flows.
Before you start, it helps to know the two records this API manages:
  • Brand: one AdButler advertiser. Its campaigns, reports, wallet and payment cards belong to the brand, so everyone on the brand shares them.
  • User: a person who signs in to the brand portal, with single sign-on or a password. A user can work on several brands (for example an agency), and a brand can have several users. Users switch brands from the portal header.
A user with no brand yet can sign in and is told their account isn't linked to a brand.


Overview

Setting Value
Base address https://commercecatalyst.adbutler.com/api/v1/self-service
Authentication Authorization: Bearer cc_…
Request and response format JSON (Content-Type: application/json)
Requests use a Commerce Catalyst API key, not an AdButler API key. Create one in Commerce Catalyst under Settings → API Keys.


A key acts with the rights of the person who created it. Reading needs any signed-in role. Creating or changing users and brands needs the Manage Self-Service permission.


Environments. Each Commerce Catalyst organization (for example staging and production) has its own users, brands, AdButler account and keys. A key only ever sees its own organization.


Campaigns, ads and reporting stay on the AdButler API (https://api.adbutler.com/v2, AdButler key). A brand's adbutlerAdvertiserId is the advertiser to use there.




Conventions

Errors

Errors come back as JSON with an HTTP status. field names the input that caused it, when there is one. Errors about a record that already exists also give that record's id, so a retry can carry on with it.

{"error": {"code": "email_taken", "message": "A user with this email already exists.", "field": "email", "userId": 10045}}
Status Code Meaning
400 invalid_field An input is missing or wrong; field names it
400 invalid_body The body isn't a JSON object
400 nothing_to_update A change request named no field
404 not_found No such user or brand in this organization
404 adbutler_advertiser_not_found No advertiser with that id in the organization's AdButler account
409 email_taken Another user has this email (userId given)
409 external_id_taken Another user or brand has this externalId (userId or brandId given)
409 adbutler_advertiser_taken Another brand uses this AdButler advertiser (brandId given)
502 adbutler_unavailable AdButler couldn't be reached; nothing was created, try again
A missing or wrong key (401), or a key whose owner lacks Manage Self-Service (403), answers in a different shape: {"object": "error", "type": "…", "http_status": 403, "message": "…"}


Your own ids

Users and brands each take an optional externalId (for example a CRM user id or account id). It is unique within the organization, so records can be looked up by it.


Lists and times

Lists take limit (1 to 200, default 50) and offset, and return {"data": […], "total": n, "limit": 50, "offset": 0}.

Times are UTC, ISO 8601 (2026-10-26T15:04:05Z).




Users

Method Path What it does
GET /users Find users. Filters: email (exact, any case), externalId, brandId, q (part of the email or name)
POST /users Create a user
GET /users/{id} Get a user
PATCH /users/{id} Change a user
DELETE /users/{id} Remove a user (204). Their brands, campaigns and payments stay
POST /users/{id}/brands Give a user a brand: {"brandId": 10031}
PUT /users/{id}/brands Set exactly these brands: {"brandIds": [10031, 10032]} (an empty list removes all)
DELETE /users/{id}/brands/{brandId} Take a brand away


Create a user

POST /users. Only email is required.

Field Type Notes
email string Unique in the organization
name string
password string, 8+ characters Leave out for single sign-on only
externalId string Your id for the person
status active or suspended Default active
brandIds integer list Existing brands to give them
newBrand object A brand to create and give them, same fields as POST /brands
Commerce Catalyst sends the new user no email. They sign in with single sign-on (matched by email) or with the password you set.
POST /users
Authorization: Bearer cc_…
Content-Type: application/json

{"email": "buyer@supplier.example", "name": "Jamie Buyer", "externalId": "USR-1001", "brandIds": [10031]}

Response: 201 Created

{
  "id": 10045,
  "email": "buyer@supplier.example",
  "name": "Jamie Buyer",
  "status": "active",
  "externalId": "USR-1001",
  "hasPassword": false,
  "lastLoginAt": null,
  "createdAt": "2026-10-26T15:04:05Z",
  "updatedAt": "2026-10-26T15:04:05Z",
  "brands": [
    {"id": 10031, "name": "Supplier Co", "adbutlerAdvertiserId": 5574, "status": "approved", "externalId": "ACC-2001"}
  ]
}
"hasPassword": false means single sign-on only.


Change a user

PATCH /users/{id}. Send only what changes: name, email, status, externalId, password (null removes it). Returns the user.

A new email, a new or removed password, or "status": "suspended" signs the user out everywhere.




Brands and AdButler advertisers

Method Path What it does
GET /brands Find brands, each with its users. Filters: name (part, any case), externalId, adbutlerAdvertiserId, status
POST /brands Create a brand
GET /brands/{id} Get a brand with its users
PATCH /brands/{id} Change a brand
GET /adbutler-advertisers?name= Search the AdButler account's advertisers by name


Create a brand

POST /brands

Field Type Notes
name string Required unless adbutlerAdvertiserId is given (then the advertiser's name is used)
adbutlerAdvertiserId integer Use this existing advertiser. It must be in the organization's AdButler account and not used by another brand. Without it, a new AdButler advertiser is created
externalId string Your id for the brand
status approved, pending or rejected Default approved
POST /brands

{"adbutlerAdvertiserId": 467992, "externalId": "ACC-2002"}

Response: 201 Created

{
  "id": 10032,
  "name": "Paper Goods Co",
  "adbutlerAdvertiserId": 467992,
  "status": "approved",
  "externalId": "ACC-2002",
  "createdAt": "2026-10-26T15:06:10Z",
  "users": []
}


Change a brand

PATCH /brands/{id}. Send only what changes: name, status, externalId, adbutlerAdvertiserId.

A brand that isn't approved can't be worked on in the portal; its users move to their other brands.

A new adbutlerAdvertiserId must pass the same checks as on create, and the portal then shows that advertiser's campaigns and reports.


Search advertisers

GET /adbutler-advertisers?name=paper returns advertisers in the organization's AdButler account whose name contains the text, with the brand already using each.

{"data": [{"id": 467992, "name": "Paper Goods Co", "createdAt": "2026-10-15T20:50:44Z", "brandId": 10032}]}
Use it to spot duplicates before creating a brand.




Common flows

A new supplier signs up on the retailer's site

Create the brand, then the user. Or do both in one call with newBrand:

POST /users

{"email": "owner@newsupplier.example", "name": "Sam Owner", "externalId": "USR-1002",
 "newBrand": {"name": "New Supplier Co", "externalId": "ACC-2003"}}
If the supplier already has an AdButler advertiser, find it with GET /adbutler-advertisers?name= and pass adbutlerAdvertiserId instead of a name.


A new person at a supplier that already has a brand

GET /brands?externalId=ACC-2001          → the brand's id
POST /users {"email": "buyer@supplier.example", "brandIds": [10031]}


An agency or partner working for several suppliers

POST /users {"email": "planner@agency.example", "brandIds": [10031, 10032, 10040]}
POST /users/10050/brands {"brandId": 10041}
DELETE /users/10050/brands/10032


Keeping records in sync

  • Name or email change: PATCH /users/{id}. A new email signs the person out; single sign-on matches the new email from then on.
  • Someone leaves: PATCH /users/{id} with {"status": "suspended"} (reversible) or DELETE /users/{id}.
  • A supplier stops: PATCH /brands/{id} with {"status": "rejected"}.
  • Retries: creating something that already exists returns 409 with the existing record's id, and nothing is created.




This concludes the guide. With these endpoints, your own systems can keep Commerce Catalyst brand portal users and brands in step with your supplier records, while campaigns, ads and reporting continue to run through the AdButler API.

We encourage you to read more of our documentation. You can also get in touch with our support team by opening a support ticket while you are logged in to your account.


Can't find what you're looking for?

Send us an email

hello@adbutler.com

Visit the blog

For more ad serving tips, industry news and AdButler insights.