TerraFlow Hosted preview

Synthetic data only · no uptime commitment · 10:00–22:00 IST (04:30–16:30 UTC)

Integration guide

Your application keeps the official OpenAI or Anthropic SDK it already uses. Change two things, the base URL and the API key, and every request is governed: identified, classified, policy-checked, routed or denied, budgeted and evidenced. Optional headers tell TerraFlow more about a request; none is required to start.

The preview is available 10:00–22:00 IST (04:30–16:30 UTC). Outside those hours the server is off and an SDK call fails to connect (a connection error, not an HTTP response).

Your base URL and key

Starter workloads

Workload What it is for Data it accepts Notes
starter-assistant General questions public, internal Routed across Claude, GPT and Gemini models; exact and semantic cache on
starter-extraction Structured extraction public, internal The answer must match a JSON schema (vendor, amount, currency, due date), so a stream is held back until the answer is checked; exact cache on
starter-knowledge Questions over a synthetic public handbook public, internal Retrieval from the handbook, embedded on the server; answers must cite their sources
starter-confidential Shows where confidential data goes internal, confidential External egress off: only the simulated private model can answer
starter-budget Shows a hard budget stop public, internal A tiny hourly budget: after the first call in an hour, the next is denied BUDGET_EXHAUSTED

Each starter workload accepts up to 30 requests a minute. You can author your own workloads in the console; a colleague must approve a contract you wrote before it runs.

Python

OpenAI SDK

# pip install openai
import os
from openai import OpenAI

client = OpenAI(base_url="https://<your-workspace-host>/v1", api_key=os.environ["TERRAFLOW_API_KEY"])  # your key starts with tf_
reply = client.chat.completions.create(
    model="starter-assistant",  # a TerraFlow workload, not a vendor model: TerraFlow picks the route
    messages=[{"role": "user", "content": "In two sentences, what does an AI control plane do?"}],
    extra_headers={"X-TerraFlow-Data-Class": "internal"},
)
print(reply.choices[0].message.content)

Anthropic SDK

# pip install anthropic
import os
import anthropic

client = anthropic.Anthropic(base_url="https://<your-workspace-host>", api_key=os.environ["TERRAFLOW_API_KEY"])  # starts with tf_
reply = client.messages.create(
    model="starter-assistant",
    max_tokens=300,
    messages=[{"role": "user", "content": "In two sentences, what does an AI control plane do?"}],
    extra_headers={"X-TerraFlow-Data-Class": "internal"},
)
print(reply.content[0].text)

TypeScript

OpenAI SDK

// npm install openai
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://<your-workspace-host>/v1", apiKey: process.env.TERRAFLOW_API_KEY }); // starts with tf_
const reply = await client.chat.completions.create(
  { model: "starter-assistant", messages: [{ role: "user", content: "In two sentences, what does an AI control plane do?" }] },
  { headers: { "X-TerraFlow-Data-Class": "internal" } },
);
console.log(reply.choices[0].message.content);

Anthropic SDK

// npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({ baseURL: "https://<your-workspace-host>", apiKey: process.env.TERRAFLOW_API_KEY }); // starts with tf_
const reply = await client.messages.create(
  { model: "starter-assistant", max_tokens: 300, messages: [{ role: "user", content: "In two sentences, what does an AI control plane do?" }] },
  { headers: { "X-TerraFlow-Data-Class": "internal" } },
);
console.log(reply.content[0].type === "text" ? reply.content[0].text : "");

Endpoints

Endpoint Speaks Use it for
POST /v1/chat/completions OpenAI Chat Completions: messages, stream, tools, response_format, max_tokens or max_completion_tokens, temperature, user The OpenAI SDKs and anything OpenAI-compatible
POST /v1/messages Anthropic Messages: system, messages, tools, stream, max_tokens, temperature, metadata.user_id The Anthropic SDKs
POST /v1/invocations TerraFlow's native API, with its options in the body (see below) When you want the whole decision back in the response body
GET /v1/models OpenAI model list Lists your workloads and the chat models in your workspace's catalogue
GET /v1/requests/{id}/evidence JSON The evidence events of one of your own requests
DELETE /v1/requests/{id} JSON Cancel one of your requests that is still queued or running
POST /v1/requests/{id}/feedback JSON: outcome (accepted, rejected or reworked), optional rating and reason_code; no free text Tell TerraFlow whether an answer was useful; the feedback is evidenced
GET /v1/reviews/{id} JSON Fetch an answer that was held for human review, once a reviewer released it

