Facts and Schema
A Context's schema defines what data it collects. There are three kinds of facts: base facts (the raw data you submit), derived facts (computed automatically), and one special identity fact (what uniquely identifies each instance).
Base Facts
Base facts are the data points you collect: things like credit_score, annual_income, or employment_verified. They come from API integrations, user input, third-party systems, or anywhere else in your application.
Each fact has a type (string, number, boolean, date, list, or object) and can be marked as:
- Required: Counts toward the context's overall
completestatus; each rule still runs as soon as its own inputs are present - Output only: Can only be written by rules, not submitted externally
- Track history: Stores previous values for temporal queries
The Identity Fact
One fact must be the identity fact, the unique identifier for each context instance. For a loan application context, this might be application_id. For transaction processing, transaction_id.
The identity appears in your API URLs:
POST /api/v1/contexts/loan-application/APP-12345
↑ identity valueIdentity facts must be strings or numbers, must be required, and can't be output-only. Once set, they shouldn't change.
Derived Facts
Derived facts compute automatically from other facts using expressions:
// Debt-to-income ratio
monthly_debt / (annual_income / 12)
// Risk category based on score
ifelse(
credit_score >= 750,
'low',
ifelse(credit_score >= 650, 'medium', 'high')
)
// Aggregate from the configured "transactions" relationship
sum($transactions, 'total')
// Available functions are listed directly in app UIThey update whenever their dependencies change. No manual recalculation needed.
Relationships
Contexts can relate to each other. A customer context can have many transaction contexts. A transaction context can belong to a customer. These relationships let you build decisions that span multiple entities.
The Pattern
Customer (one) → Transactions (many)
A customer context tracks lifetime data: total spend, transaction count, loyalty tier. Each transaction context tracks a single transaction. When an transaction completes, you might want to update the customer's lifetime stats.

Defining Relationships
In the Context Editor, add a relationship field:
On the Customer context:
transactions: has_many(Transaction, 'customer_id')On the Transaction context:
customer_id: string (foreign key)Now transactions link to their customer, and customers can reference their transactions. In expressions, use $<relationship-name>. For this example, $transactions on Customer and $customer on Transaction. An unnamed relationship uses the target context slug.
Using Relationships
Derived facts are required for you to expose and use data from related Contexts.
For example, on a Customer context, I might use:
// Total spend across all transactions
total_spend: sum($transactions, 'total')
// Number of completed transactions
transaction_count: count($transactions, 'status == "complete"')
// Most recent transaction date
last_transaction_date: max($transactions, 'created_at')These recalculate automatically when any related transaction changes.
Cascading Across Relationships
When an transaction is marked complete, it can trigger rules on the customer context:
- Transaction context receives
status: "complete" - Customer's
transaction_countderived fact recalculates - If
transaction_countcrosses a threshold, a loyalty tier rule executes - Customer's
loyalty_tierfact updates
This cascade happens automatically based on the relationships and rule bindings you've defined.
Relationship queries ($transactions in this example) only include live, non-expired
context instances. Expired transactions won't appear in aggregations. If this
is an issue, consider maintaining a running statistic in your systems and
sending it into the Context via a base fact. We still consider this a data
concern rather than a Rulebricks problem.
Navigating the Other Direction
From an transaction, access the parent customer:
// Get customer's tier for transaction-level pricing
customer_tier: $customer.loyalty_tierThis creates a dependency: the transaction context waits for the customer's loyalty_tier before derived facts using it can calculate.
Solvability
A context instance is "solvable" when all required facts are present. The API always tells you where you stand:
{
"status": "pending",
"have": ["application_id", "credit_score"],
"need": ["annual_income", "employment_verified"]
}When need is empty, status becomes complete. Rule and flow readiness is asset-specific: a bound asset can execute while the context is still pending if all inputs for that asset are already present.
Fact History
Enable Track history on facts whose prior values matter. Contexts append a history entry only when a tracked value changes and retain up to the context's configured history_limit entries per fact.
Read history with:
GET /api/v1/contexts/{context-slug}/{identity}/history
GET /api/v1/contexts/{context-slug}/{identity}/history?field=credit_limit&limit=10The response groups entries by fact, newest first:
{
"context": "customer:c_001",
"history": {
"credit_limit": [
{ "timestamp": "2026-07-28T15:00:00.000Z", "value": 500 },
{ "timestamp": "2026-07-28T14:00:00.000Z", "value": 300 }
]
}
}Derived expressions can access tracked values through $history, such as last($history.credit_limit).value.