Skip to main content

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.

CRS keeps your B2B customers, their trusted agents and their projects, optionally per business unit group. Your back office pushes them in through external ingest. The Checkout App searches and reads customers, checks credit limits and creates customers, and Entity Conditions checkout evaluation reads project restrictions. CRS forwards credit-limit checks and customer creation to your system at the address in the proxy configuration. Business Unit Management sends CRS the changes to which business units belong to which group.

Who reads CRS​

ReaderWhat it uses CRS for
Checkout App, at the tillThe 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 evaluationReads 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​

  1. 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.
  2. 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.
  3. Push your data. Push each customer first, then its agents and projects. See External ingest.
  4. Connect your system, if it decides credit or creates customers. Store its address and credentials with PUT /api/v1/proxy-config and implement the two endpoints. Until a proxy configuration is stored, credit-limit checks and customer creation answer 412. See Proxy configuration and Tenant system endpoints.
  5. 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.

Which integration fits depends on what your system does. A back office that owns your customer data pushes customers, agents and projects into CRS through external ingest, addressed by its own ids. An ERP that decides credit and creates customers stores its address and credentials once in the proxy configuration, and CRS then calls its tenant system endpoints. For a till there is nothing to build: you enable the B2B Customer feature in the Checkout App. If your system answers a customer creation with 202, it pushes that customer in later through external ingest.

Who calls whomEndpointsRead
Ayour system calls CRSPUT, PATCH, DELETE /api/v1/external-ingest/customers, /agents, /projectsExternal ingest
BCRS calls your systemyou store the address with PUT /api/v1/proxy-config; CRS sends POST {baseUrl}/credit-limit:validate and POST {baseUrl}/customers:createProxy configuration, Tenant system endpoints
Cthe till calls CRSnone for you to buildCheckout 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​

  1. 🛑 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. A PUT with your id and another group creates a second, separate record; a PATCH or DELETE without the record's group answers 404. Always send the group the record was created with.
  2. 🛑 Treating a customer PUT as a partial update. PUT replaces the record: every optional field you leave out is cleared, lists become empty and a customer's status goes back to Active. Send the complete customer every time. See Customer.
  3. Using a group CRS does not know. A record created with a businessUnitGroup needs a group from the Business Unit Management hierarchy of type b2b-customer-registry that has at least one business unit assigned directly to it. Otherwise the write answers 409 with Business Unit Group does not exist, or is not part of Customer Registry type. See Business unit group.
  4. Repeating the credit limit's versionTimestamp. A credit limit is stored only when its versionTimestamp is 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.
  5. Writing in the wrong order. Agents and projects need an existing, not deleted customer (404 Customer not found otherwise), 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.
  6. Granting only crs.credit-limit.validate for credit checks. Checking a credit limit also needs crs.customer.read on the customer's business unit group, or the call answers 403. The predefined role crs.readonly does not include crs.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 as GET /api/v1/customers/{customerId}. An agent can also take an agentId you send when you create it.
  • Your own id is externalCustomerId, externalAgentId or externalProjectId. External ingest addresses records by it. externalCustomerId is unique within one business unit group, not across your tenant, so the same externalCustomerId in two groups is two customers. externalAgentId and externalProjectId are 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 PUT or PATCH of a customer, agent or project answers with the stored record; a DELETE answers 204, and so does PUT /proxy-config. CRS publishes no events.
  • Deletes are soft. A deleted record disappears from reads and searches, but its key stays reserved: a later PUT with the same own id (and the same group) restores it under its original CRS id and answers 201.
  • Unknown properties are rejected. A body or query parameter the API does not define answers 400, for example property 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​

TenantBase URL
Tenant ids ending in testhttps://crs-api.retailsvc-test.com/api/v1
All other tenantshttps://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.

AreaReadWrite
Customerscrs.customer.read: search, read by idcrs.customer.write: PUT, PATCH, DELETE, POST /customers:create, external ingest of customers
Credit-limit checkcrs.credit-limit.validate and crs.customer.read on the customer's business unit group: POST /customers/{customerId}/credit-limit:validate
Trusted agentscrs.agent.read: search, read by idcrs.agent.write: PUT, PATCH, DELETE, external ingest of agents
Projectscrs.project.read: search, read by id, read restrictionscrs.project.write: PUT, PATCH, DELETE, external ingest of projects
Proxy configurationcrs.proxy-config.read: GET /proxy-configcrs.proxy-config.write: PUT /proxy-config

Roles​

RoleNamePermissions
crs.apiCRS APIall 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.readonlyCRS Read Onlycrs.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:

BodyMeaning
empty, with response header x-ext-auth-allow: nothe 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.