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.

OperatorMeaningValue
seenEvent was submitted at least oncenone
not seenEvent has not been submittednone
count_gteSubmitted at least N timesinteger
count_lteSubmitted at most N timesinteger
count_gtSubmitted more than N timesinteger
count_ltSubmitted fewer than N timesinteger
count_eqSubmitted exactly N timesinteger

Comparison. Check the event's properties.value against a threshold.

OperatorMeaningValue
matchValue equalsstring, number, or boolean
gteValue at leastnumber
lteValue at mostnumber
gtValue greater thannumber
ltValue less thannumber

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".

OperatorMeaning
not gteEvent missing OR value below N
not lteEvent missing OR value above N
not gtEvent missing OR value at most N
not ltEvent 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 }
]
Agent editor showing a billable condition with four leaves: agent_replied is seen, escalated is not seen, reopened is not seen, csat is missing or has value above 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:

  1. All previous events for this outcome are retrieved.
  2. The new event is added to the list.
  3. For comparison operators, only the most recent properties.value for each action is used.
  4. For count operators, every occurrence is counted.
  5. Every leaf is checked. The condition is satisfied when every leaf passes.
  6. If the condition is now satisfied, the outcome moves toward confirmation when the settlement window closes.
  7. 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.

On this page