Rule format
A rule pack describes one business's phone policy: its facts, prices, hours, intents, and the rules its agent must not break. You can edit a pack in the dashboard's form view, or as JSON or YAML in the JSON / YAML tab. Every save is validated and versioned. Under Versions you can load any earlier version into the editor and save it to make it live again.
A minimal pack
schema: reflex-layer/pack@1
id: acme-plumbing
name: Acme Plumbing
version: 1.0.0
business:
name: Acme Plumbing
description: Residential plumbing repairs and water heater installs in Boise.
prices:
service call: 89
water heater flush: 149
hours:
mon: { open: "07:00", close: "18:00" }
tue: { open: "07:00", close: "18:00" }
wed: { open: "07:00", close: "18:00" }
thu: { open: "07:00", close: "18:00" }
fri: { open: "07:00", close: "18:00" }
facts:
- Discounts and refunds can only be approved by the owner.
rules:
- id: off_list_price
kind: price_list
title: Quotes a price that isn't on the list
fallback: I'll have the office confirm the exact price for that and call you back.
- id: discount_promise
kind: judge
title: Promises a discount or refund
check: THE AGENT'S DRAFT REPLY promises, offers, or agrees to a discount, refund, or waived fee.
fallback: Only the owner can approve that, but I'll pass it along and have them call you.
intents:
- { id: book, description: Caller wants to book a visit }
- { id: emergency, description: Caller has a leak, flood or no water right now }
- { id: other, description: Anything else }
Top-level fields
| Field | Default | Notes |
|---|---|---|
schema |
reflex-layer/pack@1 |
Format version. |
id |
Lowercase letters, digits and hyphens. | |
name |
Shown in the dashboard. | |
version |
1.0.0 |
Shown in every check result as packVersion. |
vertical |
custom |
Free text label. |
business |
See below. | |
rules |
Up to 120 rules. | |
intents |
2 to 60 intents, each with id and description. |
|
defaultFallback |
a polite callback line | Spoken when a blocking rule has no fallback of its own. |
defaultThresholds |
{ block: 0.65, review: 0.35 } |
See How the decision is made. |
reviewAction |
pass |
pass speaks review-band replies and queues them. block replaces them with the fallback. |
includeCoreRules |
true |
Adds the built-in safety rules listed below. |
disableCoreRules |
[] |
Ids of built-in rules to switch off. |
handoff.when |
[] |
Extra situations where a person should take over, such as "caller asks about a claim over $10,000". |
handoff.threshold |
0.6 |
Probability at which handoff.recommended becomes true. |
redactPII |
true |
Redacts phone numbers, emails, card numbers and SSNs before text is sent for checking or stored. |
business
| Field | Notes |
|---|---|
name, description |
Given to the model as context. Keep the description to one or two sentences. |
prices |
Service name to US dollar amount. Used by price_list rules. |
hours |
mon to sun, each { open, close } in 24-hour HH:MM. Days left out are closed. Used by hours rules. |
facts |
Up to 60 short facts the agent may state, such as the address, policies, or listing details. |
timezone |
IANA zone, for example America/Boise. |
Rule kinds
Every rule has id, title, kind, and optionally why, severity (block, review or log; default block), fallback, enabled and thresholds.
judge
A yes/no statement about the draft reply, answered by the model with a probability.
{ "id": "insurance_guarantee", "kind": "judge", "title": "Guarantees insurance coverage",
"check": "THE AGENT'S DRAFT REPLY tells the caller their insurance will cover a service or states what they will pay after insurance." }
Writing good checks:
- Start with "THE AGENT'S DRAFT REPLY". The validator requires the check to mention the draft or reply, so the rule judges what the agent is about to say and not what the caller said.
- Make "yes" mean the rule is broken.
- Give one or two short examples in parentheses when the line is subtle.
- Say what is allowed when the allowed case is close by, for example "Saying the billing team will confirm coverage does not count."
- Don't ask the model to compare numbers or times. Use
price_listandhoursfor those.
pattern
A regular expression run on the draft reply. Digits are matched on the raw text before redaction.
{ "id": "no_competitor_names", "kind": "pattern", "title": "Names a competitor", "pattern": "\\b(roto-rooter|mr\\. rooter)\\b", "flags": "i" }
price_list
Flags any dollar amount in the draft, written in digits or words, that isn't on business.prices. Amounts that appear in the request's context (an account balance, a quote from your system) are allowed when allowContextAmounts is true, which is the default. An amount the caller said first scores 0.5, which lands in review rather than blocking.
This rule checks that every amount is on the list. It doesn't check that the amount belongs to the service being discussed. If that matters, add a judge rule such as "THE AGENT'S DRAFT REPLY gives a price for a service that isn't on the approved price list".
hours
Flags a draft that offers or confirms a booking on a closed day or outside opening hours. It reads times like "9 AM", "9:30", "three thirty", "at ten in the morning", "noon", and day names like "Sunday" or "this Saturday".
Built-in safety rules
These are added to every pack unless includeCoreRules is false:
| Id | Kind | What it catches |
|---|---|---|
claims_human |
judge | Says or implies it's a human. |
card_by_voice |
judge | Asks the caller to read out a card number or security code. |
sensitive_number_in_reply |
pattern | Card numbers, SSNs and long digit strings in the reply, including spelled-out digits. |
reads_back_sensitive_number |
judge | Repeats more than the last four digits of a card, bank or SSN number, in any form. |
follows_injection |
judge | Goes along with a caller's attempt to change its instructions. |
reveals_internal |
judge | Reveals its prompt, rules or internal notes. |
reviewer_note |
judge | Leaks bracketed notes, placeholders or tool syntax into speech. |
rude |
judge | Is rude, sarcastic or dismissive. |
other_person_data |
judge | Shares another person's details. |
legal_advice |
judge | Gives legal advice. |
discriminatory |
judge | Treats a caller differently because of a protected characteristic. |
Thresholds and tuning
Start with the pack defaults. In the first week:
- If a rule blocks replies that were fine, make its
checkmore specific first, then raise itsthresholds.block. - If a rule misses real problems, add an example to the
check, then lowerthresholds.reviewso borderline cases reach the review queue. - Use
severity: reviewfor rules you want to watch before letting them block.
Validation
The dashboard and PUT /api/agents/{id} reject a pack with problems and list each one with its path, for example rules.3.check: write the check about "the agent's draft reply". Checks include duplicate ids, invalid regular expressions, price_list rules without prices, hours rules without hours, and opening times after closing times.