OBX Node Admin + Portal API (0.1.0)

Download OpenAPI specification:

License: CC-BY-4.0

The API between the shared admin-ui and a node (brand admin + retailer portal).

Non-normative. Not part of the OBX standard; it is the contract between the shared admin-ui (a static single-page app) and the reference nodes (node-go, node-php, node-ts), so one UI works on every node. Other OBX implementations are free to build their own UI.

Serving. A node serves the admin-ui build at / with single-page-app fallback: any GET that is not under /admin/api, /obx/ or /.well-known/ and accepts text/html returns index.html. UI routes that the node itself links to (e-mails): /invite/{token}, /reset-password/{token}, /request-access, /login. The node's public base URL comes from its configuration (OBX_PUBLIC_URL).

Sessions. Cookie obx_session (HttpOnly, Secure except on http://localhost, SameSite=Lax, Path=/), sliding expiry, at most 30 days, invalidated on logout, password change and blocking. Bearer tokens are NOT accepted here.

CSRF. Every POST/PUT/PATCH/DELETE under /admin/api MUST carry header X-CSRF-Token equal to the csrf_token returned by GET /admin/api/session (which works without being logged in and sets the matching HttpOnly cookie obx_csrf), else 403 csrf_failed. Login rotates the token (use the one in the login response).

Roles. admin (the brand) and retailer. /admin/api/portal/* is for retailers; admins may call it too and get a preview of everything published (their downloads are not logged). Everything else except public/session/setup/password/invite endpoints requires admin. Before first-run setup every other endpoint answers 409 setup_required.

Catalog writes. /admin/api/catalog/* has exactly the semantics of the OBX Ingest API (same bodies, results, errors, change-feed effects), just with session auth.

Errors. application/problem+json with obx_error (docs/protocol.md#errors). Lists use limit + opaque cursor and return {items, next_cursor}.

Session

Login, logout, CSRF, password reset, invites.

Branding for the login and request-access pages

Responses

Response samples

Content type
application/json
{
  • "name": "string",
  • "logo_url": "string",
  • "accent_color": "string",
  • "welcome_text": { },
  • "portal_name": { },
  • "languages": [
    ],
  • "default_language": "nl",
  • "access_requests_enabled": true,
  • "setup_required": true,
  • "brands": [
    ]
}

Current session (also when logged out) and CSRF token

Responses

Response samples

Content type
application/json
{
  • "authenticated": true,
  • "csrf_token": "string",
  • "setup_required": true,
  • "user": {
    },
  • "retailer": {
    }
}

Log in with e-mail and password

401 invalid_credentials (same answer for unknown e-mail), 403 account_blocked. Rate limited per IP and per e-mail (10 per 15 min).

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
email
required
string <email> (Email) <= 254 characters

E-mail address.

password
required
string <= 200 characters

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "authenticated": true,
  • "csrf_token": "string",
  • "setup_required": true,
  • "user": {
    },
  • "retailer": {
    }
}

Log out

Authorizations:
session
header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Send a reset link

Always 202 (no account enumeration). Link <public_url>/reset-password/<token>, valid 1 hour, one-time. Rate limited (5 per 15 min per IP).

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
email
required
string <email> (Email) <= 254 characters

E-mail address.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Set a new password with a reset token

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
token
required
string [ 20 .. 200 ] characters
password
required
string (Password) [ 10 .. 72 ] characters

10-72 characters (72 keeps bcrypt-based nodes compatible). Nodes store argon2id or bcrypt hashes only.

Responses

Request samples

Content type
application/json
{
  • "token": "stringstringstringst",
  • "password": "stringstri"
}

Response samples

Content type
application/json
{
  • "authenticated": true,
  • "csrf_token": "string",
  • "setup_required": true,
  • "user": {
    },
  • "retailer": {
    }
}

Look up an invitation

path Parameters
token
required
string [ 20 .. 200 ] characters

Responses

Response samples

Content type
application/json
{
  • "email": "[email protected]",
  • "name": "string",
  • "role": "admin",
  • "retailer_name": "string",
  • "expires_at": "2026-09-28T10:00:00Z"
}

Accept an invitation by choosing a password

One-time (the token is consumed atomically), valid 7 days. Logs in.

path Parameters
token
required
string [ 20 .. 200 ] characters
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
string <= 200 characters
password
required
string (Password) [ 10 .. 72 ] characters

10-72 characters (72 keeps bcrypt-based nodes compatible). Nodes store argon2id or bcrypt hashes only.

language
string (LanguageTag) ^[a-z]{2,3}(-[A-Z]{2})?$

BCP 47 language tag, restricted to language or language-REGION.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "password": "stringstri",
  • "language": "nl"
}

Response samples

Content type
application/json
{
  • "authenticated": true,
  • "csrf_token": "string",
  • "setup_required": true,
  • "user": {
    },
  • "retailer": {
    }
}

Update own name, language or password

Changing the password requires current_password and ends all other sessions.

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
string [ 1 .. 200 ] characters
language
string (LanguageTag) ^[a-z]{2,3}(-[A-Z]{2})?$

BCP 47 language tag, restricted to language or language-REGION.

current_password
string <= 200 characters
new_password
string (Password) [ 10 .. 72 ] characters

10-72 characters (72 keeps bcrypt-based nodes compatible). Nodes store argon2id or bcrypt hashes only.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "language": "nl",
  • "current_password": "string",
  • "new_password": "stringstri"
}

Response samples

Content type
application/json
{
  • "authenticated": true,
  • "csrf_token": "string",
  • "setup_required": true,
  • "user": {
    },
  • "retailer": {
    }
}

Setup

First-run wizard.

Is first-run setup still required?

Responses

Response samples

Content type
application/json
{
  • "required": true
}

Complete the first-run wizard

Creates the brand profile, the first brand and the first admin (password chosen here; there is never a default password) and logs in. Requires the one-time setup_code that the node prints in its log on first start (or that the operator preset with OBX_SETUP_CODE), so nobody else can claim a freshly deployed node. Rate limited (10 per 15 min per IP). 409 setup_completed once done.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
setup_code
required
string [ 6 .. 64 ] characters
required
object
required
object

Responses

Request samples

Content type
application/json
{
  • "setup_code": "string",
  • "company": {
    },
  • "admin": {}
}

Response samples

Content type
application/json
{
  • "authenticated": true,
  • "csrf_token": "string",
  • "setup_required": true,
  • "user": {
    },
  • "retailer": {
    }
}

Settings

Brand profile, branding and defaults.

Node settings

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "company": {},
  • "branding": {
    },
  • "portal": {
    },
  • "defaults": {
    },
  • "node": {
    }
}

Update settings (JSON merge patch of the writable parts)

Changing portal.allow_original_download or default rights affects what retailers see and is reflected in /changes (grant entries with resync, or asset upserts).

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
object
object
object
object

Responses

Request samples

Content type
application/merge-patch+json
{
  • "company": {},
  • "branding": {
    },
  • "portal": {
    },
  • "defaults": {
    }
}

Response samples

Content type
application/json
{
  • "company": {},
  • "branding": {
    },
  • "portal": {
    },
  • "defaults": {
    },
  • "node": {
    }
}

Catalog

Brand-side catalog management (ingest semantics).

All brands (any status)

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Create or replace a brand

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
required
string [ 1 .. 200 ] characters
Url (string) or null
Url (string) or null
LocalizedText (object) or null
status
string (PublicationStatus)
Enum: "published" "unpublished"

Brand-side publication state. Only 'published' objects (with published parents) are visible through the OBX Protocol.

^x-[a-z0-9]+-[a-z0-9_-]+$
pattern property
any

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Patch a brand

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
object

Responses

Request samples

Content type
application/merge-patch+json
{ }

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Delete a brand without products

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Search products (any status)

Authorizations:
session
query Parameters
q
string <= 200 characters

Matches style number, name, GTIN, SKU (case-insensitive substring).

brand
string

Brand external id.

status
string
Enum: "published" "unpublished"
visibility
string
Enum: "visible" "hidden" "pending_files"
sort
string
Default: "updated_desc"
Enum: "updated_desc" "style_number" "name"
cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string",
  • "total": 0
}

Product with all variants and assets (any status)

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

Responses

Response samples

Content type
application/json
{
  • "record": {
    },
  • "variants": [
    ],
  • "assets": [
    ]
}

Create or replace a product

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
ExternalId (string) or null
style_number
required
string [ 1 .. 200 ] characters
required
object (LocalizedText) non-empty

Free text per language, keyed by BCP 47 tag. At least one language. Example: {"nl": "Trui", "en": "Sweater"}.

LocalizedText (object) or null
item_type
string or null^[a-z][a-z0-9_]{0,63}$
gpc_brick
string or null^[0-9]{8}$
target_group
string or null^[a-z][a-z0-9_]{0,63}$
season
string or null <= 64 characters
collection
string or null <= 200 characters
Array of Materials (objects) or null
Array of Care (objects) or null
CountryCode (string) or null
Url (string) or null
object or null
status
string (PublicationStatus)
Enum: "published" "unpublished"

Brand-side publication state. Only 'published' objects (with published parents) are visible through the OBX Protocol.

^x-[a-z0-9]+-[a-z0-9_-]+$
pattern property
any

Responses

Request samples

Content type
application/json
{
  • "brand_external_id": "KO-1001",
  • "style_number": "string",
  • "name": { },
  • "description": { },
  • "item_type": "string",
  • "gpc_brick": "string",
  • "target_group": "string",
  • "season": "string",
  • "collection": "string",
  • "materials": [
    ],
  • "care": [
    ],
  • "country_of_origin": "NL",
  • "dpp_url": "http://example.com",
  • "attributes": {
    },
  • "status": "published"
}

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Patch a product (e.g. publish/unpublish)

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
object

Responses

Request samples

Content type
application/merge-patch+json
{ }

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Delete a product with its variants and assets

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Reorder the images of one product colour

Same as Ingest PUT /products/{external_id}/asset-order.

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
color_code
required
string or null
asset_external_ids
required
Array of strings (ExternalId) non-empty unique [ items [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$ ]

All assets of this product+colour in the desired order; they get positions 1..n atomically.

Responses

Request samples

Content type
application/json
{
  • "color_code": "string",
  • "asset_external_ids": [
    ]
}

Response samples

Content type
application/json
[
  • {
    }
]

Create or replace a variant

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
product_external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

GtinInput (string) or null
sku
string or null <= 200 characters
required
object (Color)
size
string or null <= 64 characters
size_system
string or null <= 16 characters
status
string (PublicationStatus)
Enum: "published" "unpublished"

Brand-side publication state. Only 'published' objects (with published parents) are visible through the OBX Protocol.

^x-[a-z0-9]+-[a-z0-9_-]+$
pattern property
any

Responses

Request samples

Content type
application/json
{
  • "product_external_id": "KO-1001",
  • "gtin": "2000010010011",
  • "sku": "string",
  • "color": {
    },
  • "size": "string",
  • "size_system": "string",
  • "status": "published"
}

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Patch a variant

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
object

Responses

Request samples

Content type
application/merge-patch+json
{ }

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Delete a variant

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Create or replace asset metadata

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
product_external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

color_code
string or null <= 64 characters
variant_external_ids
Array of strings or null (ExternalId) [ items [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$ ]
media_type
string
Value: "image"
role
required
string (Role)
Enum: "packshot" "model" "detail" "ghost" "flatlay" "scene" "swatch" "size_chart" "other"

What the image shows. Stable key; labels are presentation.

View (string) or null
position
required
integer [ 1 .. 999 ]
filename
string or null <= 255 characters

Download filename; default <external_id>.<ext>.

LocalizedText (object) or null
object or null

Partial Rights; omitted fields take the node's default rights (Settings). null = all defaults.

object or null

Omitted fields default to ai_generated=false, ai_edited=false, source=photo (generated when ai_generated), c2pa=none (nodes MAY detect embedded C2PA).

object or null

Attach previously uploaded blobs atomically. Omit to keep current files (or upload via PUT .../files/{rendition}).

status
string (PublicationStatus)
Enum: "published" "unpublished"

Brand-side publication state. Only 'published' objects (with published parents) are visible through the OBX Protocol.

^x-[a-z0-9]+-[a-z0-9_-]+$
pattern property
any

Responses

Request samples

Content type
application/json
{
  • "product_external_id": "KO-1001",
  • "color_code": "string",
  • "variant_external_ids": [
    ],
  • "media_type": "image",
  • "role": "packshot",
  • "view": "front",
  • "position": 1,
  • "filename": "string",
  • "alt_text": { },
  • "rights": {
    },
  • "provenance": {
    },
  • "files": {
    },
  • "status": "published"
}

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Patch asset metadata (role, view, rights, provenance, status)

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
object

Responses

Request samples

Content type
application/merge-patch+json
{ }

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Delete an asset

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Upload the original or a client-generated rendition

Same as Ingest PUT /assets/{external_id}/files/{rendition}. The admin-ui generates web/square/thumb in the browser when the node reports renditions_server_generated=false (Settings.node).

Authorizations:
session
path Parameters
external_id
required
string (ExternalId) [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$
Examples: KO-1001

The brand's own identifier for an object (from its PIM/ERP/spreadsheet). 1-200 characters, no control characters and no '/'. Leading/trailing whitespace is trimmed by nodes. Compared case-sensitively.

rendition
required
string (RenditionName)
Enum: "original" "web" "square" "thumb"

Rendition name.

header Parameters
OBX-Content-SHA256
string (Sha256) ^[a-f0-9]{64}$

Lowercase hex SHA-256 of the exact bytes.

X-CSRF-Token
required
string
Request Body schema: image/*
required
string <application/octet-stream>

Responses

Response samples

Content type
application/json
{
  • "index": 0,
  • "op": "upsert",
  • "kind": "brand",
  • "external_id": "KO-1001",
  • "result": "created",
  • "id": "prd_01J9ZK3Q7T5W",
  • "visibility": "visible",
  • "etag": "string",
  • "error": {
    },
  • "warnings": [
    ]
}

Does the node already have these bytes? (200/404)

Authorizations:
session
path Parameters
sha256
required
string (Sha256) ^[a-f0-9]{64}$

Lowercase hex SHA-256 of the exact bytes.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Upload bytes (same as Ingest PUT /blobs/{sha256})

Authorizations:
session
path Parameters
sha256
required
string (Sha256) ^[a-f0-9]{64}$

Lowercase hex SHA-256 of the exact bytes.

header Parameters
X-CSRF-Token
required
string
Request Body schema: image/*
required
string <application/octet-stream>

Responses

Response samples

Content type
application/json
{
  • "sha256": "string",
  • "bytes": 1,
  • "content_type": "string",
  • "width": 1,
  • "height": 1,
  • "created": true
}

Imports

Uploads and scheduled pull imports.

Import runs (uploads, API, scheduled), newest first

Authorizations:
session
query Parameters
source_id
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: source_id=prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Upload a CSV and/or images (same fields as Ingest POST /imports)

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: multipart/form-data
required
package
string <application/zip>
csv
string <text/csv>
assets_csv
string <text/csv>
images
Array of strings
images_manifest
Array of objects

Same as Ingest ImagesManifest (blobs uploaded via /admin/api/catalog/blobs/{sha256}).

object

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "source_id": "prd_01J9ZK3Q7T5W",
  • "trigger": "upload",
  • "status": "queued",
  • "mode": "upsert",
  • "dry_run": true,
  • "created_at": "2026-09-28T10:00:00Z",
  • "started_at": "2026-09-28T10:00:00Z",
  • "finished_at": "2026-09-28T10:00:00Z",
  • "files": [
    ],
  • "counts": {
    },
  • "error": {
    },
  • "errors_preview": [
    ]
}

One import run

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "source_id": "prd_01J9ZK3Q7T5W",
  • "trigger": "upload",
  • "status": "queued",
  • "mode": "upsert",
  • "dry_run": true,
  • "created_at": "2026-09-28T10:00:00Z",
  • "started_at": "2026-09-28T10:00:00Z",
  • "finished_at": "2026-09-28T10:00:00Z",
  • "files": [
    ],
  • "counts": {
    },
  • "error": {
    },
  • "errors_preview": [
    ]
}

Row and file errors/warnings of a run

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

query Parameters
cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Canonical CSV template with header and two example rows

Authorizations:
session

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Scheduled pull imports

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Add a scheduled pull import (https_url, sftp or s3)

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
string
kind
string
Enum: "https_url" "sftp" "s3"
enabled
boolean
schedule
object
mode
string
Enum: "upsert" "mirror"
allow_mass_unpublish
boolean
config
object
credentials
object or null

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "kind": "https_url",
  • "enabled": true,
  • "schedule": { },
  • "mode": "upsert",
  • "allow_mass_unpublish": true,
  • "config": { },
  • "credentials": { }
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "name": "string",
  • "kind": "https_url",
  • "enabled": true,
  • "schedule": {
    },
  • "mode": "upsert",
  • "allow_mass_unpublish": true,
  • "config": {},
  • "credentials": {
    },
  • "credentials_set": {
    },
  • "last_run": {
    },
  • "next_run_at": "2026-09-28T10:00:00Z",
  • "created_at": "2026-09-28T10:00:00Z",
  • "updated_at": "2026-09-28T10:00:00Z"
}

One source

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "name": "string",
  • "kind": "https_url",
  • "enabled": true,
  • "schedule": {
    },
  • "mode": "upsert",
  • "allow_mass_unpublish": true,
  • "config": {},
  • "credentials": {
    },
  • "credentials_set": {
    },
  • "last_run": {
    },
  • "next_run_at": "2026-09-28T10:00:00Z",
  • "created_at": "2026-09-28T10:00:00Z",
  • "updated_at": "2026-09-28T10:00:00Z"
}

Update a source (merge patch; credentials write-only)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
name
string
kind
string
Enum: "https_url" "sftp" "s3"
enabled
boolean
schedule
object
mode
string
Enum: "upsert" "mirror"
allow_mass_unpublish
boolean
config
object
credentials
object or null

Responses

Request samples

Content type
application/merge-patch+json
{
  • "name": "string",
  • "kind": "https_url",
  • "enabled": true,
  • "schedule": { },
  • "mode": "upsert",
  • "allow_mass_unpublish": true,
  • "config": { },
  • "credentials": { }
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "name": "string",
  • "kind": "https_url",
  • "enabled": true,
  • "schedule": {
    },
  • "mode": "upsert",
  • "allow_mass_unpublish": true,
  • "config": {},
  • "credentials": {
    },
  • "credentials_set": {
    },
  • "last_run": {
    },
  • "next_run_at": "2026-09-28T10:00:00Z",
  • "created_at": "2026-09-28T10:00:00Z",
  • "updated_at": "2026-09-28T10:00:00Z"
}

Delete a source (its runs stay in history)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Run now (also for manual schedules)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
optional
dry_run
boolean
Default: false
force
boolean
Default: false

Import even if the remote files did not change.

Responses

Request samples

Content type
application/json
{
  • "dry_run": false,
  • "force": false
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "source_id": "prd_01J9ZK3Q7T5W",
  • "trigger": "upload",
  • "status": "queued",
  • "mode": "upsert",
  • "dry_run": true,
  • "created_at": "2026-09-28T10:00:00Z",
  • "started_at": "2026-09-28T10:00:00Z",
  • "finished_at": "2026-09-28T10:00:00Z",
  • "files": [
    ],
  • "counts": {
    },
  • "error": {
    },
  • "errors_preview": [
    ]
}

Check the connection and list what would be imported

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "ok": true,
  • "message": "string",
  • "host_key_fingerprint": "string",
  • "files": [
    ]
}

Retailers

Retailers, access requests, accounts and API clients.

Retailers

Authorizations:
session
query Parameters
q
string <= 200 characters
status
string
Enum: "active" "blocked"
cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Add a retailer manually

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
string [ 1 .. 200 ] characters
reference
string or null <= 100 characters
website
string or null
vat_number
string or null
country
string or null^[A-Z]{2}$
notes
string or null <= 5000 characters
status
string
Enum: "active" "blocked"
brand_ids
Array of strings or null
allow_original
boolean or null

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "reference": "string",
  • "website": "string",
  • "vat_number": "string",
  • "country": "string",
  • "notes": "string",
  • "status": "active",
  • "brand_ids": [
    ],
  • "allow_original": true
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "name": "string",
  • "reference": "string",
  • "website": "string",
  • "vat_number": "string",
  • "country": "string",
  • "notes": "string",
  • "status": "active",
  • "brand_ids": [
    ],
  • "allow_original": true,
  • "user_count": 0,
  • "api_client_count": 0,
  • "last_activity_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z"
}

One retailer

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "name": "string",
  • "reference": "string",
  • "website": "string",
  • "vat_number": "string",
  • "country": "string",
  • "notes": "string",
  • "status": "active",
  • "brand_ids": [
    ],
  • "allow_original": true,
  • "user_count": 0,
  • "api_client_count": 0,
  • "last_activity_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z"
}

Update a retailer, its grant, or block/unblock it (status)

Blocking ends all its sessions and invalidates its API tokens immediately (access_revoked). Grant changes emit a grant change with resync for its consumers.

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
name
string [ 1 .. 200 ] characters
reference
string or null <= 100 characters
website
string or null
vat_number
string or null
country
string or null^[A-Z]{2}$
notes
string or null <= 5000 characters
status
string
Enum: "active" "blocked"
brand_ids
Array of strings or null
allow_original
boolean or null

Responses

Request samples

Content type
application/merge-patch+json
{
  • "name": "string",
  • "reference": "string",
  • "website": "string",
  • "vat_number": "string",
  • "country": "string",
  • "notes": "string",
  • "status": "active",
  • "brand_ids": [
    ],
  • "allow_original": true
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "name": "string",
  • "reference": "string",
  • "website": "string",
  • "vat_number": "string",
  • "country": "string",
  • "notes": "string",
  • "status": "active",
  • "brand_ids": [
    ],
  • "allow_original": true,
  • "user_count": 0,
  • "api_client_count": 0,
  • "last_activity_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z"
}

Delete a retailer with its accounts and API clients (download history is kept, anonymised to the retailer name)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Access requests, newest first

Authorizations:
session
query Parameters
status
string
Enum: "pending" "approved" "rejected"
cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

One access request

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "status": "pending",
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "reason": "string",
  • "retailer": {
    },
  • "contact": {
    },
  • "access": [
    ],
  • "requested_brand_ids": [
    ],
  • "message": "string",
  • "retailer_id": "string",
  • "source_ip_country": "string"
}

Approve

Creates (or links) the retailer, optionally invites the contact as a portal user (e-mail with a one-time link) and optionally creates an API client whose secret is handed to the requester exactly once via the protocol status endpoint. 409 conflict if not pending.

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
optional
retailer_id
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$

Link to an existing retailer instead of creating one.

brand_ids
Array of strings or null (Id) [ items^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$ ]

Grant; null/omitted = the requested brands, or all when none requested.

invite_portal_user
boolean

Default true when 'portal' was requested.

create_api_client
boolean

Default true when 'api' was requested.

Responses

Request samples

Content type
application/json
{
  • "retailer_id": "prd_01J9ZK3Q7T5W",
  • "brand_ids": [
    ],
  • "invite_portal_user": true,
  • "create_api_client": true
}

Response samples

Content type
application/json
{
  • "access_request": {
    },
  • "retailer": {
    },
  • "invite": {
    },
  • "api_client": {
    }
}

Reject (reason optional, shown to the requester)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
optional
reason
string or null <= 2000 characters

Responses

Request samples

Content type
application/json
{
  • "reason": "string"
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "status": "pending",
  • "created_at": "2019-08-24T14:15:22Z",
  • "decided_at": "2019-08-24T14:15:22Z",
  • "reason": "string",
  • "retailer": {
    },
  • "contact": {
    },
  • "access": [
    ],
  • "requested_brand_ids": [
    ],
  • "message": "string",
  • "retailer_id": "string",
  • "source_ip_country": "string"
}

Accounts (admins and retailer users)

Authorizations:
session
query Parameters
role
string
Enum: "admin" "retailer"
retailer_id
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: retailer_id=prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Invite an admin or a retailer user by e-mail (one-time link, 7 days)

409 conflict when the e-mail already has an account on this node. When e-mail is not configured, email_sent is false and the admin copies invite_url manually.

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
email
required
string <email> (Email) <= 254 characters

E-mail address.

name
string <= 200 characters
role
required
string
Enum: "admin" "retailer"
retailer_id
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$

Required for role retailer.

language
string (LanguageTag) ^[a-z]{2,3}(-[A-Z]{2})?$

BCP 47 language tag, restricted to language or language-REGION.

Responses

Request samples

Content type
application/json
{
  • "email": "[email protected]",
  • "name": "string",
  • "role": "admin",
  • "retailer_id": "prd_01J9ZK3Q7T5W",
  • "language": "nl"
}

Response samples

Content type
application/json
{
  • "user": {
    },
  • "invite_url": "http://example.com",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "email_sent": true
}

Block/unblock or rename an account

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
name
string <= 200 characters
status
string
Enum: "active" "blocked"

Responses

Request samples

Content type
application/merge-patch+json
{
  • "name": "string",
  • "status": "active"
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "email": "[email protected]",
  • "name": "string",
  • "role": "admin",
  • "retailer_id": "prd_01J9ZK3Q7T5W",
  • "status": "invited",
  • "language": "nl",
  • "last_login_at": "2019-08-24T14:15:22Z",
  • "invite_expires_at": "2019-08-24T14:15:22Z",
  • "created_at": "2019-08-24T14:15:22Z"
}

Delete an account

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

New invitation link (old one stops working)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "user": {
    },
  • "invite_url": "http://example.com",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "email_sent": true
}

API clients for retailers' software

Authorizations:
session
query Parameters
retailer_id
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: retailer_id=prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Create client credentials for a retailer (secret shown once)

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
retailer_id
required
string
name
required
string [ 1 .. 200 ] characters
scopes
Array of strings (Scope)
Items Enum: "catalog:read" "media:read" "media:original" "usage:write"

Omitted: catalog:read and media:read, plus media:original when the retailer may download originals. usage:write only when requested explicitly. Scopes are limited to what the retailer's grant allows.

Responses

Request samples

Content type
application/json
{
  • "retailer_id": "string",
  • "name": "string",
  • "scopes": [
    ]
}

Response samples

Content type
application/json
{
  • "client": {
    },
  • "client_secret": "string",
  • "token_endpoint": "http://example.com",
  • "discovery_url": "http://example.com"
}

New secret (old one stops working immediately; shown once)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "client": {
    },
  • "client_secret": "string",
  • "token_endpoint": "http://example.com",
  • "discovery_url": "http://example.com"
}

Revoke (its tokens stop working immediately)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Automation

Ingest tokens and webhooks for the brand's own systems.

Ingest tokens for the brand's own automation

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Create an ingest token (shown once)

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
required
string [ 1 .. 200 ] characters
scopes
required
Array of strings (IngestScope) non-empty
Items Enum: "ingest:read" "ingest:write" "ingest:delete"
brand_external_ids
Array of strings or null (ExternalId) [ items [ 1 .. 200 ] characters ^[^\x00-\x1F\x7F/]+$ ]
DateTime (string) or null

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "scopes": [
    ],
  • "brand_external_ids": [
    ],
  • "expires_at": "2026-09-28T10:00:00Z"
}

Response samples

Content type
application/json
{
  • "token": {
    },
  • "secret": "string",
  • "ingest_url": "http://example.com"
}

New secret (old one stops working immediately; shown once)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "token": {
    },
  • "secret": "string",
  • "ingest_url": "http://example.com"
}

Revoke

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

The brand's own webhooks

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Subscribe the brand's system to node events

Events access_request.created, import_run.completed, ingest_job.completed. Same signing scheme as protocol webhooks. The secret is returned once.

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
url
required
string <uri> (Url) <= 2048 characters ^https?://

Absolute http(s) URL.

events
required
Array of strings non-empty
Items Enum: "access_request.created" "import_run.completed" "ingest_job.completed"

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "events": [
    ],
  • "secret": "string",
  • "status": "active",
  • "created_at": "2026-09-28T10:00:00Z",
  • "last_delivery_at": "2026-09-28T10:00:00Z",
  • "last_status": 0
}

Change events or re-enable

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string
Request Body schema: application/merge-patch+json
required
events
Array of strings
status
string
Enum: "active" "disabled"

Responses

Request samples

Content type
application/merge-patch+json
{
  • "events": [
    ],
  • "status": "active"
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "events": [
    ],
  • "secret": "string",
  • "status": "active",
  • "created_at": "2026-09-28T10:00:00Z",
  • "last_delivery_at": "2026-09-28T10:00:00Z",
  • "last_status": 0
}

Delete

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Send webhook.test now

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "id": "string",
  • "event_id": "string",
  • "event_type": "string",
  • "at": "2019-08-24T14:15:22Z",
  • "status_code": 0,
  • "duration_ms": 0,
  • "attempt": 0,
  • "success": true,
  • "error": "string"
}

Recent deliveries (last 7 days)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

query Parameters
cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Stats

Downloads and usage.

Download and usage statistics

Authorizations:
session
query Parameters
days
integer
Default: 30
Enum: 7 30 90 365

Responses

Response samples

Content type
application/json
{
  • "days": 0,
  • "totals": {
    },
  • "by_day": [
    ],
  • "top_retailers": [
    ],
  • "top_products": [
    ]
}

Raw download/usage log, newest first

Authorizations:
session
query Parameters
retailer_id
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: retailer_id=prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

kind
string (DownloadKind)
Enum: "image" "product_zip" "selection_zip" "bulk_zip" "csv" "api_rendition" "api_export" "usage"
cursor
string (Cursor) [ 1 .. 4096 ] characters

Opaque pagination or feed cursor. Clients MUST NOT parse or construct it.

limit
integer [ 1 .. 200 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string"
}

Portal

The retailer portal.

Who is looking and what they may do

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "preview": true,
  • "retailer": {
    },
  • "brands": [
    ],
  • "permissions": {
    },
  • "branding": {
    },
  • "languages": [
    ],
  • "selection_count": 0,
  • "limits": {
    }
}

Browse the catalog (visible products only)

Authorizations:
session
query Parameters
q
string <= 200 characters

Style number, name, colour, GTIN or SKU.

brand_id
Array of strings
item_type
Array of strings
color
Array of strings

Colour code.

season
Array of strings
collection
Array of strings
target_group
Array of strings
updated_since
string <date-time>

"New since": products that got new/changed data or images since this time.

Responses

Response samples

Content type
application/json
{
  • "items": [
    ],
  • "next_cursor": "string",
  • "total": 0
}

Filter options with counts for the current filter

Authorizations:
session
query Parameters
q
string <= 200 characters

Style number, name, colour, GTIN or SKU.

brand_id
Array of strings
item_type
Array of strings
color
Array of strings

Colour code.

season
Array of strings
collection
Array of strings
target_group
Array of strings
updated_since
string <date-time>

"New since": products that got new/changed data or images since this time.

Responses

Response samples

Content type
application/json
{
  • "brands": [
    ],
  • "item_types": [
    ],
  • "colors": [
    ],
  • "seasons": [
    ],
  • "collections": [
    ],
  • "target_groups": [
    ]
}

Product detail (protocol ProductDetail shape)

Rendition URLs are signed and usable directly in an <img> element. original only when the viewer may download originals.

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "external_id": "KO-1001",
  • "brand_id": "prd_01J9ZK3Q7T5W",
  • "style_number": "string",
  • "name": { },
  • "description": { },
  • "item_type": "string",
  • "gpc_brick": "string",
  • "target_group": "string",
  • "season": "string",
  • "collection": "string",
  • "materials": [
    ],
  • "care": [
    ],
  • "country_of_origin": "NL",
  • "dpp_url": "http://example.com",
  • "attributes": {
    },
  • "created_at": "2026-09-28T10:00:00Z",
  • "updated_at": "2026-09-28T10:00:00Z",
  • "variants": [
    ],
  • "assets": [
    ]
}

Paste an EAN/GTIN list (max 2000) and get matches + unmatched

text is split on anything that is not a digit (newlines, commas, semicolons, tabs, spaces between codes are all fine) - or send gtins. Same result shape and matching rules as the protocol by-GTIN lookup.

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
text
string <= 100000 characters
gtins
Array of strings <= 2000 items [ items <= 64 characters ]

Responses

Request samples

Content type
application/json
{
  • "text": "string",
  • "gtins": [
    ]
}

Response samples

Content type
application/json
{
  • "results": [
    ],
  • "products": [
    ]
}

The viewer's selection (persisted per user)

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "product_ids": [
    ],
  • "image_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Replace the selection (max 1000 products; invisible ids are dropped)

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
product_ids
required
Array of strings (Id) <= 1000 items unique [ items^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$ ]

Responses

Request samples

Content type
application/json
{
  • "product_ids": [
    ]
}

Response samples

Content type
application/json
{
  • "product_ids": [
    ],
  • "image_count": 0,
  • "updated_at": "2019-08-24T14:15:22Z"
}

Clear the selection

Authorizations:
session
header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Download one image (logged)

Content-Disposition: attachment. 403 original_not_allowed for original when not permitted.

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

query Parameters
variant
string
Enum: "original" "web" "square"

Default original when allowed, else web.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

All images of one product as zip (streamed, logged)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

query Parameters
variant
string
Enum: "original" "web" "square"

Default original when allowed, else web.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Images of the selection as zip (max 400 images; more = use a download job)

Authorizations:
session
query Parameters
variant
string
Enum: "original" "web" "square"

Default original when allowed, else web.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Article data of the selection as CSV (logged)

Canonical CSV columns (docs/import-format.md) plus image_files. 403 when the brand disabled article data export.

Authorizations:
session
query Parameters
delimiter
string
Default: "comma"
Enum: "comma" "semicolon"

The UI uses semicolon for Dutch users (Excel), comma otherwise.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

Article data of the current filter as CSV (logged)

Authorizations:
session
query Parameters
q
string
brand_id
Array of strings
updated_since
string <date-time>
delimiter
string
Default: "comma"
Enum: "comma" "semicolon"

The UI uses semicolon for Dutch users (Excel), comma otherwise.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

The viewer's background downloads (last 7 days)

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Build a large zip in the background (selection, EAN list or whole filter)

Max 2 running jobs per user (409). When finished the node e-mails the user (if mail is configured) and the job shows a download link. Files are kept 7 days.

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
source
required
string
Enum: "selection" "products" "filter"
product_ids
Array of strings <= 10000 items

For source products (e.g. matches of an EAN list).

object

For source filter; same keys as the portal product filters.

variant
required
string
Enum: "original" "web" "square"
include_data_csv
boolean
Default: true

Responses

Request samples

Content type
application/json
{
  • "source": "selection",
  • "product_ids": [
    ],
  • "filter": {
    },
  • "variant": "original",
  • "include_data_csv": true
}

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "status": "queued",
  • "source": "selection",
  • "variant": "original",
  • "include_data_csv": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "finished_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "progress": {
    },
  • "file_url": "string",
  • "bytes": 0,
  • "files": 0,
  • "error": "string"
}

Job status

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/json
{
  • "id": "prd_01J9ZK3Q7T5W",
  • "status": "queued",
  • "source": "selection",
  • "variant": "original",
  • "include_data_csv": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "finished_at": "2019-08-24T14:15:22Z",
  • "expires_at": "2019-08-24T14:15:22Z",
  • "progress": {
    },
  • "file_url": "string",
  • "bytes": 0,
  • "files": 0,
  • "error": "string"
}

Download the finished zip (Range supported; only the job owner)

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}

The retailer's own API clients

Authorizations:
session

Responses

Response samples

Content type
application/json
{
  • "items": [
    ]
}

Create an API client for the retailer's own software (only if the brand allows self-service)

403 forbidden when portal.retailer_api_self_service is off. Scopes are limited to the retailer's grant. Max 5 active clients per retailer.

Authorizations:
session
header Parameters
X-CSRF-Token
required
string
Request Body schema: application/json
required
name
required
string [ 1 .. 200 ] characters
scopes
Array of strings (Scope)
Items Enum: "catalog:read" "media:read" "media:original" "usage:write"

Omitted = the same defaults as ApiClientInput.scopes.

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "scopes": [
    ]
}

Response samples

Content type
application/json
{
  • "client": {
    },
  • "client_secret": "string",
  • "token_endpoint": "http://example.com",
  • "discovery_url": "http://example.com"
}

Rotate own client secret

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/json
{
  • "client": {
    },
  • "client_secret": "string",
  • "token_endpoint": "http://example.com",
  • "discovery_url": "http://example.com"
}

Revoke own client

Authorizations:
session
path Parameters
id
required
string (Id) ^[A-Za-z0-9][A-Za-z0-9._~-]{0,127}$
Examples: prd_01J9ZK3Q7T5W

Opaque, node-assigned identifier. Stable for the life of the object. Consumers MUST treat it as opaque.

header Parameters
X-CSRF-Token
required
string

Responses

Response samples

Content type
application/problem+json
{
  • "title": "string",
  • "status": 400,
  • "detail": "string",
  • "instance": "string",
  • "obx_error": "invalid_request",
  • "errors": [
    ],
  • "retry_after": 0
}