The Tenor HTTP API lives at https://api.tenor.run. All endpoints require authentication via Bearer token. All request and response bodies are JSON. All timestamps are UTC ISO 8601.
Base URL: https://api.tenor.run
Common Headers
Every request must include:
Authorization: Bearer tk_live_abc123
Content-Type: application/json
Accept: application/jsonOptional headers:
X-Idempotency-Key: <uuid> # For execute endpoints; prevents duplicate execution
X-Request-Id: <uuid> # For tracing; echoed in responsePOST /{org}/{contract}/evaluate
Evaluate a contract against provided facts and entity states. Returns verdicts, action space, and provenance. This is a read-only operation that does not modify state.
Request
curl -X POST https://api.tenor.run/acme/escrow/evaluate \
-H "Authorization: Bearer tk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"facts": {
"payment_received": true,
"payment_amount": "5000.00",
"customer_tier": "premium"
},
"entities": {
"Order": {
"order_001": "pending"
},
"Payment": {
"pay_001": "cleared"
}
}
}'Response (200 OK)
{
"request_id": "req_20260215_abc123",
"contract_hash": "sha256:9f4a2b...",
"evaluation_time_ms": 2,
"verdicts": {
"payment_ok": {
"value": true,
"type": "Bool",
"stratum": 0,
"rule": "check_payment",
"provenance": {
"facts_used": ["payment_received"],
"expression": "payment_received = true"
}
},
"eligible_for_release": {
"value": true,
"type": "Bool",
"stratum": 1,
"rule": "check_release_eligibility",
"provenance": {
"facts_used": ["customer_tier"],
"verdicts_used": ["payment_ok"],
"expression": "verdict_present(payment_ok) AND customer_tier = \"premium\""
}
}
},
"action_space": {
"escrow_agent": [
{
"operation": "release_funds",
"precondition_met": true,
"entities_affected": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "released"}
]
}
],
"buyer": [
{
"operation": "dispute_transaction",
"precondition_met": true,
"entities_affected": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "disputed"}
]
}
],
"seller": []
}
}Request with Remote Entity State
When using the executor's managed state (no entities field), the platform loads current entity state from its database:
curl -X POST https://api.tenor.run/acme/escrow/evaluate \
-H "Authorization: Bearer tk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"facts": {
"payment_received": true,
"payment_amount": "5000.00"
}
}'The response shape is identical. The platform loads entity state via snapshot isolation (E1) before evaluation.
GET /{org}/{contract}/actions
Returns the current action space for a specific persona or all personas. This is a convenience endpoint equivalent to evaluating and extracting only the action space.
Request
# Action space for a specific persona
curl https://api.tenor.run/acme/escrow/actions?persona=escrow_agent \
-H "Authorization: Bearer tk_live_abc123"
# Action space for all personas
curl https://api.tenor.run/acme/escrow/actions \
-H "Authorization: Bearer tk_live_abc123"Response (200 OK) — Single Persona
{
"request_id": "req_20260215_def456",
"persona": "escrow_agent",
"actions": [
{
"operation": "release_funds",
"precondition_met": true,
"entities_affected": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "released"}
],
"required_facts": []
},
{
"operation": "escalate_dispute",
"precondition_met": false,
"precondition_failure": "verdict_present(active_dispute) is false",
"entities_affected": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "escalated"}
],
"required_facts": ["dispute_reason"]
}
]
}Response (200 OK) — All Personas
{
"request_id": "req_20260215_ghi789",
"action_space": {
"escrow_agent": [
{
"operation": "release_funds",
"precondition_met": true,
"entities_affected": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "released"}
]
}
],
"buyer": [
{
"operation": "dispute_transaction",
"precondition_met": true,
"entities_affected": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "disputed"}
]
}
],
"seller": []
}
}POST /{org}/{contract}/flows/{flow_id}/execute
Execute a flow or a single flow step. This is the write path. The executor loads entity state, re-evaluates the contract, verifies persona authorization and preconditions, applies effects atomically, and records provenance.
Request — Execute a Flow
curl -X POST https://api.tenor.run/acme/escrow/flows/release_flow/execute \
-H "Authorization: Bearer tk_live_abc123" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: idem_20260215_001" \
-d '{
"persona": "escrow_agent",
"facts": {
"release_approved": true,
"release_reason": "Goods received and inspected"
},
"entity_bindings": {
"Order": "order_001",
"Payment": "pay_001"
}
}'Response (200 OK)
{
"request_id": "req_20260215_jkl012",
"flow_id": "release_flow",
"execution_id": "exec_20260215_001",
"status": "completed",
"steps_executed": [
{
"step": "verify_approval",
"type": "OperationStep",
"operation": "verify_release",
"result": "success",
"entity_transitions": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "approved"}
],
"provenance_id": "prov_20260215_001"
},
{
"step": "release_payment",
"type": "OperationStep",
"operation": "release_funds",
"result": "success",
"entity_transitions": [
{"entity": "Payment", "instance": "pay_001", "from": "held", "to": "released"}
],
"provenance_id": "prov_20260215_002"
},
{
"step": "complete",
"type": "Terminal",
"result": "success"
}
],
"final_entity_states": {
"Order": {"order_001": "approved"},
"Payment": {"pay_001": "released"}
},
"provenance_chain_id": "chain_20260215_001"
}Request — Execute a Single Step
For flows that require handoffs between personas, you can execute one step at a time:
curl -X POST https://api.tenor.run/acme/escrow/flows/release_flow/execute \
-H "Authorization: Bearer tk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"persona": "escrow_agent",
"step": "verify_approval",
"facts": {
"release_approved": true
},
"entity_bindings": {
"Order": "order_001"
}
}'Response (200 OK) — Step Execution
{
"request_id": "req_20260215_mno345",
"flow_id": "release_flow",
"execution_id": "exec_20260215_002",
"status": "in_progress",
"step_executed": {
"step": "verify_approval",
"type": "OperationStep",
"operation": "verify_release",
"result": "success",
"entity_transitions": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "approved"}
],
"provenance_id": "prov_20260215_003"
},
"next_step": {
"step": "release_payment",
"type": "OperationStep",
"required_persona": "escrow_agent",
"required_facts": []
}
}GET /{org}/{contract}/.well-known/tenor
Returns the contract manifest: metadata about the deployed contract, its entities, personas, facts, and capabilities. This endpoint is public (no authentication required) and is used by SDKs and tools for auto-discovery.
Request
curl https://api.tenor.run/acme/escrow/.well-known/tenorResponse (200 OK)
{
"org": "acme",
"contract": "escrow",
"version": "1.0.0",
"contract_hash": "sha256:9f4a2b...",
"deployment_id": "dep_20260215_001",
"deployed_at": "2026-02-15T10:30:00Z",
"entities": [
{
"name": "Order",
"states": ["pending", "approved", "disputed", "released", "cancelled"],
"initial": "pending",
"terminal": ["released", "cancelled"]
},
{
"name": "Payment",
"states": ["held", "released", "refunded"],
"initial": "held",
"terminal": ["released", "refunded"]
}
],
"personas": ["escrow_agent", "buyer", "seller"],
"facts": [
{"name": "payment_received", "type": "Bool"},
{"name": "payment_amount", "type": "Money"},
{"name": "customer_tier", "type": "Text"},
{"name": "release_approved", "type": "Bool"},
{"name": "dispute_reason", "type": "Text"}
],
"operations": [
"release_funds", "dispute_transaction", "cancel_order",
"verify_release", "escalate_dispute", "refund_payment"
],
"flows": ["release_flow", "dispute_flow", "cancellation_flow"],
"rules_count": 8,
"strata_count": 3,
"evaluator_endpoint": "POST /acme/escrow/evaluate",
"executor_endpoint": "POST /acme/escrow/flows/{flow_id}/execute"
}POST /{org}/{contract}/simulate
Dry-run an operation or flow execution. The simulator evaluates the contract, checks all preconditions, and computes what would happen --- but does not commit any state changes. Useful for previewing the effect of an action before executing it.
Request
curl -X POST https://api.tenor.run/acme/escrow/simulate \
-H "Authorization: Bearer tk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"persona": "escrow_agent",
"flow": "release_flow",
"facts": {
"release_approved": true
},
"entity_bindings": {
"Order": "order_001",
"Payment": "pay_001"
}
}'Response (200 OK)
{
"request_id": "req_20260215_pqr678",
"simulation": true,
"flow_id": "release_flow",
"status": "would_succeed",
"projected_steps": [
{
"step": "verify_approval",
"operation": "verify_release",
"would_succeed": true,
"projected_transitions": [
{"entity": "Order", "instance": "order_001", "from": "pending", "to": "approved"}
]
},
{
"step": "release_payment",
"operation": "release_funds",
"would_succeed": true,
"projected_transitions": [
{"entity": "Payment", "instance": "pay_001", "from": "held", "to": "released"}
]
}
],
"projected_final_states": {
"Order": {"order_001": "approved"},
"Payment": {"pay_001": "released"}
},
"verdicts_evaluated": {
"payment_ok": true,
"eligible_for_release": true
}
}Simulation of a Failing Operation
{
"request_id": "req_20260215_stu901",
"simulation": true,
"flow_id": "release_flow",
"status": "would_fail",
"failure": {
"step": "verify_approval",
"operation": "verify_release",
"failure_reason": "precondition_not_met",
"error_message": "Release has not been approved. Cannot verify release.",
"missing_verdicts": ["release_approval_ok"],
"facts_state": {
"release_approved": false
}
}
}Error Responses
All error responses follow the same shape:
400 Bad Request
Malformed input, missing required facts, or type mismatches.
{
"error": "bad_request",
"code": 400,
"message": "Fact 'payment_amount' expected type Money, got Text",
"request_id": "req_20260215_err001",
"details": {
"fact": "payment_amount",
"expected_type": "Money",
"received_type": "Text",
"received_value": "not-a-number"
}
}401 Unauthorized
Missing or invalid API key.
{
"error": "unauthorized",
"code": 401,
"message": "Invalid or expired API key",
"request_id": "req_20260215_err002"
}403 Forbidden
Valid API key, but the resolved persona is not authorized for the requested operation.
{
"error": "forbidden",
"code": 403,
"message": "Persona 'buyer' is not authorized for operation 'release_funds'",
"request_id": "req_20260215_err003",
"details": {
"persona": "buyer",
"operation": "release_funds",
"allowed_personas": ["escrow_agent"]
}
}404 Not Found
Organization, contract, flow, or entity instance does not exist.
{
"error": "not_found",
"code": 404,
"message": "Contract 'acme/nonexistent' not found",
"request_id": "req_20260215_err004"
}409 Conflict
Concurrent modification detected. The entity state changed between evaluation and execution (snapshot isolation violation caught).
{
"error": "conflict",
"code": 409,
"message": "Entity 'Order:order_001' state changed during execution. Expected 'pending', found 'disputed'.",
"request_id": "req_20260215_err005",
"details": {
"entity": "Order",
"instance": "order_001",
"expected_state": "pending",
"actual_state": "disputed"
}
}422 Unprocessable Entity
The request is well-formed, but the operation cannot proceed due to contract logic (precondition failure, invalid state transition).
{
"error": "precondition_not_met",
"code": 422,
"message": "Payment verdict is not present. Cannot confirm order.",
"request_id": "req_20260215_err006",
"details": {
"operation": "confirm_order",
"precondition": "verdict_present(payment_ok)",
"error_contract_key": "precondition_not_met"
}
}429 Too Many Requests
Rate limit exceeded.
{
"error": "rate_limited",
"code": 429,
"message": "Rate limit exceeded. 100 requests per second per API key.",
"request_id": "req_20260215_err007",
"retry_after_ms": 1200
}500 Internal Server Error
Platform-side failure. These are rare and always include a request ID for support escalation.
{
"error": "internal_error",
"code": 500,
"message": "Internal executor error. Please retry or contact support with the request ID.",
"request_id": "req_20260215_err008"
}Rate Limits
| Tier | Requests/second | Burst |
|---|---|---|
| Free | 10 | 20 |
| Pro | 100 | 500 |
| Enterprise | Custom | Custom |
Rate limits apply per API key. The 429 response includes a retry_after_ms field indicating how long to wait before retrying. SDKs implement automatic retry with exponential backoff.
Pagination
Endpoints that return lists (entity instances, provenance records, audit logs) support cursor-based pagination:
curl "https://api.tenor.run/acme/escrow/provenance?limit=50&cursor=cur_abc123" \
-H "Authorization: Bearer tk_live_abc123"{
"items": [...],
"cursor": "cur_def456",
"has_more": true
}Pass the returned cursor value as a query parameter to fetch the next page. When has_more is false, you have reached the end.