Skip to content

Policies

Policies define rules, constraints, and procedures that govern how the assistant behaves. Unlike knowledge base entries (which answer factual questions), policies establish behavioural guidelines: escalation triggers, verification steps, response constraints, and business rules.

Required scope: policy:read for GET endpoints, policy:write for write endpoints.

Retrieve the policy entries of a workspace, of every status, in creation order. Without limit and cursor, the response is every entry, as a bare JSON array.

Terminal window
curl "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Response:

[
{
"id": "11223344-5566-4788-99aa-bbccddeeff00",
"workspaceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "escalation",
"ruleText": "If the customer mentions legal action, litigation, or a lawyer, immediately flag the conversation for human review and do not attempt to resolve the issue autonomously.",
"conditions": {
"keywords": ["legal action", "litigation", "lawyer", "court"]
},
"sourceType": "manual",
"status": "approved",
"confidence": null,
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-01-10T09:00:00.000Z"
}
]

Send limit, cursor or both to read the list one page at a time. This request asks for one entry a page:

Terminal window
curl "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries?limit=1" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Query parameters:

ParameterTypeRequiredDescription
limitintegerNoEntries per page, from 1 to 100. A request with cursor and no limit returns 50 entries per page.
cursorstringNoThe nextCursor of the previous page.

Response:

{
"data": [
{
"id": "11223344-5566-4788-99aa-bbccddeeff00",
"workspaceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "escalation",
"ruleText": "If the customer mentions legal action, litigation, or a lawyer, immediately flag the conversation for human review and do not attempt to resolve the issue autonomously.",
"conditions": {
"keywords": ["legal action", "litigation", "lawyer", "court"]
},
"sourceType": "manual",
"status": "approved",
"confidence": null,
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-01-10T09:00:00.000Z"
}
],
"nextCursor": "eyJjIjoiMjAyNi0wMS0xMFQwOTowMDowMC4wMDAwMDBaIiwiaSI6IjExMjIzMzQ0LTU1NjYtNDc4OC05OWFhLWJiY2NkZGVlZmYwMCJ9"
}

A page holds at most limit entries, ordered by createdAt and then id. nextCursor is present when more entries follow: send it as cursor to read the next page. The last page carries no nextCursor.

Terminal window
curl "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries?limit=1&cursor=eyJjIjoiMjAyNi0wMS0xMFQwOTowMDowMC4wMDAwMDBaIiwiaSI6IjExMjIzMzQ0LTU1NjYtNDc4OC05OWFhLWJiY2NkZGVlZmYwMCJ9" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

A limit that is not an integer from 1 to 100, an empty cursor, and a cursor that is not a nextCursor value each answer 422 with { "error": "Invalid request", "details": { ... } }.

Retrieve one policy entry by its id, whatever its status.

Required scope: policy:read

Terminal window
curl "https://api.ohallo.eu/api/policy-entries/11223344-5566-4788-99aa-bbccddeeff00" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Response:

{
"id": "11223344-5566-4788-99aa-bbccddeeff00",
"workspaceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "escalation",
"ruleText": "If the customer mentions legal action, litigation, or a lawyer, immediately flag the conversation for human review and do not attempt to resolve the issue autonomously.",
"conditions": {
"keywords": ["legal action", "litigation", "lawyer", "court"]
},
"sourceType": "manual",
"status": "approved",
"confidence": null,
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-01-10T09:00:00.000Z"
}

An id that names no policy entry of your account answers 404 with { "error": "Policy entry not found" }. For an API key restricted to a brand or workspace, so does an entry in a workspace outside the restriction.

Retrieve the policy topics in use in a workspace: the topic of each approved policy, each once, sorted by name. Use one of them for a new policy on the same subject, so related rules share a topic.

Required scope: policy:read

Terminal window
curl "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries/topics" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Response:

{
"topics": ["escalation", "pricing", "returns", "verification"]
}

