Entity Conditions API
Base URL: https://ecs-api.retailsvc.com/api/v1
Authenticate with an OCMS bearer token (a client_credentials token, or an IAM user
token) — your tenant is resolved from it and is never accepted as a request parameter.
Each endpoint below lists the permission your token needs.
Before using this reference, read Entity condition, Rules, Project restrictions, and Decisions and outcomes for what these calls actually mean.
Entity conditions
List entity conditions
GET /entity-conditions — permission ecs.condition.read
Returns every global entity condition (read-only, platform-owned) plus the conditions your tenant owns. On an id collision, the global condition wins.
[
{
"id": "alcohol_se",
"rule": "AGE_RESTRICTION",
"scope": "GLOBAL",
"enabled": true,
"condition_value": { "minimum_age": 18 }
}
]
Get an entity condition
GET /entity-conditions/{id} — permission ecs.condition.read
Returns the global condition for the given id if one exists, otherwise your tenant's own.
404 if neither exists.
Create or update a tenant-scoped entity condition
PUT /entity-conditions/{id} — permission ecs.condition.write
Creates or replaces your tenant's condition for {id}. Rejected with 409 if {id} is
already used by a global condition — global ids are reserved by the platform.
Request body:
{
"rule": "AGE_RESTRICTION",
"enabled": true,
"condition_value": { "minimum_age": 18 }
}
enabled defaults to true when omitted. rule is one of AGE_RESTRICTION or
LICENSE_REQUIREMENT (see Rules), and condition_value depends on it:
AGE_RESTRICTION:{ "minimum_age": <int> }LICENSE_REQUIREMENT:{ "license_type": <string>, "minimum_level": <int ≥ 1, optional> }
{
"rule": "LICENSE_REQUIREMENT",
"condition_value": { "license_type": "explosives_se", "minimum_level": 3 }
}
Any other field in condition_value is rejected with 400.
Returns 200 on update or 201 on create, with the resulting condition in the same shape
as the list endpoint.
Delete a tenant-scoped entity condition
DELETE /entity-conditions/{id} — permission ecs.condition.delete
Deletes your tenant's condition for {id}. 404 if it doesn't exist; 409 if {id} is a
global condition — those can't be deleted through this API.
Previewing a decision
Both endpoints below run the same checks a real checkout would, against a single item you supply, without needing a full transaction. Useful for testing a condition or project restriction before relying on it.
Evaluate one entity condition
POST /conditions/{conditionId}/evaluate — permission ecs.condition.evaluate
Resolves {conditionId} from your tenant's catalog, falling back to the global catalog,
and evaluates one item against it.
Request:
{
"item": { "id": "item-1" },
"context": {
"customer": { "customer_age": 16 }
}
}
context.customer is optional — pass {} when there's nothing to supply. See
Rules for what each rule reads from it (customer_age for age restriction,
licenses for license requirement).
licenses is a list of the licenses the shopper holds, each with a type and an optional
level. level is a string, e.g. "3". A plain list of strings is rejected with 400.
{
"item": { "id": "item-1" },
"context": {
"customer": { "licenses": [{ "type": "explosives_se", "level": "3" }] }
}
}
404 if no enabled condition with that id exists in either catalog, so a disabled
condition can't be previewed. A condition you've only just created previews as a soft deny
(condition.unknown) until it has reached checkout, the same delay checkout itself sees.
Response (ItemDecisionDto):
{
"id": "item-1",
"allow": false,
"results": [
{
"id": "alcohol_se",
"allow": false,
"decision": "HARD_DENY",
"reason": "age_restriction.underage",
"rule": "AGE_RESTRICTION"
}
]
}
Evaluate project restrictions
POST /project-restrictions/evaluate — permission ecs.condition.evaluate
Looks up the given project's restrictions and evaluates one item against them.
Request:
{
"projectId": "fc03c9aa-bbb9-413d-997a-be8e9f6f28f0",
"item": {
"id": "item-1",
"additionalProperties": { "department": "hardware", "ecoLabel": false }
}
}
additionalProperties carries whatever item attributes your project's whitelist/blacklist
restrictions check against (see Project restrictions) — omit it
entirely if the item has none.
Response is the same ItemDecisionDto shape as above. A project-restriction result's id
names what was checked: the property name, or "itemId" for the item-id restrictions. It's
null when the project's restrictions couldn't be looked up.
{
"id": "item-1",
"allow": false,
"results": [
{
"id": "department",
"allow": false,
"decision": "HARD_DENY",
"reason": "project_restriction.whitelist_not_met",
"rule": "PROJECT_RESTRICTION"
}
]
}
Response fields reference
ItemDecisionDto:
| Field | Type | Notes |
|---|---|---|
id | string | Echoes the item id you sent |
allow | boolean | true only if every entry in results allows |
results | array | One entry per check that ran, whether it allowed or denied |
Each entry in results:
| Field | Type | Notes |
|---|---|---|
id | string or null | Entity condition id; for a project-restriction result, the property name or "itemId". null when no single restriction applies (the project lookup failed, or the restriction type isn't recognized) |
allow | boolean | |
decision | ALLOW | SOFT_DENY | HARD_DENY | See Decisions and outcomes |
reason | string or null | Machine-readable reason code; null when decision is ALLOW |
rule | AGE_RESTRICTION | LICENSE_REQUIREMENT | PROJECT_RESTRICTION | null | null on a condition.unknown result |