Billable conditions
A billable condition is a list of leaves that determines when an outcome confirms.
Overview
Every agent has a condition in its contract. After each event, witn evaluates the condition against everything submitted for that outcome so far. If the condition is satisfied when the settlement window closes, the outcome confirms.
The condition is a flat list of leaves. The list is satisfied when every leaf is satisfied (implicit AND).
Shape
[
{ "fact": "signed_by_buyer", "operator": "seen" },
{ "fact": "signed_by_seller", "operator": "seen" }
]Leaf node
A leaf is { "fact": "<action>", "operator": "<op>", "value": <v> }. The fact is the action field of the submitted event. The operator decides how the leaf is checked. The value is required by operators that compare against a value.
{ "fact": "ticket_resolved", "operator": "seen" }Operators
Operators fall into three groups.
Occurrence. Check whether the event happened and how often.
| Operator | Meaning | Value |
|---|---|---|
seen | Event was submitted at least once | none |
not seen | Event has not been submitted | none |
count_gte | Submitted at least N times | integer |
count_lte | Submitted at most N times | integer |
count_gt | Submitted more than N times | integer |
count_lt | Submitted fewer than N times | integer |
count_eq | Submitted exactly N times | integer |
Comparison. Check the event's properties.value against a threshold.
| Operator | Meaning | Value |
|---|---|---|
match | Value equals | string, number, or boolean |
gte | Value at least | number |
lte | Value at most | number |
gt | Value greater than | number |
lt | Value less than | number |
Missing value allowed. Same as the comparison group but the leaf is also satisfied when the event has not been submitted. Useful for failure signals like "no low CSAT".
| Operator | Meaning |
|---|---|
not gte | Event missing OR value below N |
not lte | Event missing OR value above N |
not gt | Event missing OR value at most N |
not lt | Event missing OR value at least N |
Negative signals
Use not seen or the not * operators when absence is the goal.
[
{ "fact": "agent_replied", "operator": "seen" },
{ "fact": "escalated", "operator": "not seen" },
{ "fact": "reopened", "operator": "not seen" },
{ "fact": "csat", "operator": "not lte", "value": 3 }
]
If the customer disappears and no CSAT is submitted, the csat not lte 3 check stays true. The outcome can confirm when the settlement window closes.
Value matching
Use match to require a specific scalar:
{ "fact": "inspection", "operator": "match", "value": "pass" }Submit with:
curl -X POST https://api.thewitn.com/v1/events \
-H "Authorization: Bearer $WITN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "key": "...", "action": "inspection", "agent_key": "support", "customer_key": "acme", "properties": { "value": "pass" } }'Numeric comparison
Compare the event's properties.value against a number:
{ "fact": "csat", "operator": "gte", "value": 4 }properties.value is used to evaluate the condition. properties.attribution is separate and is used for billing quantity when the outcome confirms.
Examples
Single event
[{ "fact": "downloaded", "operator": "seen" }]Both parties must sign
[
{ "fact": "signed_by_buyer", "operator": "seen" },
{ "fact": "signed_by_seller", "operator": "seen" }
]Signed but not revoked
[
{ "fact": "signed", "operator": "seen" },
{ "fact": "revoked", "operator": "not seen" }
]Minimum score threshold
[{ "fact": "csat", "operator": "gte", "value": 4 }]Submitted as:
curl -X POST https://api.thewitn.com/v1/events \
-H "Authorization: Bearer $WITN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "key": "...", "action": "csat", "agent_key": "support", "customer_key": "acme", "properties": { "value": 4.8, "attribution": 0.8 } }'Multi-step verification
[
{ "fact": "identity_check", "operator": "match", "value": "verified" },
{ "fact": "credit_score", "operator": "gte", "value": 700 },
{ "fact": "document_signed", "operator": "seen" }
]Seen at least N times
[{ "fact": "warning", "operator": "count_gte", "value": 3 }]How evaluation works
On every POST /v1/events:
- All previous events for this outcome are retrieved.
- The new event is added to the list.
- For comparison operators, only the most recent
properties.valuefor each action is used. - For count operators, every occurrence is counted.
- Every leaf is checked. The condition is satisfied when every leaf passes.
- If the condition is now satisfied, the outcome moves toward confirmation when the settlement window closes.
- The settlement timer resets to now plus the agent's settlement period.
Earlier events are still stored. Their properties.attribution values can still affect billing depending on the agent's attribution_method.
Empty conditions
[] is vacuously true. Every event confirms the outcome when the settlement window closes.
Validation
The condition is validated when the agent is created or updated. Invalid input returns VALIDATION_ERROR with details pointing to the exact path.
// Wrong key name
[{ "type": "signed", "operator": "seen" }]
// Correct
[{ "fact": "signed", "operator": "seen" }]A leaf must include fact and operator. Operators in the comparison and count groups must include value. The seen and not seen operators must not include a value.