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:
- How the API is set up.
- How errors, ids, lists and times work.
- How to create and manage users.
- How to create and manage brands and AdButler advertisers.
- How to handle common flows.
- 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.
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) |
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.
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 |
{"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 |
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.
"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.
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}]}
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"}}
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) orDELETE /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.