Search policy entries by the meaning of a query and by its words. The response is a bare JSON array, the best match first, with each policy’s id, topic, rule_text, conditions and rrf_score, how well it matches, the higher the better.

Terminal window
curl "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries/search?q=when%20to%20escalate" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Query parameters:

ParameterTypeRequiredDescription
qstringYesSearch query. An empty query returns an empty array.

Response:

[
{
"id": "11223344-5566-4788-99aa-bbccddeeff00",
"topic": "escalation",
"rule_text": "If the customer mentions legal action, litigation, or a lawyer, immediately flag the conversation for human review and do not attempt to resolve the issue autonomously.",
"conditions": {
"keywords": ["legal action", "litigation", "lawyer", "court"]
},
"rrf_score": 0.0327
}
]

Retrieve the policy entries awaiting review, newest first: proposals drafted by oHallo (proposed), for example by the learning loop from resolved conversations, and entries created for review (pending_review). The response is a bare JSON array.

Terminal window
curl "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries/pending" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Response:

[
{
"id": "aabbccdd-eeff-4788-9233-445566778899",
"workspaceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "order-verification",
"ruleText": "Before looking up order details, always ask the customer to confirm their order number. Do not search by name or email alone.",
"conditions": null,
"sourceType": "extracted",
"sourceConversationId": "c9f8e7d6-b5a4-3210-fedc-ba9876543210",
"status": "proposed",
"confidence": 0.72,
"createdAt": "2026-03-14T16:30:00.000Z",
"updatedAt": "2026-03-14T16:30:00.000Z"
}
]

Create a new policy entry. An entry created with the sourceType manual is approved at once, and the assistant applies it; an entry with any other sourceType starts as pending_review and waits for approval.

Required scope: policy:write

Terminal window
curl -X POST "https://api.ohallo.eu/api/workspaces/a1b2c3d4-e5f6-7890-abcd-ef1234567890/policy-entries" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx" \
-H "Content-Type: application/json" \
-d '{
"topic": "pricing",
"ruleText": "Never disclose wholesale pricing or volume discount tiers to end customers. If asked about bulk pricing, direct them to their account manager.",
"sourceType": "manual",
"conditions": {
"accountTypes": ["end_customer"]
}
}'

Request body:

FieldTypeRequiredDescription
topicstringYesTopic category for the policy
ruleTextstringYesThe rule or instruction text
sourceTypestringYesOrigin of the entry, e.g. "manual"
conditionsobjectNoOptional structured conditions for contextual matching
sourceConversationIdstringNoThe conversation this entry was derived from
confidencenumberNoA score between 0.0 and 1.0

Response: 201 Created, with the policy.

{
"id": "ffeeddcc-bbaa-4788-9776-554433221100",
"workspaceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"topic": "pricing",
"ruleText": "Never disclose wholesale pricing or volume discount tiers to end customers. If asked about bulk pricing, direct them to their account manager.",
"conditions": {
"accountTypes": ["end_customer"]
},
"sourceType": "manual",
"status": "approved",
"confidence": null,
"createdAt": "2026-03-15T15:30:00.000Z",
"updatedAt": "2026-03-15T15:30:00.000Z"
}

A workspace holds one policy per rule text, compared without case or surrounding spaces; a rejected policy holds none. A create, an update, an approval or a restore that would give a second policy a rule text the workspace already has answers 409:

{
"error": "A policy with this rule text already exists in the workspace",
"code": "duplicate_rule_text",
"entryId": "11223344-5566-4788-99aa-bbccddeeff00"
}

entryId is the policy that holds the rule text: update that policy, or give the new one a rule text of its own. When your account reviews policy changes before they apply, an update is saved for approval in the dashboard (202), and its rule text is checked when it is approved.

Update an existing policy entry. Only the fields you include are changed.

Required scope: policy:write