Every governed answer on /v1/chat/completions and /v1/messages also carries a terraflow block in its body: request_id, outcome, reason_code, capability_id, cost_usd, savings_usd and review_id.

Request headers on /v1/chat/completions and /v1/messages

All are optional. An unknown data class or sensitive type, or a deadline that is not a positive whole number, is refused with a 400 before anything is governed, so a typo cannot silently lower a protection. An unknown X-TerraFlow-Workload is denied UNKNOWN_WORKLOAD (403).

Header Value What it does
X-TerraFlow-Workload A workload id Chooses the workload (its contract). Without it, a model that is a workload id chooses it; otherwise your workspace's default, starter-assistant, applies
X-TerraFlow-Data-Class public, internal, confidential or restricted Declares the data's class. TerraFlow also scans the request and uses whichever is more sensitive. Without it, the request is treated as the most sensitive class the workload accepts
X-TerraFlow-Sensitive-Types A comma list of pii, pci, phi, credentials Declares sensitive content. Added to what the scan detects: you can raise the sensitivity, never hide a detection
X-TerraFlow-User A username in your workspace The end user a request is made for, for entitlements and evidence; without it, the body's user (OpenAI) or metadata.user_id (Anthropic). Honoured only for an application allowed to assert users; your workspace's application is not, so in the preview it has no effect
X-TerraFlow-Category A category name from the workload's contract Selects the model tier for workloads that tier by category
X-TerraFlow-Deadline-Ms A positive whole number of milliseconds How long you are willing to wait. A request that cannot finish in time ends DEADLINE_EXPIRED (504). Starter workloads default to 60000
Idempotency-Key Your own unique string Makes a retry safe: see idempotency
X-Correlation-Id Up to 256 visible ASCII characters Your own id for the request. Recorded in the evidence and echoed back
traceparent A W3C trace context Recorded in the evidence and echoed back

Never put real names, e-mail addresses or customer identifiers in any header or in a prompt: this is a preview for synthetic data only.

/v1/invocations

The native endpoint takes its options as body fields, not headers. Only X-TerraFlow-Sensitive-Types, X-Correlation-Id and traceparent are also read from headers (a correlation_id or traceparent in the body wins). Every other X-TerraFlow-* header and Idempotency-Key are ignored here. An unknown body field is refused with INVALID_REQUEST (400).

Body field Meaning
messages Required: the conversation, in the OpenAI message shape
workload The workload id; without it, your workspace's default workload
data_class public, internal, confidential or restricted, as X-TerraFlow-Data-Class
user As X-TerraFlow-User (no effect in the preview)
model Pins a model, as described above
max_tokens, temperature As in the OpenAI API
tools OpenAI-shaped function definitions; each must be on the contract's tool list
deadline_ms As X-TerraFlow-Deadline-Ms
idempotency_key As Idempotency-Key
category As X-TerraFlow-Category
correlation_id, traceparent As the headers of the same names

It returns, at the top level of the body and with the HTTP status of the outcome (denials included): request_id, outcome, reason_code, text, capability_id, model, tool_calls, finish_reason, usage, cost_usd, baseline_cost_usd, savings_usd, timing (total_ms, queue_ms, overhead_ms), decision, validation, evidence_seq, sources and review_id. It does not stream.

cost_usd is what the request cost at list price for its real usage. baseline_cost_usd is an estimate: what the same request would have cost on the workload's baseline model at list price, a counterfactual that was never run. savings_usd is the difference between the two.

Idempotency

