B2B Customer Registry
The B2B Customer Registry (CRS) holds your business customers: companies that buy from your stores on account, each with a credit limit and a status. For every customer CRS also holds:
- Trusted agents: the people allowed to buy on that customer's behalf.
- Projects: for example a building site, whose purchases are tracked against the customer and can be limited to certain items.
Everything in CRS belongs to one tenant, and the tenant always comes from the caller's token. Within your tenant, a customer, agent or project can belong to a business unit group: a group from Business Unit Management that decides which of your business units (stores) see it. A record without a group is seen by every business unit.
Who reads CRS
| Reader | What it uses CRS for |
|---|---|
| Checkout App, at the till | The cashier searches for a customer, then for an agent and a project of that customer, and attaches them to the transaction. The till follows the customer's status, requireAgent, requireProject and additionalInputs. See B2B Customer in the Checkout App. |
| Entity Conditions checkout evaluation | Reads a project's restrictions and blocks items the project does not allow. See Project restrictions. |
⚠ Project restrictions reach checkout within about a minute, not instantly. Checkout caches a project's restrictions for up to 60 seconds. If the lookup fails, for example because the project was deleted, every item is soft-denied with the reason project_restriction.project_unreachable.
CRS does not decide credit, and it does not create customers on its own. A credit-limit check is forwarded to your system, which answers APPROVED, DECLINED or OVERRIDE-REQUIRED; CRS neither compares the requested amount with the stored credit limit nor looks at the customer's status. A customer created from the till is likewise created by your system first. Both calls go to the address you store in the proxy configuration.
Getting started
- Get a token with the right role. Register an OCMS client for your system and give it the role
crs.api, or a custom role with the permissions you need. See Authentication and permissions. - Set up your business unit groups, if you use them. In Business Unit Management, the hierarchy needs the group type
b2b-customer-registry, and every group you use needs business units assigned to it directly. See Business unit group. - Push your data. Push each customer first, then its agents and projects. See External ingest.
- Connect your system, if it decides credit or creates customers. Store its address and credentials with
PUT /api/v1/proxy-configand implement the two endpoints. Until a proxy configuration is stored, credit-limit checks and customer creation answer412. See Proxy configuration and Tenant system endpoints. - Enable B2B Customer in the Checkout App. See B2B Customer in the Checkout App.
Which integration are you building?
A. Your back office owns your customer data. Your ERP or master-data system pushes customers, agents and projects into CRS and keeps them current, addressing every record by your own ids. Use the external ingest endpoints: see External ingest.
B. Your ERP decides credit and creates customers. CRS calls your system when a till checks a credit limit or creates a customer. You store your system's address and credentials once, then implement two endpoints: see Proxy configuration and Tenant system endpoints.
C. You build or configure a till. The Checkout App calls CRS itself. See B2B Customer in the Checkout App for enabling and using the feature.
| Who calls whom | Endpoints | Read | |
|---|---|---|---|
| A | your system calls CRS | PUT, PATCH, DELETE /api/v1/external-ingest/customers, /agents, /projects | External ingest |
| B | CRS calls your system | you store the address with PUT /api/v1/proxy-config; CRS sends POST {baseUrl}/credit-limit:validate and POST {baseUrl}/customers:create | Proxy configuration, Tenant system endpoints |
| C | the till calls CRS | none for you to build | Checkout App |
A and B combine. When your system answers a customer creation with 202 Accepted, CRS stores nothing; your system then pushes the customer in through external ingest once it exists.
What integrations most often get wrong
- 🛑 Sending a different
businessUnitGroup, or none, for an existing record. The group is part of the key that your own id is matched on, and it cannot change once a record exists. APUTwith your id and another group creates a second, separate record; aPATCHorDELETEwithout the record's group answers404. Always send the group the record was created with. - 🛑 Treating a customer
PUTas a partial update.PUTreplaces the record: every optional field you leave out is cleared, lists become empty and a customer'sstatusgoes back toActive. Send the complete customer every time. See Customer. - Using a group CRS does not know. A record created with a
businessUnitGroupneeds a group from the Business Unit Management hierarchy of typeb2b-customer-registrythat has at least one business unit assigned directly to it. Otherwise the write answers409withBusiness Unit Group does not exist, or is not part of Customer Registry type. See Business unit group. - Repeating the credit limit's
versionTimestamp. A credit limit is stored only when itsversionTimestampis strictly newer than the stored one; an equal or older one is ignored without an error. Set it to the time your system changed the credit limit. See Credit limit. - Writing in the wrong order. Agents and projects need an existing, not deleted customer (
404 Customer not foundotherwise), and a customer that still has agents or projects cannot be deleted (409 Customer has active agents or projects). Create the customer first and delete it last. - Granting only
crs.credit-limit.validatefor credit checks. Checking a credit limit also needscrs.customer.readon the customer's business unit group, or the call answers403. The predefined rolecrs.readonlydoes not includecrs.credit-limit.validate.
Concepts
- Customer: the business customer, its credit limit, status, additional inputs and loyalty, and how search works.
- Trusted agent: a person allowed to buy on behalf of one customer.
- Project: a customer's project, its validity dates, its computed status and its item restrictions.
- Business unit group: which business units see which records, which groups CRS accepts, and who may read and write what.
Two ids for every record
- CRS assigns its own id to every record:
customerId,agentId,projectId. Treat it as an opaque string. It never changes, and it is the id in paths such asGET /api/v1/customers/{customerId}. An agent can also take anagentIdyou send when you create it. - Your own id is
externalCustomerId,externalAgentIdorexternalProjectId. External ingest addresses records by it.externalCustomerIdis unique within one business unit group, not across your tenant, so the sameexternalCustomerIdin two groups is two customers.externalAgentIdandexternalProjectIdare unique within their customer. - There is no read by your own id. Search for it instead, for example
GET /api/v1/customers?externalCustomerId=10042. The match is exact and case-sensitive, and can return one record per business unit group.
Behaviour that holds everywhere
- Writes are synchronous. A successful
PUTorPATCHof a customer, agent or project answers with the stored record; aDELETEanswers204, and so doesPUT /proxy-config. CRS publishes no events. - Deletes are soft. A deleted record disappears from reads and searches, but its key stays reserved: a later
PUTwith the same own id (and the same group) restores it under its original CRS id and answers201. - Unknown properties are rejected. A body or query parameter the API does not define answers
400, for exampleproperty orderNumber should not exist.
Authentication and permissions
Tokens
Every request to /api/v1 needs Authorization: Bearer <access token>. Two kinds of token are accepted:
- Hii Retail IAM user tokens, for people who sign in, such as staff at the till or in the back office.
- OCMS client-credentials tokens, for machine-to-machine calls such as your ERP. See OAuth2 authentication.
An OCMS token must be requested with the audience https://hiiretail.com; a token without it is always rejected.
POST https://auth.retailsvc.com/oauth2/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&audience=https://hiiretail.com
The tenant is always taken from the token. There is no tenant in any path or body, and a Tenant-Id request header has no effect. An id that belongs to another tenant behaves as not found (404).
You may send an optional Correlation-Id header to correlate your request with the logs of CRS; it is not echoed in the response.
Hosts
| Tenant | Base URL |
|---|---|
Tenant ids ending in test | https://crs-api.retailsvc-test.com/api/v1 |
| All other tenants | https://crs-api.retailsvc.com/api/v1 |
⚠ A token used on the wrong host is rejected with 403. A test tenant must call the retailsvc-test.com host. See Test tenants.
Permissions
All permissions have the prefix crs.
| Area | Read | Write |
|---|---|---|
| Customers | crs.customer.read: search, read by id | crs.customer.write: PUT, PATCH, DELETE, POST /customers:create, external ingest of customers |
| Credit-limit check | crs.credit-limit.validate and crs.customer.read on the customer's business unit group: POST /customers/{customerId}/credit-limit:validate | |
| Trusted agents | crs.agent.read: search, read by id | crs.agent.write: PUT, PATCH, DELETE, external ingest of agents |
| Projects | crs.project.read: search, read by id, read restrictions | crs.project.write: PUT, PATCH, DELETE, external ingest of projects |
| Proxy configuration | crs.proxy-config.read: GET /proxy-config | crs.proxy-config.write: PUT /proxy-config |
Roles
| Role | Name | Permissions |
|---|---|---|
crs.api | CRS API | all nine: crs.agent.read, crs.agent.write, crs.credit-limit.validate, crs.customer.read, crs.customer.write, crs.project.read, crs.project.write, crs.proxy-config.read, crs.proxy-config.write |
crs.readonly | CRS Read Only | crs.agent.read, crs.customer.read, crs.project.read, crs.proxy-config.read |
You can also define custom roles with any subset of these permissions. A person gets roles through IAM groups; an OCMS client gets them through the roles attached to its registration template. See Permissions and roles.
Business unit scope
Holding a permission is not always enough: most operations also check it against a business unit or a business unit group. An OCMS client has access to every business unit and group of its tenant. A person is limited to the business units and groups their roles are bound to. Which operation is checked against what, and how bindings combine, is described in Who may read and write which records.
When access is denied
A missing, invalid or insufficient token answers 403, never 401. When you diagnose a 403 by hand, the body tells you where it comes from:
| Body | Meaning |
|---|---|
empty, with response header x-ext-auth-allow: no | the token is missing or invalid, or lacks the endpoint's permission |
{"message":"Forbidden","statusCode":403} | the token has the permission, but not for this business unit or group |
Your code should branch on the status code only, not on the body or the header of a 403.
⚠ On POST /customers/{customerId}/credit-limit:validate and POST /customers:create, a 4xx with a JSON body, 401 and 403 included, can also come from your own system: CRS passes it on unchanged. See Tenant system endpoints.
API reference
The B2B Customer Registry API reference has the full schemas. The base URL is https://crs-api.retailsvc.com/api/v1 (https://crs-api.retailsvc-test.com/api/v1 for test tenants), and the OpenAPI document is served without a token at https://crs-api.retailsvc.com/schemas/v1/openapi.json. Each operation lists its required permission in x-permissions.
⚠ The reference does not list 404 and 409 responses, but the API returns them, for example 404 Customer not found and the 409 conflicts above. The concept and integration pages list them per operation.