Terminal window
curl -X PATCH "https://api.ohallo.eu/api/policy-entries/ffeeddcc-bbaa-4788-7766-554433221100" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx" \
-H "Content-Type: application/json" \
-d '{
"ruleText": "Never disclose wholesale pricing, volume discount tiers, or partner-specific rates to end customers. If asked about bulk pricing, direct them to their account manager or provide the general inquiry form link."
}'

Response: Returns the full updated entry. When your account reviews policy changes before they apply, the change is saved as a version that waits for approval in the dashboard, the entry stays as it is, and the response is 202 with { "pending": true, "versionId": "..." }.

Approve a policy entry that awaits review, making it active for the assistant. The response is the approved entry, or for a proposal that changes an existing policy, the policy it changed.

A policy that awaits review, with status proposed or pending_review, can be approved with edits in the body: the topic, ruleText and conditions you send are approved in place of the policy’s own, and the fields you do not send stay as they are. A proposal that changes an existing policy is applied to that policy: it takes the proposal’s topic, rule text and conditions, with your edits in place of the ones you send. When that policy was edited after the proposal was written, the approval answers 409 with the policy as it stands in currentTarget; approving again with expectedTargetVersionId set to its publishedVersionId applies the proposal onto it.

Required scope: policy:write

Terminal window
curl -X POST "https://api.ohallo.eu/api/policy-entries/aabbccdd-eeff-4788-2233-445566778899/approve" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx" \
-H "Content-Type: application/json" \
-d '{
"ruleText": "Before looking up order details, ask the customer to confirm their order number."
}'

Approving an entry that is already approved answers with it unchanged, and approving one that was rejected answers 400.

Reject a policy entry that awaits review. A proposal can be rejected with a rejectionReason; the learning loop reads recent rejections, with their reasons, before it drafts new proposals. Rejecting an entry that is already rejected answers with it unchanged, and rejecting one that was approved answers 400.

Required scope: policy:write

Terminal window
curl -X POST "https://api.ohallo.eu/api/policy-entries/aabbccdd-eeff-4788-2233-445566778899/reject" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx" \
-H "Content-Type: application/json" \
-d '{
"rejectionReason": "This is too restrictive; we do allow order lookups by email for verified accounts."
}'

Permanently delete a policy entry.

Required scope: policy:write

Terminal window
curl -X DELETE "https://api.ohallo.eu/api/policy-entries/ffeeddcc-bbaa-4788-7766-554433221100" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

Response: 200 OK with { "ok": true } on success.

Every change to an approved policy publishes a version, recording who made it: a user ID for a person, api-key:<id> for a key.

Required scope: policy:read to read versions, policy:write to restore one.

Terminal window
curl "https://api.ohallo.eu/api/policy-entries/ffeeddcc-bbaa-4788-9776-554433221100/versions" \
-H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"

The response is { "versions": [...] }, newest first. GET /api/policy-entries/versions/{versionId} returns what one version held, and answers for a version of a deleted policy too. POST /api/policy-entries/{entryId}/versions/{versionId}/restore publishes that content again as a new version, leaving the history complete. It answers { "versionId": "...", "created": true, "restoredFrom": 2 }, with created false when the policy already holds that content.

Each policy entry has three key fields:

topic: A category string that groups related policies. The Policy Agent retrieves the approved policies of a topic, or searches them, while it handles a conversation. Examples: escalation, pricing, returns, verification, tone.

ruleText: The actual rule or instruction, written in natural language. Be specific and actionable. The assistant interprets this text literally, so avoid ambiguity.

conditions: Optional structured data stored with the policy, as free-form JSON. The Policy Agent receives it together with the rule text when it retrieves the policy.

Good policies are specific, actionable, and testable:

Topic: returns
Rule: Accept return requests for items delivered within the last 30 days.
Require the order number and reason for return. If the item is
damaged, request a photo before processing.

Avoid vague policies that the assistant cannot act on consistently:

Topic: general
Rule: Be helpful and try to resolve issues.