The same Idempotency-Key with the same body, within 24 hours, returns the original outcome without running or charging again, marked x-terraflow-idempotent-replay: true. The same key with a different body is IDEMPOTENCY_CONFLICT (409). In the preview, answers are kept for replay in memory only, and that memory is cleared when the server stops each night: replaying an answered request on a later day returns IDEMPOTENCY_REPLAY_NO_CONTENT (409), with the original request id. A refused or failed original replays as itself.

Response headers

Responses to /v1/chat/completions, /v1/messages and /v1/invocations that TerraFlow governed, answered or refused, carry these headers, except a stream that passes straight through (see streaming). Errors raised before a request is governed (a missing or wrong key, a malformed body, an invalid header value) carry only x-terraflow-request-id, x-terraflow-correlation-id and traceparent once a request id exists, and their code in the error body.

Header Meaning
x-terraflow-request-id TerraFlow's id for the request. Quote it to find the request in the console and its evidence
x-terraflow-correlation-id Your X-Correlation-Id, or the request id when you sent none
x-terraflow-outcome EXECUTED (a cache hit included), HELD, DENIED, FAILED, EXPIRED, CANCELLED or SHED
x-terraflow-reason The reason code, for example PERMITTED, CACHE_HIT or DATA_CLASS_NOT_PERMITTED
x-terraflow-capability The TerraFlow id of the model that answered, empty when none did
x-terraflow-cost-usd What the request cost at list price for its real usage, in USD
x-terraflow-savings-usd The estimated list-price cost of the same request on the workload's baseline model, minus the real cost. The baseline was never run: this is a per-request estimate, not a claim about your traffic
x-terraflow-overhead-ms Time TerraFlow itself added, in milliseconds
x-terraflow-cache hit or miss
x-terraflow-idempotent-replay true on a replayed outcome
x-terraflow-review-id On an answer held for human review: the id to fetch it with once released
traceparent Your trace context, echoed when you sent one
retry-after Seconds to wait, on refusals that a later retry can pass (rate limits, a shed queue, the monthly ceiling)
x-should-retry false when an automatic retry cannot succeed soon (a spent budget or allowance, an exhausted deadline)

The official SDKs retry 409, 429 and 5xx responses on their own, and each retry is a new governed request. They honour x-should-retry: false. If you prefer to handle retries yourself, set the SDK's max_retries (Python) or maxRetries (TypeScript) to 0.

Errors

A refusal is an ordinary HTTP error in the shape your SDK expects, with TerraFlow's reason code inside it, so your SDK raises its usual exception.

On /v1/chat/completions (OpenAI shape):

{"error": {"type": "permission_error", "code": "DATA_CLASS_NOT_PERMITTED",
           "message": "TerraFlow denied: DATA_CLASS_NOT_PERMITTED",
           "terraflow": {"request_id": "req_01m3j…", "reason_code": "DATA_CLASS_NOT_PERMITTED"}}}

On /v1/messages (Anthropic shape):

{"type": "error",
 "error": {"type": "permission_error", "message": "TerraFlow denied: DATA_CLASS_NOT_PERMITTED"},
 "terraflow": {"request_id": "req_01m3j…", "reason_code": "DATA_CLASS_NOT_PERMITTED"}}

/v1/invocations returns its full decision body with the HTTP status of the outcome, denials included. Errors raised before a request exists (a missing or wrong key, a malformed body) use the OpenAI shape there.

The common statuses: 400 a malformed request or one the provider rejected; 401 a missing or wrong key; 403 refused by policy, data class or contract; 409 an idempotency conflict; 413 a request too large to scan; 422 an answer blocked by validation; 429 a budget, allowance, rate limit or full queue; 503 no model could answer after retries and fallback, or the spend guard, the evidence store or the policy bundle is unavailable; 504 the deadline passed. Every code, its status and what to do about it is in the reason-code reference.

Streaming

Set stream: true as usual; both channels stream server-sent events in their own format.

Tools

Tool definitions are accepted only when every function they name is on the workload's contract. The starter workloads allow no tools, so a request that offers one is denied TOOL_NOT_PERMITTED (403). On a contract that allows tools, the model's tool calls come back to your code unexecuted, after the same sensitive-data checks as text; TerraFlow never runs them for you.

Not supported in the preview