Skip to content

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/json

Optional headers:

X-Idempotency-Key: <uuid>     # For execute endpoints; prevents duplicate execution
X-Request-Id: <uuid>          # For tracing; echoed in response

POST /{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

bash
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)

json
{
  "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:

bash
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

bash
# 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

json
{
  "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

json
{
  "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

bash
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)

json
{
  "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:

bash
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

json
{
  "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

bash
curl https://api.tenor.run/acme/escrow/.well-known/tenor

Response (200 OK)

json
{
  "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

bash
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)

json
{
  "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

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

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

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

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

json
{
  "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).

json
{
  "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).

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

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

json
{
  "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

TierRequests/secondBurst
Free1020
Pro100500
EnterpriseCustomCustom

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:

bash
curl "https://api.tenor.run/acme/escrow/provenance?limit=50&cursor=cur_abc123" \
  -H "Authorization: Bearer tk_live_abc123"
json
{
  "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.