# Xailon Token Factory Docs > One OpenAI-compatible endpoint for every LLM provider, with metering, credits, and enforcement you own end-to-end. This document contains the full content of all documentation pages for AI consumption. --- ## Authentication **URL:** https://www.tokenfactory.iamsaif.ai/docs/authentication **Description:** API keys, auth headers, key hierarchy, request ids, and how auth failures are reported. ## Auth headers Two headers are accepted on every path; read order is `authorization` first, then `x-api-key`: | Header | Use with | |---|---| | `Authorization: Bearer ` | OpenAI SDKs (default) | | `x-api-key: ` | Anthropic SDKs (default) | ```bash curl https://api./v1/chat/completions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ ... ``` ```bash curl https://api./v1/messages \ -H "x-api-key: $TOKENFACTORY_API_KEY" \ ... ``` ## Key hierarchy and scope Keys are hierarchical: **Organisation → Project → API key**. A key carries: | Property | Enforced where | |---|---| | Model allowlist | before the provider is called (`403 model_not_allowed`) | | IP allowlist (CIDR) | before the provider is called (`403 ip_not_allowed`) | | Plan / model plan gating | before the provider is called (`403 model_not_on_plan`) | | RPM rate limit | token bucket, checked on every request (`429 rate_limit_exceeded`) | | Token quotas (daily/monthly) | quota windows (`429 quota_exceeded`) | | USD spend cap | pre-flight (`402 key_spend_cap_reached`) | A project-scoped key whose project is disabled returns `403 project_disabled`. Any key of a suspended organization returns `403 organization_suspended` until the organization is resumed. Per-key and project USD spend caps surface as `402` — see [Billing](/docs/billing). All of this is decided by the gateway's policy engine **before any provider is called** — a denied request never reaches a provider, and is never billed. ## Request ids - Request ids are **minted server-side**. Any `X-TF-Request-Id` or `X-Request-Id` you send is ignored and overwritten. - Every response (success or error, JSON or audio or stream) carries the `X-TF-Request-Id` response header. - Every error body carries the same id as `error.request_id`. - **Quote the `X-TF-Request-Id` in support requests** — it is the correlation key we use across the whole product. ## 401 responses | Code | Meaning | Recovery | |---|---|---| | `missing_api_key` | No `Authorization` / `x-api-key` header on the request. | Send the header. | | `invalid_api_key` | Key hash not found. | Re-copy the key; confirm the deployment. | | `inactive_api_key` | Key resolved but deactivated. | Re-enable in Settings → API keys. | | `expired_api_key` | Key's expiry has passed. | Create a new key. | ```json { "error": { "type": "authentication_error", "message": "Invalid API key", "code": "invalid_api_key", "param": null, "request_id": "req_01J8EXAMPLE" } } ``` ## When a provider credential fails If a provider rejects **our** credential, you never see a 401/403 for it — you cannot fix that, we can. Instead of a confusing provider auth failure you get a clean, actionable error: ``` 502 provider_unavailable "a provider rejected the platform credential" ``` with the provider's own text withheld. See [Errors](/docs/errors) for the full status-mapping table. --- ## Billing **URL:** https://www.tokenfactory.iamsaif.ai/docs/billing **Description:** The prepaid wallet, request-time credit reservations, metered usage, and video billing. ## Prepaid wallet Every organisation has a prepaid wallet. Usage is metered per model (SKU) from the provider's own reported `usage` — **never estimated** — and priced at the catalogue rate for that model at nano-USD precision. ## How reservations work Before calling the provider, the gateway **reserves** an estimated cost against *balance minus open reservations*: 1. The estimate is sized from `max_tokens` (falling back to a platform default when omitted), priced at the model's rate. 2. The reservation is held for the life of the request. 3. Once usage is metered, the reservation is settled to the real cost (or released, if the request failed). Because the check is against balance minus open reservations, **concurrent requests cannot collectively overspend a small balance**. This reservation gate applies to prepaid wallets — postpaid orgs are capped by a monthly budget instead (a counter cap, not a reservation). ## Why 402 is not 429 If the wallet cannot cover the reservation you get **HTTP 402**: ```json { "error": { "type": "insufficient_quota", "message": "Insufficient credit balance — top up to continue", "code": "insufficient_funds", "param": null, "request_id": "req_01J8EXAMPLE" } } ``` A funds denial is deliberately **not** a 429: - It is not congestion — SDK retry/backoff does not help. - No `Retry-After` is sent. - Retrying without topping up will keep failing. Top up in **Billing** (Stripe) and retry. ## Other money-related errors | Status | Code | Trigger | |---|---|---| | 402 | `insufficient_funds` | Wallet cannot cover the reservation. | | 402 | `key_spend_cap_reached` | Per-key USD spend cap reached. | | 402 | `project_quota_exceeded` | Project monthly cost ceiling reached. | | 429 | `quota_exceeded` / `project_quota_exceeded` | Token quota windows (not funds). | ## Cached tokens Provider-reported prompt-cache tokens are surfaced, never estimated: - OpenAI-dialect bodies: `usage.prompt_tokens_details.cached_tokens` / `.cache_write_tokens` - Anthropic-dialect bodies: `usage.cache_read_input_tokens` / `usage.cache_creation_input_tokens` - Every body: `provider_metadata.cache.read_tokens` / `.write_tokens` On OpenAI-dialect chat/embeddings responses these `prompt_tokens_details` fields are always present (`0` when the backend didn't report caching) — see [Normalized usage shape](/docs/api-reference/chat-completions#normalized-usage-shape). The billed amount always matches what's displayed, including the cache discount: the same cache figures the client sees are what's metered, never a separately-computed number. Cached tokens are priced at the model's cached rate when the catalogue defines one, and the standard rate otherwise. ## Video billing Video jobs are billed **only on confirmed completion**: | Job state | `billing.state` | Charged? | |---|---|---| | `queued` / `processing` | `pending` | No — but see the reservation above | | `completed` | `charged` | Yes | | `failed` | `void` | No | | `cancelled` | `void` | No | The billed unit is the requested duration (`seconds`). A job that expires past its retention window keeps its already-settled charge; nothing further is billed. ## Inspecting spend - Portal → **Billing**: wallet balance, top-ups, invoices. - Portal → **Analytics**: token/cost breakdown per model and per key. - Every response's `usage` block shows exactly what was metered for that request. ## Related - [Rate Limits & Quotas](/docs/rate-limits) - [Videos: Create](/docs/api-reference/videos-create) --- ## Errors **URL:** https://www.tokenfactory.iamsaif.ai/docs/errors **Description:** The uniform error envelope, status rules, and the full error-code table. ## Envelope Every error on every OpenAI-dialect path (`/v1/*` except `/v1/messages`) uses one envelope: ```json { "error": { "type": "invalid_request_error", "message": "…", "code": "unsupported_parameter", "param": "logit_bias", "request_id": "req_01J…" } } ``` | Field | Notes | |---|---| | `type` | Canonical OpenAI type: `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `rate_limit_error`, `insufficient_quota`, `server_error`, `api_error`. | | `code` | Stable machine code — **branch on this, never on `message`**. | | `param` | Offending parameter name, or `null`. Model-scoped rejections always set `param: "model"`. | | `request_id` | Always present; equals the `X-TF-Request-Id` header. | | `provider_detail` | Present only when the gateway replaced a provider's verbatim message (e.g. provider 5xx). | On `/v1/messages` the **Anthropic envelope** is used instead — `request_id` is a top-level sibling, and there is no `code`/`param`: ```json {"type": "error", "error": {"type": "not_found_error", "message": "…"}, "request_id": "req_01J…"} ``` ## Status rules | Scenario | Status | `type` | `code` | |---|---|---|---| | Body/param validation (before the provider is called) | 400 | `invalid_request_error` | specific — see table below | | Unknown endpoint (path is not a routed inference route, e.g. a typo) | 404 | `invalid_request_error` | *(none — `null`)* | | Unknown / not-allowed model | 404 / 403 | `not_found_error` / `permission_error` | `model_not_found` / `model_not_allowed` | | Provider-native id requested | 404 | `not_found_error` | `model_not_found` | | Deprecated alias past sunset | 410 | `not_found_error` | `model_deprecated` | | Image content on non-vision model | 400 | `invalid_request_error` | `vision_not_supported` | | Model does not support the endpoint | 400 | `invalid_request_error` | `unsupported_endpoint` | | Bad API key | 401 | `authentication_error` | `invalid_api_key` … | | Policy denial (allowlists, plan, project) | 403 | `permission_error` | specific | | Insufficient credit | **402** | `insufficient_quota` | `insufficient_funds` | | Gateway rate limit | 429 + `Retry-After` | `rate_limit_error` | `rate_limit_exceeded` | | Provider 400 | 400 | `invalid_request_error` | `provider_error` (verbatim provider message) | | Provider 401 / 403 | **502** (provider detail withheld) | `server_error` | `provider_unavailable` | | Provider 404 / 5xx | 502 | `server_error` | `provider_error` | | Provider 503 | 503 + `Retry-After` | `server_error` | `provider_error` | | Connection failure / no healthy backend | 503 + `Retry-After` | `server_error` | `provider_unavailable` | | Provider timeout | 504 + `Retry-After` | `server_error` | `provider_unavailable` | | Body too large | 413 | `invalid_request_error` | `request_too_large` | | Streaming failure after first byte | (200 already sent) | in-band record | `stream_interrupted` / `api_key_terminated` | | Async (video) job failure | 200 on poll; `status:"failed"` in body | — | from the job's `error` object | Two deliberate design choices: - **402 is never 429.** A funds denial is not congestion — SDK backoff does not help and no `Retry-After` is sent. Full rationale: [Billing](/docs/billing). - **Provider auth failures never reach you as 401/403.** A provider rejecting our credential is `502 provider_unavailable` — the provider's text is withheld, so you get one clean, actionable error instead of a failure you could not fix anyway. ## Model-error taxonomy | Situation | Status | Code | `param` | |---|---|---|---| | Unknown alias, provider-native id | 404 | `model_not_found` | `model` | | Model exists but excluded by **your** key/project allowlist | 403 | `model_not_allowed` | `model` | | Valid model, wrong endpoint for its mode | 400 | `unsupported_endpoint` | `model` | | Vision mismatch (image content, non-vision model) | 400 | `vision_not_supported` | `model` | Unknown models return the fused wording "does not exist or you do not have access to it" — deliberately, so model existence cannot be enumerated. 403 is returned only where the restriction came from your own configuration. ## Error codes ### Authentication (401) | Code | Meaning | |---|---| | `missing_api_key` | No auth header on the request. | | `invalid_api_key` | Key hash not found. | | `inactive_api_key` | Key deactivated. | | `expired_api_key` | Key past its expiry. | ### Permission (403) | Code | Meaning | |---|---| | `model_not_allowed` | Key's model allowlist excludes the alias. | | `model_not_on_plan` | Plan's model allowlist excludes the alias. | | `project_disabled` | Project-scoped key whose project is inactive. | | `organization_suspended` | The key's organization is suspended. Every request is refused until an operator resumes the organization; contact support. | | `ip_not_allowed` | Client IP outside the key's (or project's) CIDR allowlist. | ### Not found / gone (404 / 410) | Code | Meaning | |---|---| | `model_not_found` | Unknown alias or provider-native id (`param: "model"`). | | `model_deprecated` | Alias past its `sunset` (message names the replacement). | | `job_not_found` | Unknown or cross-org video job id. | ### Quota / funds (402 / 429) | Code | Status | Meaning | |---|---|---| | `insufficient_funds` | 402 | Prepaid wallet cannot cover the reservation. Top up and retry. | | `key_spend_cap_reached` | 402 | Per-key USD spend cap reached. | | `project_quota_exceeded` | 402 / 429 | Project monthly cost ceiling (402) or project token quota (429). | | `quota_exceeded` | 429 | Per-key daily/monthly token quota exceeded. | ### Rate limits (429) | Code | Meaning | |---|---| | `rate_limit_exceeded` | Per-key RPM limit exceeded. Honour `Retry-After`. | | `project_rate_limit_exceeded` | Project RPM limit exceeded. | | `provider_rate_limited` | Provider-side 429 — status preserved, provider message verbatim, provider `Retry-After` forwarded when present. | ### Request validation (400 / 413) | Code | Meaning | |---|---| | `invalid_request` | Generic shape violation (missing `messages`, negative `max_tokens`, `top_p` outside `(0, 1]` on the completions/responses endpoints, a streaming `/v1/completions` without `stream_options.include_usage: true`, non-POST on a JSON endpoint). | | `invalid_request_body` | Body present but not a JSON object. | | `invalid_stop_sequences` | `stop` has more than 4 entries. | | `missing_prompt` | Image/video generation: `prompt` absent. | | `empty_prompt` | `prompt` is empty. | | `unsupported_response_format` | Image `response_format:"url"` — only `b64_json` is served. | | `invalid_image_size` | Image `size` outside the deployment allowlist. | | `invalid_tool_call_id` | `role:"tool"` message's `tool_call_id` doesn't match a preceding assistant `tool_calls[].id`. | | `vision_not_supported` | Image content sent to a non-vision model (`param: "model"`). | | `invalid_image_url` | `image_url.url` empty or not http/https/data scheme. | | `unsupported_parameter` | A syntactically valid sampling parameter the backend rejects. | | `unsupported_endpoint` | Model's catalogue mode doesn't match the endpoint called (`param: "model"`). | | `request_too_large` | Body over the 8 MiB gateway cap (or the image-specific cap). | ### Server / provider (502 / 503 / 504) | Code | Meaning | |---|---| | `provider_error` | Provider returned an error (400/404/5xx classes per the status table). | | `provider_unavailable` | Connection failure, no healthy backend, provider timeout, or a provider auth failure (401/403 with provider detail withheld). | | `server_error` | Generic unclassified server error. | ### In-band stream termination (after a 200) | Code | Meaning | |---|---| | `stream_interrupted` | The provider's stream was reset. | | `api_key_terminated` | Key revoked mid-stream. | ## Retrying safely | Error class | Retry? | How | |---|---|---| | 429 (`rate_limit_exceeded`, `quota_exceeded`) | Yes | Wait `Retry-After` seconds, then retry with exponential backoff. | | 429 `provider_rate_limited` | Yes | Same — the provider's own `Retry-After` is forwarded when present. | | 502 / 503 / 504 | Yes | Backoff with jitter; the request was not metered. | | 400 / 404 / 410 / 413 | No | Fix the request. | | 401 / 403 | No | Fix credentials / policy config. | | 402 | No | Top up the wallet (or raise the cap), then retry. | | In-band `stream_interrupted` | Yes | Re-issue the request; safe — a partial stream is not billed as complete. | ## Portal and Management API errors This page covers the inference API (`https://api./v1`). The Management API (`https://portal./api/v1/mgmt`) uses the same envelope family — an `error` object with `type`, `code`, `message`, `param` and `request_id` — plus an optional `action` that says how to fix the problem. Its codes, its status rules and the `type` values it uses are listed in [Management API errors](/docs/management-api/errors). The same rules apply on both surfaces: - Branch on the HTTP status and `code`. Never parse `message`; its wording can change. - Every response carries a request id, generated by Token Factory; a request id you send is ignored. Quote it when you contact support. Where to find it: - **Inference API:** the `X-TF-Request-Id` header, repeated as `error.request_id` (on `/v1/messages`, as the top-level `request_id`). - **Portal and Management API:** the `X-Request-Id` header, repeated as `error.request_id`. - Codes are stable. New codes can appear, so treat an unknown `code` as the generic error for its status. ## Related - [Streaming](/docs/streaming) - [Rate Limits](/docs/rate-limits) - [Billing](/docs/billing) - [Authentication](/docs/authentication) - [Management API errors](/docs/management-api/errors) --- ## Models & Aliases **URL:** https://www.tokenfactory.iamsaif.ai/docs/models-and-aliases **Description:** Curated aliases, listing models, the alias echo guarantee, and the deprecation lifecycle. ## Curated aliases only `model` in any request **must be a curated alias** from the catalogue. A provider-native id (e.g. `gpt-4o-2024-08-06`, `BAAI/bge-m3`) is rejected: ``` 404 not_found_error code: model_not_found param: model ``` There is no pinning to a provider id and no way to bypass the catalogue. Aliases are visible in the portal under **Models** (with per-model pricing and capabilities), or programmatically via `GET /v1/models`. ## Listing available models `GET /v1/models` returns every enabled alias your key can call — rerank and video included — as a model card with OpenAI and OpenRouter fields and per-unit prices (full field list: [List Models](/docs/api-reference/models-list)): ```bash curl https://api./v1/models \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" ``` ```json { "object": "list", "data": [ { "id": "openai/text-embedding-3-small", "name": "openai/text-embedding-3-small", "object": "model", "created": 1756684800, "owned_by": "Token Factory", "context_length": 8191, "context_window": 8191, "architecture": { "modality": "text->embeddings", "input_modalities": ["text"], "output_modalities": ["embeddings"] }, "pricing": { "unit": "token", "prompt": "0.00000002" }, "top_provider": { "context_length": 8191, "is_moderated": false } } ] } ``` ## The alias echo guarantee Every JSON response echoes **the alias you sent** in `model` — including when the serving provider changed under failover: ```json { "model": "gpt-4o", "provider_metadata": { "provider": "openai", "actual_model": "gpt-4o-2024-08-06" } } ``` - `model` — your alias, byte-identical. - `provider_metadata.actual_model` — the provider's model id that served. - `provider_metadata.provider` — the serving provider. The same values ride in the `X-TF-Model` and `X-TF-Actual-Model` response headers on **every** response (JSON, streams, and audio bodies), so streaming and binary clients get one code path. ## Deprecation lifecycle | Phase | Behaviour | |---|---| | Active | Served normally. | | Inside deprecation window | Served normally **and** carries `provider_metadata.deprecation` plus the `X-TF-Deprecation: sunset=; replacement=` response header. | | After `sunset` | Rejected: `410 not_found_error`, `code: model_deprecated`, message names the replacement. | ```json { "provider_metadata": { "provider": "openai", "actual_model": "gpt-4o-2024-08-06", "deprecation": { "alias": "gpt-4o", "sunset": "2026-12-01T00:00:00Z", "replacement": "gpt-4o-2025", "message": "Use gpt-4o-2025 after the sunset date." } } } ``` Clients should treat the header / `provider_metadata.deprecation` as the signal to migrate — the alias keeps working until `sunset`. ## Related - [List Models endpoint](/docs/api-reference/models-list) - [API Versioning](/docs/versioning) --- ## OpenAI & Anthropic Compatibility **URL:** https://www.tokenfactory.iamsaif.ai/docs/openai-anthropic-compatibility **Description:** The gateway's two wire dialects — same underlying contract, different shapes. The gateway natively speaks the **OpenAI** schema. The **Anthropic** Messages dialect is served by a translating proxy (`/v1/messages`), so Claude SDKs work unchanged against the same keys, models, limits, and ledger. This page is the side-by-side for developers with an existing integration in either dialect. ## Which dialect do I need? | If your code uses… | Send requests to | Auth header | |---|---|---| | The OpenAI SDK (or anything OpenAI-dialect: LangChain, Vercel AI SDK, …) | `https://api./v1/…` | `Authorization: Bearer ` | | The Anthropic SDK / Claude Code | `https://api./v1/messages` | `x-api-key: ` (Bearer also accepted) | One API key works across both surfaces — the key, its model allowlist, plan, rate limits, and spend caps are enforced identically regardless of dialect. ## What is identical (the contract) Regardless of dialect: - **`model` echoes verbatim** — the response `model` field is exactly the alias you sent, even when failover changed the serving provider. - **`provider_metadata` is always present** on successful bodies, carrying `provider` (who actually served the request) and `actual_model` (the provider's internal model id). - **Every error carries a server-minted `request_id`** — quote it in support requests; it is the correlation id across all logs. - **The same validation, policy, and billing path applies**: model allowlist, rate limits, and the prepaid credit reservation run before any provider is called. - **The same `X-TF-*` side-channel headers** accompany every response (see [Streaming](/docs/streaming) and [Errors](/docs/errors)). ## What differs (the shapes) | | OpenAI dialect | Anthropic dialect | |---|---|---| | Path prefix | `/v1/*` (except `/v1/messages`) | `/v1/messages` (+ `/v1/messages/count_tokens`) | | Auth header | `Authorization: Bearer ` | `x-api-key: `; `Authorization: Bearer` also accepted | | Error envelope | `{"error": {...}, "request_id": "…"}` | `{"type": "error", "error": {...}, "request_id": "…"}` — `request_id` is a **top-level sibling**, no `code`/`param` | | Streaming events | `data: {"choices":[…]}` chunks, terminated by `data: [DONE]` | Native `message_start` / `content_block_*` / `message_delta` / `message_stop` sequence | | Usage block | `usage{prompt_tokens, completion_tokens, total_tokens}` | `usage{input_tokens, output_tokens}` (+ prompt-cache keys) | ## Worked example — same request, both dialects **OpenAI dialect** (`Authorization: Bearer`): ```bash curl https://api./v1/chat/completions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet", "messages": [{ "role": "user", "content": "Hello!" }], "max_tokens": 64 }' ``` **Anthropic dialect** (`x-api-key`): ```bash curl https://api./v1/messages \ -H "x-api-key: $TOKENFACTORY_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet", "max_tokens": 1024, "messages": [{ "role": "user", "content": "Hello!" }] }' ``` Both hit the same model alias, consume the same wallet, and return `provider_metadata`. The OpenAI response wraps text in `choices[0].message.content`; the Anthropic response uses `content[].text` with a `stop_reason`. ## Migrating an existing client - **From a provider direct to the gateway:** change the base URL and swap the API key — nothing else. See [Quickstart](/docs/quickstart) for the two-step recipe. - **Between dialects:** path prefix, auth header, and response shape change (table above). Model names are shared — the same curated alias works on both surfaces ([Models & Aliases](/docs/models-and-aliases)). ## Related - [Quickstart](/docs/quickstart) - [Anthropic Messages endpoint](/docs/api-reference/messages) - [Errors](/docs/errors) - [Streaming](/docs/streaming) --- ## Overview **URL:** https://www.tokenfactory.iamsaif.ai/docs/overview **Description:** One OpenAI-compatible endpoint for every LLM provider, with metering, credits, and enforcement you own end-to-end. ## What is the Token Factory Inference API? The Token Factory Inference API is a sovereign, multi-tenant AI gateway exposed on `https://api./v1/*`. It gives you: - **One endpoint for every provider** — OpenAI, Anthropic, and on-cluster open-weight models (vLLM, NVIDIA NIM) behind a single base URL. - **OpenAI compatibility on purpose** — point the standard OpenAI SDK at the gateway and it works. Anthropic SDKs are supported on `/v1/messages`. - **Curated model aliases** — you request a stable public name (`gpt-4o`, `bge-m3`); the gateway routes to the serving provider and virtualizes the model name. Failover can change the provider under you without changing your code or the `model` field you see back. - **Enforcement before the provider is called** — API-key auth, model allowlists, IP allowlists, rate limits, token quotas, and prepaid credit reservations are all checked before your request is sent to the provider, so a denied request never incurs any spend. ## Base URL ``` https://api./v1 ``` All paths in this documentation are relative to that base. `` is your deployment's domain (shown in the portal Dashboard and Project pages). ## Two dialects, one contract The gateway serves two wire dialects — OpenAI (`/v1/*`) and Anthropic (`/v1/messages`) — over the same keys, models, limits, and ledger (auth headers: [Authentication](/docs/authentication)): | Dialect | Path prefix | Auth header | Error envelope | |---|---|---|---| | OpenAI | `/v1/*` (except `/v1/messages`) | `Authorization: Bearer ` | OpenAI-shaped, with `request_id` | | Anthropic | `/v1/messages` | `x-api-key: ` (Bearer also accepted) | Anthropic-shaped, with `request_id` | Both dialects observe the same underlying contract: the `model` you send is echoed back verbatim, every successful body carries a `provider_metadata` object, and every error carries a server-minted `request_id`. Full side-by-side and worked examples: [OpenAI & Anthropic Compatibility](/docs/openai-anthropic-compatibility). ## What makes it different from calling providers directly | Capability | Behaviour | |---|---| | Failover | If a serving provider fails or is degraded, the alias routes elsewhere; your request shape does not change. | | Metering | Usage is read from the provider's own `usage` report — never estimated — and priced per model at nano-USD precision. | | Prepaid credits | A reservation is held against your wallet before the provider runs, so concurrent requests cannot overspend a small balance. | | Provider credential safety | Provider-internal fields are stripped or relocated, and provider credentials are never exposed. If a provider rejects our credential, you get a clean, actionable `502 provider_unavailable` instead of a confusing provider auth failure. | ## Full OpenAPI spec A machine-readable OpenAPI 3.1 description of this surface ships with TokenFactory (`docs/api/openapi/inference-v1.yaml`). Where it and these pages disagree, the contract pages here win. ## Next steps - Make your first call in five minutes: [Quickstart](/docs/quickstart) - Get and use an API key: [Authentication](/docs/authentication) - Pick aliases for your workload: [Models & Aliases](/docs/models-and-aliases) - Handle failures properly: [Errors](/docs/errors) --- ## Provider Routing & Failover **URL:** https://www.tokenfactory.iamsaif.ai/docs/provider-routing **Description:** How the gateway picks the provider behind a model alias, and what happens when one fails. Every curated model alias is backed by one or more **provider bindings** — (provider, provider-side model id, region) rows owned by the control plane. This page explains how a request lands on a provider and what happens when that provider fails. It is written for API consumers; bindings themselves are managed by the TokenFactory operators, not through the public API. ## The normal path 1. **You send an alias.** Requests match the gateway route for that alias (exact model-name match). 2. **The gateway picks the binding** configured as the model's route: the primary binding (lowest priority rank, then highest weight) serves the request. 3. **The model name is virtualized.** If the provider's internal model id differs from your alias, the gateway rewrites the model in the request body — your request shape never contains provider-internal names. 4. **Usage, cost, and `provider_metadata` reflect what actually served.** The response's `provider_metadata.provider` and `actual_model` tell you who served the request and their internal id ([Models & Aliases](/docs/models-and-aliases)). ## Failover: priority tiers A model with multiple enabled bindings is routed as ordered **priority tiers**: - Tries are ordered by the binding's **priority** (ascending — lower is more primary), then **weight** within a tier. - A lower-priority tier receives traffic only when every higher tier is **unavailable** — provider connect failures and gateway-class errors. - Within one tier with multiple bindings, traffic is distributed by **weight** (proportional). Passive health detection backs this: after repeated consecutive gateway errors, a failing provider host is temporarily ejected from its tier so failover stops paying a failed first attempt on every request. Ejection is time-boxed; the provider is retried automatically. There are no active health probes hitting providers. ## What triggers a failover Failover is **provider-side only**. It is triggered by the provider being unreachable or returning gateway-class errors (5xx / connect failure) — not by your request's content, rate limits, or credit state (those are rejected up front with a 4xx and never silently rerouted). ## What you see - **Successful failover is transparent**: the same request shape, the same alias echoed back, `200` — with `provider_metadata` telling you who actually served it. - **No failover target available**: you get a clean `502 provider_unavailable` (provider credentials and internals are never exposed). Retry semantics are yours to own; the error carries the server-minted `request_id` ([Errors](/docs/errors)). ## What you cannot do (yet) - **Pin a provider.** Binding selection and priority are platform-operator decisions. There is no per-request provider hint. - **Influence tiers.** Priorities and weights are configured per model by the operator. ## Pricing note Each binding carries its own pricing, and usage is priced at the rate of the model alias — see [Billing](/docs/billing). Whether serving-provider changes under failover can alter per-token pricing is a catalog configuration question; your bill is always computed from the provider's own reported usage, never estimated. ## Related - [Models & Aliases](/docs/models-and-aliases) - [Errors](/docs/errors) - [Billing](/docs/billing) --- ## Quickstart **URL:** https://www.tokenfactory.iamsaif.ai/docs/quickstart **Description:** Make your first API call in five minutes — curl, Python, Node.js, or the OpenAI/Anthropic SDKs. ## 1. Get an API key 1. Sign in to the portal. 2. Open a project (or create one under **Projects**). 3. Create an API key. The plaintext key is shown once at creation — store it in a secret manager, not in code. Keys are hierarchical (Organisation → Project → API key) and carry their own model allowlist, IP allowlist, plan, rate limit, and optional spend cap. ## 2. Your first chat completion ## 3. What you get back ```json { "id": "chatcmpl-1", "object": "chat.completion", "created": 1724680001, "model": "gpt-4o", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Hello! How can I help?", "refusal": null }, "finish_reason": "stop", "logprobs": null } ], "usage": { "prompt_tokens": 5, "completion_tokens": 9, "total_tokens": 14 }, "provider_metadata": { "provider": "openai", "actual_model": "gpt-4o-2024-08-06" } } ``` Two things to notice: - `model` is **the alias you sent**, even though the provider's own model id (`gpt-4o-2024-08-06`) actually served the request. That id is always in `provider_metadata.actual_model`. - `provider_metadata` is always present on every successful JSON body — you can code against it unconditionally. ## 4. Point an existing OpenAI integration at the gateway The only two lines that change are `base_url` and `api_key`: ```python client = OpenAI( base_url="https://api./v1", api_key=os.environ["TOKENFACTORY_API_KEY"], ) ``` The same is true for TypeScript, Go, and any OpenAI-compatible tool (LangChain, Vercel AI SDK, Cursor, etc.): set the base URL to `https://api./v1` and use a Token Factory key. ## 5. Try other modalities Every modality follows the same pattern — alias in, standard body out: | Task | Endpoint | Guide | |---|---|---| | Chat with tools/vision | `POST /v1/chat/completions` | [Chat Completions](/docs/api-reference/chat-completions) | | Streaming | `stream: true` on chat | [Streaming](/docs/streaming) | | Embeddings | `POST /v1/embeddings` | [Embeddings](/docs/api-reference/embeddings) | | Image generation | `POST /v1/images/generations` | [Image Generation](/docs/api-reference/images-generations) | | Speech to text | `POST /v1/audio/transcriptions` | [Transcriptions](/docs/api-reference/audio-transcriptions) | | Text to speech | `POST /v1/audio/speech` | [Text to Speech](/docs/api-reference/audio-speech) | | Reranking | `POST /v1/rerank` | [Rerank](/docs/api-reference/rerank) | | Video generation | `POST /v1/videos` (async) | [Videos: Create](/docs/api-reference/videos-create) | ## Common first-call mistakes | Symptom | Cause | Fix | |---|---|---| | `404 model_not_found` | A provider-native id was sent (`gpt-4o-2024-08-06`) or the alias is not on your key's allowlist. | Use a curated alias from **Models** in the portal. | | `401 invalid_api_key` | Key mistyped, revoked, or from another environment. | Re-copy the key; check you are on the right deployment. | | `402 insufficient_funds` | The organisation wallet has no credit. | Top up in **Billing**, then retry. | | `403 model_not_allowed` | The key's model allowlist excludes the alias. | Add the alias to the key (Settings → API keys). | --- ## Rate Limits & Quotas **URL:** https://www.tokenfactory.iamsaif.ai/docs/rate-limits **Description:** RPM windows, token quotas, the response headers, and how 429s behave. ## What is limited | Limit | Scope | Window | Exceeded returns | |---|---|---|---| | Requests per minute (RPM) | Per API key | Rolling ~60 s | 429 `rate_limit_exceeded` | | Project RPM | Per project | Rolling window | 429 `project_rate_limit_exceeded` | | Token quota (daily/monthly) | Per key | Calendar window | 429 `quota_exceeded` | | Project token quota | Per project | Calendar window | 429 `project_quota_exceeded` | | Project monthly cost ceiling | Per project | Monthly | 402 `project_quota_exceeded` | | Per-key USD spend cap | Per key | Rolling | 402 `key_spend_cap_reached` | RPM is enforced with a two-bucket weighted sliding window — it bounds you to approximately `limit` requests per any rolling 60 seconds, rather than a cliff-edge minute boundary. ## Response headers Rate-limit headers are sent on **both** 200 and 429 responses: | Header | Meaning | |---|---| | `x-ratelimit-limit-requests` | RPM limit for the key | | `x-ratelimit-remaining-requests` | Requests remaining in the current rolling minute | | `x-ratelimit-reset-requests` | Seconds until the request window resets (integer seconds) | | `x-ratelimit-limit-tokens` | Token quota window limit, when configured | | `x-ratelimit-remaining-tokens` | Tokens remaining in the quota window | | `x-ratelimit-reset-tokens` | Seconds until the token window resets | | `Retry-After` | 429 only — seconds to wait (see below) | ## `Retry-After` semantics | Source | Value | |---|---| | Gateway 429 (`rate_limit_exceeded`, `quota_exceeded`, …) | Seconds until the tracked window resets | | Provider 429 (`provider_rate_limited`) | The provider's own `Retry-After`, forwarded when present | | **402** | **Not sent** — a funds denial is not congestion ([why](/docs/billing)) | OpenAI SDKs honour `Retry-After` automatically; if you roll your own retry logic, always wait for it before retrying. ## Example 429 ```json { "error": { "type": "rate_limit_error", "message": "Rate limit exceeded", "code": "rate_limit_exceeded", "param": null, "request_id": "req_01J8EXAMPLE" } } ``` ```http HTTP/1.1 429 Too Many Requests Retry-After: 17 x-ratelimit-limit-requests: 600 x-ratelimit-remaining-requests: 0 x-ratelimit-reset-requests: 17 ``` ## Handling provider 429s A provider-side 429 is preserved as `429 provider_rate_limited` with the provider's verbatim message, e.g.: ```json { "error": { "type": "rate_limit_error", "message": "Rate limit reached for gpt-4o in organization org-x on tokens per min (TPM): Limit 30000, Used 29000, Requested 2000.", "code": "provider_rate_limited", "param": null, "request_id": "req_01J8TESTERR02" } } ``` Treat it exactly like a gateway 429: honour `Retry-After` and back off. ## Related - [Billing](/docs/billing) — why 402 is not 429 - [Errors](/docs/errors) — the full code table --- ## SDKs & Tools **URL:** https://www.tokenfactory.iamsaif.ai/docs/sdks **Description:** Which client SDKs and tools work with the gateway — what is tested vs what should work. The gateway is an OpenAI-compatible endpoint plus a native Anthropic surface, so any client that speaks either dialect points at it by changing two lines: the **base URL** and the **API key**. ## Verified These are exercised by TokenFactory's own regression suites and documented end-to-end in these docs — treat them as supported: | Client | Surface | Notes | |---|---|---| | **OpenAI Python SDK** (`pip install openai`) | `/v1/*` | The canonical example throughout these docs. `base_url="https://api./v1"`. | | **OpenAI Node SDK** (`npm install openai`) | `/v1/*` | `baseURL: 'https://api./v1'`. | | **Anthropic Python SDK** (`pip install anthropic`) | `/v1/messages` | `base_url="https://api."` — no `/v1`; the SDK appends the path. | | **curl / plain HTTP** | both dialects | Every endpoint page shows a curl sample; nothing SDK-specific is required. | The wire shapes themselves are contract-tested per modality (chat, embeddings, images, audio, rerank, anthropic, video) — an SDK that emits the standard OpenAI or Anthropic wire format hits the same tested path. ## Should work — same dialect, not individually verified Any client that emits the standard OpenAI (or Anthropic) wire format should work by setting the base URL and key. This includes tools frequently used with OpenAI-compatible endpoints: - **LangChain** (`ChatOpenAI` with `base_url`) - **Vercel AI SDK** (OpenAI provider with `baseURL`) - **Editor/agent clients** (e.g. Cursor) that accept a custom OpenAI base URL - **Claude Code** and Anthropic SDK clients via `/v1/messages` ([Auth](/docs/authentication)) These are **not** part of a tested support matrix — the gateway does not run per-tool certification. If a tool strictly follows the OpenAI or Anthropic schemas, it works; if it depends on provider-specific extensions outside those schemas (which the gateway strips or rejects — see [Errors](/docs/errors)), that specific extension may not be available. ## Endpoint coverage inside the OpenAI SDKs The OpenAI SDKs' typed surface does **not** cover every gateway endpoint: - `/v1/rerank` and `/v1/videos/*` are **not** part of the SDKs' typed resources. Use the SDK's generic request escape hatch (`client.post('/rerank', …)` in Python, `client.post('/rerank', {body})` in Node) — or plain curl. Both are shown on the [Rerank](/docs/api-reference/rerank) and [Videos — Create](/docs/api-reference/videos-create) pages. - Everything else (`chat.completions`, `completions`, `embeddings`, `images.*`, `audio.transcriptions/translations/speech`, `models.list`, `videos.create/retrieve`*) maps to typed methods. - `videos.*` typed methods exist but target the deprecated OpenAI Sora surface — the gateway's async video API is [submit + poll](/docs/api-reference/videos-create), not the Sora job shape. Use the curl samples on the video pages. ## Related - [Quickstart](/docs/quickstart) - [OpenAI & Anthropic Compatibility](/docs/openai-anthropic-compatibility) - [Models & Aliases](/docs/models-and-aliases) --- ## Single sign-on (SSO) **URL:** https://www.tokenfactory.iamsaif.ai/docs/single-sign-on **Description:** Connect your organization's identity provider, and the exact redirect URIs to register on the IdP side for Microsoft Entra ID, Google Workspace, Okta, generic OIDC and SAML. Organization admins can connect the organization's own identity provider (IdP) so members sign in with their company account. Setup lives in the portal under **Settings → SSO**: 1. **Provider.** Pick the IdP and paste the client ID and secret of an app you registered on the IdP side (Microsoft also needs the directory tenant ID). 2. **Domain.** Your email domain is claimed for the connection. 3. **Test and activate.** Run the connection test; once it passes, activate. For Microsoft, **Test sign-in** (below) also proves your domains. Only if your platform operator has enabled administrator verification is there an extra step first: a **Verify with Microsoft/Google** sign-in for Microsoft and Google, or a verified domain claim for other IdPs. Until it is activated the tab shows the connection as **Not active yet**, with the next steps numbered (1. Test sign-in, 2. Activate). Once it is **Active**: - **Who can join.** By default only people you invite can join. Anyone else who signs in with your IdP is told they are not a member of your organization yet and should ask an administrator for an invitation. An invited person joins on their first SSO sign-in. - **Automatic membership** (off by default). Turn it on to let anyone who signs in with your IdP at your verified domains join as a Member. People you removed can't rejoin this way: they are listed under **Settings → Members → Removed**, where an administrator can **Restore** them (their previous role and projects come back when they next sign in; they get an email) or send them an invite. - **Require SSO.** Everyone at your verified domains must sign in with your IdP; other sign-in methods are refused. - **Email address.** Members are matched by their IdP account. Their email comes from the IdP's `email` claim; for Microsoft, an account without a mailbox uses its user principal name (UPN) when the UPN is at one of your proven domains. An account with no usable email is told to add one at the IdP or use another sign-in method. No extra form is shown after the IdP sign-in. ### Members' own sign-in methods Each person manages their own sign-in methods under **Profile → Sign-in methods**: their password, and Microsoft, Google or GitHub accounts connected to their portal account. Your SSO settings decide what they may change: - **Require SSO on:** the page is read-only for everyone at your verified domains. It says, for example, "Managed by Acme: sign in with Acme Microsoft Entra ID"; nobody at those domains can connect a personal account, remove a method or add a password. - **Require SSO off:** members may connect a personal Microsoft, Google or GitHub account and add a password, as long as the provider account uses the same email address as their portal account (verified by the provider). The provider your own connection uses (for example Microsoft for a Microsoft Entra connection) is not offered separately: those sign-ins go through your connection. - **Your SSO link** shows on the page as managed by your organization; members cannot remove it. - People whose address is not at your domains (guests you invited) are not affected by Require SSO. **Passwords.** A password must have at least 12 characters (up to 128). Spaces and all characters are allowed, and there are no rules about capitals, digits or symbols. Common or leaked passwords, the person's own email address and their current password are refused. With Require SSO on, "Forgot password" at your domains sends no reset link: the person gets an email telling them to sign in with your SSO. People who sign in only with Microsoft, Google or GitHub get the same kind of email. Whenever a password is reset or changed, its owner gets an email saying so. The IdP only accepts a sign-in if the address it sends the user back to is registered on your app, so register the redirect URIs below **before** members try to sign in. ## Register redirect URIs The **Settings → SSO** tab shows the exact values for your organization, with a copy button, for the IdP you picked. Copy them from there; the patterns below explain where each one comes from. | Placeholder | Meaning | |---|---| | `` | The platform's sign-in host, for example `https://auth.example.com` | | `` | The portal host, for example `https://portal.example.com` | | `` | Your organization's ID. The full URI is shown in Settings → SSO | Every IdP type uses the same **sign-in redirect URI** (the SAML ACS URL is the same address): ```text /auth/realms/org/broker/org-/endpoint ``` For example: `https://auth.example.com/auth/realms/org/broker/org-0b6c2f8e-1d2a-4c3b-9e8f-7a6b5c4d3e2f/endpoint`. It is specific to your organization. It is **not** the redirect URI of the generic "Sign in with Google/Microsoft" buttons on the login page, and it is not `/auth/oidc/callback`. No logout URL needs to be registered: signing out ends the session on this platform only and does not call your IdP's logout endpoint. ### Microsoft Entra ID Microsoft Entra admin center → **App registrations** → your app → **Authentication** → **Add a platform** → **Web**. | Register | Value | |---|---| | Redirect URI (Web) | `/auth/realms/org/broker/org-/endpoint` | - Register the app in the **same Microsoft tenant as your users' email domain**. An app from another tenant (a parent company's, a partner's) does not work: Microsoft refuses it (`AADSTS7000229`) or your users sign in as guests. Settings → SSO warns when one of your domains belongs to a different tenant than the connection's. - Supported account types: **Accounts in this organizational directory only** (single tenant). - Create a client secret under **Certificates & secrets** and paste it, with the **Application (client) ID** and the **Directory (tenant) ID**, into Settings → SSO. - **API permissions** (Microsoft Graph): - **Delegated** `openid`, `profile`, `email` and `User.Read` (the default on every app registration) — for sign-in. Admin consent is not required unless your tenant blocks user consent. - **Application** `Organization.Read.All`, then **Grant admin consent** — for domain proof (Test sign-in). Only a tenant administrator can grant it. Without it sign-in still works and you can still activate, but your domains are not proven. - **Token configuration**: no optional claims are needed (adding `email` is harmless). - If the app's enterprise application has **Assignment required** set to **Yes**, only the users and groups assigned to it can sign in through SSO. - **Verify with Microsoft** appears only when administrator verification is enabled on the platform. It does not use your app: it signs you in through the platform's own Microsoft sign-in app, which returns to `/auth/sso/verify-admin/callback`. That URI is registered by the platform operator; do not add it to your app. If your tenant blocks user consent to third-party apps, an Entra administrator may need to grant consent to the platform's app the first time. #### Test sign-in: prove your domains (Microsoft) In Settings → SSO, **Test sign-in** signs you in once through your app (same redirect URI, nothing new to register). Use your own account — a member of the tenant, not a guest — whose email domain is one of the tenant's verified domains. With your app's own credentials the platform then reads your tenant's **verified domains** from Microsoft (`Organization.Read.All`, granted by your administrator) and claims them for your organization as **proven**: - `*.onmicrosoft.com` is never claimed, and you can untick any domain. - A domain another organization already claimed is shown as a conflict and is not taken; contact support to resolve it. - At a proven domain nobody can sign up on their own or create another organization: they sign in with your SSO, or you invite them. - If admin consent is missing, the result says "Ask your Microsoft administrator to grant admin consent for Organization.Read.All to this app, then test again." Nothing is claimed, but the test still counts for Activate. - Changing the client ID or tenant ID clears the proof; run Test sign-in again. ### Google Workspace Google Cloud console → **APIs & Services** → **Credentials** → **Create credentials** → **OAuth client ID** → application type **Web application**. | Register | Value | |---|---| | Authorized redirect URI | `/auth/realms/org/broker/org-/endpoint` | - No authorized JavaScript origin is needed. - **Verify with Google** appears only when administrator verification is enabled on the platform. It does not use your OAuth client: it signs you in through the platform's own Google client, which returns to `/auth/sso/verify-admin/callback`. That URI is registered by the platform operator; do not add it to your client. Sign in with a managed Google Workspace account on the claimed domain: personal Gmail accounts carry no domain and fail verification. ### Okta and generic OIDC Okta Admin Console → **Applications** → **Create App Integration** → **OIDC - OpenID Connect** → **Web Application**. For another OIDC provider, create a confidential client with the authorization-code flow. | Register | Value | |---|---| | Sign-in redirect URI | `/auth/realms/org/broker/org-/endpoint` | - Grant type: authorization code. Scopes: `openid`, `email`, `profile`. - Paste the issuer URL (for Okta `https://` or `https:///oauth2/default`), the client ID and the client secret into Settings → SSO. - There is no IdP sign-in step for verification. Only when administrator verification is enabled on the platform does activation also require a verified claim of your email domain. ### SAML SAML connections are set up through the API rather than the portal wizard. Configure the service provider on your IdP with: | Register | Value | |---|---| | Assertion Consumer Service (ACS) / Reply URL | `/auth/realms/org/broker/org-/endpoint` | | SP entity ID / Audience | `/auth/realms/org` | | SP metadata (optional, to import) | `/auth/realms/org/broker/org-/endpoint/descriptor` | Use the user's email address as the NameID (format `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`). When administrator verification is enabled on the platform, activation also requires a verified claim of your email domain. ## Troubleshooting | What you see | Usual cause | |---|---| | Microsoft: `AADSTS7000229` / "This app isn't set up in tenant …" | The app is registered in a different tenant than the Tenant ID. Use an app registered in your users' tenant, or grant admin consent there for a multi-tenant app | | "… belongs to Microsoft tenant … This connection uses …", or Test sign-in: "signed in as a guest" | The app's tenant is not the one your email domain belongs to. Register the app in the same tenant as your users' email domain and enter that Directory (tenant) ID | | Microsoft: `AADSTS50011` "redirect URI ... does not match" | The sign-in redirect URI is missing from the app registration, or it was added under a platform other than **Web** | | Google: `Error 400: redirect_uri_mismatch` | The URI is missing from **Authorized redirect URIs**, or differs by a character (trailing slash, `http` vs `https`) | | **Verify with Google/Microsoft** (only when administrator verification is enabled) fails with a redirect mismatch | The platform's own app is missing `/auth/sso/verify-admin/callback`. Contact the platform operator | | "Verification is not configured on this deployment" | The platform operator has not set up the verification client for that IdP | --- ## Streaming **URL:** https://www.tokenfactory.iamsaif.ai/docs/streaming **Description:** SSE shapes, the always-present final usage chunk, side-channel headers, and in-band error handling. Streaming is standard OpenAI server-sent events: set `"stream": true` and read `data:` records terminated by `data: [DONE]`. `/v1/messages` streams in the native Anthropic event sequence. ## Chat stream shape Each `data:` record is an OpenAI `chat.completion.chunk`: ``` data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1724680001,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":"Hel"},"finish_reason":null}]} data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1724680001,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"lo!"},"finish_reason":null}]} data: {"id":"chatcmpl-1","object":"chat.completion.chunk","created":1724680001,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":5,"completion_tokens":3,"total_tokens":8,"prompt_tokens_details":{"cached_tokens":0,"cache_write_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}} data: [DONE] ``` Three guarantees: 1. **`model` in every chunk is your alias** — chunks are otherwise passed through byte-identical, with model-name virtualization applied (upstream- private fields such as `nvext` are removed, as on non-streaming responses). 2. **A final usage chunk is always present** — on `/v1/chat/completions` the gateway forces `stream_options.include_usage: true` regardless of what you sent, so the terminating chunk carries `usage`. On the legacy `/v1/completions` it cannot add the flag, so a streaming request **must** send `"stream_options": {"include_usage": true}`; without it the request is rejected with 400 `invalid_request` (`param: "stream_options.include_usage"`), because the stream could not be metered. 3. **That usage chunk has a normalized shape** — `usage.prompt_tokens_details.{cached_tokens,cache_write_tokens}` and `usage.completion_tokens_details.reasoning_tokens` are always present on the chunk carrying `usage`, defaulting to `0` when the backend didn't report that detail, and passed through unchanged when it did. This is the same guarantee non-streaming responses get (see [Chat Completions](/docs/api-reference/chat-completions#normalized-usage-shape)) — the gateway never invents a nonzero number, and it holds regardless of how the upstream backend framed its own SSE output, so you see one consistent `usage` shape no matter which model served the request. ## Side-channel headers (set before the first byte) | Header | Value | |---|---| | `X-TF-Request-Id` | Server-minted request id (same on every response type) | | `X-TF-Provider` | `provider_metadata.provider` | | `X-TF-Actual-Model` | `provider_metadata.actual_model` | | `X-TF-Model` | Your alias | | `X-TF-Deprecation` | Only for a deprecated alias: `sunset= ## Detecting stream completion vs interruption A stream ends in exactly one of two ways: | Outcome | How you detect it | |---|---| | Clean completion | You saw `data: [DONE]`. | | Interruption after first byte | One `data: {"error":{…}}` record, then EOF **without** `[DONE]`. | The in-band error record has the uniform error shape: ``` data: {"error":{"type":"server_error","message":"upstream stream interrupted","code":"stream_interrupted","request_id":"req_…"}} ``` Codes: `stream_interrupted` (the provider's stream was reset) or `api_key_terminated` (your key was revoked mid-stream). The HTTP status is never rewritten after headers are sent — a failure after the first byte is always this in-band record, which is why the `[DONE]` check is the reliable completion signal. ## Anthropic streaming (`/v1/messages`) Streaming on the Messages dialect keeps Anthropic's native event sequence — `message_start`, `content_block_start`, `content_block_delta`, …, `message_delta`, `message_stop` — with the same side-channel headers. An interruption surfaces as the Anthropic-dialect error record and the stream ends without `message_stop`. ## Related - [Chat Completions](/docs/api-reference/chat-completions) - [Errors](/docs/errors) --- ## API Versioning **URL:** https://www.tokenfactory.iamsaif.ai/docs/versioning **Description:** Contract v1, what counts as a breaking change, and how deprecations are communicated. ## Current version: v1 This is the current and **only** public contract on `api./v1/*`. There is no rollout flag, no legacy compatibility mode, and no header-based version selection — every request, in every environment, gets the behaviour documented in these pages. ## What can change without a new version Adding a key to `provider_metadata` is a **minor** version bump of the contract (announced, additive, opt-in to read). New endpoints and new optional request fields are also additive changes. ## What is a breaking change Removing or renaming anything in a standard response body, changing a status code, or changing the error envelope is a **major** version, tracked as a new contract document revision — and it will not happen silently inside `v1`. ## How deprecations are communicated Model aliases (not the API itself) deprecate through a built-in lifecycle — served normally with `provider_metadata.deprecation` + `X-TF-Deprecation` inside the window, then `410 model_deprecated` past `sunset`. The full phase table: [Models & Aliases](/docs/models-and-aliases). Machine-readable description: the OpenAPI 3.1 spec (`inference-v1.yaml`) tracks this same surface and is validated in CI. ## Related - [Models & Aliases](/docs/models-and-aliases) - [Errors](/docs/errors) --- ## Text to Speech **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/audio-speech **Description:** Synthesize audio from input text. OpenAI-dialect TTS. Returns a raw **audio body** (`audio/*`) — there is never a JSON envelope on success. The `provider_metadata` side-channel rides **only in the `X-TF-*` response headers**. ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | A TTS alias. | | `input` | string | **yes** | Text to synthesize. | | `voice` | string | no | Model-dependent voice id. | | `response_format` | string | no | `wav`, `mp3`, `opus`, `aac`, `flac`, `pcm` — provider dependent. | | `speed` | number | no | Playback speed. | | `stream` | boolean | no | Best-effort (see warning above). | | `stream_format` | `audio` \| `sse` | no | With `stream: true`. | ## Code sample ```bash curl https://api./v1/audio/speech \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "tts-1", "input": "Today is a wonderful day to build something people love!", "voice": "alloy", "response_format": "mp3" }' \ --output speech.mp3 ``` ## Response Raw audio bytes (`audio/mpeg`, `audio/wav`, …) — save or stream them directly. Inspect the `X-TF-*` headers for provider / actual-model / request-id. ## Errors Standard error surface — see [Errors](/docs/errors). Errors are JSON envelopes even though success is binary. --- ## Transcriptions **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/audio-transcriptions **Description:** Transcribe audio into text. OpenAI-dialect speech-to-text. The audio upload and the whole request are bounded by the 8 MiB gateway body cap. ## Request body (multipart) | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | An STT alias (e.g. a Whisper-family model). | | `file` | file (binary) | **yes** | The audio file. | | `language` | string | no | ISO-639-1 hint. | | `prompt` | string | no | Optional context/style prompt. | | `response_format` | string | no | `json` (default), `verbose_json`, `text`, `srt`, `vtt`. | | `temperature` | number | no | Sampling temperature. | | `timestamp_granularities` | string[] | no | `word` and/or `segment` (with `verbose_json`). | ## Response formats - `json` / `verbose_json` → JSON body with `provider_metadata`. - `text` / `srt` / `vtt` → returned **verbatim**; `provider_metadata` is in the `X-TF-*` headers only. ## Code sample ```bash curl https://api./v1/audio/transcriptions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -F model=whisper-1 \ -F "file=@recording.mp3" ``` ```python from openai import OpenAI client = OpenAI( base_url="https://api./v1", api_key=os.environ["TOKENFACTORY_API_KEY"], ) with open("recording.mp3", "rb") as f: transcript = client.audio.transcriptions.create( model="whisper-1", file=f, ) print(transcript.text) ``` ## JSON response ```json { "text": "Today is a wonderful day…", "usage": { "type": "duration", "seconds": 12.3 }, "provider_metadata": { "provider": "vllm-staging", "actual_model": "whisper-large-v3" } } ``` With `response_format: "verbose_json"` the body also carries `language`, `duration`, `segments[]`, and `words[]` when the provider reports them. `usage` is kept exactly as the provider reported it (duration-based or token-based) — never invented. ## Errors Standard error surface — see [Errors](/docs/errors). ## Translations `POST /v1/audio/translations` — same contract as transcriptions; the output is English text regardless of the spoken language. ### Request body (multipart) | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | An STT alias. | | `file` | file (binary) | **yes** | The audio file. | | `prompt` | string | no | Optional context. | | `response_format` | string | no | `json` (default), `verbose_json`, `text`, `srt`, `vtt`. | | `temperature` | number | no | Sampling temperature. | ### Code sample ```bash curl https://api./v1/audio/translations \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -F model=whisper-1 \ -F "file=@german_speech.mp3" ``` ### Response Same shapes as transcriptions (`text` + optional verbose fields + `usage` + `provider_metadata`; non-JSON formats returned verbatim with side-channel headers). ### Errors Standard error surface — see [Errors](/docs/errors). --- ## Chat Completions **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/chat-completions **Description:** Create a model response for a chat conversation — OpenAI dialect, with tools and vision. Given a list of messages, the model produces a completion. Supports streaming (`stream: true`), tool calling, and vision (image content parts, on models with the vision capability). ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | A curated alias (`gpt-4o`). Provider-native ids are rejected with 404 `model_not_found`. | | `messages` | array | **yes** | OpenAI messages. Roles: `system`, `developer`, `user`, `assistant`, `tool`. Content may be a string or an array of parts. | | `messages[].content[].image_url.url` | string | no | Must be a non-empty `http`/`https`/`data:` URL (format-checked only, never fetched). → 400 `invalid_image_url`. | | `stream` | boolean | no | SSE streaming; see [Streaming](/docs/streaming). | | `stream_options.include_usage` | boolean | no | **Always forced to `true`** by the gateway — the final usage chunk is guaranteed. | | `stop` | string \| string[] \| null | no | At most **4** sequences → 400 `invalid_stop_sequences`. | | `max_tokens` | integer | no | Also sizes the prepaid credit reservation when the wallet check runs. | | `max_completion_tokens` | integer | no | Forwarded to the provider unchanged. | | `top_p` | number | no | Nucleus sampling. Must be in `(0, 1]` → a numeric value outside that range (notably `0`, an empty nucleus) is 400 `invalid_request` (`param: "top_p"`). Omit for the model default. | | `temperature`, `n` | number / integer | no | Forwarded to the provider unchanged. | | `tools`, `tool_choice` | array / object | no | Forwarded to the provider unchanged. `tool_call_id` on tool messages must match a preceding assistant `tool_calls[].id` → 400 `invalid_tool_call_id`. | | `response_format` | object | no | Forwarded unchanged (e.g. JSON mode / JSON schema). | | `user` | string | no | Forwarded to the provider unchanged. | ## Request validation (before the provider is called) | Trigger | Status | Code | |---|---|---| | `stop` with more than 4 sequences | 400 | `invalid_stop_sequences` | | `top_p` outside `(0, 1]` (e.g. `0`) | 400 | `invalid_request` (`param: "top_p"`) | | `tool_call_id` not matching a preceding assistant `tool_calls[].id` | 400 | `invalid_tool_call_id` | | Malformed `image_url.url` | 400 | `invalid_image_url` | | Image content on a non-vision model | 400 | `vision_not_supported` (`param: "model"`) | | Model mode is not chat | 400 | `unsupported_endpoint` (`param: "model"`) | ## Code samples ## Streaming Set `"stream": true` to receive Server-Sent Events. Every `data:` record is a `chat.completion.chunk`; the stream ends with a chunk carrying the final `usage` (the gateway always includes it) and `data: [DONE]`: ```bash curl https://api./v1/chat/completions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Count to three"}], "stream": true }' ``` ``` data: {"id":"chatcmpl-9","object":"chat.completion.chunk","created":1724680009,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":"One"},"finish_reason":null}]} data: {"id":"chatcmpl-9","object":"chat.completion.chunk","created":1724680009,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":", two, three"},"finish_reason":null}]} data: {"id":"chatcmpl-9","object":"chat.completion.chunk","created":1724680009,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":8,"completion_tokens":6,"total_tokens":14,"prompt_tokens_details":{"cached_tokens":0,"cache_write_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}} data: [DONE] ``` The same normalized usage shape described above ([Normalized usage shape](#normalized-usage-shape)) applies to this terminal chunk — always present, `0`-defaulted, never invented — regardless of how the upstream provider framed its own usage payload. Full stream contract (guarantees, side-channel headers, in-band errors): [Streaming](/docs/streaming). ## Tool calling Tools are forwarded to the provider as sent. The assistant's `finish_reason` becomes `tool_calls` and the delta carries the tool invocation; send the tool's result back as a `role: "tool"` message with the matching `tool_call_id`: ```bash curl https://api./v1/chat/completions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "What is the weather in Oslo?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } }] }' ``` Response (`finish_reason: "tool_calls"`, invocation in `message.tool_calls[]`): ```json { "choices": [{ "index": 0, "message": { "role": "assistant", "content": null, "tool_calls": [{ "id": "call_1", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"Oslo\"}" } }] }, "finish_reason": "tool_calls" }], "usage": { "prompt_tokens": 42, "completion_tokens": 18, "total_tokens": 60 }, "provider_metadata": { "provider": "openai", "actual_model": "gpt-4o-2024-08-06" } } ``` Feed the result back as a `tool` message to continue the conversation — its `tool_call_id` must match (orphan ids are rejected: 400 `invalid_tool_call_id`, table above). ```json { "model": "gpt-4o", "messages": [ { "role": "user", "content": "What is the weather in Oslo?" }, { "role": "assistant", "tool_calls": [{ "id": "call_1", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"Oslo\"}" } }] }, { "role": "tool", "tool_call_id": "call_1", "content": "4°C, light snow" } ], "tools": [] } ``` ## Response ```json { "id": "chatcmpl-1", "object": "chat.completion", "created": 1724680001, "model": "gpt-4o", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Hi", "refusal": null }, "finish_reason": "stop", "logprobs": null } ], "usage": { "prompt_tokens": 5, "completion_tokens": 1, "total_tokens": 6, "prompt_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 }, "completion_tokens_details": { "reasoning_tokens": 0 } }, "system_fingerprint": "fp_1", "service_tier": "default", "provider_metadata": { "provider": "openai", "actual_model": "gpt-4o-2024-08-06" } } ``` Guaranteed fields: `id`, `object: "chat.completion"`, `created`, `model` (your alias), `choices[].{index, message, finish_reason}`, `usage` (when the provider reported it), and `provider_metadata` (always). ### Normalized usage shape Different upstream providers (and even different open-weight backends behind the same alias) report token usage differently — some send `prompt_tokens_details`/`completion_tokens_details`, some omit them entirely, some send an explicit `null`. The gateway normalizes all of this into one consistent shape, so your code never has to branch on which backend served a given request: - `usage.prompt_tokens_details.cached_tokens` - `usage.prompt_tokens_details.cache_write_tokens` - `usage.completion_tokens_details.reasoning_tokens` are **always present** in the response body, defaulting to `0` when the provider didn't report that detail. When a provider *did* report a real value, it is passed through unchanged — the gateway never invents or estimates a nonzero number; `0` is only ever a safe "not reported" default, the same convention OpenRouter uses for its own normalized usage schema. Provider-internal fields (`nvext`, `native_finish_reason`, timing keys) are stripped or relocated into `provider_metadata`. Reasoning models return their reasoning text in `choices[].message.reasoning_content` (and `choices[].delta.reasoning_content` when streaming), next to `content` — the same field DeepSeek, vLLM and OpenRouter use. An upstream `message.reasoning` string is renamed to `reasoning_content`. `provider_metadata.reasoning_content` still carries a copy of choice 0's reasoning on non-streaming responses but is deprecated; read the message field. ## Errors Standard error surface: 400, 401, 402, 403, 404 (`model_not_found`), 410 (`model_deprecated`), 413, 429, 502, 503, 504 — see [Errors](/docs/errors) for the envelope and full code table. ## Related - [Streaming](/docs/streaming) - [Errors](/docs/errors) --- ## Text Completions **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/completions **Description:** Legacy OpenAI text completion endpoint. The legacy OpenAI Completions dialect. Same auth, policy, and validation path as chat (`stop` ≤ 4 applies). Prefer [Chat Completions](/docs/api-reference/chat-completions) for new integrations. ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | A curated alias. | | `prompt` | string \| string[] | **yes** | The text to complete. | | `stream` | boolean | no | SSE streaming of `text_completion` chunks, terminated by `data: [DONE]`. Requires `stream_options.include_usage: true`. | | `stream_options.include_usage` | boolean | with `stream: true` | **Must be `true` when streaming.** Usage is metered from the final stream chunk, which the model only sends when asked, and the gateway cannot add the flag on this endpoint. A streaming request without it is 400 `invalid_request` (`param: "stream_options.include_usage"`). | | `stop` | string \| string[] \| null | no | At most 4 sequences → 400 `invalid_stop_sequences`. | | `max_tokens` | integer | no | Also sizes the prepaid credit reservation. | | `top_p` | number | no | Nucleus sampling. Must be in `(0, 1]` → a numeric value outside that range (notably `0`, an empty nucleus) is 400 `invalid_request` (`param: "top_p"`). Omit for the model default. | | `temperature` | number | no | Forwarded to the provider unchanged. | ## Code sample ```bash curl https://api./v1/completions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "prompt": "Write a haiku about APIs." }' ``` ## Response ```json { "id": "cmpl-1", "object": "text_completion", "created": 1724680001, "model": "gpt-4o", "choices": [ { "index": 0, "text": "…", "finish_reason": "stop", "logprobs": null } ], "usage": { "prompt_tokens": 6, "completion_tokens": 20, "total_tokens": 26 }, "provider_metadata": { "provider": "openai", "actual_model": "gpt-4o-2024-08-06" } } ``` The same strip-list and `provider_metadata` guarantees as chat apply. ## Streaming ```bash curl https://api./v1/completions \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "prompt": "Write a haiku about APIs.", "stream": true, "stream_options": {"include_usage": true} }' ``` The last data chunk before `data: [DONE]` carries `usage`. Leaving out `stream_options.include_usage` (or setting it to `false`) returns: ```json { "error": { "message": "Streaming /v1/completions requires stream_options.include_usage=true (usage is metered from the final stream chunk); set it, or use stream:false or /v1/chat/completions", "type": "invalid_request_error", "code": "invalid_request", "param": "stream_options.include_usage", "request_id": "…" } } ``` The request is refused before it reaches the model and before any credit is reserved, so it is not billed. [Chat Completions](/docs/api-reference/chat-completions) streams need no flag: the gateway adds it there. ## Errors Standard error surface (400, 401, 402, 403, 404, 410, 413, 429, 502, 503, 504) — see [Errors](/docs/errors). --- ## Embeddings **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/embeddings **Description:** Create a vector embedding for input text. OpenAI-dialect embeddings. Typically served by on-cluster open-weight models (e.g. `bge-m3`). ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | An embedding alias (e.g. `bge-m3`). | | `input` | string \| string[] \| number[][] | **yes** | Text (or token arrays) to embed. | | `encoding_format` | `float` \| `base64` | no | `base64` returns the vector base64-encoded. | | `dimensions` | integer | no | Forwarded to the provider (model-dependent). | | `user` | string | no | Forwarded to the provider unchanged. | ## Code sample ```bash curl https://api./v1/embeddings \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "bge-m3", "input": "The quick brown fox" }' ``` ```python from openai import OpenAI client = OpenAI( base_url="https://api./v1", api_key=os.environ["TOKENFACTORY_API_KEY"], ) response = client.embeddings.create( model="bge-m3", input="The quick brown fox", ) print(response.data[0].embedding[:5]) ``` ## Response ```json { "object": "list", "data": [ { "object": "embedding", "index": 0, "embedding": [0.1, 0.2, "…"] } ], "model": "bge-m3", "usage": { "prompt_tokens": 4, "total_tokens": 4, "prompt_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 } }, "provider_metadata": { "provider": "vllm-staging", "actual_model": "BAAI/bge-m3" } } ``` Guaranteed fields: `object: "list"`, `data[].{object, index, embedding}`, `model` (your alias), `usage{prompt_tokens, total_tokens}`, and `provider_metadata`. `usage.prompt_tokens_details.{cached_tokens,cache_write_tokens}` are always present, defaulting to `0` when the backend didn't report caching — the gateway normalizes this shape the same way it does for [Chat Completions](/docs/api-reference/chat-completions#normalized-usage-shape), so an embeddings backend that omits the field (or sends it as `null`) never produces a different observable shape than one that reports real cache numbers. ## Errors Standard error surface — see [Errors](/docs/errors). --- ## Image Generation **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/images-generations **Description:** Generate images from a text prompt. OpenAI-dialect image generation (e.g. FLUX-family models). ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | An image alias. | | `prompt` | string | **yes** | Absent → 400 `missing_prompt`; empty → 400 `empty_prompt`. | | `response_format` | `b64_json` | no | Only `b64_json` is served. `"url"` is **rejected** with 400 `unsupported_response_format` — a recorded decision, not a gap. | | `size` | string | no | Must be on the deployment allowlist (default `512x512`, `1024x1024`, `768x1344`, `1344x768`) → 400 `invalid_image_size`. Omit for the model default. | | `n` | integer | no | ≥ 1. | | `quality`, `style` | string | no | Forwarded to the provider unchanged. | | `image` | string | no | Optional base64 reference image (image-to-image). Counts toward the body cap. | | `user` | string | no | Forwarded to the provider unchanged. | ## Body cap The request body (including any base64 reference image) is capped at 8 MiB by default → 413 `request_too_large` over the limit. ## Code sample ```bash curl https://api./v1/images/generations \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "flux-schnell", "prompt": "a cat wearing sunglasses, studio lighting", "size": "1024x1024" }' ``` ## Response ```json { "created": 1724680002, "data": [ { "b64_json": "AAAA…", "revised_prompt": "a cat" } ], "provider_metadata": { "provider": "vllm-staging", "actual_model": "black-forest-labs/FLUX.1-schnell" } } ``` Guaranteed fields: `created`, `data[].{b64_json, revised_prompt?}`, and `provider_metadata`. `usage` appears only when the provider reported OpenAI `ImageGenUsage` keys (`input_tokens`, `output_tokens`, `total_tokens`, `input_tokens_details?`) — provider timing junk is dropped. ## Errors | Status | Codes specific to this endpoint | |---|---| | 400 | `missing_prompt`, `empty_prompt`, `unsupported_response_format`, `invalid_image_size` | | 413 | `request_too_large` | Plus the standard surface — see [Errors](/docs/errors). --- ## Anthropic Messages **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/messages **Description:** Anthropic Messages dialect for Claude SDKs — translated and routed to the serving provider. The Anthropic Messages API, as used by Claude SDKs. The gateway translates the request to the serving backend and returns the **native Anthropic body** plus `provider_metadata`. `model` echoes your alias. This is the gateway's second wire **dialect** — same contract as the OpenAI endpoints, different shapes. See [OpenAI & Anthropic Compatibility](/docs/openai-anthropic-compatibility) for the side-by-side. ## Auth `x-api-key: ` (Anthropic SDK default) or `Authorization: Bearer` — both accepted ([Auth headers](/docs/authentication)). The `anthropic-version` header is accepted and forwarded to the provider. ```bash curl https://api./v1/messages \ -H "x-api-key: $TOKENFACTORY_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello!"}] }' ``` ```python from anthropic import Anthropic client = Anthropic( base_url="https://api.", # the SDK appends /v1/messages api_key=os.environ["TOKENFACTORY_API_KEY"], ) message = client.messages.create( model="claude-sonnet", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) print(message.content[0].text) ``` ## Request body Native Anthropic shape. Only `model` is gateway-validated (curated alias rule); everything else passes to the dialect adapter: | Field | Type | Required | |---|---|---| | `model` | string | **yes** | | `messages` | array | **yes** | | `max_tokens` | integer | **yes** (Anthropic requires it) | | `system` | string / blocks | no | | `stream` | boolean | no | | `temperature` | number | no | | `stop_sequences` | string[] | no | | `tools`, `metadata` | object | no | ## Response ```json { "id": "msg_1", "type": "message", "role": "assistant", "model": "claude-sonnet", "content": [{ "type": "text", "text": "Hi" }], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 10, "output_tokens": 2, "cache_read_input_tokens": 6, "cache_creation_input_tokens": 4 }, "provider_metadata": { "provider": "anthropic", "actual_model": "claude-sonnet-4-5-20250929", "native_finish_reason": "end_turn", "cache": { "read_tokens": 6, "write_tokens": 4 } } } ``` Prompt-cache usage is surfaced both in the native `usage` keys and in `provider_metadata.cache` — priced at the cached rate when the model defines one. ## Errors This path uses the **Anthropic error envelope** — not the OpenAI one. `request_id` is a top-level sibling of `error`, and there is no `code`/`param`: ```json {"type": "error", "error": {"type": "not_found_error", "message": "…"}, "request_id": "req_01J…"} ``` Status codes and their meanings are identical to the OpenAI paths ([Errors](/docs/errors)). ## Streaming With `stream: true`, the native Anthropic event sequence is preserved (`message_start` … `content_block_delta` … `message_stop`), with the `X-TF-*` side-channel headers. See [Streaming](/docs/streaming). ## Related - [OpenAI & Anthropic Compatibility](/docs/openai-anthropic-compatibility) - [Streaming](/docs/streaming) --- ## List Models **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/models-list **Description:** List the model aliases this API key can request. OpenAI-compatible model list — every **enabled alias** your key can call, rerank and video included. Each entry is a **model card**: a standard OpenAI model object that is also a valid OpenRouter `/api/v1/models` entry, so OpenAI SDKs and OpenRouter-style tooling (model pickers, cost estimators) both read it. ```bash curl https://api./v1/models \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" ``` ```python from openai import OpenAI client = OpenAI( base_url="https://api./v1", api_key=os.environ["TOKENFACTORY_API_KEY"], ) models = client.models.list() print([m.id for m in models.data]) ``` ## Response ```json { "object": "list", "data": [ { "id": "anthropic/claude-sonnet-4.5", "canonical_slug": "anthropic/claude-sonnet-4.5-20250929", "name": "Claude Sonnet 4.5", "object": "model", "created": 1759190400, "owned_by": "Token Factory", "description": "Anthropic's balanced model for coding and agents.", "context_length": 200000, "context_window": 200000, "max_output": 64000, "architecture": { "modality": "text+image->text", "input_modalities": ["text", "image"], "output_modalities": ["text"], "tokenizer": "Claude" }, "pricing": { "unit": "token", "prompt": "0.000003", "completion": "0.000015", "input_cache_read": "0.0000003", "input_cache_write": "0.00000375" }, "top_provider": { "context_length": 200000, "max_completion_tokens": 64000, "is_moderated": false }, "supported_parameters": ["max_tokens", "stop", "temperature", "tool_choice", "tools", "top_p"], "default_parameters": { "temperature": 1 }, "knowledge_cutoff": "2025-01-31" }, { "id": "openai/whisper-1", "name": "openai/whisper-1", "object": "model", "created": 1756684800, "owned_by": "Token Factory", "architecture": { "modality": "audio->text", "input_modalities": ["audio"], "output_modalities": ["text"] }, "pricing": { "unit": "second", "prompt": "0.0001" }, "top_provider": { "is_moderated": false } } ] } ``` `id`, `object`, `created` and `owned_by` are the standard OpenAI fields. `name`, `architecture` and `top_provider` are always present; every other field is **omitted** when the catalogue has no value for that model: | Field | Meaning | |---|---| | `name` | Display name set by the operator, else the `id`. | | `created` | Unix time the model was added to the catalogue. | | `canonical_slug`, `hugging_face_id` | OpenRouter slug and Hugging Face id, when known. | | `description` | Short model description. | | `context_length` | Context window in tokens (OpenRouter name). | | `context_window` | Same value as `context_length`. | | `max_output` | Maximum output tokens (same as `top_provider.max_completion_tokens`). | | `architecture` | `input_modalities` and `output_modalities` (`text`, `image`, `audio`, `video`, `file`, `embeddings`) and `modality` (`"text+image->text"`), plus `tokenizer` / `instruct_type` when known. | | `top_provider` | The model's limits as served (`context_length`, `max_completion_tokens`) and `is_moderated`. It never names a provider. | | `supported_parameters` | The list the operator set for the model, if any. Otherwise, for chat models: `max_tokens`, plus `stop`, `temperature`, `top_p` unless it is a reasoning model; tool-capable models add `tools`, `tool_choice`; reasoning models add `reasoning`; JSON-mode models add `response_format`; structured-output models add `response_format`, `structured_outputs`. Other parameters are still accepted and passed through. | | `default_parameters` | Default sampling values (`temperature`, `top_p`, `top_k`, penalties, `min_p`). | | `supported_voices` | Voices a text-to-speech model accepts. | | `knowledge_cutoff`, `expiration_date` | As published for the model. | | `pricing` | Your price for your key's billing region — exactly what you are billed. See below. | ### Pricing Every price is a decimal **string** in USD per **one** unit (OpenRouter's format). `unit` says what the unit is: | `unit` | Models | Fields | |---|---|---| | `token` | chat, embeddings | `prompt` (input token), `completion` (output token), `input_cache_read`, `input_cache_write` (prompt-cache tokens) | | `character` | text-to-speech | `prompt` (input character) | | `image` | image generation | `image_output` (generated image) | | `second` | speech-to-text | `prompt` (second of audio) | | `second` | video | `video_output` (second of video) | | `document` | rerank | `prompt` (document) | A field appears only when the model has that price: an embedding model has no `completion`, and when `input_cache_read` / `input_cache_write` are absent, cached tokens are billed at `prompt`. Every `id` is a valid `model` value for other endpoints. Deprecation markers are not surfaced in this list — a deprecated alias appears here until its sunset. Watch for `provider_metadata.deprecation` / `X-TF-Deprecation` on responses (lifecycle: [Models & Aliases](/docs/models-and-aliases)). ## Errors Standard error surface (401, 403, 429, 502, 503) — see [Errors](/docs/errors). --- ## Overview **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/overview **Description:** The complete Token Factory inference surface — every endpoint across chat, completions, messages, embeddings, images, audio, rerank, video, and the model catalogue. ## The surface at a glance Every endpoint lives under `https://api./v1/*`. Auth is `Authorization: Bearer ` (or `x-api-key` on the Anthropic path) — [which header, when](/docs/authentication). The `model` field in every request must be a curated alias; every response echoes that alias back and carries `provider_metadata`. ## Shared behaviour | Behaviour | Rule | |---|---| | Errors | One uniform envelope with `request_id` on every error — see [Errors](/docs/errors). | | Streaming | SSE with a guaranteed final usage chunk; ends with `[DONE]` — see [Streaming](/docs/streaming). | | Rate limits | `x-ratelimit-*` headers on 200 and 429; honour `Retry-After` — see [Rate Limits](/docs/rate-limits). | | Funds | `402 insufficient_funds` — never 429, never SDK-retried; top up — see [Billing](/docs/billing). | | Deprecation | `provider_metadata.deprecation` + `X-TF-Deprecation` header, then `410` past sunset — lifecycle: [Models & Aliases](/docs/models-and-aliases). | --- ## Rerank **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/rerank **Description:** Rerank documents against a query by relevance. Cohere/Jina-style rerank body, typically served by reranker models (e.g. `bge-reranker`). Rerank aliases are listed by [`GET /v1/models`](/docs/api-reference/models-list) with `pricing.unit: "document"` (`prompt` is the price per document). ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | A rerank alias. | | `query` | string | **yes** | The search query. | | `documents` | array | **yes** | Strings, or objects with a `text` field. | | `top_n` | integer | no | Return only the top N results (≥ 1). | | `return_documents` | boolean | no | Include the document text in each result (default `false`). | ## Code sample ```bash curl https://api./v1/rerank \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "bge-reranker", "query": "What is the capital of the United States?", "documents": [ "Carson City is the capital city of Nevada.", "Washington, D.C. is the capital of the United States.", "The capital of Portugal is Lisbon." ], "top_n": 2 }' ``` ## Response ```json { "id": "rr-1", "model": "bge-reranker", "results": [ { "index": 1, "relevance_score": 0.91 }, { "index": 0, "relevance_score": 0.12 } ], "usage": { "total_tokens": 40 }, "provider_metadata": { "provider": "vllm-staging", "actual_model": "BAAI/bge-reranker-v2-m3" } } ``` Guaranteed fields: `results[].{index, relevance_score, document?}`, `model` (your alias), `usage{total_tokens}` (only `total_tokens` survives the strip-list), and `provider_metadata`. Results are ordered most-relevant first; `index` refers to the position in your input `documents` array. ## Errors Standard error surface — see [Errors](/docs/errors). --- ## Videos — Cancel Job **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/videos-cancel **Description:** Cancel a video job that has not yet completed. Moves a job that has not yet completed to `cancelled`. A cancelled job is **not charged** (`billing.state: "void"`). ## Response The job resource after cancellation (HTTP 200): ```json { "id": "vid_01J8TESTVID01", "object": "video.job", "model": "ltx-2", "status": "cancelled", "created_at": 1724680000, "completed_at": null, "expires_at": 1725284800, "billing": { "state": "void" }, "provider_metadata": { "provider": "ltx-staging", "actual_model": "ltx-2.3-distilled" } } ``` Cancelling a job already in a terminal state returns the resource unchanged with 200 (idempotent). ## Errors | Status | Code | Meaning | |---|---|---| | 404 | `job_not_found` | No job with this id is visible to your organisation. | Plus the standard surface (401, 403, 429, 502, 503) — see [Errors](/docs/errors). ## Related - [Videos — Create Job](/docs/api-reference/videos-create) - [Videos — Poll Job](/docs/api-reference/videos-poll) - [Billing](/docs/billing) --- ## Videos — Create Job **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/videos-create **Description:** Submit an async video-generation job. Creates a durable video job and returns the **job resource**. Generation is asynchronous — poll `GET /v1/videos/{id}` until a terminal state. ## Idempotency | Mechanism | Behaviour | |---|---| | `Idempotency-Key` header | Same key + same organisation ⇒ the **same** job is returned (HTTP 200 instead of 201). Max length 255. | | No key sent | The gateway fingerprints `(org, key, model, prompt, size, seconds)` for a 10-minute window — an exact retry within that window returns the same job. | | Same key, different body | 409 conflict. | ## Request body | Field | Type | Required | Notes | |---|---|---|---| | `model` | string | **yes** | A video alias. | | `prompt` | string | **yes** | Description of the video. | | `size` | string | no | e.g. `1280x720` — provider dependent. Part of the idempotency fingerprint. | | `seconds` | integer | no | Requested duration (≥ 1). Part of the fingerprint **and the billed unit**. | | `input_reference` | string | no | Optional base64 reference image (provider-dependent). | ## Code sample ```bash curl https://api./v1/videos \ -H "Authorization: Bearer $TOKENFACTORY_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-4831-intro" \ -d '{ "model": "ltx-2", "prompt": "a slow pan across a mountain valley at dawn", "size": "1280x720", "seconds": 8 }' ``` ## Response (201 created / 200 idempotent replay) ```json { "id": "vid_01J8TESTVID01", "object": "video.job", "model": "ltx-2", "status": "queued", "created_at": 1724680000, "started_at": null, "completed_at": null, "expires_at": 1725284800, "progress": 0, "billing": { "state": "pending" }, "provider_metadata": { "provider": "ltx-staging", "actual_model": "ltx-2.3-distilled" } } ``` ## Billing `billing.state` starts `pending`; it becomes `charged` **only on confirmed completion**. Failed or cancelled jobs are `void` — never charged. See [Billing](/docs/billing). ## Next step Poll [Videos — Poll Job](/docs/api-reference/videos-poll). ## Related - [Videos — Cancel Job](/docs/api-reference/videos-cancel) - [Billing](/docs/billing) --- ## Videos — Poll Job **URL:** https://www.tokenfactory.iamsaif.ai/docs/api-reference/videos-poll **Description:** Retrieve the current state of a video job. Returns the job resource with **HTTP 200 for every state, including `failed` and `expired`**. A failed generation is never an HTTP error on poll — read `status: "failed"` plus the `error` object inside the body. ## Path parameters | Param | Notes | |---|---| | `id` | The opaque job id returned on submit (`vid_01J…`). Valid until `expires_at`. | ## Polling pattern 1. Submit a job → poll every few seconds. 2. Terminal states: `completed`, `failed`, `cancelled`, `expired`. 3. Stop polling when `status` is terminal; read `result` (completed) or `error` (failed). ## Response **Completed** — terminal, `result` populated, billing `charged`: ```json { "id": "vid_01J8TESTVID01", "object": "video.job", "model": "ltx-2", "status": "completed", "created_at": 1724680000, "started_at": 1724680001, "completed_at": 1724680030, "expires_at": 1725284800, "progress": 100, "result": { "url": "https://api./v1/videos/vid_01J8TESTVID01/content", "content_type": "video/mp4", "seconds": 8 }, "billing": { "state": "charged" }, "provider_metadata": { "provider": "ltx-staging", "actual_model": "ltx-2.3-distilled" } } ``` **Failed** — also HTTP 200, never an HTTP error on poll; read the `error` object and note billing is `void` (never charged): ```json { "id": "vid_01J8TESTVID01", "object": "video.job", "model": "ltx-2", "status": "failed", "created_at": 1724680000, "started_at": 1724680001, "completed_at": 1724680030, "expires_at": 1725284800, "progress": 0, "error": { "type": "server_error", "message": "generation failed upstream", "code": "provider_error", "request_id": "req_01J8TESTVID01" }, "billing": { "state": "void" }, "provider_metadata": { "provider": "ltx-staging", "actual_model": "ltx-2.3-distilled" } } ``` ### Field reference | Field | Notes | |---|---| | `status` | `queued → processing → completed \| failed \| cancelled`; `expired` after retention. | | `progress` | 0–100 when the provider reports it. | | `result` | Only when `completed` (and not expired): `{url, content_type, seconds}` — download the video from `url`. | | `error` | Only when `failed`: the uniform error shape with `request_id`. | | `billing.state` | `pending` → `charged` (completed) / `void` (failed, cancelled). | | `expires_at` | Completion time + retention window (default 7 days). After it passes, `status` becomes `expired`, `result` is removed, and polls still return 200. | ## Errors | Status | Code | Meaning | |---|---|---| | 404 | `job_not_found` | No job with this id is visible to your organisation. Also returned for `vid_` ids looked up without an organisation scope. | Plus the standard surface (401, 403, 429, 502, 503) — see [Errors](/docs/errors). ## Related - [Videos — Cancel Job](/docs/api-reference/videos-cancel) - [Billing](/docs/billing) --- ## Audit log **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/audit **Description:** Read and export the audit trail of administrative actions in your organization. The organization's audit log: who did what, when, from where, on what — and, for actions taken through this API, **with which token**. It is the same log the portal shows, and it is **append-only**: no endpoint edits or deletes an entry. | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /audit`](#list-audit-entries) | `audit:read` | **Admin** | | [`GET /audit/export`](#export-the-audit-log) | `audit:read` | **Admin** | Both routes need an owner who is currently an organization **Admin**; a Member's token gets `403 insufficient_scope`. An organization sees its own log and nothing else. ## What is recorded | Recorded | Examples | |---|---| | Writes on this API | `project_create`, `project_rename`, `project_archived`, `key_create`, `key_rotate`, `member_role_update`, `invite_revoke`, `org_settings_update`, … — the same action names the portal records. The table below maps each call to its action. | | Token lifecycle | `pat_create`, `pat_revoke` (with the token id, name, scopes and expiry). | | Secret replays | `key_create_replayed`, `key_rotate_replayed` — an [idempotent replay](/docs/management-api/idempotency#secrets-in-replayed-responses) delivered a secret a second time. | | Bulk exports | `audit_export`, `usage_export`, with `row_count` and `format`. | | Portal actions | The same entries the portal's activity log shows, without the token fields. | ### Which action each call records The action names are the portal's, so some differ from the API verbs: | API call | `action` | |---|---| | `PATCH /org` | `org_settings_update` | | `PATCH /members/{id}` — `role` | `member_role_update` | | `PATCH /members/{id}` — `name` | `member_update` | | `POST /members/{id}/disable`, `POST /members/{id}/enable` | `member_update` (`metadata.is_active` is `false` or `true`) | | `PUT /members/{id}/projects` | `member_projects_update` | | `DELETE /members/{id}` | `member_remove` | | `DELETE /members/{id}/sessions` | `member_sessions_revoke` | | `POST /invites` | `invite_create` | | `DELETE /invites/{id}` | `invite_revoke` | | `POST /projects` | `project_create` | | `PATCH /projects/{id}` — `name` or `description` | `project_rename` | | `POST /projects/{id}/disable` | `project_archived` | | `POST /projects/{id}/enable` | `project_unarchive` | | `DELETE /projects/{id}` | `project_purged` | | `POST /projects/{id}/keys` | `key_create` | | `PATCH /keys/{id}` — `name` | `key_rename` | | `PATCH /keys/{id}` — `ip_allowlist` | `key_ip_allowlist_update` | | `POST /keys/{id}/disable` | `key_deactivate` | | `POST /keys/{id}/enable` | `key_activate` | | `POST /keys/{id}/rotate` | `key_rotate` | | `POST /keys/{id}/rotate/cancel` | `key_rotate_cancel` | | `DELETE /keys/{id}` | `key_revoke` | A project's `guardrails`, and a key's `allowed_models`, `rpm_limit`, `expires_at` and `description`, are not yet recorded as separate entries when they change. Ordinary reads are not individually audited — an Admin reading their own organization's data is not a privacy event. Exports are the exception, because an export is what a leak looks like when it is one. ## The audit entry | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `created_at` | string | When, ISO-8601 UTC. | | `actor` | object | Who: `{ "user_id", "email" }`. For API calls, the token's owner. | | `action` | string | What, e.g. `key_rotate`. | | `resource_type`, `resource_id` | string \| null | On what, e.g. `api_key` and its id. | | `metadata` | object | Action-specific detail (for example `row_count` on an export). | | `ip_address` | string \| null | From where. | **Which token acted.** An entry written by a call made with a personal access token carries the token in `metadata`, next to the action's own detail: | `metadata` key | Value | |---|---| | `credential` | `"pat"` | | `pat_id` | The token's id. | | `pat_name` | The token's name. | | `scope` | The scope the call used, e.g. `keys:write`. | Portal actions have none of these keys. ```json { "id": "0b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e", "created_at": "2026-09-24T10:05:31Z", "actor": { "user_id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example" }, "action": "key_rotate", "resource_type": "api_key", "resource_id": "c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74", "metadata": { "grace_expires_at": "2026-09-24T12:05:31Z", "credential": "pat", "pat_id": "d9e8f7a6-b5c4-4d3e-9f2a-1b0c9d8e7f6a", "pat_name": "CI · payments-deploy", "scope": "keys:write" }, "ip_address": "203.0.113.24" } ``` ## List audit entries ```http GET /api/v1/mgmt/audit ``` Newest first, [cursor-paginated](/docs/management-api/pagination). | Query parameter | Type | Notes | |---|---|---| | `actor` | string | The actor's user id, or their email address (case-insensitive). | | `action` | string | Exact action name, e.g. `key_revoke`. | | `from` | string | RFC 3339, inclusive. | | `to` | string | RFC 3339, exclusive. Must be after `from`. | | `limit`, `cursor` | | [Pagination](/docs/management-api/pagination). | Any other parameter returns `422 unsupported_filter`; a malformed `actor`, `from` or `to` is `400 invalid_request` with `param` naming it. ## Export the audit log ```http GET /api/v1/mgmt/audit/export ``` Streams every matching entry, newest first, in one response — for SIEM ingestion or an evidence request. | Query parameter | Type | Required | Notes | |---|---|---|---| | `format` | string | no | `jsonl` (default; one audit entry per line, `application/x-ndjson`) or `csv`. | | `actor`, `action`, `from`, `to` | | no | As for `GET /audit`. | The CSV columns are `id`, `created_at`, `actor_user_id`, `actor_email`, `action`, `resource_type`, `resource_id`, `ip_address` and `metadata` (the metadata object as JSON). **At most 10,000 entries per call.** A filter that matches more is refused with `422 export_too_large` before anything streams — narrow `from`/`to` and export in windows, or page through `GET /audit`. Every completed export is itself recorded as `audit_export` with the `row_count`. ## Errors specific to the audit log | Status | Code | When | |---|---|---| | 400 | `invalid_request` (`param` set) | `actor` neither a user id nor an email; `from`/`to` not RFC 3339; `from` not before `to`; `format` not `jsonl`/`csv`. | | 403 | `insufficient_scope` | Token lacks `audit:read`, or the owner is not currently an Admin. | | 422 | `unsupported_filter` | An unknown query parameter. | | 422 | `export_too_large` | The export matches more than 10,000 entries. | --- ## Authentication **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/authentication **Description:** How Management API requests are authenticated with a personal access token. ## The Authorization header Every request carries a [personal access token](/docs/management-api/personal-access-tokens) as a bearer credential: ``` Authorization: Bearer tf-pat_… ``` | Rule | Detail | |---|---| | Header only | Send the token in `Authorization`. It is never accepted as a query parameter or a cookie, and `x-api-key` is not read on this surface. | | Personal access tokens only | Project API keys and portal session tokens are rejected with `401 credential_type_mismatch`. | | HTTPS only | Always call `https://portal./api/v1/mgmt` — the portal host, not the API host. | ## Credentials never cross surfaces Inference is on the API host (`https://api./v1`); management is on the portal host under `/api/v1/mgmt`. Each credential is rejected at the other surface, and each case gets an error that names the fix: | You sent | To | Result | |---|---|---| | A project API key | `https://portal./api/v1/mgmt/*` | `401 credential_type_mismatch` — "this endpoint needs a personal access token; API keys authenticate inference only" | | A personal access token | `https://api./v1/*` inference routes | `401 credential_type_mismatch` — "this endpoint needs a project API key; personal access tokens authenticate the Management API only" | | A personal access token | The portal's own API (everything on the portal host outside `/api/v1/mgmt`) | A plain `401` — personal access tokens are not portal credentials | ```bash # A personal access token on the inference API is rejected before any model is called: curl https://api./v1/chat/completions \ -H "Authorization: Bearer $TF_PAT" \ -H "Content-Type: application/json" \ -d '{"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "hi"}]}' # → 401 {"error": {"code": "credential_type_mismatch", ...}} ``` Inference authentication is documented in [Authentication](/docs/authentication). ## How a request is checked Checks run in this order; the first failure is returned. | Situation | Status | `error.code` | `error.type` | |---|---|---|---| | No `Authorization` header, or not `Bearer` | 401 | `missing_credential` | `authentication_error` | | Bearer is a project API key, a portal session token, or any other value that is not a personal access token | 401 | `credential_type_mismatch` | `authentication_error` | | Unknown token; owner or organization no longer exists | 401 | `invalid_credential` | `authentication_error` | | Token past its `expires_at` | 401 | `token_expired` | `authentication_error` | | Token revoked; owner disabled; organization suspended | 401 | `token_revoked` | `authentication_error` | | The token could not be checked because of an internal error | 500 | `internal_error` — retry | `api_error` | | Per-token rate limit reached | 429 | `rate_limit_exceeded` (+ `Retry-After`) | `rate_limit_error` | | Token valid, but missing the scope or the creator lacks the right | 403 | `insufficient_scope` | `permission_error` | The last row is the [intersection rule](/docs/management-api/scopes#the-intersection-rule). Only a request that passes the credential checks is counted against the [rate limit](/docs/management-api/rate-limits) and carries the `X-RateLimit-*` headers; a `401` never does. ```json { "error": { "message": "this personal access token has expired", "type": "authentication_error", "code": "token_expired", "param": null, "action": "Mint a new token in Settings → API Tokens and replace it in your integration.", "request_id": "req_e2443998c57f4bf28e50f4288406380d" } } ``` ## Portal scoping A token works only on the portal where it was created; a token from any other Token Factory portal is unknown here (`401 invalid_credential`), exactly as a random string would be. Keep one secret per portal in your CI system. ## Non-enumeration The credential layer never tells you *why* an unknown value is unknown. A typo, a token from another portal, and a token whose organization was deleted all return the same generic `401 invalid_credential`. The same rule holds for resources: every query is bound to the token's organization, no parameter accepts another organization's id, and an object that belongs to another organization returns `404 not_found_error` — exactly like one that does not exist. A 403 would confirm the id is real. ## Request ids Every response — success or error — carries an `X-Request-Id` header, and every error body carries the same value as `error.request_id`. Log it, and quote it in support requests. --- ## Billing **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/billing **Description:** Read your balance, credit grants, and invoices. Reads only — no money moves through this API. What your organization has, what it owes, and what it bought. **Reads only.** | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /billing/balance`](#retrieve-the-balance) | `billing:read` | **Admin** | | [`GET /billing/grants`](#list-credit-grants) | `billing:read` | **Admin** | | [`GET /billing/invoices`](#list-invoices) | `billing:read` | **Admin** | | [`GET /billing/invoices/{id}`](#retrieve-an-invoice) | `billing:read` | **Admin** | Billing is admin-only: the token owner must currently be an organization **Admin** (`403 insufficient_scope` otherwise). Payments, payment methods, credit purchases and billing-mode changes are handled in the portal. Credit amounts are **integer nano-USD** (`*_nanousd`, 10⁻⁹ USD — exact), with a convenience `*_usd` number alongside; invoices carry integer **cents** in their own currency. Balances and invoices are views of the same record as [Usage](/docs/management-api/usage); if an API response and an issued invoice ever disagree, the invoice governs. Python examples share this setup: ```python BASE = "https://portal./api/v1/mgmt" s = requests.Session() s.headers["Authorization"] = f"Bearer {os.environ['TF_PAT']}" ``` ## Retrieve the balance ```http GET /api/v1/mgmt/billing/balance ``` The organization's prepaid credit wallet — the balance every inference request is checked against. | Field | Type | Notes | |---|---|---| | `available_nanousd` | integer | Credit available, in nano-USD. | | `available_usd` | number | The same amount in USD, for display. | | `currency` | string | Always `USD`. | | `wallet_present` | boolean | `false` when the organization has never been granted credit. | ```json { "available_nanousd": 1835617883000, "available_usd": 1835.617883, "currency": "USD", "wallet_present": true } ``` If billing data is temporarily unavailable the call is `502 upstream_unavailable` — retry. ## List credit grants ```http GET /api/v1/mgmt/billing/grants ``` Every credit movement on the organization — grants, top-ups, refunds — newest first. [Paginated](/docs/management-api/pagination). | Field | Type | Notes | |---|---|---| | `id` | string | A stable id for the row (`grant_…`). | | `created_at` | string | | | `source` | string | Where the credit came from, e.g. `stripe` (a top-up), `signup`, `promo`, `admin_grant`, `refund`. Treat it as an open set. | | `memo` | string | Human description. | | `amount_nanousd` | integer | Negative for refunds and reversals. | | `amount_usd` | number | The same amount in USD. | | `reference` | string \| null | The payment reference for a top-up, when there is one. | ```json { "data": [ { "id": "grant_9f2c41d07a5b3e18", "created_at": "2026-09-01T12:00:00Z", "source": "stripe", "memo": "Credit top-up", "amount_nanousd": 2000000000000, "amount_usd": 2000, "reference": "pi_3P…" }, { "id": "grant_1ab07c3de4f59260", "created_at": "2026-03-02T09:41:17Z", "source": "signup", "memo": "Starter credit", "amount_nanousd": 25000000000, "amount_usd": 25, "reference": null } ], "has_more": false, "next_cursor": null } ``` ## List invoices ```http GET /api/v1/mgmt/billing/invoices ``` [Paginated](/docs/management-api/pagination), newest first. | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `stripe_invoice_id` | string | The payment provider's invoice id. | | `amount_cents` | integer | Invoice total in cents of `currency`. | | `currency` | string | | | `status` | string | The invoice status reported by the payment provider, e.g. `open` or `paid`. | | `period_start`, `period_end` | string \| null | The billed period. | | `pdf_url` | string \| null | The provider-hosted PDF, once issued. | | `created_at` | string | | ```json { "data": [ { "id": "f0e1d2c3-b4a5-4968-8776-5a4b3c2d1e0f", "stripe_invoice_id": "in_1Q…", "amount_cents": 421193, "currency": "usd", "status": "paid", "period_start": "2026-08-01T00:00:00Z", "period_end": "2026-09-01T00:00:00Z", "pdf_url": "https://…/invoice.pdf", "created_at": "2026-09-01T02:00:00Z" } ], "has_more": false, "next_cursor": null } ``` ## Retrieve an invoice ```http GET /api/v1/mgmt/billing/invoices/{id} ``` Returns one invoice — the same object as a list row. An id that is not one of your organization's invoices returns `404`. ## Reconcile usage with billing 1. Pull the period from [`GET /usage`](/docs/management-api/usage#query-usage) with `from`/`to` set to the invoice's `period_start`/`period_end`. 2. When the invoice's `currency` is `usd`, compare the usage `totals.cost_nanousd` with the invoice's `amount_cents` directly (1 cent = 10,000,000 nano-USD). 3. Between invoices, watch `GET /billing/balance` fall as usage accrues. Both sides are the same record, so this is a **check, not a fix-up** — if they disagree, contact support. Quote the `X-Request-Id` of both responses. ## Errors specific to billing | Status | Code | When | |---|---|---| | 403 | `insufficient_scope` | Token lacks `billing:read`, or the owner is not currently an Admin. | | 404 | `not_found` | Invoice absent or in another organization. | | 422 | `unsupported_filter` | Any query parameter other than `limit`/`cursor` on the lists. | | 502 | `upstream_unavailable` | Billing data (balance, grants) is temporarily unavailable. Retry. | --- ## Catalog **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/catalog **Description:** List the models and regions available to your organization — the vocabulary allowlists are written in. Read-only lists of the models and regions your organization can use. | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /catalog/models`](#list-models) | `catalog:read` | Any member | | [`GET /catalog/regions`](#list-regions) | `catalog:read` | Any member | **Why this lives on the Management API:** it is the vocabulary allowlists are written in. Before a pipeline writes a project's [model allowlist](/docs/management-api/projects#update-a-project), it should validate every id it is about to write against this list. A management client that guesses model ids will guess wrong. This API reflects the catalog; it never changes it. At inference time, [`GET /v1/models`](/docs/api-reference/models-list) lists what one key can route to. ## List models ```http GET /api/v1/mgmt/catalog/models ``` The live model catalog as the portal's Models page shows it: **enabled models only** — disabled and retired models never appear. Each entry is the **public model card**, the same object [`GET /v1/models`](/docs/api-reference/models-list) returns, priced at the rate your organization is billed, plus four Management API fields: `mode`, `status`, `capabilities` and `regions`. Ordered by `id` descending (the catalog has no creation time); [paginated](/docs/management-api/pagination) with `limit` and `cursor` like every other list. `id`, `name`, `object`, `created`, `owned_by`, `architecture`, `top_provider`, `mode`, `status`, `capabilities` and `regions` are always present. Every other card field is **omitted** when the catalog has no value for that model. | Field | Type | Notes | |---|---|---| | `id` | string | The model name to send as `model` and to write in allowlists (`provider/model`). See [Models & aliases](/docs/models-and-aliases). | | `name` | string | Display name set by the operator, else the `id`. | | `object` | string | Always `model`. | | `created` | integer | Unix time the model was added to the catalog. | | `owned_by` | string | Always `Token Factory`. | | `canonical_slug`, `hugging_face_id` | string | OpenRouter slug and Hugging Face id, when known. | | `description` | string | Short model description. | | `context_length`, `context_window` | integer | Context window in tokens (the same value under its OpenRouter and product names). | | `max_output` | integer | Maximum output tokens (same as `top_provider.max_completion_tokens`). | | `architecture` | object | `input_modalities` and `output_modalities` (`text`, `image`, `audio`, `video`, `file`, `embeddings`) and `modality` (e.g. `"text+image->text"`), plus `tokenizer` / `instruct_type` when known. | | `top_provider` | object | The model's limits as served (`context_length`, `max_completion_tokens`) and `is_moderated`. It never names a provider. | | `supported_parameters` | string[] | Request parameters the model accepts. | | `default_parameters` | object | Default sampling values. | | `supported_voices` | string[] | Voices a text-to-speech model accepts. | | `knowledge_cutoff` | string | As published for the model. | | `expiration_date` | string | Announced end-of-service date, when one is set. | | `pricing` | object | What your organization is billed (below). | | `mode` | string | `chat`, `embedding`, `image`, `tts`, `stt`, `video`, `rerank` — which endpoints it serves. | | `status` | string | Provisioning status of the model, e.g. `ready`. | | `capabilities` | object | Capability flags, boolean only, e.g. `{"vision": true, "tools": true}`. | | `regions` | string[] | Regions the model is served and priced in, e.g. `["default"]`. `pricing` is the `default` region price. | `pricing` is the same object as on [`GET /v1/models`](/docs/api-reference/models-list#pricing): every price is a decimal **string** in USD per **one** unit, and `unit` says what the unit is. | `unit` | Models | Fields | |---|---|---| | `token` | chat, embeddings | `prompt` (input token), `completion` (output token), `input_cache_read`, `input_cache_write` (prompt-cache tokens) | | `character` | text-to-speech | `prompt` (input character) | | `image` | image generation | `image_output` (generated image) | | `second` | speech-to-text | `prompt` (second of audio) | | `second` | video | `video_output` (second of video) | | `document` | rerank | `prompt` (document) | A field appears only when the model has that price: an embedding model has no `completion`, and when `input_cache_read` / `input_cache_write` are absent, cached tokens are billed at `prompt`. A model without `pricing` has an **unknown** price, not a free one. To quote per 1M tokens, multiply `prompt` by 1,000,000. ```json { "data": [ { "id": "openai/gpt-4o", "name": "GPT-4o", "object": "model", "created": 1715558400, "owned_by": "Token Factory", "description": "OpenAI's multimodal flagship.", "context_length": 128000, "context_window": 128000, "max_output": 16384, "architecture": { "modality": "text+image->text", "input_modalities": ["text", "image"], "output_modalities": ["text"] }, "pricing": { "unit": "token", "prompt": "0.0000025", "completion": "0.00001", "input_cache_read": "0.00000125" }, "top_provider": { "context_length": 128000, "max_completion_tokens": 16384, "is_moderated": false }, "supported_parameters": ["max_tokens", "stop", "temperature", "tool_choice", "tools", "top_p"], "knowledge_cutoff": "2023-10", "mode": "chat", "status": "ready", "capabilities": { "vision": true, "tools": true }, "regions": ["default"] } ], "has_more": false, "next_cursor": null } ``` A model with an `expiration_date` is still callable until that date; plan the move off it before then. ## List regions ```http GET /api/v1/mgmt/catalog/regions ``` Derived from the model list: the distinct regions your organization's models are served in, with how many models each serves. [Paginated](/docs/management-api/pagination). | Field | Type | Notes | |---|---|---| | `id` | string | The region id. | | `model_count` | integer | How many of the listed models this region serves. | ```json { "data": [ { "id": "default", "model_count": 42 } ], "has_more": false, "next_cursor": null } ``` ## Errors specific to the catalog | Status | Code | When | |---|---|---| | 403 | `insufficient_scope` | Token lacks `catalog:read`. | | 422 | `unsupported_filter` | Any query parameter other than `limit`/`cursor`. | | 502 | `upstream_unavailable` | The model catalog is temporarily unavailable. Retry. | --- ## Automate from a CI pipeline **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/ci-pipeline **Description:** Use a scoped personal access token to create and rotate a project's keys from CI. **The job:** a build system deploys an application that needs an inference key it did not have yesterday — and the old key must stop working before anyone forgets about it. Nothing about this flow should need a human on the day. The steps below use `payments` as the project and `$CI_RUN_ID` as your pipeline's unique run id. ## 1. Mint a token for CI (once, in the portal) An Admin opens **Settings → API Tokens** and creates a token: | Setting | Value | |---|---| | Note | `CI · payments-deploy` | | Scopes | `projects:read`, `keys:read`, `keys:write`, `catalog:read` — nothing else | | Expiry | 365 days | Store it in the CI system's secret store as `TF_PAT`. The token appears in the Admin's token list with its scopes and last use from day one, so a security review can see exactly what CI can do. ## 2. Validate the target Confirm the project exists and its model allowlist names models the catalog still serves. The catalog lists enabled models only, so anything in the allowlist that is missing from it is a problem. Catching it here is cheaper than catching a failed call in production. ```bash PROJECT_ID=5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08 curl -fsS https://portal./api/v1/mgmt/projects/$PROJECT_ID \ -H "Authorization: Bearer $TF_PAT" | jq -r '.guardrails.model_allowlist[]' > allowlist.txt curl -fsS "https://portal./api/v1/mgmt/catalog/models?limit=200" \ -H "Authorization: Bearer $TF_PAT" \ | jq -r '.data[].id' > live.txt # follow next_cursor if has_more is true comm -23 <(sort allowlist.txt) <(sort live.txt) | grep . && { echo "allowlist names non-live models"; exit 1; } || true ``` ## 3. Create the key Use the run id in the `Idempotency-Key`, so a retried step returns the same key instead of creating a second one. The response carries the secret **once**: write it to the application's secret store and nowhere else. Log the key id, never the value. Deploy. Once the deploy is healthy, retire the key the new one replaces (`$OLD_KEY_ID`, the id your pipeline recorded on its previous run): ```bash curl -fsS -X DELETE https://portal./api/v1/mgmt/keys/$OLD_KEY_ID \ -H "Authorization: Bearer $TF_PAT" \ -H "Idempotency-Key: run-$CI_RUN_ID-delete-old" ``` ## 4. Or rotate the existing key If the application keeps one long-lived key, rotate it instead. Size `grace_hours` (1–168, default 24) to your rollout: the old secret keeps working until `rotation.grace_expires_at`. `$KEY_ID` is that key's id — [`GET /projects/{id}/keys`](/docs/management-api/keys#list-a-projects-keys) lists it. ```bash KEY_ID=c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74 RESP=$(curl -fsS -X POST https://portal./api/v1/mgmt/keys/$KEY_ID/rotate \ -H "Authorization: Bearer $TF_PAT" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: run-$CI_RUN_ID-rotate" \ -d '{"grace_hours": 2}') echo "$RESP" | jq -r .secret | your-secret-cli put payments/TOKENFACTORY_API_KEY echo "old secret valid until $(echo "$RESP" | jq -r .rotation.grace_expires_at)" ``` Deploy. **If the deploy fails**, cancel the rotation — the new secret is discarded and the original keeps working with no deadline: ```bash curl -fsS -X POST https://portal./api/v1/mgmt/keys/$KEY_ID/rotate/cancel \ -H "Authorization: Bearer $TF_PAT" \ -H "Idempotency-Key: run-$CI_RUN_ID-rotate-cancel" ``` ## 5. Prove it The classic failure is a rotation window that ends with the new secret never deployed — every call then fails `401`. Run a standing check that lists keys still mid-rotation and alerts when the list is not empty: ```bash curl -fsS https://portal./api/v1/mgmt/projects/$PROJECT_ID/keys \ -H "Authorization: Bearer $TF_PAT" \ | jq -e '[.data[] | select(.rotation != null)] | length == 0' \ || echo "ALERT: keys mid-rotation in payments" ``` ## Rotate the CI token itself The token rotates on the same discipline, before its `expires_at`: 1. An Admin mints the successor in **Settings → API Tokens** with the same scopes. 2. Swap `TF_PAT` in the CI secret store and run the pipeline once. 3. Revoke the predecessor — in the portal, or from CI with the new token: `DELETE /me/tokens/{old_id}` ([Tokens](/docs/management-api/tokens#revoke-one-of-your-tokens)). A scheduled job calling [`GET /me/tokens`](/docs/management-api/tokens#list-your-tokens) can warn a week before expiry. --- ## Errors **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/errors **Description:** The error envelope, HTTP status codes, and error codes returned by the Management API. ## Envelope Every error uses one envelope — the same shape as the [Inference API's](/docs/errors), with one addition, `action`: ```json { "error": { "message": "this token does not carry the projects:write scope", "type": "permission_error", "code": "insufficient_scope", "param": "projects:write", "action": "Mint a token that includes projects:write in Settings → API Tokens.", "request_id": "req_2fb3a75644e94b719e13b23639e5ff22" } } ``` | Field | Notes | |---|---| | `type` | The error class; always matches the HTTP status (table below). | | `code` | Stable machine-readable code. **Branch on `code`, never on `message`.** | | `message` | A human sentence. It may be reworded at any time. | | `param` | The offending parameter, body field, header or scope, or `null`. Nested body fields use a dotted path (`guardrails.spend_limit_usd`). | | `action` | A short human fix, present where the fix is not obvious from the code. May be absent. | | `request_id` | Always present; equals the `X-Request-Id` response header. Quote it to support. Request ids look like `req_` followed by 32 hex characters (`req_6f1c2d9e8a4b4c3d9e2f1a2b3c4d5e6f`); treat them as opaque strings. An `X-Request-Id` you send is ignored; the response always carries a new one. | ## Status and type | Status | `type` | When | Retry? | |---|---|---|---| | 400 | `invalid_request_error` | Malformed or unknown body field, bad parameter value, bad `limit`/`cursor`, missing `Idempotency-Key`. `param` names the field. | No — fix the request. | | 401 | `authentication_error` | Missing, invalid, expired or revoked token; wrong credential type. Generic where the reason would leak. | No — fix the credential. | | 403 | `permission_error` | `insufficient_scope`: the token lacks the scope, or its owner's current role or project access does not allow the call. The message names which. | No. | | 404 | `not_found_error` | The object does not exist **or** belongs to another organization — indistinguishable on purpose. Also any path that is not a Management API endpoint. | No. | | 409 | `conflict_error` | The current state doesn't allow it: last admin, the organization owner, acting on yourself, the default project, a taken name or slug, a revoked key, no rotation to cancel, an `Idempotency-Key` reused for a different request. | No — change state first. | | 422 | `validation_error` | Understood but refused by rule: an unknown query parameter, a filter value out of range, an export that matches too many rows. | No. | | 429 | `rate_limit_error` | Per-token rate limit reached. | **Yes**, after `Retry-After` seconds. | | 500 | `api_error` | Our fault (`internal_error`). | **Yes**, with exponential backoff and the **same** `Idempotency-Key`. | | 502 | `api_error` | A service the API depends on is temporarily unavailable (`upstream_unavailable`). | **Yes**, with exponential backoff and the **same** `Idempotency-Key`. | 400 means "say it properly"; 422 means "that is understood and not allowed". ## Error codes These are all the codes the Management API returns. | Code | Status | Meaning | |---|---|---| | `missing_credential` | 401 | No `Authorization: Bearer` header. | | `credential_type_mismatch` | 401 | A project API key, a portal session or another bearer that is not a personal access token was sent. The message names the right credential. | | `invalid_credential` | 401 | Unknown token, or its owner or organization no longer exists. Deliberately generic. | | `token_expired` | 401 | The token is past `expires_at`. Mint a new one. | | `token_revoked` | 401 | The token was revoked, its owner was disabled, or the organization is suspended. | | `rate_limit_exceeded` | 429 | Per-token rate limit reached. See [Rate limits](/docs/management-api/rate-limits). | | `insufficient_scope` | 403 | Token scope missing (`param` names it), or the owner's role or project access does not allow the call (the message says which). | | `invalid_request` | 400 | A body field, query value, `limit`, `cursor` or header is malformed or unknown. `param` names it. | | `idempotency_key_required` | 400 | A `POST`, `PATCH`, `PUT` or `DELETE` without `Idempotency-Key`. | | `idempotency_conflict` | 409 | The `Idempotency-Key` was already used in the last 24 hours for a different request, or the first request with it is still in progress. | | `idempotency_replay_unavailable` | 409 | A key create or rotate already succeeded under this `Idempotency-Key`, but its one-time secret is not available for replay. The secret was delivered once, on the original response; retry with a **new** `Idempotency-Key` and revoke the unused key. See [Idempotency](/docs/management-api/idempotency#secrets-in-replayed-responses). | | `not_found` | 404 | The object does not exist or is in another organization, or the path is not a Management API endpoint. | | `conflict` | 409 | Any other state conflict; the message explains it. | | `last_admin` | 409 | The change would leave the organization without an active admin. | | `owner_protected` | 409 | The organization owner's role cannot be changed, and the owner cannot be removed. | | `cannot_act_on_self` | 409 | You cannot disable or remove yourself. | | `sessions_unavailable` | 409 | The member has no active sign-in session to end. | | `default_project_protected` | 409 | The default project cannot be renamed, disabled or deleted. | | `name_taken` | 409 | You already have an active key with this name (names are unique among each creator's active keys; `param: "name"`). | | `slug_taken` | 409 | The organization slug could not be saved because it is already in use (`param: "slug"`). | | `key_revoked` | 409 | The key was permanently revoked (deleted) and cannot be enabled again. | | `no_rotation_in_progress` | 409 | There is no rotation on this key to cancel. | | `unsupported_filter` | 422 | A query parameter the endpoint does not support (`param` names it). Filters are never silently ignored. | | `validation_failed` | 422 | A filter value outside what the endpoint accepts, for example `status=archived`, or a `group_by=day` range over 31 days. | | `export_too_large` | 422 | An export matches more than 10,000 rows. Narrow `from`/`to` and export in windows. | | `internal_error` | 500 | Something failed on our side, including verifying your token. Retry; if it persists, contact support with the `request_id`. | | `upstream_unavailable` | 502 | A service the API depends on was unreachable. For a write, the request may or may not have been applied — retry with the **same** `Idempotency-Key`. | ## Stability promise Once a code appears in a released version, **its meaning never changes and it is never reused** for something else. New codes may be added within v1 — handle unknown codes by falling back on the HTTP status and `type`. Removing a code is a breaking change and gets a new version. See [Versioning](/docs/management-api/versioning). ## Handling errors in code --- ## Idempotency **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/idempotency **Description:** Safely retry mutating Management API requests with idempotency keys. Provisioning code retries — after a timeout, a dropped connection, a redeployed runner. On the Inference API a retry costs tokens; on the Management API a retry **creates resources**. A duplicate project or a second live key is worse than a failed request, so idempotency here is **required, not optional**. ## The header Every `POST`, `PATCH`, `PUT` and `DELETE` must carry an `Idempotency-Key` header. `GET` requests do not need one. ``` Idempotency-Key: 5f0c1c4e-2b7a-4d38-9a51-7d2e6c0b9f13 ``` - The value is a client-generated string. Use a **V4 UUID**, or a value that is unique per logical operation — for example your CI run id plus the step name (`run-48213-create-key`). - At most **255 characters**. Keys are scoped to the **personal access token that sent them** — the same key value sent by a different token (even the same person's, even for the same body) is a new request, not a replay. - Stored responses are kept for **24 hours**, then pruned. - Do not put secrets or personal data in the key. ## Behaviour | Situation | Result | |---|---| | Missing header on a non-GET | `400 idempotency_key_required` (`param: "Idempotency-Key"`). Nothing is changed. | | Same **token**, same key, **same** request, within 24 h | The original status and body are **replayed**, with the header `Idempotent-Replayed: true`. The first call's `201` is the second call's `201`, with the same object id. Nothing is changed again. | | Same key value, **different** personal access token | Not a replay — a brand-new request, even if the method, path and body are identical. | | Same key, **different** request, within 24 h | `409 idempotency_conflict`. The key was already used for a different method, path or body. | | Same key while the first request is still running | `409 idempotency_conflict` ("still in progress") — retry shortly with the same key. | | The first attempt failed with `5xx`, `401`, `403` or `429` | Nothing is stored: retrying with the **same** key runs the request again. | | Key older than 24 h | Treated as new. | Every other outcome is stored and replayed — including a `4xx` such as `400` or `409`. That is why a corrected request needs a new key (below). "Same request" means the same method, path and body. The most common integration bug is a constant key reused for every call — the `idempotency_conflict` error exists to name it. ## Secrets in replayed responses Two operations return a secret: [creating a key](/docs/management-api/keys#create-a-key) and [rotating a key](/docs/management-api/keys#rotate-a-key). A replay of either returns **the same secret** as the first response — which is what makes a retry after a lost response safe: you get the key you created, not a second one. A replay of either is never handed back blindly: the route's scope and your **current** project and key access are checked again before the cached response is served. If you could see the key when you created or rotated it but have since lost access to the project, the replay is `404 not_found` — the same as if the key did not exist — and the secret is not returned again. If the secret is not available for replay, the replay fails with `409 idempotency_replay_unavailable` instead of a secret: the secret was delivered once, on the original response. If you lost it, retry with a **new** `Idempotency-Key` and revoke the unused key. The stored response is protected like every other secret, and each successful replay is recorded separately in the [audit log](/docs/management-api/audit) (`key_create_replayed`, `key_rotate_replayed`), so you can see when a secret was delivered twice. Stored responses — including any stored secret — are kept for **24 hours** and then deleted; after that a repeated key is treated as new. --- ## Incident response **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/incident-response **Description:** Use the Management API to contain a leaked key, a runaway workload, or a compromised account quickly. **The job:** something is wrong — a leaked secret, a runaway workload, a compromised account — and the response must be shorter than the time it takes to find someone with a browser. Each containment move below is one call, so it can live in a script in your repository and run from the incident channel, attributed to the person running it. ## Prepare before the incident Mint an incident-response token for each on-call engineer who is an Admin (**Settings → API Tokens**), with only the scopes the playbook needs: | Scope | Used for | |---|---| | `keys:read`, `keys:write` | Find and kill keys | | `projects:read`, `projects:write` | Disable a project | | `members:read`, `members:write` | Disable a member | | `audit:read` | See what happened | Store it in the team's secret manager, and rehearse the scripts against a test project. Use a fixed, incident-scoped `Idempotency-Key` (for example `incident-4711-…`) so a panicked double-run is harmless. ## The containment moves | Move | Call | Why it is safe as one call | |---|---|---| | Contain a leaked key | `DELETE /keys/{id}` with `{"confirmed_leak": true}` | Permanent by design: a compromised secret does not get a grace window, and requests already streaming on it are cut off. | | Stop a runaway workload | `POST /projects/{id}/disable` | Reversible and project-scoped: every key in the project stops, nothing else is touched. `POST /projects/{id}/enable` undoes it. | | Contain a person | `POST /members/{id}/disable` | Blocks their sign-in, signs them out of every browser session and revokes **all their personal access tokens**. Reversible with `POST /members/{id}/enable`. | | Contain a leaked token | Revoke in **Settings → API Tokens**, or `DELETE /me/tokens/{id}` by its owner | Immediate: the next call fails `401 token_revoked`. | ### Contain a leaked key 1. Find the key. If you have its masked prefix from a scanner alert, match it against the project's keys: ```bash curl -fsS https://portal./api/v1/mgmt/projects/$PROJECT_ID/keys \ -H "Authorization: Bearer $TF_PAT" \ | jq -r '.data[] | "\(.id) \(.key_prefix) \(.status) \(.name) last used \(.last_used_at)"' ``` 2. Delete it. `confirmed_leak: true` also cuts off requests already streaming on the key: ```bash curl -fsS -X DELETE https://portal./api/v1/mgmt/keys/$KEY_ID \ -H "Authorization: Bearer $TF_PAT" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: incident-$INCIDENT-delete-$KEY_ID" \ -d '{"confirmed_leak": true}' ``` 3. Issue a replacement for the legitimate workload with [`POST /projects/{id}/keys`](/docs/management-api/keys#create-a-key). ### Stop a runaway workload When the cause is fixed, `POST /projects/{id}/enable` restores it. To cap rather than stop, lower `guardrails.spend_limit_usd` with [`PATCH /projects/{id}`](/docs/management-api/projects#update-a-project). ### Contain a person ```bash curl -fsS -X POST https://portal./api/v1/mgmt/members/$MEMBER_ID/disable \ -H "Authorization: Bearer $TF_PAT" \ -H "Idempotency-Key: incident-$INCIDENT-disable-$MEMBER_ID" ``` API keys the person created keep working — they are project assets. If the person may have copied secrets, list the keys they created and rotate or delete them by id: ```bash curl -fsS https://portal./api/v1/mgmt/projects/$PROJECT_ID/keys \ -H "Authorization: Bearer $TF_PAT" \ | jq -r --arg uid "$MEMBER_ID" '.data[] | select(.created_by.id == $uid) | .id' ``` The last active admin cannot be disabled (`409 last_admin`), and you cannot disable yourself (`409 cannot_act_on_self`). To sign someone out of every browser without disabling them, use [`DELETE /members/{id}/sessions`](/docs/management-api/members#sign-a-member-out-everywhere). ## Afterwards: what happened Pull the audit trail for the window, including every action taken through the API and which token took it: ```bash curl -fsS -G https://portal./api/v1/mgmt/audit/export \ -H "Authorization: Bearer $TF_PAT" \ -d format=jsonl \ -d from=2026-09-24T08:00:00Z \ -o incident-$INCIDENT-audit.jsonl ``` Every containment call above appears in it under the on-call engineer's name, with the token in `metadata.pat_id` / `metadata.pat_name`. An export covers at most 10,000 entries per call (`422 export_too_large` beyond that) — pass `to` as well and export in windows for a long incident. Changes to keys and projects take effect on the data plane within the [60-second propagation window](/docs/management-api/scopes#the-60-second-propagation-window). --- ## Invites **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/invites **Description:** List your organization's pending invitations and revoke the ones you no longer want. An invite is an offer to join your organization with a role. It never creates a person: the invitee accepts it themselves, by email. | Endpoint | Scope | Token owner's role | |---|---|---| | [`POST /invites`](#create-an-invite) | `members:write` | **Admin** | | [`GET /invites`](#list-invites) | `members:read` | **Admin** | | [`DELETE /invites/{id}`](#revoke-an-invite) | `members:write` | **Admin** | All three routes need an owner who is currently an organization **Admin**; a Member's token gets `403 insufficient_scope`. Invitations can be sent from the portal (**Settings → Team → Invite Member**) or with `POST /invites` below; this API also lists and revokes the pending ones. ## The invite object | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `email` | string | | | `role` | string | `admin` or `member`. | | `project_ids` | string[] | Initial project access for a `member`, applied on acceptance. May be empty. | | `status` | string | Always `pending` — the list holds pending invites only. | | `expires_at` | string | When the invitation link stops working. | | `created_at` | string | | The invitation token itself is never returned. ```json { "id": "e2f3a4b5-c6d7-4e8f-9a0b-1c2d3e4f5a6b", "email": "sam@acme.example", "role": "member", "project_ids": ["5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08"], "status": "pending", "expires_at": "2026-10-01T10:15:00Z", "created_at": "2026-09-24T10:15:00Z" } ``` ## Create an invite ```http POST /api/v1/mgmt/invites ``` Creates an invitation for `email`. It runs the same flow as the portal: the address is normalized, `role` is `member` (default) or `admin` (never `superadmin`), and `project_ids` grants a `member` project access on acceptance (every id must be a project of your organization; ignored for `admin`). An organization with an active SSO connection only accepts the admin's own email domain. No interactive re-login or acceptance step is added here — the invitee accepts by email. Requires `Idempotency-Key`. The response carries a `status`, and its HTTP status code reflects whether a new invite was actually created: | `status` | HTTP status | Meaning | |---|---|---| | `created` | `201` | A new invitation was sent. | | `already_member` | `200` | The address already belongs to the organization; no invite is sent (the member's `name`/`is_active` are returned). | | `already_pending` | `200` | A pending invitation already exists; it is returned, never duplicated. | ```json { "status": "created", "id": "e2f3a4b5-c6d7-4e8f-9a0b-1c2d3e4f5a6b", "email": "sam@acme.example", "role": "member", "project_ids": ["5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08"], "expires_at": "2026-10-01T10:15:00Z" } ``` ## List invites ```http GET /api/v1/mgmt/invites ``` The organization's pending invites, newest first. [Paginated](/docs/management-api/pagination) with `limit` and `cursor`; there are no filters. ```json { "data": [ { "id": "e2f3a4b5-c6d7-4e8f-9a0b-1c2d3e4f5a6b", "email": "sam@acme.example", "role": "member", "project_ids": ["5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08"], "status": "pending", "expires_at": "2026-10-01T10:15:00Z", "created_at": "2026-09-24T10:15:00Z" } ], "has_more": false, "next_cursor": null } ``` ## Revoke an invite ```http DELETE /api/v1/mgmt/invites/{id} ``` Immediate: the link stops working and the invite leaves the pending list. No body. Requires `Idempotency-Key`. Returns `204 No Content`. ## Errors specific to invites | Status | Code | When | |---|---|---| | 400 | `invalid_request` (param `email`) | `email` is missing, malformed, or not a single bare address. | | 400 | `invalid_request` (param `role`) | `role` is not `member` or `admin`. | | 400 | `invalid_request` (param `project_ids`) | An id in `project_ids` is not a project of your organization, or more than 100 ids. | | 403 | `insufficient_scope` | Token lacks `members:read`/`members:write`, or the owner is not currently an Admin. | | 404 | `not_found` | Invite absent, already gone, or in another organization. | | 409 | `conflict` | The org has an SSO connection and `email` is not the admin's own domain. | | 409 | `conflict` (param `email`) | The address cannot be invited to this organization. | | 422 | `unsupported_filter` | Any query parameter other than `limit` and `cursor` on the list. | --- ## API keys **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/keys **Description:** Create, list, rotate, disable, and delete a project's inference API keys. Project API keys are the credentials your applications use on the [Inference API](/docs/authentication). This page manages their whole lifecycle — **provision · configure · rotate · retire** — with the same semantics as the portal, and every write is recorded in the [audit log](/docs/management-api/audit) with the token that made it. | Endpoint | Scope | |---|---| | [`GET /projects/{id}/keys`](#list-a-projects-keys) | `keys:read` | | [`POST /projects/{id}/keys`](#create-a-key) | `keys:write` | | [`GET /keys/{id}`](#retrieve-a-key) | `keys:read` | | [`PATCH /keys/{id}`](#update-a-key) | `keys:write` | | [`POST /keys/{id}/rotate`](#rotate-a-key) | `keys:write` | | [`POST /keys/{id}/rotate/cancel`](#cancel-a-rotation) | `keys:write` | | [`POST /keys/{id}/disable`](#disable-a-key) | `keys:write` | | [`POST /keys/{id}/enable`](#enable-a-key) | `keys:write` | | [`DELETE /keys/{id}`](#delete-a-key) | `keys:write` | Keys are created under a project and addressed flat by id afterwards. Unlike project, member and organization writes, key routes are **not Admin-only**. They follow the portal's rules for the token owner: an Admin manages every key; a Member sees and manages **only the keys they created**, and creates keys only in projects they belong to. Anything else is `403 insufficient_scope` (the message says which rule) or `404`. The Python examples share this setup: ```python BASE = "https://portal./api/v1/mgmt" s = requests.Session() s.headers["Authorization"] = f"Bearer {os.environ['TF_PAT']}" def idem(): return {"Idempotency-Key": str(uuid.uuid4())} ``` ## The key object | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `project_id` | string | The owning project. | | `name` | string | | | `description` | string \| null | | | `key_prefix` | string | Masked form for display, e.g. `tf-4b8a9c-7d21e0-Qm8...k2Zw`. Never the secret. | | `status` | string | `active`, `disabled` or `revoked`. | | `created_at` | string | ISO-8601 UTC. | | `last_used_at` | string \| null | | | `expires_at` | string \| null | `null` = no expiry. | | `revoked_at` | string \| null | Set once the key is deleted. | | `requests_7d` | integer | Requests in the last 7 days. | | `allowed_models` | string[] | Models this key may call (`provider/model`); empty = the project's allowlist applies. | | `rpm_limit` | integer \| null | The key's own requests-per-minute limit. | | `spend_cap` | object \| null | Legacy, read-only: a key-level spend cap stored before spend limits moved to the project — `{ "amount_usd", "reset_period" }`, where `reset_period` is `daily`, `weekly`, `monthly` or `never`. `null` = no key cap. It cannot be set or changed through this API; set spend limits on the [project](/management-api/projects). | | `ip_allowlist` | string[] | CIDR ranges; empty = the project's allowlist applies. | | `rotation` | object \| null | Present while a rotation is in progress: `{ "grace_expires_at": "…" }` — the previous secret keeps working until then. | | `created_by` | object \| null | `{ "id", "email" }` of the creator. Immutable history — it persists after the creator leaves, and nothing reassigns it. | ```json { "id": "c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74", "project_id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "payments-prod", "description": null, "key_prefix": "tf-4b8a9c-7d21e0-Qm8...k2Zw", "status": "active", "created_at": "2026-09-20T08:20:44Z", "last_used_at": "2026-09-24T09:58:12Z", "expires_at": "2027-03-31T00:00:00Z", "revoked_at": null, "requests_7d": 48213, "allowed_models": [], "rpm_limit": null, "spend_cap": null, "ip_allowlist": [], "rotation": null, "created_by": { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example" } } ``` ## List a project's keys ```http GET /api/v1/mgmt/projects/{id}/keys ``` Returns the project's keys (masked), newest first — including revoked keys, for the record. A Member sees only the keys they created. [Paginated](/docs/management-api/pagination) with `limit` and `cursor`. | Query parameter | Notes | |---|---| | `status` | `active`, `disabled` or `revoked`. Any other value → `422 validation_failed`. | ## Create a key ```http POST /api/v1/mgmt/projects/{id}/keys ``` | Body field | Type | Required | Notes | |---|---|---|---| | `name` | string | **yes** | Unique among your active keys (`409 name_taken`). | | `description` | string | no | | | `allowed_models` | string[] | no | `provider/model` ids. An unknown model → `400`, `param: "allowed_models"`. | | `rpm_limit` | integer | no | At least 1. | | `expires_at` | string | no | RFC 3339 timestamp. | | `ip_allowlist` | string[] | no | CIDR notation. | Requires `Idempotency-Key`. Returns `201` with the key object **plus `secret`** — the full key value. Spend limits are set on the [project](/management-api/projects) (`spend_limit_usd`), not on keys. A body carrying `spend_cap_amount`, `spend_cap_reset_period` or `clear_spend_cap` — any value, including `null` — is rejected with `400 invalid_request`, `param` naming the field. ```json { "id": "c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74", "project_id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "payments-prod", "description": null, "key_prefix": "tf-4b8a9c-7d21e0-Qm8...k2Zw", "status": "active", "created_at": "2026-09-24T10:02:40Z", "last_used_at": null, "expires_at": "2027-03-31T00:00:00Z", "revoked_at": null, "requests_7d": 0, "allowed_models": [], "rpm_limit": null, "spend_cap": null, "ip_allowlist": [], "rotation": null, "created_by": { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example" }, "secret": "tf-4b8a9c-7d21e0-Qm8xR2v…(full value, shown once)" } ``` ## Retrieve a key ```http GET /api/v1/mgmt/keys/{id} ``` Returns the key object (masked). ## Update a key ```http PATCH /api/v1/mgmt/keys/{id} ``` Changes configuration only. Send at least one field; only the fields you send change. Returns `200` with the key object. Key-level spend-cap fields are rejected exactly as on create — set spend limits on the project. | Body field | Type | Notes | |---|---|---| | `name` | string | | | `expires_at` | string \| null | RFC 3339 timestamp; `null` removes the expiry. | | `ip_allowlist` | string[] \| null | CIDR notation; `[]` or `null` = no key-level restriction. | State has its own verbs (`disable`, `enable`, `delete`) — `PATCH` never changes state. ## Rotate a key ```http POST /api/v1/mgmt/keys/{id}/rotate ``` Issues a **new secret** for the same key id. The previous secret keeps working for a grace window you choose, so you can deploy the new one without downtime. | Body field | Type | Required | Notes | |---|---|---|---| | `grace_hours` | integer | no | How long the previous secret keeps working: 1–168 (7 days). Default 24. The body may be omitted. | Returns `200` with the key object plus `secret` (the new value, **once**). The old secret stops working at `rotation.grace_expires_at`. A replay with the same `Idempotency-Key` returns the same secret and is audited as `key_rotate_replayed`. ```json { "id": "c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74", "project_id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "payments-prod", "key_prefix": "tf-4b8a9c-7d21e0-Zt4...9pQa", "status": "active", "rotation": { "grace_expires_at": "2026-09-24T12:05:00Z" }, "secret": "tf-4b8a9c-7d21e0-Zt4mW8e…(full value, shown once)", "...": "..." } ``` ## Cancel a rotation ```http POST /api/v1/mgmt/keys/{id}/rotate/cancel ``` Aborts an in-progress rotation: the **new** secret is discarded and the original secret stays active with no deadline. This is the clean abort for a failed deploy. No body. Returns `200` with the key object (`rotation: null`). A key with no rotation in progress is `409 no_rotation_in_progress`. ## Disable a key ```http POST /api/v1/mgmt/keys/{id}/disable ``` Reversible: the key stops authenticating inference until you enable it. No body. Returns `200` with the key object (`status: "disabled"`). ## Enable a key ```http POST /api/v1/mgmt/keys/{id}/enable ``` The inverse of disable. No body. Returns `200` with the key object (`status: "active"`). A deleted (revoked) key cannot come back: `409 key_revoked`. ## Delete a key ```http DELETE /api/v1/mgmt/keys/{id} ``` The **permanent** kill. The key stops authenticating and cannot be restored; it stays readable with `status: "revoked"` and a `revoked_at`, and its usage history and audit trail are retained. A compromised secret gets no grace window — use this for a confirmed leak. Returns `204 No Content`. | Body field | Type | Required | Notes | |---|---|---|---| | `confirmed_leak` | boolean | no | `true` also cuts off requests already streaming on the key. Default `false`. The body may be omitted. | ## Errors specific to keys | Status | Code | When | |---|---|---| | 400 | `invalid_request` (`param` set) | Missing `name`; unknown field; `grace_hours` outside 1–168; bad `expires_at`; bad CIDR; a key-level spend-cap field (`spend_cap_amount`, `spend_cap_reset_period`, `clear_spend_cap` — spend limits are set on the project); an unknown model in `allowed_models`. | | 403 | `insufficient_scope` | Token lacks `keys:read`/`keys:write`; a Member owner acting on a key they did not create, or in a project they do not belong to. | | 404 | `not_found` | Key or project absent, or in another organization. | | 409 | `name_taken` | You already have an active key with this name. | | 409 | `key_revoked` | Enabling a deleted key. | | 409 | `no_rotation_in_progress` | Cancelling when no rotation is in progress. | | 409 | `idempotency_conflict` | The `Idempotency-Key` was used for a different request. | | 422 | `unsupported_filter` · `validation_failed` | An unknown query parameter, or a `status` filter value outside the list above. | --- ## Members **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/members **Description:** List organization members and change their role, project access, and state. The organization directory and membership management, with the portal's guards. Invitations are on their own page: [Invites](/docs/management-api/invites). | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /members`](#list-members) | `members:read` | Any member | | [`GET /members/{id}`](#retrieve-a-member) | `members:read` | **Admin** | | [`PATCH /members/{id}`](#change-a-members-role-or-name) | `members:write` | **Admin** | | [`PUT /members/{id}/projects`](#set-a-members-project-access) | `members:write` | **Admin** | | [`POST /members/{id}/disable`](#disable-a-member) | `members:write` | **Admin** | | [`POST /members/{id}/enable`](#enable-a-member) | `members:write` | **Admin** | | [`DELETE /members/{id}`](#remove-a-member) | `members:write` | **Admin** | | [`DELETE /members/{id}/sessions`](#sign-a-member-out-everywhere) | `members:write` | **Admin** | | [`GET /members/removed`](#list-removed-members) | `members:read` | **Admin** | | [`POST /members/removed/{id}/restore`](#restore-a-removed-member) | `members:write` | **Admin** | `members:write` is a [sensitive scope](/docs/management-api/scopes#the-sensitive-class). Every route except the list needs an owner who is currently an organization **Admin**; a Member's token gets `403 insufficient_scope`. People join through invitations, which are sent from the portal; accepting one is the invitee's step. Python examples share this setup: ```python BASE = "https://portal./api/v1/mgmt" s = requests.Session() s.headers["Authorization"] = f"Bearer {os.environ['TF_PAT']}" def idem(): return {"Idempotency-Key": str(uuid.uuid4())} ``` ## The member object | Field | Type | Notes | |---|---|---| | `id` | string | The member's user id. | | `email` | string | | | `name` | string \| null | | | `role` | string | `admin` or `member`. | | `status` | string | `active` or `disabled`. | | `email_verified` | boolean | | | `created_at` | string | When they joined. | | `last_login_at` | string \| null | | | `key_count` | integer | API keys this member created. | | `project_ids` | string[] | Projects the member belongs to. Always present on single-member responses; list rows may omit it. Admins can access every project regardless. | This is the full object an **Admin** token owner gets. A **Member** token owner listing members gets only each member's identity; see [List members](#list-members). ```json { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example", "name": "Dana Okafor", "role": "admin", "status": "active", "email_verified": true, "created_at": "2026-03-02T09:41:17Z", "last_login_at": "2026-09-24T08:12:40Z", "key_count": 3, "project_ids": [] } ``` ## List members ```http GET /api/v1/mgmt/members ``` Newest first. [Paginated](/docs/management-api/pagination) with `limit` and `cursor`. The rows depend on the token owner's current role: - **Admin:** the full [member object](#the-member-object). - **Member:** only each member's identity: `id`, `email`, `name`, `role` and `created_at`. Member state (`status`, `email_verified`, `last_login_at`, `key_count`) is admin-only. ```json { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example", "name": "Dana Okafor", "role": "admin", "created_at": "2026-03-02T09:41:17Z" } ``` | Query parameter | Notes | |---|---| | `role` | `admin` or `member`. Any other value → `422 validation_failed`. | | `status` | `active` or `disabled`. Any other value → `422 validation_failed`. Admin only: a Member's token gets `403 insufficient_scope`. | ## Retrieve a member ```http GET /api/v1/mgmt/members/{id} ``` ## Change a member's role or name ```http PATCH /api/v1/mgmt/members/{id} ``` | Body field | Type | Notes | |---|---|---| | `role` | string | `admin` or `member`. | | `name` | string | Display name. | Send at least one field. Returns `200` with the member object. The last active admin cannot be demoted (`409 last_admin`), and the organization owner's role cannot be changed (`409 owner_protected`). A demotion also narrows the member's [personal access tokens](/docs/management-api/scopes#the-intersection-rule). ## Set a member's project access ```http PUT /api/v1/mgmt/members/{id}/projects ``` Replaces the set of projects the member belongs to. Returns `200` with the member object. | Body field | Type | Required | Notes | |---|---|---|---| | `project_ids` | string[] | **yes** | The full set. `[]` removes all project access. A project outside the organization → `400 invalid_request`, `param: "project_ids"`. | ## Disable a member ```http POST /api/v1/mgmt/members/{id}/disable ``` Reversible containment. The member can no longer sign in, is signed out of every browser session, and **every personal access token they created is revoked**. API keys they created keep working — they are project assets. No body. Returns `200` with the member object (`status: "disabled"`). The last active admin cannot be disabled (`409 last_admin`), and you cannot disable yourself (`409 cannot_act_on_self`). ## Enable a member ```http POST /api/v1/mgmt/members/{id}/enable ``` Restores a disabled member's sign-in. No body. Returns `200` with the member object (`status: "active"`). ## Remove a member ```http DELETE /api/v1/mgmt/members/{id} ``` Permanent. The member's account in this organization is deleted, and their personal access tokens are deleted with it. Past audit entries remain. The person is listed under [removed members](#list-removed-members) and can't rejoin through SSO automatic membership until you restore or invite them. Returns `204 No Content`. | Body field | Type | Required | Notes | |---|---|---|---| | `delete_keys` | boolean | no | `true` also revokes every API key the member created. Default `false`: their keys keep working. | The last active admin cannot be removed (`409 last_admin`), you cannot remove yourself (`409 cannot_act_on_self`), and the organization owner is protected (`409 owner_protected`). ## Sign a member out everywhere ```http DELETE /api/v1/mgmt/members/{id}/sessions ``` Ends every sign-in session of the member, so each browser has to sign in again. It does not revoke their personal access tokens (disable the member for that). No body. Returns `204 No Content`. When the member has no active sign-in session to end, the call is `409 sessions_unavailable`; if sign-out is temporarily unavailable, `502 upstream_unavailable` — retry with the same `Idempotency-Key`. ## List removed members ```http GET /api/v1/mgmt/members/removed ``` People an admin removed from the organization, newest removal first, in the standard list envelope (`limit`, `cursor`). A removed person can't rejoin through your SSO's automatic membership; [restore](#restore-a-removed-member) or invite them to give access again. | Field | Type | Notes | |---|---|---| | `id` | string | The removal's id, used to restore. | | `email` | string | | | `name` | string \| null | | | `last_role` | `admin` \| `member` \| null | Their role when removed; `null` if not recorded. | | `projects_recorded` | boolean | `false` for removals made before project access was recorded. | | `projects` | array of `{id, name}` | Recorded projects that still exist. | | `removed_at` | string (date-time) | | | `removed_by` | `{id, name, email}` \| null | The admin who removed them. | | `invite_pending` | boolean | A restore or invitation was sent and not accepted yet; they stay removed until they accept it. | ```bash curl https://portal./api/v1/mgmt/members/removed \ -H "Authorization: Bearer $TF_PAT" ``` ## Restore a removed member ```http POST /api/v1/mgmt/members/removed/{id}/restore ``` Gives the person access again with their previous role and the recorded projects that still exist (Member with no projects when nothing was recorded). No body. Removing someone deletes their sign-in, so they become a member again the next time they sign in: your organization's SSO lets them straight in, and any other sign-in method uses the link in the email they get ("You've been given access to `` again"). Until then they appear under [`GET /invites`](/docs/management-api/invites) as `invite_id`, and they are still listed as removed (`invite_pending: true`): they stay removed until they accept. If you revoke the invitation or it expires, they stay removed and automatic membership never lets them back in. ```json { "status": "restored", "email": "ann@example.com", "role": "member", "project_ids": ["3f2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9"], "invite_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "expires_at": "2026-10-14T10:00:00Z" } ``` `409 already_a_member` when the person is a member again; `409 conflict` when the invitation rules refuse the address (for example, your SSO only allows invitations at your own email domain). ```bash curl -X POST https://portal./api/v1/mgmt/members/removed/6f1c2b0a-3d4e-4f50-8a9b-0c1d2e3f4a5b/restore \ -H "Authorization: Bearer $TF_PAT" \ -H "Idempotency-Key: 1e2f3a4b-5c6d-4e7f-8a9b-0c1d2e3f4a5b" ``` ## Errors specific to members | Status | Code | When | |---|---|---| | 400 | `invalid_request` (`param` set) | Empty `PATCH` body; unknown field; unknown `role`; `project_ids` missing or naming a project outside the organization. | | 403 | `insufficient_scope` | Token lacks `members:read`/`members:write`, or the owner is not currently an Admin. | | 404 | `not_found` | Member absent or in another organization. | | 409 | `last_admin` | Demoting, disabling or removing the organization's last active admin. | | 409 | `owner_protected` | Changing the role of, or removing, the organization owner. | | 409 | `cannot_act_on_self` | Disabling or removing yourself. | | 409 | `sessions_unavailable` | `DELETE /members/{id}/sessions` for a member with no active sign-in session. | | 409 | `already_a_member` | Restoring someone who is a member again. | | 422 | `unsupported_filter` · `validation_failed` | An unknown query parameter, or a `role`/`status` filter value outside the list above. | ```json { "error": { "message": "this change would leave the organization without an active admin", "type": "conflict_error", "code": "last_admin", "param": null, "request_id": "req_d5f16075d7984e1b99c537a18f7fc58a" } } ``` --- ## Organization **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/org **Description:** Read your organization, and read and update its settings. The organization is implied by the token — there is no org id in any path. | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /org`](#retrieve-the-organization) | `org:read` | Any member (response depends on the role) | | [`GET /org/settings`](#retrieve-the-organization-settings) | `org:read` | **Admin** | | [`PATCH /org`](#update-the-organization) | `org:write` (sensitive) | **Admin** | `org:write` changes exactly the settings the portal's **Settings → Organization** form edits. Suspending or deleting the organization and changing its billing mode are done in the portal. ## The organization object For an **Admin** token owner, the organization record and its settings are returned as **one flat object**. Settings fields appear once they have been set; an organization that never set `monthly_budget`, for example, has no `monthly_budget` key. | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `name` | string | | | `slug` | string | | | `is_active` | boolean | `false` while the organization is suspended. | | `billing_mode` | string | The organization's billing mode (read-only here). | | `region` | string | The organization's region. | | `created_at` | string | ISO-8601 UTC. | | `spend_ceiling` | number | Upper bound (USD) no project spend limit may exceed. Absent = unbounded. | | `default_model_allowlist` | string[] | Allowlist applied to new projects. | | `default_rpm_limit` | integer | Default requests-per-minute limit. | | `default_daily_cap` | number | Default daily spend cap (USD). | | `default_monthly_cap` | number | Default monthly spend cap (USD). | | `monthly_budget` | number | Organization monthly budget (USD). | | `budget_behavior` | string | `alert_only` or `hard_stop` when the budget is reached. | | `email_otp_enabled` | boolean | Email one-time-code MFA for member sign-ins. Admins always use MFA. | Treat the object as open: new fields may appear within v1. ## The organization identity (members) A token owner who is not an Admin gets only the organization's identity. Settings and billing mode are admin-only. | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `name` | string | | | `slug` | string | | | `region` | string | The organization's region. | | `is_active` | boolean | `false` while the organization is suspended. | | `created_at` | string | ISO-8601 UTC. | | `role` | string | The token owner's own role, `admin` or `member`. | ```json { "id": "3f9a7c21-5d4e-4b8a-9c0f-1e2d3a4b5c6d", "name": "Acme Corp", "slug": "acme", "region": "default", "is_active": true, "created_at": "2026-03-02T09:41:17Z", "role": "member" } ``` ## Retrieve the organization ```http GET /api/v1/mgmt/org ``` An Admin gets [the organization object](#the-organization-object); any other member gets [the organization identity](#the-organization-identity-members). ```json { "id": "3f9a7c21-5d4e-4b8a-9c0f-1e2d3a4b5c6d", "name": "Acme Corp", "slug": "acme", "is_active": true, "billing_mode": "prepaid", "region": "default", "created_at": "2026-03-02T09:41:17Z", "spend_ceiling": 10000, "default_model_allowlist": ["openai/gpt-4o-mini"], "default_rpm_limit": 600, "default_monthly_cap": 1000, "monthly_budget": 5000, "budget_behavior": "alert_only", "email_otp_enabled": true } ``` ## Retrieve the organization settings ```http GET /api/v1/mgmt/org/settings ``` Admin only. Returns [the organization object](#the-organization-object) with every settings key. A member gets `403 insufficient_scope`. ```bash curl https://portal./api/v1/mgmt/org/settings \ -H "Authorization: Bearer $TF_PAT" ``` ## Update the organization ```http PATCH /api/v1/mgmt/org ``` Send any of the fields below at the top level of the body. Only the fields you send change; an empty body or an unknown field is `400 invalid_request` (`param` names the field). Requires `Idempotency-Key` and an owner who is currently an **Admin**. Returns `200` with the updated organization object. | Body field | Type | Notes | |---|---|---| | `name` | string | Cannot be empty. | | `slug` | string | Cannot be empty. Already in use → `409 slug_taken`. | | `default_model_allowlist` | string[] | | | `default_rpm_limit` | integer | | | `default_daily_cap` | number | USD. | | `default_monthly_cap` | number | USD. | | `monthly_budget` | number | USD. | | `budget_behavior` | string | `alert_only` or `hard_stop`. | | `email_otp_enabled` | boolean | | | `spend_ceiling` | number | USD, not negative. `0` clears the bound. | ## Errors specific to the organization | Status | Code | When | |---|---|---| | 400 | `invalid_request` (`param` set when known) | Empty body; unknown field; empty `name` or `slug`; `budget_behavior` not `alert_only`/`hard_stop`; negative `spend_ceiling`; a field of the wrong type. | | 403 | `insufficient_scope` | Token lacks `org:read`/`org:write`, or (for `GET /org/settings` and `PATCH`) the owner is not currently an Admin. | | 409 | `slug_taken` | The `slug` is already in use. | --- ## Overview **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/overview **Description:** What the Management API is — a programmatic, token-authenticated interface for administering your organization. ## What is the Management API? The Management API lets your scripts, CI pipelines and FinOps tooling do what an Admin does in the portal — create projects, set guardrails, create and rotate API keys, manage members and pending invites, and read usage, balances, the model catalog and the audit log — with an HTTP call. ``` https://portal./api/v1/mgmt ``` It is served on the **portal host** under `/api/v1/mgmt` — the same address you open the portal on — while the [Inference API](/docs/overview) stays on the API host (`https://api./v1`). It is authenticated with a [personal access token](/docs/management-api/personal-access-tokens) (`tf-pat_…`) — never with a project API key. The portal and the Management API act on the same data with the same permission rules, so a change made in one is immediately visible in the other. ## Inference API and Management API Token Factory has two APIs. They follow the same conventions but do different jobs with different credentials — and each credential type is rejected by the other API, so the two never mix. | | Inference API | Management API | |---|---|---| | Serves | Tokens: completions, embeddings, images, speech | Configuration: org, members, projects, keys, usage, billing reads, audit | | Base URL | `https://api./v1` | `https://portal./api/v1/mgmt` | | Credential | Project API key — belongs to a **project** | Personal access token `tf-pat_…` — belongs to a **person** | | Authenticates | A workload | A human, or the automation standing in for one | | Audience | Your application | Your Admins and their pipelines | | Billable | Yes, metered per request | No — the API itself is free (it is rate limited) | | A failure means | A request did not run | A misconfiguration, or automation that cannot see your organization | Usage and billing reads are a **view of the same usage records** the portal and your invoice use. The API computes nothing of its own; if an invoice and an API response ever disagreed, the invoice governs. ## Four rules for tokens Four rules shape how personal access tokens behave. Each has a cost, stated here so you can plan around it. | # | Rule | Why | What it costs you | |---|---|---|---| | 1 | **A token is a person's credential, never the org's.** | Every audited action is attributable to one person in one org. When someone leaves, their tokens die with their membership — nothing is orphaned. | A departing member's pipelines stop when their membership ends. Re-home automation under someone who stays. | | 2 | **A token can never exceed its creator.** | Effective permission is the token's scopes **intersected** with the creator's live rights, on every request. Privilege escalation by token is impossible. | If the creator is demoted, the token narrows with them. The error says so — see [the intersection rule](/docs/management-api/scopes#the-intersection-rule). | | 3 | **One organization per token.** | A token is minted inside one org and never resolves outside it — not in a listing, an export or an error. | A user in three orgs needs three tokens for three automations. There is no cross-org view. | | 4 | **Admins mint tokens.** | Organization, member, project, usage and audit management is Admin-only, so a Member's token could reach little beyond their own keys. | Developers ask an Admin to mint their CI token — one portal action, once. | ## Boundaries - **Not the Inference API.** Inference is on the API host (`https://api./v1`), management on the portal host under `/api/v1/mgmt`. A personal access token is rejected at the inference routes, and a project API key is rejected here — both with `401 credential_type_mismatch` and a message that names the fix. - **Billing is read-only.** The API tells you what you owe and what you bought; payments, payment methods and credit purchases are handled in the portal. See [Billing](/docs/management-api/billing). - **Tokens are created in the portal.** Minting needs re-authentication in the portal, so a leaked token cannot extend its own life. A token can list and revoke **its owner's own** tokens — see [Tokens](/docs/management-api/tokens). - **One organization at a time.** No parameter accepts another organization's id, and an object in another org is indistinguishable from one that does not exist (404). - **Automation acts as a person.** Every token belongs to a named member, which is what keeps every action attributable. ## What you can do with it ## OpenAPI specification The complete Management API is described by an OpenAPI 3.1 specification, published in YAML and JSON. Its `servers` URL already points at the environment you are reading these docs on. Use it with Swagger Editor, Postman, Insomnia, Bruno or any OpenAPI-compatible code generator to explore the API or generate a client: open the link above, or import the JSON URL into your tool. Every call needs a [personal access token](/docs/management-api/personal-access-tokens), and every write needs an `Idempotency-Key` header. ## Resources at a glance All paths are relative to `https://portal./api/v1/mgmt`. The organization is implied by the token — there is no `/orgs/{org}` segment. | Resource | Paths | Scopes | |---|---|---| | [Organization](/docs/management-api/org) | `/org` | `org:read` · `org:write` | | [Members](/docs/management-api/members) | `/members` | `members:read` · `members:write` | | [Invites](/docs/management-api/invites) | `/invites`, `/invites/{id}` | `members:read` · `members:write` | | [Projects](/docs/management-api/projects) | `/projects` | `projects:read` · `projects:write` | | [API keys](/docs/management-api/keys) | `/projects/{id}/keys`, `/keys/{id}` | `keys:read` · `keys:write` | | [Usage](/docs/management-api/usage) | `/usage`, `/usage/export` | `usage:read` | | [Billing](/docs/management-api/billing) | `/billing/*` | `billing:read` | | [Audit log](/docs/management-api/audit) | `/audit`, `/audit/export` | `audit:read` | | [Catalog](/docs/management-api/catalog) | `/catalog/models`, `/catalog/regions` | `catalog:read` | | [Tokens](/docs/management-api/tokens) | `/me/tokens` | none — any valid token | ## Conventions shared by every endpoint | Convention | Rule | |---|---| | Format | JSON in and out, `snake_case` fields, opaque string ids, ISO-8601 UTC timestamps. | | Authentication | `Authorization: Bearer tf-pat_…` on every call — [Authentication](/docs/management-api/authentication). | | Idempotency | `Idempotency-Key` is **required** on every `POST`, `PATCH`, `PUT` and `DELETE` — [Idempotency](/docs/management-api/idempotency). | | Pagination | Cursor-based: `data`, `has_more`, `next_cursor` — [Pagination](/docs/management-api/pagination). | | Errors | `error.code` is stable; `error.action` tells you the fix — [Errors](/docs/management-api/errors). | | Rate limits | Per token, with `X-RateLimit-*` headers on authenticated responses and `Retry-After` on 429 — [Rate limits](/docs/management-api/rate-limits). | | Versioning | The path is the version (`/api/v1/mgmt`); v1 changes are additive only — [Versioning](/docs/management-api/versioning). | | Request ids | Every response carries `X-Request-Id` (`req_…`); every error body carries the same id as `error.request_id`. | | Response time | Requests running longer than 60 seconds are cut off; exports return at most 10,000 rows per call. | --- ## Pagination **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/pagination **Description:** How list endpoints page through results. Every list endpoint is **cursor-paginated**. There is no offset pagination anywhere on this surface: the audit log changes while you read it, and offset pages in a shifting list silently skip records — which is exactly the record someone is looking for. `GET /usage` is a report rather than a list, and the two exports (`/usage/export`, `/audit/export`) stream in one response; none of them takes `limit` or `cursor`. ## Parameters | Parameter | Type | Default | Notes | |---|---|---|---| | `limit` | integer | `50` | 1–200. Outside that range, or not an integer → `400 invalid_request`, `param: "limit"`. | | `cursor` | string | — | The `next_cursor` from the previous page. Opaque: do not parse or construct it. An invalid cursor → `400 invalid_request`, `param: "cursor"`. | ## Response shape ```json { "data": [ { "id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "payments", "created_at": "2026-09-20T08:14:03Z" }, { "id": "a17c2f90-6e4b-4d1a-b3c5-0f8e2d9a7b61", "name": "search", "created_at": "2026-09-18T16:02:47Z" } ], "has_more": true, "next_cursor": "MjAyNi0wOS0xOFQxNjowMjo0N1p8YTE3YzJmOTA" } ``` | Field | Notes | |---|---| | `data` | The page of objects. | | `has_more` | `true` if another page exists. | | `next_cursor` | Pass as `cursor` to get the next page; `null` on the last page. | Results are ordered **newest first** (`created_at` descending, ties broken by `id` descending), so a new object never shifts a page you have already read. The catalog has no creation time, so `/catalog/models` and `/catalog/regions` are ordered by `id` descending instead. ## Iterating through a list ## Filtering Filters are **per collection** and listed on each resource page (for example `actor`, `action`, `from` and `to` on the [audit log](/docs/management-api/audit)). A query parameter that an endpoint does not support is rejected with `422 unsupported_filter`, **never silently ignored**. Ignoring a filter would answer a different question than the one you asked, which on a usage query becomes a wrong number in someone's report. ```json { "error": { "message": "unsupported query parameter region for this collection", "type": "validation_error", "code": "unsupported_filter", "param": "region", "request_id": "req_44e85412d33443378ea22ffc821820e4" } } ``` A supported filter with a value outside its set — `status=archived` on `/projects`, say — is `422 validation_failed`, with `param` naming the filter. --- ## Personal access tokens **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/personal-access-tokens **Description:** Create, scope, rotate, and revoke the personal access tokens that authenticate Management API calls. A **personal access token** is the only credential the Management API accepts. It is a bearer credential that belongs to **one person in one organization**, carries an explicit list of [scopes](/docs/management-api/scopes), and always expires. Personal access tokens authenticate the Management API only; they are rejected on the portal's own API (a plain `401`) and on the Inference API (`401 credential_type_mismatch`). | Property | How it works | |---|---| | Format | `tf-pat_` followed by 43 URL-safe characters (256 bits of randomness). The fixed prefix lets secret scanners recognise a Token Factory management credential on sight. | | Shown once | The full secret is displayed **exactly once**, when you create it. Afterwards the portal shows `tf-pat_…a91f` and the API returns a masked `prefix` such as `tf-pat_AbCdE...a91f`, plus its name, scopes, expiry and last use. | | Hashed at rest | Token Factory stores a hash of the token, never the token itself. Nobody — including support — can show it to you again. | | Attributable | Every token names its creator, and every call made with it is recorded in the [audit log](/docs/management-api/audit) as that person using that token. A token is never anonymous and never a shared "service" identity. | | One org | A token acts only in the organization it was created in. | | Portal-bound | A token works only on the portal where it was created; a token from any other Token Factory portal is unknown here (`401 invalid_credential`). | A token carries: `id` · `name` · `prefix` · `scopes` · `created_by` · `created_at` · `expires_at` · `last_used_at` · `status` (`active` · `revoked`; expiry is read from `expires_at`). It carries no project, no region and no key material. ## Mint policy | Rule | Value | |---|---| | Who can mint | **Organization Admins**, in the portal only. There is no API endpoint that creates a token. | | Re-authentication | Minting is a sensitive action. If your last sign-in is not recent, the portal asks you to re-authenticate before the token is created. | | Note | Required, up to 64 characters, unique **case-insensitively** among your **active** tokens. A revoked token's note is free again — `ci-deploy` and `CI-Deploy` cannot both be active at once, but you can reuse a note once the token that held it is revoked. | | Scopes | At least one, from the [scope vocabulary](/docs/management-api/scopes). No wildcard; an unknown scope is refused. | | Expiry | Mandatory. Default **90 days**; the portal offers 30, 60, 90, 180 or 365. Maximum **365 days**. There are no eternal tokens. | | Active-token cap | **20** active tokens per user. Minting a 21st is refused (`token_limit_reached`) — revoke one you no longer use. | These are portal rules: minting is not a Management API call, so a refused mint is reported in the portal's token dialog (`invalid_name`, `invalid_scopes`, `invalid_expiry`, `token_name_taken`, `token_limit_reached`), not in the Management API error envelope. Why mandatory expiry: a standing credential with no review point is how organizations fail their own access reviews. Renewal is "mint the successor, revoke the predecessor", which also gives every integration a rehearsed rotation procedure. ## Lifecycle | Stage | How | Notes | |---|---|---| | **Mint** | Portal → **Settings → API Tokens** → **Create token** opens a dialog: **Note**, **Expiration**, **Select scopes** (a scope tree) → **Generate token**. Admin only, with re-authentication. | The one credential action a token can never perform itself. Sensitive scopes are flagged at selection. | | **Use** | `Authorization: Bearer tf-pat_…` on every call. | Never a query parameter, never a cookie. `last_used_at` updates as you use it. | | **Expire** | Automatic at `expires_at`. | The next call fails with `401 token_expired`. | | **Revoke** | Portal → **Revoke**, or `DELETE /me/tokens/{id}` for your own tokens. Automatic when the owner is disabled; the tokens are deleted with the member when they are removed. | **Immediate** — the next call fails with `401 token_revoked` (or `401 invalid_credential` once the owner is removed). There is no grace window. | | **Audit** | Mint (`pat_create`) and revoke (`pat_revoke`) are audited; every write made with the token records its id, name and the scope used. | See [Audit log](/docs/management-api/audit). | A token's scopes are fixed at mint. Nothing widens a token: to grant more, mint a new token. Role changes can only **narrow** what a token can do ([the intersection rule](/docs/management-api/scopes#the-intersection-rule)). When a member is disabled or removed, every personal access token they created stops working immediately and irreversibly. **Project API keys they created are not touched** — keys are project assets; a personal access token represents a person still acting. (Removing a member with `"delete_keys": true` is the explicit exception — see [Members](/docs/management-api/members#remove-a-member).) ## Create a token in the portal 1. Sign in to the portal as an organization **Admin** and open **Settings → API Tokens**. The card is titled **Management API tokens**: > Personal access tokens authenticate the Management API — > provisioning keys, projects, and members programmatically. They can't > make inference calls, and project API keys can't call the Management API. 2. Click **Create token**. This opens the **Create Management API token** dialog — nothing is created yet. 3. Enter a **Note** that says what the token is for and who runs it — for example `CI · payments-deploy`. It must be unique (case-insensitively) among your active tokens. 4. Choose an **Expiration**: 30, 60, 90, 180 or 365 days (default 90). Each option shows the calendar date the token would expire on. There is no "never" option — every token expires. 5. Choose scopes under **Select scopes** — a two-column tree, one row per resource group in scope order. `org`, `members`, `projects` and `keys` each show a **parent row**: ticking it grants both the group's `:read` and `:write` scopes, and the two child rows then show ticked and greyed out (still one scope each on the wire — read and write stay independent). The remaining groups (`usage:read`, `billing:read`, `audit:read`, `catalog:read`) are single rows. Every row shows that scope's description. The three **Sensitive** writes — `org:write`, `members:write`, `projects:write` — carry a badge, and selecting any of them shows a one-line warning below the list. Nothing is preselected. A summary strip below the list tracks how many scopes are selected and when the token would expire. 6. Click **Generate token**. If your last sign-in is not recent, the portal asks you to confirm your identity: choose **Sign in again**, sign in, and you return to the dialog with your note, expiration and scopes kept — click **Generate token** once more to create the token. 7. The dialog switches to **Token created**, showing the secret with a **Copy** button and the token's name, expiry and scopes. Copy the secret **now** and store it in your secret manager — it is shown once, and the dialog ignores Escape and outside clicks so you can't lose it to a stray click. Click **Done** to close. Each token then appears as a row with its name, masked prefix, creation date, expiry and last use (`tf-pat_…a91f · created Mar 20, 2026 · expires Jun 18, 2026 · last used never`), its scopes, and (for active tokens) a red **Revoke** button that asks you to confirm. ## Verify a new token The cheapest authenticated call is reading your own token list — it needs no scope: ## Rotate a token Tokens have no grace window: mint the successor first; both work side by side until you revoke the old one. Rotation is three steps: 1. **Mint the successor** in the portal with the same scopes and a fresh expiry. 2. **Deploy it** — replace the secret in your CI system or secret manager and confirm one call succeeds. 3. **Revoke the predecessor** — in the portal, or with [`DELETE /me/tokens/{id}`](/docs/management-api/tokens#revoke-one-of-your-tokens) using the new token. Put a reminder on the calendar a week before `expires_at`. An expired token fails with `401 token_expired` and an `action` telling you to mint a new one. ## If a token leaks Revoke it immediately (portal, or `DELETE /me/tokens/{id}`); revocation is effective on the next call. Then read the [audit log](/docs/management-api/audit) filtered to the token owner (`actor`) and look for entries whose `metadata.pat_id` is that token's id to see what it did. See [Incident response](/docs/management-api/incident-response). --- ## Projects **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/projects **Description:** Create, list, update, disable, and delete projects and their guardrails. A project groups API keys and carries the **guardrails** every key in it inherits: a model allowlist, a requests-per-minute limit, a spend limit and an IP allowlist. Guardrails are project-level — a key is never wider than its project. | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /projects`](#list-projects) | `projects:read` | Any member (members see their own projects) | | [`POST /projects`](#create-a-project) | `projects:write` | **Admin** | | [`GET /projects/{id}`](#retrieve-a-project) | `projects:read` | Any member with access to the project | | [`PATCH /projects/{id}`](#update-a-project) | `projects:write` | **Admin** | | [`POST /projects/{id}/disable`](#disable-a-project) | `projects:write` | **Admin** | | [`POST /projects/{id}/enable`](#enable-a-project) | `projects:write` | **Admin** | | [`DELETE /projects/{id}`](#delete-a-project) | `projects:write` | **Admin** | `projects:write` is a [sensitive scope](/docs/management-api/scopes#the-sensitive-class). Every write needs an owner who is currently an organization **Admin**; a Member's token gets `403 insufficient_scope`. The Python examples on this page share this setup: ```python BASE = "https://portal./api/v1/mgmt" s = requests.Session() s.headers["Authorization"] = f"Bearer {os.environ['TF_PAT']}" def idem(): return {"Idempotency-Key": str(uuid.uuid4())} ``` ## The project object | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `name` | string | Display name. | | `slug` | string | URL-safe name, derived from the name at creation unless you pass one. | | `description` | string \| null | | | `status` | string | `active` or `disabled`. | | `is_default` | boolean | `true` for the project created with the organization. | | `created_at`, `updated_at` | string | ISO-8601 UTC. | | `monthly_spend_usd` | number | Spend over the trailing 30 days, in USD. | | `active_key_count` | integer | On list rows. | | `guardrails` | object | The project's guardrails (below). Present on single-project responses (get, create, update, disable, enable); list rows omit it. | | `guardrails` field | Type | Notes | |---|---|---| | `model_allowlist` | string[] | Models keys in this project may call (`provider/model`). Empty = every model your plan allows. Ids come from [`GET /catalog/models`](/docs/management-api/catalog). | | `rpm_limit` | integer \| null | Requests per minute for the project; `null` = no project limit. | | `spend_limit_usd` | number \| null | Spend cap for the project in USD; `null` = no project cap. Cannot exceed the organization's `spend_ceiling`. | | `ip_allowlist` | string[] | CIDR ranges keys may be used from. Empty = any address. | ```json { "id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "payments", "slug": "payments", "description": "Payments service inference", "status": "active", "is_default": false, "created_at": "2026-09-20T08:14:03Z", "updated_at": "2026-09-22T11:30:55Z", "monthly_spend_usd": 412.37, "guardrails": { "model_allowlist": ["openai/gpt-4o", "openai/gpt-4o-mini"], "rpm_limit": 1200, "spend_limit_usd": 2000, "ip_allowlist": ["203.0.113.0/24"] } } ``` ## List projects ```http GET /api/v1/mgmt/projects ``` Returns the projects the token owner can see, newest first: every project for an Admin, the projects they belong to for a Member. [Paginated](/docs/management-api/pagination) with `limit` and `cursor`. | Query parameter | Notes | |---|---| | `status` | `active` or `disabled`. Any other value → `422 validation_failed`. | ```json { "data": [ { "id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "payments", "slug": "payments", "description": "Payments service inference", "status": "active", "is_default": false, "created_at": "2026-09-20T08:14:03Z", "updated_at": "2026-09-22T11:30:55Z", "monthly_spend_usd": 412.37, "active_key_count": 2 } ], "has_more": false, "next_cursor": null } ``` ## Create a project ```http POST /api/v1/mgmt/projects ``` | Body field | Type | Required | Notes | |---|---|---|---| | `name` | string | **yes** | Display name. | | `slug` | string | no | Derived from `name` when omitted. | | `description` | string | no | | | `region_id` | string | no | An inactive or unknown region → `400`, `param: "region_id"`. | | `guardrails` | object | no | Any of `model_allowlist`, `rpm_limit`, `spend_limit_usd`. **`ip_allowlist` cannot be set at creation** — set it with [`PATCH`](#update-a-project) afterwards. | Requires `Idempotency-Key`. Returns `201` with the project object, including its guardrails. ## Retrieve a project ```http GET /api/v1/mgmt/projects/{id} ``` Returns the project with its guardrails. A project in another organization — or, for a Member, a project they do not belong to — returns `404`, exactly like one that does not exist. ## Update a project ```http PATCH /api/v1/mgmt/projects/{id} ``` Changes the name, description and guardrails. Send at least one field. Only the fields you send are changed; inside `guardrails`, each field you send **replaces** the current value (send the full allowlist, not a delta) and `null` clears it. | Body field | Type | Notes | |---|---|---| | `name` | string | Cannot be empty. The default project cannot be renamed (`409 default_project_protected`). | | `description` | string \| null | | | `guardrails.model_allowlist` | string[] \| null | `[]` or `null` clears the allowlist. | | `guardrails.rpm_limit` | integer \| null | Not negative. `null` removes the project limit. | | `guardrails.spend_limit_usd` | number \| null | Not negative. `null` removes the project cap. Must not exceed the org `spend_ceiling`. | | `guardrails.ip_allowlist` | string[] \| null | CIDR notation. `[]` or `null` clears it. | State is not part of `PATCH`: use the `disable` / `enable` verbs. One request, one intent. Returns `200` with the project object. Guardrail changes take effect within [60 seconds](/docs/management-api/scopes#the-60-second-propagation-window). ## Disable a project ```http POST /api/v1/mgmt/projects/{id}/disable ``` The **reversible kill switch**. Every key in the project stops working for inference (their calls fail with [`403 project_disabled`](/docs/authentication#key-hierarchy-and-scope)); nothing else is touched, and no key is deleted. This is the fastest way to stop a whole project — see [Incident response](/docs/management-api/incident-response). No body. Returns `200` with the project object (`status: "disabled"`). The default project cannot be disabled (`409 default_project_protected`). ## Enable a project ```http POST /api/v1/mgmt/projects/{id}/enable ``` The inverse of disable. Keys that were active before are active again. No body. Returns `200` with the project object (`status: "active"`). ## Delete a project ```http DELETE /api/v1/mgmt/projects/{id} ``` **Permanent and irreversible**: the project, its settings and its memberships are deleted, and its keys stop working. The one guard is the default project, which cannot be deleted (`409 default_project_protected`); any other project can be deleted whatever its state. No body. Returns `204 No Content`. ## Errors specific to projects | Status | Code | When | |---|---|---| | 400 | `invalid_request` (`param` set) | Missing or empty `name`; unknown field; negative `rpm_limit`/`spend_limit_usd`; a spend limit above the org `spend_ceiling`; an unknown model in `model_allowlist`; a bad CIDR; `ip_allowlist` on create; an invalid `region_id`. | | 403 | `insufficient_scope` | Token lacks `projects:read`/`projects:write`, or (for writes) the owner is not currently an Admin. | | 404 | `not_found` | Project absent, in another organization, or not visible to a Member owner. | | 409 | `default_project_protected` | Renaming, disabling or deleting the default project. | | 422 | `unsupported_filter` · `validation_failed` | An unknown query parameter, or a `status` filter value other than `active`/`disabled`. | --- ## Quickstart **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/quickstart **Description:** Create a personal access token and make your first Management API call. This walkthrough takes you from nothing to a rotated project API key in four calls: read your organization, create a project, create a key, and rotate it. Every write carries an `Idempotency-Key`, as the API requires. ## Before you start - You are an **Admin** of your Token Factory organization. - You have `curl` or Python 3 with `requests` (`pip install requests`). The Management API is plain HTTPS and JSON, so any HTTP client works. ## 1. Mint a token in the portal Open **Settings → API Tokens** and click **Create token** to open the create-token dialog. Enter a note such as `quickstart`, keep the 90-day expiration, and under **Select scopes** tick `org:read`, the `projects` parent row (grants `projects:read` and `projects:write` — sensitive, the dialog shows a warning), the `keys` parent row (grants `keys:read` and `keys:write`), and `catalog:read`. Click **Generate token**, re-authenticate if the portal asks, and copy the secret from the "Token created" step — it is shown once. Full walkthrough: [Personal access tokens](/docs/management-api/personal-access-tokens#create-a-token-in-the-portal). ```bash export TF_PAT="tf-pat_…" export TF_MGMT="https://portal./api/v1/mgmt" ``` The cURL steps below use [`jq`](https://jqlang.org/) to carry ids from one step to the next. ## 2. Read your organization The first call proves the token works. It needs `org:read`. ```http HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Request-Id: req_6f1c2d9e8a4b4c3d9e2f1a2b3c4d5e6f X-RateLimit-Limit: 600 X-RateLimit-Remaining: 99 X-RateLimit-Reset: 1790236810 ``` ```json { "id": "3f9a7c21-5d4e-4b8a-9c0f-1e2d3a4b5c6d", "name": "Acme Corp", "slug": "acme", "is_active": true, "billing_mode": "prepaid", "region": "default", "created_at": "2026-03-02T09:41:17Z" } ``` Every response carries `X-Request-Id`; once the token is verified, it also carries the `X-RateLimit-*` headers ([Rate limits](/docs/management-api/rate-limits)). A `401` here means the token is wrong, expired or revoked — the `error.code` says which ([Authentication](/docs/management-api/authentication)). ## 3. Create a project Every `POST`, `PATCH`, `PUT` and `DELETE` needs an `Idempotency-Key`. ```json { "id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "quickstart", "slug": "quickstart", "description": "Created from the Management API quickstart", "status": "active", "is_default": false, "created_at": "2026-09-24T10:02:11Z", "updated_at": "2026-09-24T10:02:11Z", "monthly_spend_usd": 0, "guardrails": { "model_allowlist": [], "rpm_limit": null, "spend_limit_usd": null, "ip_allowlist": [] } } ``` Run the same command twice with the same `Idempotency-Key`: the second response is the stored `201` with the same id, carries `Idempotent-Replayed: true`, and no second project is created. That is [idempotency](/docs/management-api/idempotency). ## 4. Create an API key Create a project API key in the project. **The response is the only time the secret is returned** — store it now. ```json { "id": "c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74", "project_id": "5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08", "name": "quickstart-app", "description": null, "key_prefix": "tf-4b8a9c-7d21e0-Qm8...k2Zw", "status": "active", "created_at": "2026-09-24T10:02:40Z", "last_used_at": null, "expires_at": null, "revoked_at": null, "requests_7d": 0, "allowed_models": [], "rpm_limit": null, "spend_cap": null, "ip_allowlist": [], "rotation": null, "created_by": { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example" }, "secret": "tf-4b8a9c-7d21e0-Qm8xR2v…(full value, shown once)" } ``` The key works on the [Inference API](/docs/quickstart) at `https://api./v1` straight away — it cannot call the Management API, and your personal access token cannot call the Inference API. ## 5. Rotate the key Rotation issues a new secret while the old one keeps working for a grace window you choose (1–168 hours, default 24), so you can deploy without downtime. Deploy the new secret before `rotation.grace_expires_at`. If you change your mind, [`POST /keys/{id}/rotate/cancel`](/docs/management-api/keys#cancel-a-rotation) discards the new secret and keeps the original. ## Clean up Delete the project — this is permanent, and its keys stop working: Then revoke the `quickstart` token in **Settings → API Tokens**, or keep it for the next pages. ## Next steps To explore every other endpoint, load the [OpenAPI specification](/docs/management-api/overview#openapi-specification) into Swagger Editor, Postman or Insomnia. | Goal | Page | |---|---| | Understand what a token can reach | [Scopes](/docs/management-api/scopes) | | Automate key rotation in CI | [CI pipeline](/docs/management-api/ci-pipeline) | | Contain a leaked key | [Incident response](/docs/management-api/incident-response) | | Pull spend into FinOps tooling | [Usage](/docs/management-api/usage) | --- ## Rate limits **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/rate-limits **Description:** Request limits for the Management API and how to handle 429 responses. The Management API is not billable, but it is limited — **per token**, separately from inference traffic. A busy CI pipeline cannot throttle your production inference, and inference load cannot starve your automation. ## Default limits Each token has a **token bucket**: it holds up to 100 requests (the burst) and refills continuously at 600 requests per minute (10 per second). Every request spends one. A full bucket lets you send 100 requests at once; after that you can sustain 600 per minute. | Limit | Default | |---|---| | Sustained rate (refill) | 600 requests per minute, per token | | Burst (bucket size) | 100 requests | ## Response headers Every **authenticated** Management API response carries the bucket state — success, `4xx` from the API and `429` alike. A request rejected before the token is verified (`401`, or a `500` while verifying it) is not counted and carries none of these headers. Do not treat a missing `X-RateLimit-*` header as an error; read them when present. | Header | Meaning | |---|---| | `X-RateLimit-Limit` | The sustained rate for this token, in requests per minute (the refill rate, e.g. `600`). It is **not** the number of requests you can send right now. | | `X-RateLimit-Remaining` | Whole requests left in the bucket after this one. Never more than the burst (`100`), however high `X-RateLimit-Limit` is. | | `X-RateLimit-Reset` | When the bucket will be **full** again (back to the burst) if you send nothing more, as a Unix timestamp in seconds. It is not a window boundary: requests become available again continuously before then. | ## When you hit the limit The request is rejected with `429` and a `Retry-After` header (seconds until the bucket holds one request again). No change was made. ``` HTTP/1.1 429 Too Many Requests Retry-After: 1 X-RateLimit-Limit: 600 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1790236810 X-Request-Id: req_6f1c2d9e8a4b4c3d9e2f1a2b3c4d5e6f ``` ```json { "error": { "message": "rate limit exceeded for this token; retry after 1 s", "type": "rate_limit_error", "code": "rate_limit_exceeded", "param": null, "action": "Wait Retry-After seconds, then retry.", "request_id": "req_6f1c2d9e8a4b4c3d9e2f1a2b3c4d5e6f" } } ``` Wait `Retry-After` seconds, then retry. For a write, retry with the **same** `Idempotency-Key` — see [Idempotency](/docs/management-api/idempotency). Inference rate limits are separate and documented in [Rate limits](/docs/rate-limits). --- ## Scopes **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/scopes **Description:** The permission scopes a personal access token can carry and which operations each scope allows. Scopes are the **token half** of every authorization decision; the creator's live rights are the other half ([the intersection rule](#the-intersection-rule)). They are grouped by the object they touch — the same objects the portal's screens are built over. Scopes are enforced by the Management API, the only place a personal access token works: tokens are rejected on the portal's own API and on the Inference API. ## The scope vocabulary | Scope | Class | Reaches | Endpoints | |---|---|---|---| | `org:read` | standard | Read the organization record and its settings: defaults, budget, MFA, spend ceiling | `GET /org` | | `org:write` | **sensitive** | Update organization settings: name, slug, model and rate defaults, budget, email OTP, spend ceiling | `PATCH /org` † | | `members:read` | standard | List members with their roles and states, and pending invites | `GET /members`, `GET /members/{id}` †, `GET /invites` † | | `members:write` | **sensitive** | Invite members, change roles and project access, disable/enable, remove members, sign them out, revoke invites | `POST /invites`, `PATCH`/`DELETE /members/{id}`, `PUT /members/{id}/projects`, `POST /members/{id}/disable`, `POST /members/{id}/enable`, `DELETE /members/{id}/sessions`, `DELETE /invites/{id}` — all † | | `projects:read` | standard | List projects and read their effective guardrails | `GET /projects`, `GET /projects/{id}` | | `projects:write` | **sensitive** | Create, rename, disable/enable, delete projects; set allowlist, spend cap, IP allowlist | `POST /projects`, `PATCH`/`DELETE /projects/{id}`, `POST /projects/{id}/disable`, `POST /projects/{id}/enable` — all † | | `keys:read` | standard | List a project's API keys (masked) with their status and creator | `GET /projects/{id}/keys`, `GET /keys/{id}` | | `keys:write` | standard | Create, rename, rotate, disable/enable, revoke API keys; set expiry, IP allowlist (spend limits are set on the project) | `POST /projects/{id}/keys`, `PATCH`/`DELETE /keys/{id}`, `POST /keys/{id}/rotate`, `POST /keys/{id}/rotate/cancel`, `POST /keys/{id}/disable`, `POST /keys/{id}/enable` | | `usage:read` | standard | Read metered usage and cost for a time window and export per-request usage records | `GET /usage` †, `GET /usage/export` † | | `billing:read` | standard | Read the prepaid credit balance, credit grants and top-ups, and invoices | `GET /billing/balance`, `GET /billing/grants`, `GET /billing/invoices`, `GET /billing/invoices/{id}` | | `audit:read` | standard | List and export the organization audit log | `GET /audit` †, `GET /audit/export` † | | `catalog:read` | standard | List the models the organization can call and the regions they are served in | `GET /catalog/models`, `GET /catalog/regions` | **Parent scopes in the portal.** The Create token dialog groups the tree under four parent rows — `org`, `members`, `projects`, `keys` — one for each group that has both a read and a write scope. Ticking a parent is a UI convenience that ticks both child scopes; it does not mint an extra scope. The token itself still carries only the scopes above, exactly as ticked. In particular, write does not imply read on the wire (see below), so a token minted with only `projects:write` (tick that child row on its own, without the parent) cannot list projects — `GET /projects` fails with `insufficient_scope` naming `projects:read`. † **Admin-only**: the token owner must currently be an organization Admin, whatever the token's scopes — the same rule the portal applies to those screens. The other routes are open to any member, within the portal's member rules (a Member sees only their own projects and the keys they created). [`/me/tokens`](/docs/management-api/tokens) needs **no scope**: any valid token may list and revoke its owner's own tokens. ## The sensitive class Three scopes — `org:write`, `members:write` and `projects:write` — govern the changes the portal protects with step-up re-authentication: security and organization settings, who is in the org and with which role, deletions, and the guardrails that move money. A token cannot answer an interactive challenge, so the equivalent controls are structural instead: - Sensitive scopes are **off by default** at mint and carry a plain-language warning when selected. - **Minting** any token requires re-authentication in the portal. - Every write made under a token is **audited with the scope named**. A token is never sent automatically — every call presents it explicitly — and it belongs to one person who can be offboarded in one action. ## Two rules for any scope set 1. **A token can only do what its creator can do.** Scopes never add rights the creator lacks (see below). 2. **Read and write are independent.** `keys:write` does not imply `keys:read`. A monitoring integration should hold only the `:read` scopes it reads; a provisioning pipeline should hold only the `:write` scopes it changes plus the reads it checks. There is **no `*` wildcard**. Naming every scope costs one more click at mint time and saves an incident later. A token must carry at least one scope, and the portal refuses to mint one with an unknown scope. ## The intersection rule > **Effective permission = token scopes ∩ creator's live rights.** > The creator's live rights are their role in this organization (Admin or > Member) intersected with their project access, for any call that touches a > project. Evaluated on every request. Three consequences: - **Demotion narrows tokens — and the API says so.** An Admin demoted to Member keeps their tokens, but those tokens' reach collapses to a Member's rights on the next evaluation. - **Promotion never adds scopes.** Scopes are fixed at mint. A promoted owner's token can use the scopes it already carries on Admin-only routes again — nothing it was not minted with. - **Removal kills tokens, not keys.** Disabling or removing a member ends every personal access token they created. The project API keys they created keep working, unless the member is removed with `"delete_keys": true`. ### When a call is refused Both halves fail the same way — `403 permission_error` with `code: insufficient_scope` — and the message names **which half** is missing, so the pipeline log reads like a sentence rather than a bare 403. **The token lacks the scope.** `param` names the scope the call needs: ```json { "error": { "message": "this token does not carry the projects:write scope", "type": "permission_error", "code": "insufficient_scope", "param": "projects:write", "action": "Mint a token that includes projects:write in Settings → API Tokens.", "request_id": "req_cd71a3d856604660b8f12bbebb5bd8dc" } } ``` **The creator lacks the right.** Example: Dana minted a token with `projects:write` while she was an Admin. She has since been changed to Member. The token still carries the scope, but `POST /projects` now fails because Dana can no longer create projects: ```json { "error": { "message": "your role in this organization is member; this call needs admin", "type": "permission_error", "code": "insufficient_scope", "param": null, "action": "Ask an organization admin to perform this call, or to change your role.", "request_id": "req_607c5f49f2ea43778fa6dde880ec4bca" } } ``` The project-access variant reads `your project access does not include this project`; a Member acting on a key someone else created gets `members can only manage keys they created`. ## The 60-second propagation window Authorization-relevant changes take effect within **60 seconds**. | Change | Takes effect | |---|---| | Token minted, revoked or expired | Immediately — the credential is checked on every request | | Member role change, disable, removal (the intersection rule) | Within 60 seconds | | Project access change for a member | Within 60 seconds | | Project guardrail change (model allowlist, spend limit, IP allowlist) | Within 60 seconds | | Project or key state (disable, enable, delete) on the data plane | Within 60 seconds | In practice: a just-minted token works at once; a just-demoted Admin's token may keep its previous reach for up to a minute. --- ## Tokens **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/tokens **Description:** List and revoke your own personal access tokens from the API. `/me/tokens` is the one place a token touches credentials — and only its owner's own. It needs **no scope**: any valid token may call it. | Endpoint | Scope | |---|---| | [`GET /me/tokens`](#list-your-tokens) | none | | [`DELETE /me/tokens/{id}`](#revoke-one-of-your-tokens) | none | It is **self-only**: you see and revoke the tokens you minted in this organization, never anyone else's. Tokens are created only in the portal, with re-authentication, so a leaked token can never renew or widen itself. See [Personal access tokens](/docs/management-api/personal-access-tokens). ## The token object | Field | Type | Notes | |---|---|---| | `id` | string | Opaque id. | | `name` | string | | | `prefix` | string | Masked form, e.g. `tf-pat_AbCdE...a91f`. The secret is never returned. | | `scopes` | string[] | Fixed at mint. | | `status` | string | `active` or `revoked`. An expired token stays `active` here — compare `expires_at` with the current time. | | `created_at` | string | | | `expires_at` | string \| null | When the token expires. Tokens always expire. | | `last_used_at` | string \| null | | | `created_by` | object | `{ "id", "email" }` — you. | ## List your tokens ```http GET /api/v1/mgmt/me/tokens ``` [Paginated](/docs/management-api/pagination), newest first. | Query parameter | Notes | |---|---| | `status` | `active` or `revoked`. Any other value → `422 validation_failed`. | ```json { "data": [ { "id": "d9e8f7a6-b5c4-4d3e-9f2a-1b0c9d8e7f6a", "name": "CI · payments-deploy", "prefix": "tf-pat_AbCdE...a91f", "scopes": ["projects:read", "keys:read", "keys:write", "catalog:read"], "status": "active", "created_at": "2026-03-20T09:00:00Z", "expires_at": "2027-03-20T09:00:00Z", "last_used_at": "2026-09-24T10:05:31Z", "created_by": { "id": "8e7d6c5b-4a39-4281-9f0e-1d2c3b4a5968", "email": "dana@acme.example" } } ], "has_more": false, "next_cursor": null } ``` ## Revoke one of your tokens ```http DELETE /api/v1/mgmt/me/tokens/{id} ``` Revocation is immediate and permanent — the next call with that token fails with `401 token_revoked`. You may revoke the token you are calling with, and revoking an already-revoked token succeeds again. Returns `204 No Content`. A token id that is not yours returns `404`. Like every non-`GET` Management API call, this route requires `Idempotency-Key`: a missing header is `400 idempotency_key_required`, and reusing a key for a different request is `409 idempotency_conflict` — see [Idempotency](/docs/management-api/idempotency). The revocation is recorded in the audit log as `pat_revoke`. --- ## Usage **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/usage **Description:** Query token usage and spend across your organization and projects. Read-only, and the most-used group in practice: what was consumed, when, and by what. Usage is a view of the same usage records the portal's Analytics and Logs pages and your invoice use. The API computes nothing of its own, so a late correction appears everywhere at the same time. | Endpoint | Scope | Token owner's role | |---|---|---| | [`GET /usage`](#query-usage) | `usage:read` | **Admin** | | [`GET /usage/export`](#export-usage) | `usage:read` | **Admin** | Both routes need an owner who is currently an organization **Admin** — the same rule as the portal's Analytics page. A Member's token gets `403 insufficient_scope`. Two rules make this data safe to build on: - **Breakdowns sum to totals — usually.** For every field of `totals` except `units`, the sum over `breakdown[]` equals the total exactly when `reconciled` is `true` (the normal case). `totals` are never raised to match the breakdown; usage that cannot be attributed to a group, plus the tail beyond the top 20 groups, is reported in an `other` row rather than dropped. In rare cases the breakdown can briefly exceed the totals; the response then has `reconciled: false` and a `breakdown_note`. Retry for a consistent report — see [below](#the-usage-report). - **No content, ever.** Usage reports counts, money and identifiers only. Prompt and completion content, error text and client addresses are never returned by this API. Money is reported twice: `cost_nanousd` is the exact integer amount in nano-USD (10⁻⁹ USD), and `cost_usd` is the same amount as a decimal string (`"12.345678901"`). Sum `cost_nanousd` or parse `cost_usd` as a decimal — never as a float. ## Query usage ```http GET /api/v1/mgmt/usage ``` Aggregated usage and cost over `[from, to)`, broken down by **one** dimension. | Query parameter | Type | Required | Notes | |---|---|---|---| | `from` | string | no | RFC 3339, inclusive. Default: `to` minus 30 days. | | `to` | string | no | RFC 3339, exclusive. Default: now. The range may span at most **366 days**. | | `group_by` | string | no | One of `project`, `model`, `key`, `day`. Default `model`. One dimension per call. | | `project_id` | string | no | Only usage of this project. | | `key_id` | string | no | Only usage of this API key. | | `model` | string | no | Only usage of this model (`provider/model`). | `GET /usage` is a report, not a list: it is not paginated, and `limit` or `cursor` return `422 unsupported_filter`, as does any other unknown parameter. | `group_by` | `breakdown[].key` | Notes | |---|---|---| | `model` | The model (`provider/model`) | | | `project` | The project id | | | `key` | The API key id | | | `day` | The UTC date (`2026-09-23`) | One exact row per UTC calendar day; the first and last may be partial days. At most **31 days** per call. | For `project`, `model` and `key`, the top 20 groups are returned individually; the rest, plus any usage that could not be attributed (for example requests with no key), are folded into a single row with `key: "other"`. ### The usage report | Field | Type | Notes | |---|---|---| | `from`, `to` | string | The window actually read. | | `group_by` | string | The dimension of `breakdown`. | | `currency` | string | Always `USD`. | | `totals` | object | The usage quantities for the whole window (below), plus `units`. | | `breakdown` | object[] | One row per group: `key` plus the usage quantities. Sums to `totals` exactly, field by field, when `reconciled` is `true`. | | `reconciled` | boolean | `true` when `breakdown` sums to `totals` exactly (always the case for `group_by=day`). `false` in the rare case the breakdown briefly exceeds `totals`; `breakdown_note` then explains. Retry for a consistent report. | | `breakdown_note` | string | Present only when `reconciled` is `false`. | | `usage_stale` | boolean | `true` when usage counts may be incomplete for the most recent minutes. Money is always exact. | | `usage_as_of` | string \| null | Usage counts are complete up to this instant (`null` when unknown). | Every total and every breakdown row carries the same usage quantities: | Field | Notes | |---|---| | `requests` | Metered requests. | | `errors` | Requests that ended in an error status. | | `input_tokens`, `output_tokens`, `total_tokens` | Token counts. | | `cached_input_tokens` | Prompt-cache read tokens (a subset of `input_tokens`). | | `cache_write_tokens` | Prompt-cache write tokens (a subset of `input_tokens`). | | `reasoning_tokens` | Reasoning tokens (a subset of `output_tokens`). | | `cost_nanousd` | Billed cost, integer nano-USD. | | `cost_usd` | The same cost as a decimal string. | `totals.units[]` gives the native-unit quantity per billing unit (`token`, `image`, `second`, `character`, `document`, …) — `unit`, `requests`, `quantity_in`, `quantity_out`, `cost_nanousd`, `cost_usd`. Quantities of different units are never summed together. ```json { "from": "2026-09-01T00:00:00Z", "to": "2026-09-08T00:00:00Z", "group_by": "model", "currency": "USD", "totals": { "requests": 105, "errors": 2, "input_tokens": 1500, "output_tokens": 700, "total_tokens": 2200, "cached_input_tokens": 40, "cache_write_tokens": 0, "reasoning_tokens": 0, "cost_nanousd": 3123456789, "cost_usd": "3.123456789", "units": [ { "unit": "token", "requests": 105, "quantity_in": 1500, "quantity_out": 700, "cost_nanousd": 3123456789, "cost_usd": "3.123456789" } ] }, "breakdown": [ { "key": "openai/gpt-4o", "requests": 60, "errors": 0, "input_tokens": 1000, "output_tokens": 500, "total_tokens": 1500, "cached_input_tokens": 0, "cache_write_tokens": 0, "reasoning_tokens": 0, "cost_nanousd": 2000000000, "cost_usd": "2.00" }, { "key": "other", "requests": 45, "errors": 2, "input_tokens": 500, "output_tokens": 200, "total_tokens": 700, "cached_input_tokens": 40, "cache_write_tokens": 0, "reasoning_tokens": 0, "cost_nanousd": 1123456789, "cost_usd": "1.123456789" } ], "reconciled": true, "usage_stale": false, "usage_as_of": "2026-09-24T09:59:15Z" } ``` Need two dimensions — per project **per day**, say? Make one `group_by=day` call per project (`project_id=…`), or one `group_by=project` call per day. Several values in one call (`group_by=project,day`) are refused with `422 unsupported_filter`. ## Export usage ```http GET /api/v1/mgmt/usage/export ``` The backfill path: one record per **metered request** in `[from, to)`, newest first, streamed as JSON Lines or CSV. | Query parameter | Type | Required | Notes | |---|---|---|---| | `format` | string | no | `jsonl` (default; `application/x-ndjson`, one record per line) or `csv` (with a header row). | | `from`, `to` | string | no | As for `GET /usage`: RFC 3339, default the last 30 days, at most 366 days. | | `project_id`, `key_id`, `model` | string | no | As for `GET /usage`. | There is no `group_by`, and the export is not paged: `limit`, `cursor` and any other parameter return `422 unsupported_filter`. **At most 10,000 records per call.** A window that matches more is refused with `422 export_too_large` before anything streams — narrow `from`/`to` and export in windows, or read the aggregates with `GET /usage`. Requests running longer than 60 seconds are cut off. Each record: | Field | Notes | |---|---| | `id` | The usage record id. | | `created_at` | When the request was metered. | | `project_id`, `key_id` | | | `model`, `provider` | | | `status` | The request's outcome, e.g. `ok` or `error`. | | `unit` | The billing unit (`token`, `image`, …). | | `input_tokens`, `output_tokens`, `total_tokens`, `cached_input_tokens`, `cache_write_tokens`, `reasoning_tokens` | Token counts. | | `quantity_in`, `quantity_out` | Native quantity for non-token units (images, seconds, …). | | `cost_nanousd`, `cost_usd` | The request's cost. | | `cost_available` | `false` when this record's cost could not be read — the cost fields are then `0`, not a real zero. | | `latency_ms` | | | `streaming` | Whether the request streamed. | ### Truncation An export can stop early after the `200` status has been sent. A truncated export is therefore not an error response; it is a stream that ends with a marker line: - **JSONL:** a last line `{"truncated":true,"rows":N,"reason":"row_cap"|"upstream_error"}`. - **CSV:** a last line `# truncated after N rows ()`. A response that was cut short also carries an `X-Export-Truncated: true` HTTP **trailer** (declared in the `Trailer` header) — but trailers are sent after the body and some proxies silently drop them, so treat the trailer as a convenience, not the source of truth. **The final line of the stream is the reliable signal**: always check for the truncation marker before treating an export as complete. `reason: row_cap` means the window matched more than 10,000 records after all — narrow `from`/`to` and export the rest separately. `reason: upstream_error` means reading the usage data failed mid-stream — re-run the same export. The export reads records newest first, in pages of 200, with a cursor on (`created_at`, `id`). Records that share the same `created_at` are all exported, each exactly once, however many there are. Every completed export is recorded in the [audit log](/docs/management-api/audit) as `usage_export`, with `row_count`, `format`, `from` and `to` — bulk data movement deserves the same trail as any other. ```json {"id":"7c2e4f1a-9b3d-4e8a-a1c6-5d0f2b8e3a97","created_at":"2026-09-23T23:59:41.203Z","project_id":"5b3e9a41-0c7d-4e2f-8a16-9d4b7c2e1f08","key_id":"c41d8e2a-7b90-4f3c-a6e5-2d1f0b9c8a74","model":"openai/gpt-4o","provider":"openai","status":"ok","unit":"token","input_tokens":812,"output_tokens":164,"total_tokens":976,"cached_input_tokens":0,"cache_write_tokens":0,"reasoning_tokens":0,"quantity_in":0,"quantity_out":0,"cost_nanousd":3670000,"cost_usd":"0.00367","cost_available":true,"latency_ms":1840,"streaming":true} ``` ## Errors specific to usage | Status | Code | When | |---|---|---| | 400 | `invalid_request` (`param` set) | `from`/`to` not RFC 3339; `from` not before `to`; a range over 366 days; `group_by` not one of the four values; `project_id`/`key_id` not an id; `format` not `jsonl`/`csv`. | | 403 | `insufficient_scope` | Token lacks `usage:read`, or the owner is not currently an Admin. | | 422 | `unsupported_filter` | An unknown parameter, `limit`/`cursor`, or several `group_by` values. | | 422 | `validation_failed` | `group_by=day` over a range longer than 31 days. | | 422 | `export_too_large` | The export window matches more than 10,000 requests. | | 502 | `upstream_unavailable` | Usage data is temporarily unavailable. Retry. | To reconcile usage with balances and invoices, see [Billing](/docs/management-api/billing#reconcile-usage-with-billing). --- ## Versioning **URL:** https://www.tokenfactory.iamsaif.ai/docs/management-api/versioning **Description:** How the Management API is versioned, what can change within v1, and how deprecations are announced. ## The path is the version ``` https://portal./api/v1/mgmt ``` The version is the path segment `v1`. There is no version header to send. It follows the same stance as the [Inference API](/docs/versioning): `v1` until a breaking change forces `v2`. ## What can change within v1 Only **additive** changes: - new endpoints; - new optional request parameters; - new fields in response objects; - new error codes and new values of `action`; - new values in enumerated fields. Your client must tolerate these: ignore unknown fields, and fall back on the HTTP status and `error.type` for an error code it does not recognise. These are **breaking** and only ever happen in a new version: removing or renaming a field or endpoint, making an optional parameter required, changing a default, or changing what an existing error code means. Error codes are never reused. ## Security exceptions A change that closes a security or privacy gap can narrow **who** may call an endpoint within v1. It never renames or removes a field for a caller who keeps access. Each one is listed here. | Date | Change | Why | |---|---|---| | 2026-09-30 (0.16.0) | `GET /billing/balance`, `/billing/grants`, `/billing/invoices` and `/billing/invoices/{id}` need an **Admin** token owner; a member gets `403 insufficient_scope`. | Organization billing is admin-only. | | 2026-09-30 (0.16.0) | `GET /org` returns only the [organization identity](/docs/management-api/org#the-organization-identity-members) to a member. An Admin still gets the full record. `GET /org/settings` (Admin) is new. | Members must not read the organization's security settings. | ## Deprecation policy When a successor version ships: - it is announced with **at least 90 days' notice** before the old version is retired; - both versions run **in parallel for at least six months**; - the old version keeps its behaviour unchanged for the whole period. An API version is structure, not price: code has to be changed, tested and released on your schedule, not ours — hence the long window. ## Request ids Every response carries `X-Request-Id`. It is the correlation key for support across both surfaces. --- ## Data Processing Agreement **URL:** https://www.tokenfactory.iamsaif.ai/docs/legal/data-processing-agreement **Description:** How Infinia Technologies processes personal data on your behalf, its sub-processors, and the standard contractual clauses. > **Draft — pending legal approval.** > This placeholder is provided for product integration and review only. It contains unresolved bracketed fields and is not a final or legally approved version. Do not rely on it as legal advice or as the final terms governing the service. These annexes form part of the [Xailon Token Factory Terms of Use](/docs/legal/terms-of-use). Capitalised terms have the meanings given in the Terms. ## Annex B — Data Processing Addendum **B.1** Scope and roles **B.1.1** This Data Processing Addendum ("DPA") forms part of the Agreement and applies to the extent Infinia processes Personal Data on behalf of the Customer in providing the Service. In respect of Personal Data contained in Customer Content, the Customer is the controller (or a processor acting for its own controller) and Infinia is the processor. In respect of account, billing, identity-verification and Usage Data that Infinia processes for its own purposes, including to administer the relationship, meter and bill, detect abuse, comply with law and improve the Service, Infinia is an independent controller and the Privacy Notice applies. **B.1.2** Definitions. "Personal Data", "controller", "processor", "data subject", "processing", "special categories" and "supervisory authority" have the meanings given in the applicable Data Protection Law and, where none is given, in the GDPR. "Security Incident" means a breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorised disclosure of, or access to, Personal Data processed by Infinia on the Customer's behalf. "Sub-processor" means a third party engaged by Infinia to process Customer Personal Data. **B.1.3** Details of processing. The subject matter of processing is the provision of the Service. The duration is the Term plus the deletion period in B.9. The nature of processing is transient computation of Inputs to generate Outputs, and storage where the Customer uses a storage feature. The purpose is to provide the Service in accordance with the Customer's instructions. The types of Personal Data and categories of data subjects are determined by the Customer and may include any type or category the Customer chooses to submit, including special categories, subject to Section 8.5 of the Terms. **B.2** Customer's instructions and obligations **B.2.1** Infinia will process Customer Personal Data only on the Customer's documented instructions, which are: the Agreement; the Customer's configuration of the Service, including Region Scope, Service Mode, Model selection and storage settings; each API request; and any further written instruction agreed between the parties. Infinia will inform the Customer if, in its opinion, an instruction infringes Data Protection Law, without obligation to carry out a legal review. **B.2.2** The Customer is responsible for the lawfulness of the Personal Data it submits, for having a legal basis and any consents required, for providing notices to data subjects, for conducting any data-protection impact assessment its use requires, for selecting a Sovereign Zone and configuration appropriate to the data, and for responding to data subjects. **B.3** Confidentiality of personnel **B.3.1** Infinia will ensure that persons authorised to process Customer Personal Data are bound by written confidentiality obligations or an appropriate statutory duty, receive appropriate training, and have access only on a need-to-know basis in accordance with Section 6.7 of the Terms. **B.4** Security **B.4.1** Infinia will implement and maintain appropriate technical and organisational measures to protect Customer Personal Data against Security Incidents, taking into account the state of the art, the cost of implementation, and the nature, scope, context and purposes of processing, including the measures described in B.12. Infinia may update those measures provided the overall level of protection is not materially reduced. **B.4.2** Zero retention as a security measure. Because Inputs and Outputs are not retained and are not inspected (Section 6.3 of the Terms), the only Personal Data that Infinia holds at rest on the Customer's behalf is that contained in Stored Objects. The Customer acknowledges that the zero-retention design also means Infinia cannot retrieve, correct, produce or review an Input or Output after a request has completed, and cannot assist with a data-subject request that relates to one. The two integrity controls in Section 6.10 of the Terms are applied to Stored Objects as a security and child-protection measure; neither reads the substantive content of a Stored Object, and neither is applied to Inputs or Outputs. **B.5** Sub-processors **B.5.1** The Customer gives general authorisation for Infinia to engage Sub-processors, including Zone Operators, compute partners and the providers of payment, identity-verification, notification and support tooling. The Sub-processors engaged as at the effective date of these Terms are listed in Annex D, which states for each its role, the Personal Data it processes, its place of processing and the Sovereign Zones for which it is used. The current list is also published at [Sub-processor URL], and the Customer may subscribe there to notifications of changes. **B.5.2** Infinia will give the Customer not less than thirty days' notice by email or portal notification before authorising a new Sub-processor to process Customer Personal Data. The Customer may object on reasonable data-protection grounds within that period. If the parties cannot resolve the objection within thirty days, the Customer may terminate the affected part of the Service on notice and receive a refund of prepaid Fees for the unperformed period as its sole remedy. **B.5.3** Infinia will impose on each Sub-processor, by written contract, data-protection obligations no less protective than this DPA, and remains liable to the Customer for the Sub-processor's performance. No Sub-processor is permitted to process Inputs or Outputs outside the Region Scope, or to use Customer Content for training or any purpose other than providing the Service. **B.6** No international transfers outside the Region Scope **B.6.1** Infinia will not transfer, and will not permit any Sub-processor to transfer, Customer Personal Data contained in Customer Content outside the Region Scope. Where the Customer's Region Scope comprises more than one Sovereign Zone in different jurisdictions, the Customer's selection constitutes its instruction that Personal Data may be processed in any of them, and the Customer is responsible for any transfer mechanism its own law requires between them. **B.6.2** Account, billing and support Personal Data for which Infinia is a controller may be processed in the locations stated in the Privacy Notice, subject to appropriate safeguards under Data Protection Law, including standard contractual clauses or adequacy decisions where required. **B.7** Assistance **B.7.1** Taking into account the nature of processing and the information available to it, Infinia will assist the Customer by appropriate technical and organisational measures in responding to requests from data subjects, and in meeting the Customer's obligations regarding security, Security Incident notification, impact assessments and prior consultation with supervisory authorities. Because Inputs and Outputs are not retained, Infinia's assistance with data-subject requests relating to them will ordinarily be limited to confirming that they are not held. Infinia may charge reasonable fees for assistance that exceeds what the Service and Documentation provide by default. **B.7.2** If Infinia receives a request directly from a data subject relating to Customer Personal Data, it will, to the extent legally permitted, redirect the data subject to the Customer and not respond substantively except on the Customer's instruction or as required by law. **B.8** Security Incident notification **B.8.1** Infinia will notify the Customer without undue delay, and in any event within twenty-four hours, after becoming aware of a Security Incident affecting Customer Personal Data. The notification will describe, to the extent then known, the nature of the incident, the categories and approximate number of data subjects and records concerned, the likely consequences, the measures taken or proposed, and a point of contact, and Infinia will supplement it as further information becomes available. Notification is not an acknowledgement of fault. The Customer is responsible for any notification to supervisory authorities and data subjects that its law requires, and Infinia will provide reasonable cooperation. **B.8.2** Accelerated notification for regulated financial customers. Where the Customer has identified itself under Section 24.4 of the Terms as subject to Regulation (EU) 2022/2554 (DORA) or to an equivalent regime, Infinia will, in addition to Section B.8.1, provide an initial notification of any incident capable of being a major ICT-related incident for the Customer within four hours of becoming aware of it, by the escalation channel recorded against the Customer's Organisation, together with the information then available and a named contact who can be reached continuously until the incident is closed. The initial notification may be provisional and may be corrected. Infinia will thereafter provide intermediate and final reports at the intervals the Customer reasonably requires in order to meet its own regulatory deadlines, and will not treat the fact of an incident as Confidential Information to the extent the Customer must disclose it to its regulator. **B.9** Deletion and return **B.9.1** Inputs and Outputs are deleted on completion of each request under Section 6.3 of the Terms. A Stored Object is deleted when the Customer deletes it or, at the latest, within thirty days after the export period in Section 21.6(e) of the Terms ends, and Customer Models and Fine-Tuned Models are deleted in the same period. Annex C states the retention period for each category of data and the backup cycle within which residual encrypted copies are overwritten. Infinia will certify deletion on written request. Infinia may retain Personal Data to the extent required by Applicable Law, including the preservation required by Section A.3(b) of Annex A, in which case it will protect it in accordance with this DPA and process it only for that purpose. **B.10** Audit and information **B.10.1** Infinia will make available to the Customer the information reasonably necessary to demonstrate compliance with this DPA, including its then-current third-party audit reports and certifications listed in the Trust Centre, responses to reasonable written security questionnaires not more than once per year, and the residency and attestation evidence available through the Service. **B.10.2** Where the information in B.10.1 is insufficient to satisfy a requirement of Data Protection Law or a supervisory authority, the Customer or an independent auditor bound by confidentiality and reasonably acceptable to Infinia may conduct an audit, not more than once in any twelve-month period except following a Security Incident or where required by a supervisory authority, on not less than thirty days' notice, during business hours, in a manner that does not disrupt the Service or compromise the security or confidentiality of other customers, and at the Customer's cost. Infinia may require the audit to be conducted by its own independent auditor where physical access to a Sovereign Zone is requested. The Customer will share the audit findings with Infinia and treat them as Infinia's Confidential Information. **B.11** Jurisdiction-specific terms **B.11.1** GDPR and UK GDPR. Where the GDPR or UK GDPR applies to the Customer's processing, this DPA constitutes the contract required by Article 28(3), the parties agree that Infinia's obligations under B.2 to B.10 satisfy the requirements of Article 28(3)(a) to (h), and Infinia will make available the information necessary to demonstrate compliance under Article 28(3)(h). The Customer acknowledges that Infinia's processing of Inputs and Outputs within an EU Sovereign Zone involves no transfer to a third country, because Section 6.1 of the Terms confines them to the Region Scope and Section 6.2 refuses any request that cannot be served within it. For the account, billing, identity-verification, support and website Personal Data described in Section B.6.2 — which is transferred to the Abu Dhabi Global Market, in respect of which the European Commission has made no adequacy decision — the parties incorporate the Standard Contractual Clauses approved by Commission Implementing Decision (EU) 2021/914, Module Two where the Customer is a controller and Module Three where the Customer is itself a processor, and the UK International Data Transfer Addendum where the UK GDPR applies. The clauses are incorporated with the options selected and the annexes completed as set out in Annex E, and Infinia has performed and will maintain the transfer impact assessment described in Annex E, Part 4, which it will make available to the Customer on request. **B.11.2** ADGM Data Protection Regulations 2021 and UAE Federal Decree-Law No. 45 of 2021. Where those laws apply, this DPA constitutes the written processing contract they require, Infinia will assist the Customer with any registration, notification or breach-reporting obligation under them, and the Customer acknowledges that processing within a UAE Sovereign Zone involves no cross-border transfer for the purposes of those laws. **B.11.3** Indian Digital Personal Data Protection Act 2023. Where that Act applies, the Customer is the Data Fiduciary and Infinia the Data Processor. Infinia will process personal data only under this DPA and the Customer's instructions, will implement reasonable security safeguards, will notify the Customer of personal data breaches under B.8 so that the Customer can notify the Data Protection Board and affected Data Principals, and will erase personal data on completion of the purpose or on the Customer's instruction under B.9. Processing within an Indian Sovereign Zone involves no transfer outside India. **B.11.4** Other laws. Where another Data Protection Law applies, the parties will comply with its requirements for processor contracts to the extent they are not already satisfied by this DPA and will, on either party's reasonable request, document any additional terms required. **B.12** Technical and organisational measures **B.12.1** Infinia maintains, and will maintain during the Term, measures including: (a) residency controls that bind each API Credential to a Region Scope and refuse requests that cannot be served within it; (b) zero retention of Inputs and Outputs, with logging limited to Usage Data; (c) encryption of Customer Content in transit using current industry-standard protocols and of stored objects at rest, with keys managed within the Sovereign Zone; (d) where offered for a Zone, confidential-computing execution with per-request attestation; (e) logical tenant isolation in shared Service Modes and physical or virtualised isolation in Dedicated Model Inference and GPU Clusters; (f) identity and access management with federated authentication, role-based access control and re-authentication for sensitive actions; (g) an immutable audit log of administrative and configuration changes, with signed residency exports available to the Customer; (h) least-privilege, logged and periodically reviewed access for Infinia personnel, restricted to the Zone for which they are authorised; (i) vulnerability management, secure development practices and independent penetration testing at least annually; (j) malware and unsafe-format scanning of Customer Models and uploaded files; (k) business-continuity and disaster-recovery arrangements appropriate to each Service Mode as described in the SLA; (l) security-incident response procedures with defined escalation and notification; (m) background verification of personnel to the extent permitted by local law; and (n) independent certification of its information-security and AI-management systems as listed in the Trust Centre. **B.13** Liability and precedence **B.13.1** Each party's liability under this DPA is subject to Section 20 of the Terms, including Section 20.4. In the event of conflict between this DPA and any other part of the Agreement in respect of the processing of Personal Data, this DPA prevails, and in the event of conflict between this DPA and the Standard Contractual Clauses or other mandatory transfer mechanism, that mechanism prevails to the extent of the conflict. **B.13.2** This DPA survives termination of the Agreement for as long as Infinia processes Customer Personal Data. **B.14** Personal Data for which Infinia is a controller **B.14.1** This DPA governs Infinia's processing of Personal Data on the Customer's behalf. Infinia separately processes Personal Data as a controller in its own right, as described in Section 6.11 of the Terms and in the Privacy Notice. That data comprises account and Organisation records, Authorised User identity and role records, billing and tax records, identity-verification and sanctions-screening records, support correspondence, security and audit logs, Usage Data and website and portal data. It does not include Inputs or Outputs, which Infinia does not retain, and it does not include the content of Stored Objects. **B.14.2** Section B.7.2 requires Infinia to redirect a data subject to the Customer. That requirement applies only to Customer Personal Data. Where an individual asks Infinia to exercise a right in respect of Personal Data for which Infinia is the controller under B.14.1 — for example an Authorised User asking for a copy of their own identity-verification record, or asking Infinia to correct it — Infinia will handle the request itself, will verify the requester's identity before doing so, and will respond within the period Data Protection Law allows. Requests are made to [privacy@●]. The Privacy Notice describes the available rights, the verification step and the limited grounds on which a request may be refused. **B.14.3** Where an automated decision is taken about an individual in the course of that controller-side processing — namely identity and ownership verification under Section 3.7 of the Terms, sanctions and restricted-party screening under Section 23.3, and the credit assessment under Section 10.12 — Infinia will disclose the decision in the Privacy Notice, will not treat the output of the screening system as determinative without human review where the decision would prevent the Customer or an Authorised User from using the Service, and will provide a named route by which a decision may be contested. Nothing in this B.14.3 requires Infinia to disclose information whose disclosure would breach Applicable Law, including sanctions and anti-money-laundering law, or would prejudice the prevention or detection of crime. ## Annex D — Sub-processor List This Annex is the list referred to in Section B.5.1 and forms part of the Agreement. The current version is also published at [Sub-processor URL], where the Customer may subscribe to change notifications. Infinia will give not less than thirty days' notice before authorising a new Sub-processor, and the Customer may object on reasonable data-protection grounds under Section B.5.2. No Sub-processor listed below receives, processes or has access to Inputs or Outputs, because Inputs and Outputs are not retained and do not leave the serving Zone. A Sub-processor that processes Stored Objects does so only within the Region Scope, and is contractually prohibited from using Customer Content for training or for any purpose other than providing the Service (Section B.5.3). ### D.1 Zone Operators and compute partners | Sub-processor | Role | Personal Data processed | Place of processing | Zones | |---|---|---|---|---| | [●] | Hosts and supplies compute for the Sovereign Zone; operates the physical environment. | Stored Objects at rest. No Inputs or Outputs. No control-plane identity data. | Italy | EU | | [●] | As above. | As above. | United Arab Emirates | UAE | | [●] | As above. | As above. | India | India | ### D.2 Control-plane and corporate Sub-processors | Sub-processor | Role | Personal Data processed | Place of processing | Zones | |---|---|---|---|---| | [●] | Payment processing and hosted payment page. | Billing contact, billing address, payment-instrument token. Infinia does not receive card details (Section 10.4). | [●] | All | | [●] | Identity and ownership verification, sanctions and adverse-media screening. | Identity documents, ownership and control records, screening results. | [●] | All | | [●] | Transactional email and notification delivery. | Name, email address, notification content. | [●] | All | | [●] | Support ticketing and case management. | Name, email address, Organisation, correspondence. | [●] | All | | [●] | Website and portal product analytics. | IP address, device and browser data, pages viewed. Consent-based where required. | [●] | All | | Infinia Affiliates | Administration, support and engineering under Section 27.2. | Account, billing and support data. Access to a Sovereign Zone only by personnel authorised for that Zone under Section 6.7. | [●] | All | ## Annex E — Standard Contractual Clauses: options and completed annexes This Annex completes the Standard Contractual Clauses incorporated by Section B.11.1. It applies only to the account, billing, identity-verification, support and website Personal Data described in Section B.6.2 for which Infinia is a data importer. It does not apply to Inputs, Outputs or Stored Objects, which never leave the Region Scope and are therefore not the subject of a restricted transfer (Sections 6.1, 6.2 and B.6.1). ### E.1 Options selected | Clause | Selection | |---|---| | Instrument | The Standard Contractual Clauses annexed to Commission Implementing Decision (EU) 2021/914 of 4 June 2021. | | Module | Module Two (controller to processor) where the Customer is a controller. Module Three (processor to processor) where the Customer is itself a processor. The Customer's status determines which applies; no further election is required. | | Clause 7 (docking) | Included. An Affiliate of either party may accede. | | Clause 9 (sub-processors) | Option 2, general written authorisation. The notice period is thirty days, as provided in Section B.5.2. The current list is Annex D. | | Clause 11 (redress) | The optional independent dispute-resolution body is not used. | | Clause 13 and Annex I.C (supervisory authority) | The supervisory authority of the Member State in which the Customer, as data exporter, is established. Where the Customer is established in Italy, the Garante per la protezione dei dati personali. Where the Customer is not established in the Union, the supervisory authority of the Member State in which Infinia's Article 27 representative is established. | | Clause 17 (governing law) | The law of Italy. | | Clause 18(b) (forum) | The courts of Italy. Section 26 of the Terms does not apply to a dispute arising under these clauses. | | United Kingdom | The International Data Transfer Addendum issued by the Information Commissioner under section 119A of the Data Protection Act 2018, version B1.0, applies where the UK GDPR governs the transfer. Tables 1 to 3 are completed by this Annex; in Table 4, neither party may end the Addendum when the Approved Addendum changes. | | Precedence | Where these clauses conflict with any other part of the Agreement, these clauses prevail to the extent of the conflict (Section 1.3(c)). | ### E.2 Annex I — Parties, transfer and supervisory authority | Item | Completion | |---|---| | A. Data exporter | The Customer, as identified in its Organisation billing identity. Role: controller (Module Two) or processor (Module Three). Contact: the Owners and billing contact recorded in the Organisation. Signature and date: effected by acceptance of the Terms under the acceptance record. | | A. Data importer | [Infinia Legal Entity Name], [registered address], Abu Dhabi Global Market. Role: controller in respect of the data described in Section B.6.2. Contact: [dpo@●]. Signature and date: effected by publication of these Terms. | | B. Categories of data subject | The Customer's Authorised Users, its billing and technical contacts, its beneficial owners and directors to the extent required for identity verification, and individuals who correspond with Infinia's support function. | | B. Categories of personal data | Name, business contact details, job role and Organisation role; authentication identifiers issued by the Customer's identity provider; billing identity, address and tax identifiers; identity documents and ownership records supplied for verification; sanctions and adverse-media screening results; support correspondence; Usage Data and portal activity; IP address and device data. | | B. Sensitive data | None is required or requested. Identity documents may incidentally reveal data such as nationality or date of birth; such data is processed only for verification, is access-restricted under Section B.12(h), and is retained under Annex C, section C.3. | | B. Frequency of transfer | Continuous, for the duration of the Term. | | B. Nature and purpose | Administration of the customer relationship, identity and sanctions verification, metering and invoicing, support, security, and compliance with Applicable Law. It does not include inference, which occurs wholly within the Region Scope. | | B. Retention | As stated in Annex C, section C.3. | | B. Sub-processors | As listed in Annex D, section D.2, for the periods in Annex C. | | C. Competent supervisory authority | As stated in Annex E, section E.1. | ### E.3 Annex II — Technical and organisational measures The measures required by Clause 8.6 and Annex II are those set out in Section B.12 of the Data Processing Addendum, which is incorporated into this Annex E in full. The measures that bear specifically on the transferred data are: encryption in transit and at rest with keys managed within the relevant jurisdiction; federated authentication with role-based access control and re-authentication for sensitive actions; least-privilege, logged and periodically reviewed personnel access, restricted to the function for which it is authorised; segregation of identity-verification records from operational systems; an immutable audit log of administrative access; annual independent penetration testing; and the incident-response and notification procedures in Section B.8. The measures applicable to a Sub-processor are those imposed under Section B.5.3, which must be no less protective than these. ### E.4 Annex III — Sub-processors, and the transfer impact assessment The sub-processors authorised under Clause 9, Option 2 are those listed in Annex D, section D.2, together with their roles, the data they process and their places of processing. Assessment under Clause 14. Infinia has assessed, and will keep under review, whether the law and practice of the Abu Dhabi Global Market prevent it from meeting its obligations under these clauses. The assessment records the following, and is available to the Customer on request under Section B.10.1. | Factor | Assessment | |---|---| | Nature of the data transferred | Business contact, billing, verification and support data relating to Authorised Users acting in a professional capacity. No Inputs, Outputs or Stored Objects. No special categories are sought. | | Legal framework of the destination | The Abu Dhabi Global Market applies the ADGM Data Protection Regulations 2021, which are modelled on the GDPR and are administered by an independent Commissioner of Data Protection with enforcement and appeal rights. The ADGM is a common-law jurisdiction with its own courts. | | Government access | Infinia has received no request from any authority for the transferred data. Section 6.8 of the Terms requires Infinia to notify the Customer of any request unless legally prohibited, to challenge any request it considers overbroad or unlawful, and to disclose only the minimum required. Requests are recorded in the transparency reporting published in the Trust Centre. | | Supplementary measures | Encryption in transit and at rest; segregation and access restriction of verification records; data minimisation, in that no Customer Content is transferred; the challenge and notification commitments in Section 6.8; and the option under Section 27.3 to substitute an in-region contracting entity, which would remove the transfer entirely. | | Residual risk and review | Assessed as low for the categories transferred. The assessment is reviewed annually and on any material change in law or practice, and the Customer is notified under Section 22.2 if the conclusion changes. | | Counsel confirmation | REQUIRED BEFORE PUBLICATION. This assessment states Infinia's position; it must be confirmed by qualified counsel in the Union and in the Abu Dhabi Global Market before these Terms are published. | --- ## Privacy Policy **URL:** https://www.tokenfactory.iamsaif.ai/docs/legal/privacy-policy **Description:** How Infinia Technologies processes personal data as a controller in connection with Xailon Token Factory. > **Draft — pending legal approval.** > This placeholder is provided for product integration and review only. It contains unresolved bracketed fields and is not a final or legally approved version. Do not rely on it as legal advice or as the final terms governing the service. Version 1.0 · Effective [●] 2026 · Published at [Privacy URL] This Privacy Notice is issued by [Infinia Legal Entity Name] ("Infinia") in respect of Personal Data processed by it as a controller in connection with Xailon Token Factory. It is incorporated into the Xailon Token Factory Terms of Service by Section 1.2(h) of those Terms and is referred to in Section 6.11 of those Terms. It is given for the purposes of Articles 13 and 14 of Regulation (EU) 2016/679, the corresponding provisions of the UK General Data Protection Regulation, the ADGM Data Protection Regulations 2021, UAE Federal Decree-Law No. 45 of 2021 and the Digital Personal Data Protection Act 2023, and of any other Data Protection Law that applies to that processing. ## 1. Introduction and status of this Notice **1.1** Infinia operates Xailon Token Factory, a sovereign inference and artificial-intelligence control-plane platform under which requests are executed within a defined Sovereign Zone. In the course of operating that platform Infinia processes Personal Data in two distinct capacities, and the distinction determines which document governs. **1.2** Where Personal Data is comprised in Customer Content, the Customer determines the purposes and means of the processing and Infinia acts only on the Customer's documented instructions. In that capacity Infinia is a processor and Annex B of the Terms (Data Processing Addendum) governs. This Notice does not apply to that processing. **1.3** Where Infinia processes Personal Data in order to establish and administer the contractual relationship, to verify identity, to meter and invoice, to provide support, to secure the Service and to comply with obligations imposed on it by Applicable Law, Infinia determines the purposes and means of that processing and is a controller in its own right. This Notice applies to that processing, and to it alone. **1.4** This Notice forms part of the Agreement. It does not vary Sections 6.1 to 6.4 of the Terms or Annex B, and in the event of any inconsistency those provisions prevail in accordance with the order of precedence in Section 1.3 of the Terms. **1.5** The Service is offered to businesses, public bodies, academic and research institutions and individuals acting in the course of a trade, business, profession or research activity, and is not offered to consumers or to any person under the age of eighteen (Sections 3.1 and 3.2 of the Terms). Infinia does not knowingly process the Personal Data of children and will delete any such data that comes to its attention. **1.6** Capitalised terms used but not defined in this Notice have the meanings given to them in the Terms. ## 2. Definitions "Data Protection Law", "Personal Data", "controller", "processor", "data subject", "processing", "special categories" and "supervisory authority" have the meanings given to them in Section 2.1 of the Terms and in Section B.1.2 of Annex B and, where no meaning is given, in the GDPR. "Individual" means a natural person to whom Personal Data processed by Infinia under this Notice relates, including an Authorised User, a billing, technical or security contact of a Customer, a director or beneficial owner whose particulars are verified under Section 3.7 of the Terms, a person who corresponds with Infinia, and a visitor to the Site. "Personal Data Breach" means a breach of security leading to the accidental or unlawful destruction, loss, alteration, unauthorised disclosure of, or access to, Personal Data processed by Infinia as a controller. "Site" means the website operated by Infinia at [Site URL] and the Xailon Token Factory portal. References in this Notice to "you" and "your" are to the Individual. References to a Section without further identification are to a Section of this Notice; references to a Section of the Terms, to Schedule 1 or to an Annex are identified as such. ## 3. Identity of the controller and contact particulars | Matter | Particulars | |---|---| | Controller | [Infinia Legal Entity Name], a company incorporated in [●] with registered number [●] and registered office at [registered address], Abu Dhabi Global Market. | | Data Protection Officer | [dpo@●]. | | Representative in the European Union appointed under Article 27 of the GDPR | [name and address]. | | Representative in the United Kingdom | [name and address], where required by Article 27 of the UK GDPR. | | Requests by Individuals and privacy correspondence | [privacy@●]. | | Security and vulnerability disclosure | [security@●]. | | Reports under Annex A (Acceptable Use Policy) | [abuse@●]. | | Legal notices and notices under Section 15.7 of the Terms | [legal@●]. | ## 4. Scope **4.1** This Notice applies to the Personal Data described in Section 6, being the account, Organisation, billing, identity-verification, screening, support, security and Site data processed by Infinia as a controller. **4.2** This Notice does not apply to Personal Data comprised in Inputs, Outputs or Stored Objects. In respect of that data the Customer is the controller (or a processor acting for its own controller) and Infinia is a processor. An Individual whose Personal Data has been submitted to the Service by a Customer should address any request to that Customer, which is the controller of it. Section 13.6 sets out how Infinia will deal with a request of that kind that is addressed to it, and Section 5 explains the limitations that arise from Infinia's design. **4.3** Where an Authorised User makes a request in respect of Personal Data described in Section 6, Section 13.6 does not apply and Infinia will deal with the request itself. Section B.14.2 of the Terms so provides. ### Index of Sections | Subject matter | Section | |---|---| | Treatment of Inputs and Outputs | 5 | | Categories of Personal Data processed | 6 | | Cookies and similar technologies | 7 | | Purposes of processing and legal bases | 8 | | Automated decision-making | 9 | | Recipients and disclosures | 10 | | Location of processing and international transfers | 11 | | Retention | 12 | | Rights of Individuals | 13 | | Marketing communications | 14 | | Biometric Data | 15 | | Security and Personal Data Breaches | 16 | | Jurisdiction-specific provisions | 17 | | Amendment of this Notice | 18 | | Complaints | 19 | ## 5. Treatment of Inputs and Outputs **5.1** An Input may contain Personal Data, and an Output may reproduce or generate it, in each case as determined by the Customer. The following provisions of the Terms govern Infinia's treatment of that data and are summarised here because they materially affect the rights that can be exercised against Infinia in respect of it. **5.2** Non-retention. Infinia does not store, log, retain or otherwise persist Inputs or Outputs beyond the transient processing required to serve a request and return a response, and does not retain them after the response has been returned (Section 6.3 of the Terms). **5.3** Non-examination. Infinia does not read, inspect, review, filter, screen, sample or otherwise examine the content of any Input or Output, whether by human or automated means. That commitment is expressed in the Terms as absolute and admitting of no exception. **5.4** Residency. Each request is executed wholly within the Sovereign Zone or Zones comprising the Region Scope configured by the Customer. A request that cannot be served within that Region Scope is refused and is not served from another location (Sections 6.1 and 6.2 of the Terms). **5.5** No training. Neither Infinia nor any Model Provider, Zone Operator or other third party may use Inputs, Outputs, Customer Models or other Customer Content to train, retrain, fine-tune, evaluate or otherwise improve any Model or machine-learning system. That provision is not subject to opt-in and may not be varied except by written agreement signed by both parties (Section 6.4 of the Terms). **5.6** Consequences of the foregoing. Infinia is unable to produce a copy of an Input or an Output, to rectify it, to confirm whether a particular Input was submitted, to restore it following loss, or to screen it for unlawful or harmful content, and makes no representation that it does so. Where Infinia receives a request or order from a court, regulator or law-enforcement body for access to an Input or an Output, it will ordinarily have no such material in its possession, and Section 6.8 of the Terms requires it to notify the Customer unless legally prohibited from doing so, to challenge any request it reasonably considers overbroad or unlawful, to disclose only the minimum required, and to record the request in the transparency reporting published in the Trust Centre. **5.7** Usage Data. Infinia retains Usage Data in respect of each request, comprising request timestamps, Model and Sovereign Zone identifiers, token counts, latency, error codes, routing decisions, attestation records, API-key identifiers, region identifiers and billing records. Usage Data contains no part of the content of an Input or an Output. Usage Data is processed for the purposes stated in Section 8 and is retained for the period stated in Annex C of the Terms. **5.8** Stored Objects. A Customer may elect to store Batch input and output files, uploaded datasets and files, Customer Models and Fine-Tuned Models. Those are Stored Objects, are held within the Region Scope for the periods stated in Annex C of the Terms, and are Customer Content for which the Customer is the controller. Section 6.10 of the Terms permits two automated integrity controls to be applied to Stored Objects and to no other material: comparison of stored image, audio and video files against cryptographic hash lists of material identified as child sexual abuse material, which returns a match or no match and discloses nothing further; and structural scanning of Customer Models, Fine-Tuned Models and uploaded files for malware and unsafe serialisation formats. Neither control reads, interprets or retains the substantive content of a Stored Object, and neither is applied to an Input or an Output. ## 6. Categories of Personal Data processed **6.1** Personal Data provided to Infinia. Infinia processes the following categories of Personal Data provided to it by an Individual or by the Customer on whose behalf the Individual acts. | Category | Description | |---|---| | Account and Organisation data | Name, business electronic mail address, the Organisation to which the Individual belongs and the role held within it (Owner, Admin or Developer). Access to the portal is by federated sign-in through the Customer's identity provider under Section 3.4 of the Terms; Infinia neither issues nor stores a password and receives an authentication assertion only. | | Billing identity | The Customer's legal entity name, registration number, tax identification number and address, and the name and electronic mail address of its billing contact. | | Identity-verification data | Where verification is required under Section 3.7 of the Terms: identity documents, proof of address, records of ownership and control, and the particulars of directors and beneficial owners. | | Support and other correspondence | Correspondence with Infinia and any material submitted in connection with it, including configuration particulars supplied in support of a request. | | Reports and disclosures | Contact particulars and the content of any report made under Section 9.3 of the Terms (security research and vulnerability disclosure) or Section A.7 of Annex A (reports of abuse). | | Configuration data | Region Scope, project structure, spend limits, Model allow-lists and logging settings, together with the identity of the Individual by whom each was configured. | **6.2** Personal Data collected automatically. Infinia collects the following categories of Personal Data in the course of an Individual's use of the Site. | Category | Description | |---|---| | Portal activity | Pages accessed, actions performed, API Credentials created or revoked, settings changed, and the time of each. Administrative and configuration actions are additionally recorded in the immutable audit log referred to in Section 6.6 of the Terms, which is available to the Customer. | | Device and connection data | Internet protocol address, browser and operating system, display and locale settings, and the approximate geographic region indicated by the internet protocol address. Approximate region is processed for security purposes and for the compliance signals described in Section 8.8 of the Terms. Infinia does not collect precise location data. | | Security and access records | Authentication events and unsuccessful sign-in attempts, and, in respect of Infinia personnel, each access to a production system, which is logged and periodically reviewed under Section 6.7 of the Terms. | | Site analytics | Data concerning the use of the Site, collected by means of the technologies described in Section 7 and, where Data Protection Law so requires, only with the Individual's consent. | **6.3** Personal Data obtained from third parties. Infinia obtains the following categories of Personal Data from the sources stated. Each source is a Sub-processor identified in Annex D of the Terms. | Source | Personal Data obtained | |---|---| | Identity-verification and screening provider | The outcome of identity, ownership and sanctions verification, any adverse-media match, and the records on which the provider relied. | | Payment provider | Confirmation of the success or failure of a payment, the final four digits and expiry date of the payment instrument, and a token representing it. Payment is effected on the provider's hosted payment page and Infinia does not receive or store a primary account number or security code (Section 10.4 of the Terms). | | The Customer's identity provider | The authentication assertion confirming the Individual's identity and any attribute released with it by the Customer. | | The Customer | The fact of the Individual's invitation to an Organisation and the role assigned. | **6.4** Personal Data not processed. Infinia does not process the content of Inputs or Outputs; does not receive or store primary account numbers or security codes; does not extract, derive or store any biometric identifier (Section 15); does not obtain Personal Data from data brokers; does not construct advertising profiles; and does not track Individuals across third-party websites or services. **6.5** Special categories. Infinia does not require or request special categories of Personal Data. An identity document supplied under Section 3.7 of the Terms may incidentally disclose data of that character, including nationality or date of birth. Such data is processed solely for the purpose of verification, is held segregated and subject to the access restrictions in Section B.12(h) of the Terms, and is retained in accordance with Annex C of the Terms. ## 7. Cookies and similar technologies **7.1** Infinia deploys cookies and similar technologies on the Site. Certain of those technologies are strictly necessary for the Site to function. In respect of all others, where Data Protection Law requires the prior consent of the Individual, including Directive 2002/58/EC as implemented in Italy and the guidance of the Garante per la protezione dei dati personali, no such technology is deployed until consent has been obtained, refusal is presented as readily as acceptance, and consent may be withdrawn at any time by means of the link presented in the footer of each page of the Site. **7.2** Infinia does not deploy cookies or similar technologies for advertising purposes. Infinia does not conduct interest-based, personalised or targeted advertising, does not participate in advertising networks, and does not disclose Personal Data to advertisers. Infinia does not deploy session-replay technology, does not record the screen or keystrokes of any Individual, and does not deploy tracking pixels in operational communications. **7.3** The categories of technology deployed are as follows. | Category | Purpose | Consent | Maximum duration | |---|---|---|---| | Strictly necessary | Session management, authentication, security, load balancing, and the record of the Individual's cookie preferences. The Site cannot function without these technologies. | Not required. | Session, save for the record of preferences, which persists for 12 months. | | Functional | Retention of interface preferences, including default project, region filter and display settings. | Required. | 12 months. | | Analytics | Measurement of the use of pages and features for the purpose of improving them. Reported in aggregate. | Required. | 14 months. | **7.4** The individual technologies within each category, the provider of each and the duration of each are set out in the preferences panel accessible from the footer of each page of the Site, which is maintained so as to reflect the technologies actually deployed. Providers of analytics technologies are identified in Annex D of the Terms. An Individual may also configure the browser to block or delete cookies, in which case the portal may not function correctly. **7.5** Infinia may measure whether a marketing communication has been opened and which links within it have been followed. An Individual may object to that measurement by notice to [privacy@●]. No such measurement is applied to operational communications. ## 8. Purposes of processing and legal bases **8.1** Infinia processes Personal Data for the purposes and on the legal bases set out in the following table. Where Infinia relies on legitimate interests, it has conducted and documented an assessment balancing those interests against the interests, rights and freedoms of the Individual, and will provide a summary of that assessment on request made under Section 13. | Purpose | Personal Data | Legal basis | |---|---|---| | Establishing and administering the Organisation and the account, and providing the Service | Account and Organisation data; configuration data | Performance of the contract with the Customer. Where the Individual is not a party to that contract, the legitimate interests of Infinia in administering it. | | Metering, invoicing and the collection of Fees | Billing identity; Usage Data; payment confirmations | Performance of the contract, and compliance with a legal obligation in respect of tax and accounting records. | | Verification of identity and ownership, and screening against sanctions and restricted-party lists | Identity-verification data; screening results | Compliance with a legal obligation under anti-money-laundering and sanctions law and, to the extent any check exceeds what is strictly required by that law, the legitimate interests of Infinia in not transacting with a person with whom it is prohibited from dealing. | | Assessment of creditworthiness prior to approval for postpaid invoicing | Billing identity; payment history; third-party credit data | Steps taken at the Customer's request prior to entering into the postpaid arrangement, and the legitimate interests of Infinia in the management of credit risk. | | Securing the Service, investigating suspected abuse and enforcing Annex A | Usage Data; security and access records; reports received | The legitimate interests of Infinia in protecting the Service, its customers, its Zone Operators and third parties from harm, and compliance with a legal obligation where one applies. | | Provision of support | Support correspondence; account data; Usage Data | Performance of the contract, and the legitimate interests of Infinia in supporting users of the Service. | | Operational communications, including notice of incidents, maintenance, Model deprecation, Credit expiry, price changes and changes to the Agreement | Account and Organisation data; billing contact | Performance of the contract. Such communications do not constitute marketing and are not subject to Section 14. | | Improvement of the Service and analysis of its use | Usage Data; portal activity; Site analytics | The legitimate interests of Infinia in developing the Service and, in respect of analytics technologies requiring it, the consent of the Individual. | | Publication of aggregate platform statistics | Usage Data in aggregated and de-identified form from which no Customer, Organisation or Individual may be identified | The legitimate interests of Infinia. Upon completion of aggregation the resulting data is no longer Personal Data. | | Marketing communications concerning Xailon Token Factory | Name and business electronic mail address | The consent of the Individual or, where Data Protection Law permits communication with an existing business contact in respect of similar services, the legitimate interests of Infinia. Section 14 applies in either case. | | Establishment, exercise or defence of legal claims, and response to lawful requests of competent authorities | Such Personal Data as is relevant and necessary | The legitimate interests of Infinia, and compliance with a legal obligation. | ## 9. Automated decision-making **9.1** Three of the determinations made by Infinia are made by automated means and are capable of producing legal effects concerning an Individual or of similarly significantly affecting an Individual, in that each may result in access to the Service being refused, suspended or restricted. They are disclosed below for the purposes of Article 13(2)(f) of the GDPR and the corresponding provisions of other Data Protection Law. | Determination | Logic involved | Consequence | Route of contest | |---|---|---|---| | Verification of identity and ownership (Section 3.7 of the Terms) | A verification provider compares the documents and records of ownership supplied against its own sources and returns a result of pass, fail or referral. | The account, or the affected functionality, remains suspended pending successful verification. Infinia may decline to proceed. | Notice to [privacy@●]. The referral and the underlying documents are reviewed by a member of Infinia's compliance function, who will state the respect in which the material supplied was insufficient so far as Applicable Law permits. | | Sanctions and restricted-party screening (Section 23.3 of the Terms) | Names, entities and payments are compared against sanctions and restricted-party lists and adverse-media sources. Comparison is approximate, and a common name may generate a result requiring resolution. | The Service may be refused or suspended and Credit frozen pending resolution. | Notice to [privacy@●], together with any evidence distinguishing the Individual from the listed person. Each result is reviewed by a person before it is treated as final. Infinia may be prohibited by sanctions or anti-money-laundering law from disclosing the particulars of a result, in which case it will say that this is so. | | Assessment of creditworthiness (Section 10.12 of the Terms) | An assessment of payment history, billing identity and third-party credit data determines whether a credit limit is granted and its amount. | Postpaid invoicing is declined or a lower credit limit is set. Prepaid use of the Service is unaffected. | Notice to [privacy@●] requesting review by a person, together with any information said to have been omitted from the assessment. | **9.2** Where a determination of the kind described in Section 9.1 would prevent the Customer or the Individual from using the Service, Infinia will not treat the output of the automated system as determinative without human review. The Individual is entitled to obtain that review, to express a point of view and to contest the determination, and Infinia will respond within the period stated in Section 13.4. Section B.14.3 of the Terms so provides. **9.3** Infinia does not profile Individuals for marketing purposes, does not score Individuals for advertising purposes, and does not use Usage Data to make determinations concerning Individuals. ## 10. Recipients and disclosures **10.1** Sub-processors. Infinia engages the service providers identified in Annex D of the Terms, which states for each its role, the Personal Data it processes, its place of processing and the Sovereign Zones for which it is engaged. Each is bound by written terms imposing obligations no less protective than those to which Infinia is subject, may process the Personal Data only for the purpose for which it is engaged, and is prohibited from using Customer Content for training or for any purpose of its own. Infinia gives not less than thirty days' notice before authorising a new Sub-processor, and the Customer may object in accordance with Section B.5.2 of the Terms. **10.2** Zone Operators. A Sovereign Zone may be hosted, powered or operated by a Zone Operator. A Zone Operator does not receive Inputs or Outputs, which are not held at rest, and does not receive control-plane identity data. Section 17.1 of the Terms makes Infinia responsible to the Customer for the performance of a Zone Operator and requires Infinia to impose on each Zone Operator obligations as to security, residency, confidentiality and data protection that are no less protective than those in the Agreement. **10.3** Affiliates. Personnel of Infinia's Affiliates may process account, billing and support data for the purpose of administering and supporting the Service. Access to production systems within a Sovereign Zone is restricted to personnel authorised for that Zone, is granted on a least-privilege basis and is logged (Section 6.7 of the Terms). Where the Documentation identifies the jurisdiction of the personnel operating a Zone, Infinia gives not less than thirty days' notice of a change that would move operational access outside that jurisdiction. **10.4** The Customer. Where the Individual is an Authorised User, the Owners and Admins of the Organisation may view the Individual's role, API Credentials and administrative actions through the portal and the audit log. The allocation of those roles is a matter for the Customer. **10.5** Competent authorities. Infinia discloses Personal Data to a court, regulator or law-enforcement body where required to do so by Applicable Law. Section 6.8 of the Terms requires Infinia to notify the Customer unless legally prohibited from doing so, to challenge any request it reasonably considers overbroad or unlawful, to disclose only the minimum required, and to record the request in the transparency reporting published in the Trust Centre. **10.6** Professional advisers and corporate transactions. Infinia's auditors, legal advisers and insurers may receive Personal Data where necessary, subject to professional obligations of confidence. In the event of a merger, acquisition or transfer of the whole or part of Infinia's business, Personal Data may be transferred as part of that transaction, of which affected Customers will be notified in advance, and the transferee remains bound by this Notice until it is lawfully superseded. **10.7** Disclosures not made. Infinia does not sell Personal Data within the meaning of any Data Protection Law, and does not share Personal Data for cross-context behavioural advertising or targeted advertising. Infinia does not disclose Personal Data to Model Providers. A Model Provider licenses a Model for inclusion in the Model Catalogue and the Model is executed within the Sovereign Zone; no request, Input or Output is transmitted to the Model Provider. ## 11. Location of processing and international transfers **11.1** Customer Content. Inputs, Outputs and Stored Objects are processed and held solely within the Region Scope configured by the Customer, are not transferred outside it by Infinia or by any Sub-processor, and a request that cannot be served within it is refused rather than served elsewhere. There is accordingly no transfer of Customer Content to a third country for the purposes of Chapter V of the GDPR. **11.2** Personal Data processed by Infinia as controller. The Personal Data described in Section 6 is processed by Infinia, which contracts from the Abu Dhabi Global Market. Where the Individual is in the European Economic Area or the United Kingdom, that Personal Data — and not any Input, Output or Stored Object — is transferred to a jurisdiction in respect of which the European Commission has adopted no adequacy decision. **11.3** Safeguards. That transfer is made in reliance on the standard contractual clauses approved by Commission Implementing Decision (EU) 2021/914 and, where the UK GDPR applies, on the International Data Transfer Addendum issued by the Information Commissioner under section 119A of the Data Protection Act 2018. Those clauses are incorporated by Section B.11.1 of the Terms. Annex E of the Terms records the module selected, the options taken and the completed Annexes I, II and III, and contains the assessment made under Clause 14 of those clauses together with the supplementary measures applied, being encryption in transit and at rest with keys managed within the relevant jurisdiction, segregation and access restriction of verification records, minimisation of the data transferred, and the challenge and notification obligations in Section 6.8 of the Terms. A copy of that assessment is available on request made under Section 13. **11.4** The Abu Dhabi Global Market applies the ADGM Data Protection Regulations 2021, which are administered by an independent Commissioner of Data Protection with powers of enforcement and from whose decisions an appeal lies. That circumstance is recorded in the assessment referred to in Section 11.3 and does not displace the safeguards there described. **11.5** Section 27.3 of the Terms permits Infinia to designate an Affiliate incorporated in or near the jurisdiction of a Sovereign Zone as the contracting and invoicing entity for that Zone. Where Infinia does so in respect of the European Union, the transfer described in Section 11.2 ceases in respect of Customers contracting with that entity, and this Notice will be amended accordingly in accordance with Section 18. ## 12. Retention **12.1** Annex C of the Terms is the retention schedule for the Agreement and section C.3 of that Annex governs the Personal Data described in this Notice. The principal periods are set out below. Where more than one period is capable of applying to the same record, the longer governs. Where Applicable Law requires retention for a period longer than Infinia would otherwise apply, the record is retained for that period and is processed for no other purpose. | Personal Data | Retention period | |---|---| | Inputs and Outputs | Not retained. Deleted upon return of the response. | | Account, Organisation and Authorised User records | The Term, and thereafter 7 years from closure. | | Identity-verification records and supporting documents | 5 years from the end of the business relationship, or such longer period as Applicable Law requires. Not capable of deletion on request during that period. | | Sanctions and screening records, including resolved results | 5 years from the screening event. Not capable of deletion on request during that period. | | Credit assessment records | 2 years from the assessment, or the Term if longer. | | Billing and tax records | 7 years from the end of the tax year to which they relate. | | Support correspondence | 24 months from closure of the case. | | Usage Data | 13 months, and thereafter irreversibly aggregated. | | Security and access records | 12 months. | | Site analytics | 14 months. | | Marketing contact records | Until consent is withdrawn, and thereafter 3 years in respect of the record of withdrawal only, so that the withdrawal may be given effect. | | Encrypted backups | Overwritten on a rolling 35-day cycle. | **12.2** Upon expiry of the applicable period the Personal Data is deleted or irreversibly aggregated. Deletion takes effect in active systems immediately and in backups within the cycle stated above. ## 13. Rights of Individuals **13.1** Subject to the conditions and exceptions imposed by the Data Protection Law applicable to the Individual, an Individual has the rights set out below. Infinia applies them irrespective of the Individual's location, save where to do so would contravene a law binding upon it. | Right | Scope | |---|---| | Access | To obtain confirmation whether Personal Data concerning the Individual is processed, a copy of that data, and the information set out in this Notice. | | Rectification | To obtain the rectification of inaccurate Personal Data and the completion of incomplete Personal Data. Personal Data maintained by the Customer in its identity provider is not within Infinia's control and must be corrected by the Customer. | | Erasure | To obtain the erasure of Personal Data where Infinia no longer requires it and no legal obligation requires its retention. The records identified in Section 12 as not capable of deletion on request are the principal exception. | | Portability | To receive Personal Data provided by the Individual in a structured, commonly used and machine-readable format, where processing is carried out by automated means on the basis of consent or of a contract. | | Objection | To object to processing carried out on the basis of legitimate interests. Infinia will cease that processing unless it demonstrates compelling legitimate grounds overriding the interests, rights and freedoms of the Individual, and will state which conclusion it has reached. | | Restriction | To obtain the restriction of processing in the circumstances provided by Data Protection Law, including pending verification of the accuracy of Personal Data or of an objection made under this Section. | | Withdrawal of consent | To withdraw consent at any time where processing is based on consent, including in respect of marketing communications and non-essential cookies. Withdrawal does not affect the lawfulness of processing carried out before it. | | Human intervention | To obtain human intervention in respect of a determination described in Section 9, to express a point of view and to contest that determination. | | Complaint | To lodge a complaint with a supervisory authority, as provided in Section 19. | **13.2** Exercise of rights. A request may be made by notice to [privacy@●] or to the Data Protection Officer at [dpo@●], identifying the right invoked and, where applicable, the Personal Data to which the request relates. **13.3** Verification. Infinia will verify the identity of the requester before giving effect to a request, since to act upon the request of a person other than the Individual would itself constitute a contravention of Data Protection Law. Verification will ordinarily be effected through the electronic mail address associated with the account, and further information may be requested where the request is broad in scope or relates to sensitive material. Information supplied for the purpose of verification is processed for that purpose alone. **13.4** Time for response. Infinia will respond within one month of receipt of a valid request. Where a request is complex, or where an Individual has made a number of requests, that period may be extended by up to two further months, and Infinia will notify the Individual of the extension and of the reasons for it within the first month. No fee is charged, save that where a request is manifestly unfounded or excessive Infinia may charge a reasonable fee or decline to act, and will state which course it has taken and why. **13.5** Refusal. Infinia may decline a request in whole or in part where retention is required by Applicable Law, including the anti-money-laundering and sanctions records identified in Section 12; where compliance would disclose Personal Data concerning another person; where the request is manifestly unfounded or excessive; or where compliance would prejudice the prevention or detection of crime or contravene sanctions law. Infinia will in every case notify the Individual that the request has been declined and the ground relied upon, unless to do so would itself be unlawful. **13.6** Requests concerning Customer Content. Where a request concerns Personal Data comprised in Customer Content, Infinia is a processor and Section B.7.2 of the Terms requires it to refer the Individual to the Customer, which is the controller. Infinia will do so promptly. For the avoidance of doubt, and as Section 5.6 records, Infinia does not hold Inputs or Outputs and is not able to give effect to such a request. **13.7** This Section 13 is given effect contractually by Section B.14.2 of the Terms. ## 14. Marketing communications **14.1** Infinia may send communications concerning Xailon Token Factory, including the availability of Models and Sovereign Zones, changes to the Service, events and published research, on the basis stated in Section 8. **14.2** Each such communication contains a facility by which the Individual may unsubscribe. An Individual may also object by notice to [privacy@●]. Effect will be given to an objection without undue delay, subject only to communications already dispatched. **14.3** The operational communications identified in Section 8 do not constitute marketing, are sent in performance of the contract, and are not capable of being unsubscribed from while the Customer uses the Service. Section 27.5 of the Terms requires the Customer to maintain current contact particulars for that purpose. ## 15. Biometric Data **15.1** Certain Models in the Model Catalogue accept audio, image or video Inputs, and certain of those Models perform processing constituting the processing of Biometric Data, including voice cloning, voice conversion, speaker identification, voice-preserving translation and the generation of a likeness of a person's face. The Model Catalogue identifies each such Model. **15.2** Infinia does not extract, derive, generate, store or retain any voiceprint, faceprint, biometric template or other biometric identifier. Audio, image and video material is transmitted to the Model within the Sovereign Zone and the result is returned; no such material is retained, by reason of Section 6.3 of the Terms. Infinia does not use Biometric Data to identify any person and does not disclose Biometric Data, holding none. **15.3** The Customer submitting such material is the controller of any Biometric Data it comprises. Section 8.9 of the Terms and Section A.5 of Annex A set out the Customer's obligations, which include identifying a condition under Article 9 of the GDPR or the equivalent provision of the applicable Data Protection Law, which will ordinarily be the explicit consent of the individual concerned; holding documented consent from any third party whose voice, face or likeness is submitted; and observing any additional requirement as to notice, consent, retention schedule or non-disclosure imposed by Applicable Law in respect of biometric identifiers. **15.4** An Individual who believes that their voice, face or likeness has been submitted to the Service without their consent may give notice to [privacy@●] and to [abuse@●]. Infinia will act upon that notice in accordance with Section A.8 of Annex A. Infinia will not be able to produce the material, holding none, but may take action in respect of the account concerned and will inform the Individual of the action taken so far as Applicable Law permits. **15.5** Where audio, image or video material is stored as a Stored Object, it is retained for the period stated in Annex C of the Terms and the hash-matching control in Section 6.10(a) of the Terms is applied to it. That control compares the file against lists of material identified as unlawful and discloses nothing further; it does not recognise faces, identify speakers or generate any biometric identifier. ## 16. Security and Personal Data Breaches **16.1** Infinia maintains the technical and organisational measures set out in Section B.12 of the Terms. The measures material to the Personal Data described in this Notice are: encryption in transit and at rest, with keys managed within the relevant jurisdiction; federated authentication with role-based access control, Infinia holding no password; least-privilege access for Infinia personnel restricted to the Sovereign Zone for which they are authorised, logged and periodically reviewed; segregation of identity-verification records from operational systems; an immutable audit log of administrative access; and independent penetration testing not less than annually. Infinia's current certifications are listed in the Trust Centre at [Trust URL]. **16.2** No measure of security can be guaranteed to be effective in every circumstance. Where a Personal Data Breach occurs, Infinia will notify the competent supervisory authority without undue delay and, where feasible, not later than 72 hours after becoming aware of it, unless the breach is unlikely to result in a risk to the rights and freedoms of natural persons; and will communicate the breach to affected Individuals without undue delay where it is likely to result in a high risk to their rights and freedoms, in clear and plain language and stating the nature of the breach, the likely consequences and the measures taken or proposed. **16.3** Where a security incident affects Customer Content, Infinia notifies the Customer within twenty-four hours in accordance with Section B.8.1 of the Terms, and within four hours in the case of a Customer that has identified itself under Section 24.4 of the Terms as subject to Regulation (EU) 2022/2554 or an equivalent regime. **16.4** The Individual is responsible for maintaining the confidentiality of the credentials issued by the Customer's identity provider and of any API Credential, and must notify Infinia at [security@●] within twenty-four hours of becoming aware of any actual or suspected compromise (Section 3.9 of the Terms). ## 17. Jurisdiction-specific provisions **17.1** European Union and United Kingdom. This Notice is given for the purposes of Articles 13 and 14 of the GDPR and of the corresponding provisions of the UK GDPR. Infinia's representative appointed under Article 27 of the GDPR is identified in Section 3. The Data Protection Officer is contactable at [dpo@●]. **17.2** Italy. Where the Individual is in Italy, the competent supervisory authority is the Garante per la protezione dei dati personali. The consent requirements in Section 7 reflect the guidance of that authority in respect of cookies and other tracking tools. **17.3** Abu Dhabi Global Market and United Arab Emirates. Infinia processes Personal Data in accordance with the ADGM Data Protection Regulations 2021 and, where applicable, UAE Federal Decree-Law No. 45 of 2021. Processing within a Sovereign Zone in the United Arab Emirates does not constitute a cross-border transfer for the purposes of those instruments. **17.4** India. Where the Digital Personal Data Protection Act 2023 applies, Infinia is the Data Fiduciary in respect of the Personal Data described in this Notice, and processing within a Sovereign Zone in India involves no transfer outside India. In respect of Customer Content the Customer is the Data Fiduciary and Infinia the Data Processor, in accordance with Section B.11.3 of the Terms. **17.5** United States. Infinia does not sell Personal Data and does not share Personal Data for cross-context behavioural advertising within the meaning of the privacy laws of the several States. The rights set out in Section 13 are available to residents of those States, and Infinia does not discriminate against an Individual by reason of the exercise of any of them. ## 18. Amendment of this Notice **18.1** Infinia may amend this Notice. The current version and its effective date are published at [Privacy URL], where superseded versions are archived. **18.2** Where an amendment materially affects the manner in which Personal Data is collected, used or disclosed, or materially reduces the rights available under this Notice, Infinia will give not less than thirty days' notice by electronic mail to the Owners and billing contact of each Organisation and by notice in the portal before the amendment takes effect, corresponding to the notice required by Section 22.2 of the Terms in respect of the Terms. Other amendments take effect upon publication. ## 19. Complaints **19.1** An Individual dissatisfied with Infinia's processing of Personal Data or with Infinia's response to a request may raise the matter with Infinia by notice to [privacy@●] or to the Data Protection Officer at [dpo@●]. **19.2** An Individual is entitled to lodge a complaint with a supervisory authority, and the exercise of that right is without prejudice to any other administrative or judicial remedy. The competent authority is that of the Individual's habitual residence or place of work, or of the place of the alleged contravention. For Italy the authority is the Garante per la protezione dei dati personali; for the United Kingdom the Information Commissioner's Office; for the Abu Dhabi Global Market the Commissioner of Data Protection; for the United Arab Emirates outside the financial free zones the UAE Data Office; and for India the Data Protection Board of India. Xailon Token Factory Privacy Notice · Version 1.0 · © [year] [Infinia Legal Entity Name]. All rights reserved. --- ## Terms of Use **URL:** https://www.tokenfactory.iamsaif.ai/docs/legal/terms-of-use **Description:** The terms governing your use of Xailon Token Factory, including service modes, acceptable use, and data retention. > **Draft — pending legal approval.** > This placeholder is provided for product integration and review only. It contains unresolved bracketed fields and is not a final or legally approved version. Do not rely on it as legal advice or as the final terms governing the service. Version 1.1 · Effective [●] 2026 · Governs self-serve and enterprise use of the Xailon Token Factory platform operated by Infinia This document comprises Part A (Terms of Service), Schedule 1 (Service Mode Terms), Annex A (Acceptable Use Policy), Annex B (Data Processing Addendum), Annex C (Retention Schedule), Annex D (Sub-processor List) and Annex E (Standard Contractual Clauses — completed annexes). It is presented for acceptance at account creation and is binding on every use of the Service. Enterprise customers may additionally be party to a Master Services Agreement, which takes precedence to the extent stated in Section 1.3. The Privacy Notice published at [Privacy URL] is incorporated into these Terms and governs the Personal Data that Infinia processes as a controller in its own right. **PLEASE READ THESE TERMS CAREFULLY. BY CLICKING "I AGREE", CREATING AN ACCOUNT, PURCHASING CREDIT, GENERATING AN API KEY OR OTHERWISE ACCESSING OR USING THE SERVICE, YOU ACCEPT THESE TERMS AND AGREE TO BE LEGALLY BOUND BY THEM. IF YOU ARE ACCEPTING ON BEHALF OF A COMPANY, GOVERNMENT BODY OR OTHER ORGANISATION, YOU REPRESENT THAT YOU HAVE AUTHORITY TO BIND THAT ORGANISATION, AND "CUSTOMER" AND "YOU" REFER TO IT. IF YOU DO NOT HAVE THAT AUTHORITY, OR YOU DO NOT AGREE TO THESE TERMS, DO NOT ACCEPT THEM AND DO NOT USE THE SERVICE.** **THESE TERMS CONTAIN A BINDING ARBITRATION CLAUSE AND A WAIVER OF CLASS AND REPRESENTATIVE PROCEEDINGS (SECTION 26), EXCLUSIONS AND LIMITATIONS OF INFINIA'S LIABILITY (SECTION 20), A DISCLAIMER OF WARRANTIES INCLUDING AS TO THE ACCURACY OF MODEL OUTPUT (SECTION 18), AND RULES UNDER WHICH PREPAID CREDIT EXPIRES AND IS NON-REFUNDABLE (SECTION 10). THEY ALSO INCORPORATE THE ACCEPTABLE USE POLICY (ANNEX A), THE DATA PROCESSING ADDENDUM (ANNEX B) AND THE SERVICE MODE TERMS (SCHEDULE 1).** > The Data Processing Addendum (Annex B), the Sub-processor List (Annex D) and the Standard Contractual Clauses (Annex E) are published on the [Data Processing Agreement](/docs/legal/data-processing-agreement) page. ## Part A — Terms of Service ## 1. Introduction, parties and structure of the Agreement **1.1** These Terms of Service (the "Terms") are a legally binding agreement between [Infinia Legal Entity Name], a company incorporated in [●] with registered number [●] and registered office at [●] ("Infinia", "we", "us" or "our"), and the person or organisation that accepts them ("Customer", "you" or "your"). They govern access to and use of Xailon Token Factory, Infinia's sovereign inference and AI control-plane platform, including the portal, the application programming interfaces, the model catalogue, the Documentation and all related software, tools and services that Infinia makes available (together, the "Service"). **1.2** The following documents are incorporated into and form part of these Terms, and together with any Order Form constitute the entire agreement between the parties in respect of the Service (the "Agreement"): (a) Schedule 1 (Service Mode Terms); (b) Annex A (Acceptable Use Policy); (c) Annex B (Data Processing Addendum); (d) Annex C (Retention Schedule); (e) Annex D (Sub-processor List); (f) Annex E (Standard Contractual Clauses — completed annexes), which applies only to the processing described in Section B.6.2; (g) the Service Level Agreement for each Service Mode as published at [SLA URL] (the "SLA"); (h) the Privacy Notice published at [Privacy URL] (the "Privacy Notice"); (i) the Model Terms referred to in Section 7; and (j) the technical documentation, rate list and policies published at [Documentation URL] (the "Documentation"), each as updated from time to time in accordance with Section 22. **1.3** Order of precedence. If there is a conflict between the documents forming the Agreement, the following order applies, the earlier prevailing over the later: (a) a Master Services Agreement or other agreement signed by authorised representatives of both parties that expressly states it overrides these Terms (an "MSA"); (b) an Order Form signed or electronically accepted by both parties; (c) Annex E (Standard Contractual Clauses), to the extent of any conflict and only in respect of the transfers it governs; (d) Annex B (Data Processing Addendum), but only in respect of the processing of Personal Data; (e) the body of these Terms; (f) Schedule 1; (g) Annex A; (h) Annex C; (i) Annex D; (j) the Privacy Notice, but only in respect of Personal Data for which Infinia is a controller; (k) the SLA; (l) the Documentation. Model Terms govern the use of the relevant Model to the extent the Model Provider requires, as described in Section 7. **1.4** Enterprise customers. These Terms are the default contract for every use of the Service, including use by enterprise, public-sector and Zone Operator customers, unless and until an MSA is signed. Where an MSA is signed, these Terms continue to apply to any matter the MSA does not address, and any use of the Service by the Customer outside the scope of the MSA is governed by these Terms alone. **1.5** Self-serve customers. If you access the Service through the portal without a signed Order Form, your Order Form is the set of purchases, reservations and configuration choices you make in the portal, each of which is recorded in your account and billing history, and each of which you accept by completing the relevant purchase or activation step. **1.6** No other terms. Any terms that the Customer seeks to impose or incorporate, including terms in a purchase order, vendor-onboarding portal, supplier code or invoice acknowledgement, are excluded and have no effect, even if Infinia processes the purchase order or is registered on the portal. ## 2. Definitions and interpretation **2.1** In the Agreement the following capitalised terms have the meanings set out below. Other capitalised terms are defined where they first appear. **"Affiliate" means any entity that directly or indirectly controls, is controlled by, or is under common control with a party, where control means ownership of more than fifty per cent of the voting interests or the power to direct management and policies.** **"Applicable Law" means all laws, regulations, regulatory requirements, binding codes of practice and orders of any court or authority of competent jurisdiction that apply to a party, to the Service or to the Customer Content, including Data Protection Law, Export Control Law and AI Law.** **"AI Law" means any law or regulation governing the development, placing on the market, deployment or use of artificial intelligence systems, including Regulation (EU) 2024/1689 (the EU Artificial Intelligence Act) and any equivalent legislation in the UAE, India or another jurisdiction in which the Customer deploys the Service.** **"Authorised User" means an individual whom the Customer permits to access its Organisation, whether as an Owner, Admin or Developer, or through an application the Customer operates.** **"Batch" has the meaning given in Schedule 1.** "Biometric Data" means Personal Data resulting from specific technical processing relating to the physical, physiological or behavioural characteristics of a natural person which allows or confirms the unique identification of that person, including a voiceprint, voice model or faceprint, and includes biometric identifiers and biometric information as those terms are used in Applicable Law. **"Credit" means the prepaid, US-dollar-denominated balance recorded in the Customer's account and available to be drawn down against Fees, whether Purchased Credit, Promotional Credit, Service Credit or Contracted Credit, each as defined in Section 10.** **"Customer Content" means Inputs and Outputs together, and any Customer Model, dataset, file or other material that the Customer or its Authorised Users submit to or generate through the Service.** **"Customer Model" means a model, adapter, fine-tuned checkpoint or model weights supplied by the Customer for deployment on the Service, including through a Bring-Your-Own-Model feature.** **"Data Protection Law" means all Applicable Law relating to privacy and the processing of Personal Data, including Regulation (EU) 2016/679 (GDPR), the UK GDPR and Data Protection Act 2018, the ADGM Data Protection Regulations 2021, UAE Federal Decree-Law No. 45 of 2021 on the Protection of Personal Data, the Indian Digital Personal Data Protection Act 2023, and any implementing or successor legislation.** **"Dedicated Model Inference" has the meaning given in Schedule 1.** **"Documentation" has the meaning given in Section 1.2.** **"Export Control Law" means all Applicable Law relating to export, re-export, transfer and import controls, trade embargoes and economic or financial sanctions, including those of the United Arab Emirates, the United Nations Security Council, the European Union, the United Kingdom and the United States.** **"Fees" means all amounts payable to Infinia under the Agreement, including consumption charges, capacity charges, professional-services fees and applicable Taxes.** **"GPU Cluster" has the meaning given in Schedule 1.** **"High-Risk Use" means any use of the Service or Output in a context where failure or inaccuracy could reasonably be expected to result in death, personal injury, severe environmental or property damage, or the deprivation of a person's legal rights, liberty or essential services, and any use classified as high-risk or prohibited under AI Law.** **"Input" means any prompt, text, image, audio, file, embedding request, parameter, system instruction, tool definition or other data submitted to the Service by or on behalf of the Customer.** **"Model" means a machine-learning model made available through the Service, whether an open-weight model listed in the catalogue, a model supplied by a Model Provider under commercial terms, or a Customer Model.** **"Model Provider" means the person that licenses or otherwise makes a Model available for inclusion in the Service, including the publisher of an open-weight model.** **"Model Terms" means the licence, acceptable use policy and other terms that a Model Provider applies to a Model, as identified in the Model Catalogue.** **"Order Form" means an ordering document, quote or in-portal purchase or reservation that references the Agreement and specifies the Service Modes, capacity, rates, term and other commercial details.** **"Organisation" means the Customer's tenant within the Service, comprising its projects, Authorised Users, API keys, billing identity, spend controls and Region Scope.** **"Output" means any content, token stream, embedding, completion, classification, function call or other result generated by a Model in response to an Input.** **"Personal Data" has the meaning given in Annex B.** "Privacy Notice" means the notice published at [Privacy URL] describing the Personal Data that Infinia processes as a controller in its own right, including account, billing, identity-verification, support and website data. "Retention Schedule" means Annex C, which states every retention period referred to in the Agreement. "Stored Object" means Customer Content that the Customer elects to store through a feature of the Service that expressly provides for storage, including Batch input and output files, uploaded datasets and files, Customer Models and Fine-Tuned Models. Inputs and Outputs are not Stored Objects. "Sub-processor List" means Annex D, as updated in accordance with Section B.5.2. **"Region Scope" means the set of Sovereign Zones within which the Customer's Admin has configured the Organisation, or a particular project or API key, to be served.** **"Reserved Throughput" has the meaning given in Schedule 1.** **"Serverless" has the meaning given in Schedule 1.** **"Service Mode" means each of Serverless, Batch, Reserved Throughput, Dedicated Model Inference and, when made available, GPU Clusters.** **"Sovereign Zone" or "Zone" means a physical and logical deployment of the Service, comprising both the inference plane and the control plane, located within a defined country or jurisdictional bloc and identified as such in the Documentation.** **"Taxes" means all value added, goods and services, sales, use, withholding, digital-services and similar taxes, duties and levies, other than taxes on Infinia's net income.** **"Usage Data" means data generated by the operation of the Service that does not include the content of Inputs or Outputs, including request timestamps, model identifiers, token counts, latency, error codes, routing decisions, attestation records, API-key identifiers, region identifiers and billing records.** **"Zone Operator" means a third party that hosts, supplies compute for or operates a Sovereign Zone under agreement with Infinia.** **2.2** In the Agreement, headings are for convenience only and do not affect interpretation; "including" and similar expressions are illustrative and do not limit the words that precede them; references to a Section, Schedule or Annex are to those of these Terms unless stated otherwise; "written" and "in writing" include email and in-portal notices; and the singular includes the plural and vice versa. ## 3. Eligibility, accounts and organisations **3.1** Business use only. The Service is offered exclusively to businesses, public bodies, academic and research institutions and individuals acting in the course of a trade, business, profession or research activity. The Service is not offered to consumers, and by accepting these Terms you represent that you are not acting as a consumer. **3.2** Age and capacity. You must be at least eighteen years old and have the legal capacity to enter into a binding contract in your jurisdiction. Infinia may refuse registration or close any account that does not meet this requirement. **3.3** Representations on registration. By registering you represent and warrant that: (a) all registration, billing-identity and know-your-customer information you provide is complete, accurate and kept up to date; (b) you have not previously been suspended or removed from the Service; (c) neither you nor any of your Affiliates, owners or Authorised Users is a Restricted Party under Section 23; and (d) your use of the Service will comply with Applicable Law. **3.4** Authentication. Access to the portal is by federated sign-in through the identity providers listed in the Documentation. You are responsible for the security of your identity-provider account and for any multi-factor or conditional-access controls your organisation applies to it. Infinia does not issue or store passwords for the portal. **3.5** Organisations, roles and Authorised Users. The Service is organised around Organisations. The individual who creates an Organisation is its first Owner. Owners and Admins may invite Authorised Users and assign them the Owner, Admin or Developer role described in the Documentation. The Customer is responsible for every act and omission of its Authorised Users, and of any end user of an application the Customer operates using the Service, as if it were the Customer's own, and must ensure that each of them complies with the Agreement and the applicable Model Terms. There is no charge per Authorised User. **3.6** Billing identity. Before purchasing Credit or being invoiced the Customer must provide a billing identity consisting of its legal entity name, registration number, tax identification number and address. Infinia may verify this information and may decline to transact with a Customer whose billing identity cannot be verified. **3.7** Identity verification (KYC). Infinia performs identity, ownership and sanctions verification through a third-party verification provider identified in the Documentation. Verification is required before a Customer may (a) prepay more than the threshold stated in the Documentation (currently USD 1,000) in any calendar month, (b) be approved for postpaid invoicing, (c) purchase Reserved Throughput, Dedicated Model Inference or GPU Clusters, or (d) in any other case where Infinia reasonably considers verification necessary to comply with Applicable Law or manage risk. The Customer must promptly supply the information, documents and confirmations requested. Infinia may suspend the relevant functionality, or the account, until verification is complete, and may decline to proceed if verification fails. **3.8** One Organisation per legal entity for billing purposes. The Customer may operate multiple Organisations, but may not create Organisations or accounts to evade spend controls, credit limits, KYC thresholds, suspension or any other limit imposed under the Agreement. **3.9** Account security. The Customer must keep its account, identity-provider credentials and API credentials confidential and must notify Infinia at [security@●] immediately, and in any event within twenty-four hours, upon becoming aware of any actual or suspected unauthorised access to or use of its Organisation. Infinia is not liable for any loss arising from unauthorised use of the Customer's account or credentials, except to the extent the unauthorised use is directly caused by Infinia's breach of the Agreement. ## 4. Access to the Service and API credentials **4.1** Grant of access. Subject to the Agreement and payment of the Fees, Infinia grants the Customer a non-exclusive, non-transferable, non-sublicensable (except to Authorised Users and to end users of the Customer's applications as expressly permitted) right during the Term to access and use the Service, within the Customer's Region Scope, for the Customer's internal business purposes and for the purpose of developing and operating applications that the Customer offers to its own customers. **4.2** API keys and tokens. The Customer creates and manages API keys, personal access tokens and other credentials ("API Credentials") within its Organisation. Each API Credential is bound to a project and to a Region Scope and may be further restricted by model allow-list, spend cap and rate limit. The Customer is solely responsible for the confidentiality and security of its API Credentials and for all consumption, Fees and activity generated through them, whether or not authorised by the Customer, including consumption caused by misconfigured or runaway workloads and consumption following the leak or compromise of an API Credential, except to the extent directly caused by Infinia's breach of the Agreement. The Customer must revoke any API Credential it suspects has been compromised without delay. **4.3** Consumption record. All consumption is metered to a single authoritative record maintained within the Sovereign Zone in which the request was served. In any dispute about consumption or Fees, that record is conclusive absent manifest error. **4.4** No commercial rate limits; abuse protection. Infinia does not impose commercial rate limits on consumption: consumption is bounded only by the Customer's Credit, spend limits and credit limit. Infinia does apply throttles and safety mechanisms designed to stop runaway, abusive or malicious traffic and to protect the Service and other customers. These mechanisms are not tiered and cannot be removed commercially. Infinia may also apply temporary limits during incidents or capacity constraints. **4.5** Suspension, revocation and limitation of credentials. Infinia may suspend, revoke or limit any API Credential or account access, with or without notice, where it reasonably believes this is necessary to protect the Service, its infrastructure, other customers, a Model Provider, a Zone Operator, Infinia or any third party, or to comply with Applicable Law or a request from a competent authority. Infinia will notify the Customer and restore access as soon as reasonably practicable once the cause has been addressed. **4.6** Compatible interfaces. The Service exposes APIs that are designed to be compatible with published third-party API specifications. Compatibility is provided for the Customer's convenience only. Infinia does not warrant complete equivalence with any third-party interface, is not affiliated with or endorsed by any third party whose interface is emulated, and may change the compatibility surface in accordance with Section 22. **4.7** Software development kits and sample code. Any SDK, client library, sample code or reference implementation that Infinia makes available is licensed under the open-source or other licence stated with it or, where none is stated, under the licence in this Section 4.1, and is provided as-is under Section 18. ## 5. The Service and Service Modes **5.1** Description. The Service provides inference on Models, a management portal and API, identity and access management, metering and billing, audit logging, residency controls and related features, delivered from Sovereign Zones. The published model catalogue available within the Customer's Region Scope, via the published API, constitutes the scope of what the Customer may access. **5.2** Service Modes. The Service is sold in the Service Modes described in Schedule 1. Each Service Mode carries its own commitments, exclusions, billing basis, minimum term and cancellation rules, all set out in Schedule 1 and the SLA. Where this Part A and Schedule 1 both address a matter, Schedule 1 governs for the Service Mode concerned. **5.3** Planned Service Modes. Any Service Mode, feature or capability marked as planned, coming soon, beta, preview or similar in Schedule 1 or the Documentation is not contractually offered until Infinia notifies the Customer that it is generally available or the Customer accepts an Order Form for it. Section 16 applies to beta and preview features. **5.4** Changes to the Service. Infinia may modify the Service, including by adding, changing or removing Models, features, regions and interfaces, provided that it does not materially reduce the core functionality of a Service Mode that the Customer has paid for during its committed term. Section 7.7 governs Model deprecation. Section 22 governs changes to the Agreement. **5.5** Documentation and configuration. The Customer is solely responsible for its selection of Models, Service Modes, Sovereign Zones, parameters and account configuration, including permissions, spend controls, Region Scope and logging settings, and for determining whether the Service, a Model and the resulting Output are suitable for the Customer's intended use, including any regulated, safety-critical or customer-facing use. ## 6. Sovereignty, residency and data handling **6.1** Region Scope and residency. Infinia will serve each request wholly within the Sovereign Zone or Zones comprising the Region Scope applicable to the API Credential that made it, and will keep the control plane for that request, including identity records, metering, routing decisions and audit logs, within the same Region Scope. Inputs and Outputs are not transmitted to, processed in or stored in any location outside the Region Scope. **6.2** Fail-closed refusal. A request that cannot be served within its Region Scope, whether because of capacity, Model availability, residency constraint or licensing restriction, will be refused with an error indication and will not be served from another region. The Customer acknowledges that this is a deliberate design property of the Service and that a refusal for this reason is not a failure of the Service or an SLA event, except as expressly provided in the SLA for Reserved Throughput. **6.3** Zero content retention. Infinia does not store, log, retain or otherwise persist Inputs or Outputs beyond the transient processing required to serve the request and return the response, and does not retain them after the response has been returned. Infinia does not read, inspect, review, filter, screen, sample or otherwise examine the content of any Input or Output, whether by human or by automated means, and has no ability to retrieve, reproduce or produce an Input or Output once the request has completed. This commitment is absolute and admits of no exception. The only records retained are Usage Data and Stored Objects, each retained for the period stated in Annex C and within the Region Scope. **6.4** No training on Customer Content. Infinia will not use Inputs, Outputs, Customer Models or any other Customer Content to train, retrain, fine-tune, evaluate or otherwise improve any Model or any machine-learning system, and will not permit any Model Provider, Zone Operator or other third party to do so through the Service. This Section 6.4 is not subject to opt-in and cannot be varied except by a written agreement signed by both parties. **6.5** Usage Data. Infinia collects and processes Usage Data to operate, secure, meter, bill, support and improve the Service, to attribute consumption to Model Providers and Zone Operators for settlement, to detect abuse and to produce aggregated statistics. Usage Data is retained within the Region Scope for the period stated in Annex C, and for as long as required by Applicable Law for billing and tax records, as Annex C also states. Infinia may use Usage Data in aggregated or de-identified form that does not identify the Customer, an Authorised User or any individual for any lawful purpose, including publishing aggregate platform statistics. **6.10** Integrity controls on Stored Objects. Section 6.3 prevents any examination of Inputs and Outputs. Stored Objects are different, because they rest on Infinia's infrastructure and can carry risk to the Service, to other customers and to third parties. Infinia therefore applies two controls, and only these two, to Stored Objects: (a) comparison of stored image, audio and video files against cryptographic hash lists of material identified by a competent authority or recognised child-protection organisation as child sexual abuse material, which returns only a match or no match and reveals nothing else about the file; and (b) structural scanning of Customer Models, Fine-Tuned Models and uploaded files for malware and for unsafe serialisation and deserialisation formats. Neither control reads, interprets or retains the substantive content of a Stored Object, neither is applied to Inputs or Outputs, and neither is used for any purpose other than the one stated. Infinia will describe both controls, and any change to them, in the Trust Centre. **6.11** Personal Data for which Infinia is a controller. In addition to the Customer Content that Infinia processes on the Customer's behalf under Annex B, Infinia processes Personal Data as a controller in its own right in order to administer the relationship, verify identity, meter and invoice, provide support, secure the Service and comply with Applicable Law. That processing is described in the Privacy Notice, which states the categories of data, the purposes and legal bases, the retention periods, the recipients, the transfer mechanisms and the rights available to individuals. Nothing in the Privacy Notice varies Sections 6.1 to 6.4 or Annex B. **6.6** Attestation and audit evidence. Where the Documentation states that a Sovereign Zone offers per-request attestation, signed residency exports or an immutable audit log, Infinia will make the relevant evidence available through the portal or API as described. Attestation depends on hardware and firmware supplied by third parties; Infinia does not warrant the security of that hardware or firmware beyond the commitments in Section 13, and the Customer is responsible for verifying attestation evidence against its own policies. **6.7** Personnel access. Infinia restricts operational and support access to production systems in each Sovereign Zone to personnel authorised for that Zone, applies least-privilege and logged access, and does not permit debugging or support activity that would break the commitments in Sections 6.1 to 6.4. Where the Documentation identifies the jurisdiction of operating personnel for a Zone, Infinia will give the Customer not less than thirty days' notice of a change that would move operational access outside that jurisdiction. **6.8** Government and legal requests. If Infinia or a Zone Operator receives a request or order from a governmental authority, court or law-enforcement body for access to Customer Content or Usage Data, Infinia will, unless legally prohibited, promptly notify the Customer, challenge any request that it reasonably considers overbroad or unlawful, disclose only the minimum required, and record the request in the transparency reporting described in the Documentation. Because Infinia does not retain Inputs or Outputs, Infinia will ordinarily have no Customer Content to disclose. **6.9** Customer responsibilities. The Customer is responsible for configuring its Region Scope, project and key settings so that they reflect the Customer's own legal and regulatory requirements, for confirming that the Sovereign Zone it selects is appropriate for the data it intends to process, and for any consequence of submitting Customer Content to a Zone that its own obligations do not permit. ## 7. Models, Model Terms and lifecycle **7.1** Model catalogue. Infinia makes Models available in the model catalogue published in the portal and Documentation (the "Model Catalogue"), which identifies for each Model its provider, licence, Model Terms, supported modalities, the Sovereign Zones in which it is available and any geographic or use restrictions. Infinia may add Models to, or subject to Section 7.7 remove Models from, the Model Catalogue at any time. **7.2** Model Terms are binding. The Customer must comply, and must ensure that its Authorised Users and the end users of its applications comply, with the Model Terms applicable to each Model it uses, including any attribution, acceptable-use, redistribution, geographic and field-of-use restrictions, and any obligation that flows down to downstream users of the Model or of derivative works. The Customer is solely responsible for reviewing the Model Terms before using a Model and for determining that its use is permitted. Model Providers may amend their Model Terms; the Customer's continued use of a Model after Infinia publishes or links to the amended Model Terms constitutes acceptance of them. **7.3** Restricted Models. Some Model Providers prohibit use of their Models by persons in, or nationals of, certain countries, or by certain categories of entity or for certain purposes ("Restricted Models"). The Customer must not access or attempt to access a Restricted Model in breach of those restrictions, including by using a VPN, proxy, misdeclared location or another Customer's account, and must not circumvent any safeguard Infinia applies to enforce them. Breach of this Section 7.3 is a material breach that entitles Infinia to suspend or terminate immediately. **7.4** Conflicts. As between the Customer and Infinia, the Agreement governs. As between the Customer and a Model Provider, the Model Terms govern the Customer's use of that Model to the extent the Model Provider requires. Nothing in the Agreement modifies, waives or limits any Model Terms, and Infinia gives no warranty that any Model Terms are accurate, current or enforceable, or that links to them are correct, although Infinia will use reasonable efforts to keep the Model Catalogue accurate. **7.5** Model Provider requirements and enforcement. The Customer will promptly supply any information, certification or confirmation that Infinia reasonably requests to demonstrate compliance with Model Terms. Infinia may suspend, restrict or disable the Customer's access to any Model where it reasonably believes the Customer is in breach of the Model Terms, where a Model Provider so requires or requests, or where continued availability would expose Infinia or a Zone Operator to legal risk. Infinia will use commercially reasonable efforts to give advance notice but does not guarantee it, and is not liable for any suspension, removal or degradation of a Model that results from the acts or requirements of a Model Provider. **7.6** Model Providers as third-party beneficiaries. Each Model Provider is an intended third-party beneficiary of Sections 7, 8.4, 9, 19.1 and 20, to the extent those provisions relate to the Customer's access to and use of that provider's Model, and may enforce them directly against the Customer. **7.7** Model lifecycle and deprecation. Infinia will give not less than the deprecation notice period stated in Annex C for the relevant Model tier (and in any event not less than thirty days for a generally available Model, and ninety days for a Model that is the subject of an active Reserved Throughput or Dedicated Model Inference commitment) before retiring a Model from a Sovereign Zone, will name a successor Model where one exists, and will describe the migration path. Version-pinned requests must be migrated by the Customer before the stated sunset date, after which they will be refused. Infinia may shorten the notice period where the Model Provider withdraws the Model, where the Model is found to present a material security, legal or safety risk, or where Applicable Law so requires, in which case Infinia will give as much notice as is reasonably practicable. **7.8** Customer Models. Where the Service permits the Customer to deploy a Customer Model, the Customer (a) is solely responsible for the Customer Model, including its provenance, licensing, safety, export-control classification and fitness for purpose; (b) represents and warrants that it has all rights necessary to deploy the Customer Model on the Service and to grant the licence in Section 8.3; (c) grants Infinia the licence in Section 8.3 in respect of the Customer Model; and (d) acknowledges that Infinia applies the structural scanning described in Section 6.10(b) to Customer Models, and may refuse or remove any Customer Model that fails that scan or that Infinia reasonably believes is unlawful or a breach of the Agreement. That scan is structural only and does not involve any examination of the substantive content of the Customer Model. **7.9** Fine-tuned models. Where Infinia offers fine-tuning, any model weights, adapters or checkpoints produced by fine-tuning a base Model on the Customer's data (a "Fine-Tuned Model") are Customer Content as between the Customer and Infinia, subject always to the Model Terms of the base Model, which may impose conditions on derivative models. Infinia will not use a Fine-Tuned Model for any purpose other than serving the Customer, and will delete it in accordance with Section 21.6 and Annex B on termination or on the Customer's instruction. ## 8. Customer Content: Inputs and Outputs **8.1** Ownership. As between the parties, the Customer retains all right, title and interest in and to its Inputs. Infinia does not claim ownership of Outputs and, to the extent Infinia has any rights in Outputs, assigns them to the Customer on generation, subject to the Model Terms of the Model that generated them. The Customer acknowledges that (a) Outputs are generated by statistical models and may not be unique, so that the same or similar Outputs may be generated for other customers, and no Customer acquires rights in Output generated for another; (b) the extent to which Output is protectable by copyright or other rights varies by jurisdiction and Infinia gives no warranty about it; and (c) Output does not confer any right in the Service, any Model or any Infinia or Model Provider intellectual property. **8.2** Responsibility for Customer Content. The Customer is solely responsible for its Customer Content, for its use of Outputs, and for the lawfulness, accuracy, appropriateness and consequences of both. Content passes through the Service unmoderated. Infinia does not monitor, review, filter or moderate Inputs or Outputs and, as provided in Section 6.3, has no capability to do so; the Customer must not rely on Infinia to detect, prevent or remedy any unlawful, harmful or infringing content. Infinia's only examination of Customer Content is the two integrity controls applied to Stored Objects under Section 6.10, and Infinia may refuse, block or remove a Stored Object, or suspend or terminate access under Section 21, on the basis of what those controls return or of any other information lawfully available to it. Enforcement of Annex A otherwise proceeds on Usage Data, on reports made under Section A.7 and on information the Customer supplies, not on inspection of content. **8.3** Licence to Infinia. The Customer grants Infinia and its sub-processors a non-exclusive, worldwide, royalty-free licence during the Term to receive, transmit, process, cache transiently, reproduce and, where a storage feature is used, store Customer Content, solely to the extent necessary to provide, secure and support the Service in accordance with the Agreement and the Customer's instructions. This licence does not permit the use described in Section 6.4 and terminates automatically when the relevant Customer Content is deleted or the Agreement ends, save for transient copies in the course of deletion. **8.4** Customer warranties. The Customer represents and warrants that (a) it owns or has obtained all rights, licences, consents and permissions, including any consents required under Data Protection Law and any notices to data subjects, necessary to submit its Inputs and Customer Models to the Service and to grant the licence in Section 8.3; (b) its Inputs, Customer Models and its intended use of Outputs do not and will not infringe, misappropriate or violate any intellectual-property, privacy, publicity, confidentiality or other right of any person or breach Applicable Law; and (c) it will not submit Inputs that it is prohibited by Applicable Law or by contract from disclosing to a processor located in the relevant Sovereign Zone. **8.5** Sensitive and regulated data. The Service is designed for regulated workloads, and the Customer may submit Personal Data, including special categories of Personal Data, health data and financial data, provided that the Customer (a) has selected a Sovereign Zone appropriate for that data, (b) has executed Annex B and any additional jurisdiction-specific terms that the Documentation requires, (c) has the legal basis and consents required, and (d) complies with all Applicable Law relating to that data. The Customer must not submit payment-card primary account numbers, security codes or other cardholder data unless the Documentation confirms that the relevant Sovereign Zone and Service Mode are validated for that purpose. Infinia is not a covered entity or business associate under any healthcare law, and does not accept any obligation as such except in a signed agreement. **8.6** Files, datasets and stored objects. Where the Customer uses a feature that stores files, datasets, Batch inputs or outputs, or other objects, those objects are Customer Content, are stored within the Region Scope, are retained until the Customer deletes them or for the period stated in Annex C, and are subject to the size, type and frequency limits stated in the Documentation. Stored Objects are subject to the integrity controls in Section 6.10. The Customer is responsible for maintaining its own copies. Infinia is not liable for loss, corruption or unavailability of stored objects except to the extent caused by its gross negligence or wilful misconduct. **8.7** Output disclaimer and human oversight. Output is generated automatically and may be inaccurate, incomplete, offensive, biased, out of date or otherwise unsuitable, and may appear authoritative while being wrong. The Customer must not rely on Output as a sole basis for any decision affecting a person's legal rights, safety, health, finances, employment, education, access to essential services or similar interests without appropriate human review, and must implement the human oversight, testing, monitoring, logging and transparency measures that Applicable Law, including AI Law, requires of a deployer of AI systems in its context. The Customer is the deployer, and where applicable the provider, of any AI system it builds using the Service for the purposes of AI Law. Infinia is not the deployer or the provider of the Customer's AI system, and nothing in the Agreement makes it so. Infinia's own role in respect of the Models it makes available in the Model Catalogue, including whether it acts as a distributor, importer or provider of any Model in a given jurisdiction, is determined by AI Law and not by this Section 8.7; Infinia will comply with the obligations that attach to whatever role it occupies, will state in the Model Catalogue the role it understands itself to occupy for each Model, and will make available in the Trust Centre the documentation and instructions for use that a deployer requires from it under AI Law. **8.8** Location signals. Where the Service passes information about the geographic origin of a request to a Model or Model Provider for compliance purposes, Infinia will use reasonable efforts to pass accurate information but does not warrant its accuracy and is not liable for any consequence of inaccuracy attributable to network topology or Customer configuration. **8.9** Biometric Data. Certain Models in the Model Catalogue accept image, audio or video Inputs, or generate them as Outputs, and certain of those Models perform processing that constitutes the processing of Biometric Data, including voice cloning, voice conversion, speaker identification, voice-preserving translation and the generation of a likeness of a person's face from a sample. The Model Catalogue identifies each Model that performs such processing. Where the Customer uses such a Model: (a) the Customer is the controller of the resulting Biometric Data and is responsible for identifying a condition under Article 9 of the GDPR or the equivalent provision of the applicable Data Protection Law, which will in most cases be the explicit consent of the individual concerned; (b) the Customer must not submit the voice, face or likeness of any individual other than itself without that individual's informed, specific and documented consent, obtained in the form Applicable Law requires, and must be able to evidence that consent on request; (c) the Customer acknowledges that certain jurisdictions impose additional written-notice, consent, retention-schedule and non-disclosure requirements in respect of biometric identifiers, and that compliance with them is the Customer's responsibility; and (d) because of Section 6.3, Infinia does not extract, derive, generate, store or retain any biometric identifier, template, voiceprint or faceprint from an Input or Output, and acts solely as a conduit between the Customer and the Model within the Sovereign Zone. Where the Customer stores image, audio or video material as a Stored Object, Section 6.10 applies to it and Annex C states the retention period. ## 9. Acceptable use **9.1** The Customer must comply, and must ensure that its Authorised Users and end users comply, with the Acceptable Use Policy in Annex A, which forms part of the Agreement. Infinia may update Annex A in accordance with Section 22; where a change is required to respond to a new legal requirement or a material risk of harm, it may take effect on publication. **9.2** Without limiting Annex A, the Customer must not: (a) resell, sublicense, rent, lease, time-share or otherwise make the Service available to third parties as a stand-alone inference service or as a substitute for the Service, other than as embedded functionality within the Customer's own application and other than under a written reseller, white-label or Zone Operator agreement with Infinia; (b) use the Service to develop, train or benchmark a competing inference service, or publish comparative benchmarks of the Service without Infinia's prior written consent, other than as permitted by Applicable Law; (c) reverse engineer, decompile or disassemble any part of the Service except to the extent Applicable Law expressly permits notwithstanding this restriction; (d) probe, scan or test the vulnerability of the Service, or circumvent any security, authentication, residency, attestation, metering or rate-limiting control, other than under Section 9.3; (e) interfere with the integrity or performance of the Service or with any other customer's use of it; (f) introduce malicious code; or (g) attempt, encourage or assist any of the foregoing. **9.3** Security research and red-teaming. Prompt injection, jailbreaking, model extraction and other adversarial testing of Models or of the Service are prohibited unless Infinia has approved them in writing in response to a request submitted at [security@●] describing the scope, method, duration and purpose. Infinia will use reasonable efforts to respond within five business days. Approval is discretionary, may be conditioned, and does not waive any Model Terms. Responsible disclosure of vulnerabilities discovered in the ordinary course of use is welcome at the same address and, where made in good faith and in accordance with the Documentation's disclosure policy, will not be treated as a breach of Section 9.2(d). **9.4** Enforcement. Infinia may investigate any suspected breach of this Section 9 or Annex A, may cooperate with law enforcement and competent authorities, and may suspend or terminate access under Section 21. Infinia's rights under this Section 9 are without prejudice to any other right or remedy. ## 10. Fees, Credit and payment **10.1** Rates. Fees for consumption are calculated at the rates published in the rate list in the Documentation for the applicable Model, Service Mode and Sovereign Zone at the time the request is served (the "Published Rates"), unless an Order Form specifies different rates. Published Rates are not negotiated and may differ by Sovereign Zone. Fees for capacity-based Service Modes are as stated in the Order Form. The cost of each request is reported in the API response and in the portal. **10.2** Currency. All pricing, balances, invoices and settlement are denominated in United States dollars. Any conversion by the Customer's bank or card issuer, and any associated fees or exchange-rate loss, is outside Infinia's pricing and is the Customer's responsibility. **10.3** Payment instruments. The Customer funds consumption either by prepaid Credit or, if approved under Section 10.12, by postpaid monthly invoice. Capacity charges for Reserved Throughput, Dedicated Model Inference and GPU Clusters are always invoiced. The Customer must maintain the funding required for each Service Mode as described in Schedule 1, including funded Credit or postpaid headroom for traffic above a reservation. **10.4** Purchasing Credit. Credit is purchased through the hosted payment page of Infinia's payment provider. Infinia does not see or store card details. Purchased Credit is spendable immediately upon successful payment; Credit funded by bank transfer or another delayed method is available only once funds have cleared. Infinia may set a minimum purchase amount and maximum per-transaction amount, stated in the Documentation. Volume purchases are expressed as additional Credit granted, not as a reduced charge, and the additional Credit is Promotional Credit for the purposes of Section 10.8. **10.5** Nature of Credit. Credit is a prepayment for Services. It is not money, electronic money, a stored-value instrument, a deposit or a security; it has no cash value except as expressly provided in Section 10.7; it accrues no interest; it confers no property right; it may not be transferred between Organisations or to any third party; and it may be used only to pay Fees within the Service. Infinia may hold Credit in any manner it chooses and is not obliged to segregate funds. **10.6** Credit expiry. All Credit expires. (a) Purchased Credit expires twelve months from the date of purchase; each purchase carries its own expiry, and a later purchase does not extend an earlier one. (b) Promotional Credit expires on the date stated when it is granted, and in any event no later than twelve months after grant. (c) Service Credit expires twelve months from issue. (d) Contracted Credit expires at the end of the contract term to which it relates. Non-use of the Service does not pause or extend any expiry. Credit is consumed in the following order: Promotional Credit before Purchased Credit; within each type, the earliest-expiring grant first; where expiry dates match, the oldest grant first. Infinia will notify the Owners, Admins and billing contact by email and in the portal not less than thirty days and again not less than seven days before any grant expires, stating the amount and date. On expiry the unspent balance of that grant is voided, is recorded in the Customer's credit history and cannot be spent, reinstated or refunded. **10.7** Refunds. Credit purchases are non-refundable, and Infinia has no obligation to refund any Credit, except that Infinia will refund: (a) a duplicate or accidental purchase reported within twenty-four hours of the transaction, to the extent unspent; (b) any amount charged as a result of Infinia's own billing error; (c) any amount charged after the Customer's account has been closed; (d) unspent Purchased Credit where Infinia terminates the Agreement other than for the Customer's breach, as provided in Section 21.5; and (e) unspent Purchased Credit where Infinia becomes unable to serve the Customer for regulatory or sanctions reasons not attributable to the Customer. Any other refund is at Infinia's sole discretion, is not an entitlement, and where granted is made to the original payment method. Change of mind, project cancellation, expiry of Credit, consumption by the Customer's own code including runaway workloads, dissatisfaction with Output, migration to another provider and unspent Credit at voluntary closure are not grounds for refund. Where Infinia has failed to meet a commitment, the remedy is Service Credit under the SLA, not a cash refund. **10.8** Promotional Credit. Infinia may grant starter, campaign, referral, migration, startup, academic or other promotional credit ("Promotional Credit") on the conditions stated when it is offered. Promotional Credit has no cash value, is non-refundable, non-transferable and non-exchangeable, may not be applied against Taxes, may be subject to eligibility verification, and may be withdrawn if obtained through misrepresentation or in breach of its conditions. Infinia may modify or discontinue a promotional programme prospectively but will not retroactively reduce unexpired Promotional Credit already granted except where the grant was obtained in breach of its conditions or Applicable Law so requires. Where a separate programme agreement applies, it governs the Promotional Credit in the event of conflict. **10.9** Automatic top-up. The Customer may opt in to automatic top-up of Credit when the balance falls below a threshold the Customer sets. Automatic top-up is off by default. It is subject to the guardrails stated in the Documentation, including a maximum number of top-ups per day, a maximum single amount, a daily ceiling and a cool-off period after a failed charge, each of which the Customer may tighten but not exceed. By enabling automatic top-up the Customer authorises Infinia and its payment provider to charge the nominated payment method accordingly. Infinia will notify the Customer immediately if an automatic charge fails and is not liable for any interruption of service that results from a failed automatic charge. **10.10** Credit exhaustion. When Credit reaches zero and no postpaid headroom exists, the Service stops serving requests that would draw on Credit. A streaming response in progress stops at the point of exhaustion and only the consumption actually served is charged. Nothing is served that cannot be billed, and Infinia will not invoice for consumption it declined to serve. **10.11** Spend controls. Owners and Admins may set spend limits at Organisation and project level. A project limit cannot exceed the Organisation limit. When a limit is reached the relevant scope stops serving until the limit is raised; Infinia does not throttle below a limit. Spend limits are financial controls set by the Customer for its own protection and their operation is not an SLA event. Warnings are sent at the thresholds stated in the Documentation. **10.12** Postpaid invoicing. Infinia may, at its discretion and subject to identity and ownership verification, a credit assessment and any additional signed terms it requires, approve a Customer for postpaid invoicing and set a credit limit. The credit limit is the maximum amount the Customer may owe at any time and is Infinia's risk control, not the Customer's budget; when it is reached, consumption stops across the whole Organisation until the balance is settled or Infinia raises the limit. Invoices are issued shortly after the close of each monthly billing cycle and are payable in full within thirty days of the invoice date by bank transfer or card. A Customer may return to prepaid only after settling its outstanding balance in full. Infinia may withdraw postpaid approval, reduce a credit limit or require security or prepayment at any time on notice where its assessment of credit risk changes. **10.13** Late payment. If any invoiced amount is not paid when due, Infinia may (a) send reminders at the due date and seven days thereafter; (b) after a grace period ending fourteen days after the due date, suspend new consumption and disable the Customer's API Credentials from the fifteenth day; (c) charge interest on the overdue amount at the lower of one and a half per cent per month and the maximum rate permitted by Applicable Law, accruing daily from the due date until payment; (d) refer the debt to collections from the thirtieth day and recover its reasonable costs of collection, including legal fees; and (e) set off any amount the Customer owes against any amount Infinia owes the Customer. Suspension is lifted promptly on payment. Suspension for non-payment does not terminate any Reserved Throughput, Dedicated Model Inference or GPU Cluster commitment, and the capacity charges for those commitments continue to accrue during suspension as provided in Schedule 1, because the capacity remains committed on the Customer's behalf. **10.14** Invoice disputes. The Customer must notify Infinia of any good-faith dispute of an invoice, with reasonable detail, within fifteen days of the invoice date. The disputed amount remains owed until the dispute is resolved, but Infinia will not suspend the Customer solely for non-payment of an amount that is the subject of an open good-faith dispute raised within that period. The undisputed portion must be paid when due. Infinia will investigate against the consumption record referred to in Section 4.3, and the parties will cooperate in good faith to resolve the dispute within thirty days. **10.15** Taxes. Fees are exclusive of Taxes. The Customer is responsible for all Taxes arising from its purchases, other than taxes on Infinia's net income. If the Customer is required by Applicable Law to withhold or deduct any amount from a payment, it must gross up the payment so that Infinia receives the full amount it would have received absent the withholding, and must supply Infinia with evidence of payment of the withheld amount to the relevant authority. The Customer must provide a valid tax identification number and any exemption certificate on which it relies; if it fails to do so Infinia may charge Taxes at the applicable rate. **10.16** Price changes. Infinia will give not less than thirty days' notice by email and in the portal of any increase in Published Rates or the introduction of a new Fee. Reductions in Published Rates take effect immediately and apply to all Customers. Rates specified in an Order Form are fixed for the term of that Order Form. Fees for a request are determined by the Published Rates in force when the request is served. **10.17** Payment-provider terms. Payment is processed by a third-party payment provider whose terms apply to the payment transaction. The Customer is responsible for any chargeback, reversal or payment-provider fee caused by its own act, and Infinia may suspend an account against which a chargeback is raised pending resolution and may recover reversed amounts and associated fees as a debt. ## 11. Service levels and support **11.1** Service levels. Infinia's availability, throughput and other service-level commitments, and the Service Credits payable for failing to meet them, are set out in the SLA applicable to each Service Mode. Serverless and Batch carry no throughput, latency or availability commitment except as expressly stated in the SLA. The SLA sets out the sole and exclusive remedy for any failure to meet a service-level commitment. **11.2** Service Credits. Service Credits are issued to the Customer's account, not paid in cash, expire under Section 10.6(c), and for Reserved Throughput may be issued as additional reserved capacity time rather than Credit. Infinia will issue Service Credits proactively where it can identify affected Customers; the Customer may also claim within the period stated in the SLA. Service Credits are not payable in respect of unavailability caused by the Customer, by Customer configuration including Region Scope and spend controls, by a Model Provider's withdrawal of a Model, by force majeure, by scheduled maintenance notified in accordance with the SLA, or by suspension permitted under the Agreement. **11.3** Support. Infinia provides the support described in the Documentation for the Customer's plan. Support requests are raised through the portal or at [support@●]. Enhanced support tiers, onboarding and solution engineering are available under an Order Form. **11.4** Status and incident communication. Infinia will publish service status and incident notices at the location stated in the Documentation and will send technical notifications to the technical contacts recorded in the Organisation. The Customer must keep its contacts current. ## 12. Reserved capacity commitments **12.1** Capacity Service Modes. Reserved Throughput, Dedicated Model Inference and GPU Clusters (the "Capacity Modes") are commitments to hold capacity for the Customer for a term, and the Customer pays for the capacity whether or not it uses it. The specific terms of each Capacity Mode are in Schedule 1 and the Order Form. **12.2** No cancellation within term. Except where Schedule 1 or the Order Form expressly permits, the Customer may not cancel or reduce a Capacity Mode commitment during its term. Non-renewal requires notice before the end of the term as stated in the Order Form. Unused capacity does not roll over and is not credited or refunded. **12.3** Sizing. The Customer is responsible for sizing its reservation or deployment, with the guidance and tools Infinia provides, and for maintaining any funded Credit or postpaid headroom that Schedule 1 requires alongside the commitment. **12.4** Capacity partners. Capacity Modes are delivered on Infinia and Zone Operator compute. Infinia will not sell a reservation without confirmed capacity behind it and does not oversell reserved capacity. Infinia may use reserved capacity that the Customer is not consuming at a given moment for Batch work and internal operations, stepping aside when the Customer's traffic returns, and this does not diminish the Customer's throughput commitment. ## 13. Security and compliance **13.1** Infinia's security programme. Infinia will maintain an information-security programme with administrative, technical and physical safeguards appropriate to the sensitivity of Customer Content and designed to protect its confidentiality, integrity and availability, including the measures described in Annex B and the Documentation. Infinia will maintain the certifications and attestations listed in the Trust Centre at [Trust URL] for the periods stated there, or successor certifications of equivalent scope, and will make the associated reports available to the Customer on request under Section 14. **13.2** Regulatory readiness. Infinia will maintain and publish in the Trust Centre a description of how the Service supports Customers' obligations under the regulatory frameworks listed there. Publication of that description is not legal advice, and the Customer remains responsible for its own compliance. **13.3** Security incidents. Infinia will notify the Customer without undue delay, and in any event within the period stated in Section B.8, after becoming aware of a Security Incident (as defined in Annex B) affecting the Customer's Customer Content or Organisation, and will provide the information and cooperation Annex B describes. Where an incident affects Personal Data for which Infinia is a controller under Section 6.11, Infinia is responsible for any notification to a supervisory authority and to affected individuals that Data Protection Law requires of it, and will notify the Customer of any such incident affecting its Authorised Users within the same period. **13.4** Customer security obligations. The Customer is responsible for security within its own environment, including the security of its applications, its identity provider, its API Credentials, its end-user access controls, its selection of Region Scope and logging settings, and any content it stores through the Service. **13.5** Audit and assurance. The Customer's audit rights are as set out in Annex B. Enterprise and public-sector Customers requiring additional assurance, including on-site inspection, regulator access or bespoke evidence, may agree it in an MSA. ## 14. Confidentiality **14.1** Definition. "Confidential Information" means all non-public information disclosed by or on behalf of one party (the "Discloser") to the other (the "Recipient") in connection with the Agreement that is marked or identified as confidential or that a reasonable person would understand to be confidential, including Customer Content, the Customer's Usage Data, Infinia's security reports and audit evidence, non-public product plans, pricing in an Order Form, and the terms of any MSA. Confidential Information does not include information that (a) is or becomes public other than through the Recipient's breach; (b) was known to the Recipient without restriction before disclosure; (c) is lawfully received from a third party without restriction; or (d) is independently developed without reference to the Discloser's Confidential Information. Feedback under Section 15.4 is not Confidential Information of the Customer. **14.2** Obligations. The Recipient will use the Discloser's Confidential Information only to perform or exercise its rights under the Agreement, will protect it with at least the degree of care it uses for its own confidential information of similar sensitivity and no less than reasonable care, and will disclose it only to its and its Affiliates' employees, contractors, professional advisers and, in Infinia's case, sub-processors and Zone Operators, who need to know it for those purposes and are bound by confidentiality obligations no less protective than this Section 14. **14.3** Compelled disclosure. The Recipient may disclose Confidential Information to the extent required by Applicable Law, a court order or a competent authority, provided that (where legally permitted) it gives the Discloser prompt notice and reasonable assistance to seek protective treatment, and discloses only the minimum required. Section 6.8 applies to requests for Customer Content. **14.4** Duration and return. This Section 14 applies during the Term and for five years after it ends, except that obligations in respect of Customer Content and trade secrets continue for as long as the information remains confidential. On termination each party will, on request, return or destroy the other's Confidential Information, subject to Section 21.6 and to retention required by Applicable Law or in routine backups for a limited period, during which this Section 14 continues to apply. **14.5** Remedies. Each party acknowledges that breach of this Section 14 may cause irreparable harm for which damages would be an inadequate remedy, and that the Discloser is entitled to seek injunctive or other equitable relief in addition to any other remedy. ## 15. Intellectual property, feedback and Usage Data **15.1** Infinia IP. Infinia and its licensors own all right, title and interest, including all intellectual-property rights, in and to the Service, the Documentation, the portal, the APIs, the serving stack, the routing and control-plane software, the attestation and audit tooling, all SDKs and sample code, all Usage Data, and all improvements, modifications and derivative works of any of them (the "Infinia IP"). Except for the limited rights expressly granted in the Agreement, no right or licence in the Infinia IP is granted to the Customer, whether by implication, estoppel or otherwise, and all rights are reserved. **15.2** Model Provider IP. Models, other than Customer Models and Fine-Tuned Models to the extent of the Customer's contribution, remain the intellectual property of the relevant Model Provider or its licensors and are licensed to the Customer under the Model Terms, not by Infinia. **15.3** Customer IP. The Customer owns its Customer Content as set out in Section 8.1, its applications, and its trade marks and branding. Infinia acquires no rights in them except the licence in Section 8.3 and the limited publicity rights in Section 25. **15.4** Feedback. If the Customer or an Authorised User provides suggestions, ideas, enhancement requests or other feedback about the Service ("Feedback"), Infinia may use it without restriction or obligation, and the Customer grants Infinia a perpetual, irrevocable, worldwide, royalty-free, sublicensable licence to use and exploit Feedback for any purpose. Feedback does not include Customer Content. **15.5** Usage Data. Infinia may collect, generate and use Usage Data as described in Section 6.5. Usage Data is Infinia IP, save that the Customer may access and export the Usage Data relating to its own Organisation through the portal and API during the Term, and Infinia's use of Usage Data that identifies the Customer is subject to Section 14. **15.6** Trade marks. "Infinia", "Xailon Token Factory", the associated logos and all other Infinia trade marks and trade names are the property of Infinia or its Affiliates. The Customer may not use them without Infinia's prior written consent, except as permitted by Section 25 or by fair-use principles under Applicable Law. **15.7** Notice and action. Because Inputs and Outputs are not retained, Infinia hosts no content other than Stored Objects. Any person who believes that a Stored Object infringes their copyright or other right, or is otherwise unlawful, may submit a notice to [legal@●] identifying the right relied on, the material complained of with sufficient precision to allow it to be located, the complainant's contact details, a statement that the complaint is made in good faith, and a declaration that the information given is accurate. Infinia will acknowledge the notice, will act on it in a timely, diligent and non-arbitrary manner, will give the Customer whose Stored Object is affected a statement of the reasons for any decision to disable or remove it together with the means to contest that decision, and will inform the complainant of the outcome. Infinia may suspend the processing of notices from a complainant that repeatedly submits manifestly unfounded notices, and may suspend or terminate a Customer that is a repeat infringer. Infinia will publish in the Trust Centre a point of contact for authorities and for the recipients of the Service, and such reporting on notices received and action taken as Applicable Law requires. Nothing in this Section 15.7 obliges Infinia to monitor Stored Objects generally or to seek facts indicating unlawful activity, and nothing in it varies Section 6.3. ## 16. Beta, preview and planned features **16.1** Infinia may make features, Models, Service Modes or Sovereign Zones available on a beta, preview, early-access, planned or similar basis ("Beta Features"). Beta Features are identified as such in the portal or Documentation. **16.2** Beta Features are provided for evaluation, may contain bugs, may be changed, suspended or withdrawn at any time without notice, may not be covered by the SLA, the security commitments in Section 13 or the deprecation notice in Section 7.7, and may be subject to additional terms. The Customer uses Beta Features at its own risk and must not use them for production workloads or for the processing of Personal Data unless the Documentation for the Beta Feature expressly states that it may. **16.3** Notwithstanding Section 20, Infinia's aggregate liability in respect of any Beta Feature is limited to USD 100. **16.4** Infinia may require confidentiality in respect of a Beta Feature, in which case the Beta Feature and all information about it is Infinia's Confidential Information under Section 14. ## 17. Third-party services, Zone Operators and compute partners **17.1** Zone Operators and compute partners. Sovereign Zones may be hosted, powered or operated by Zone Operators and compute partners under agreement with Infinia. Infinia remains responsible to the Customer for the performance of the Service under the Agreement notwithstanding the involvement of a Zone Operator, and will impose on each Zone Operator contractual obligations in respect of security, residency, confidentiality and data protection that are no less protective than those in the Agreement. The identity of the Zone Operator for each Sovereign Zone is stated in Annex D and in the Documentation. **17.2** Third-party services. The Service may interoperate with third-party services that the Customer chooses to connect, including identity providers, notification channels, payment providers and the Customer's own applications ("Third-Party Services"). Third-Party Services are governed by their own terms; Infinia does not control them, is not responsible for them, gives no warranty in respect of them, and is not liable for any loss or disclosure of data through them. Infinia may change or withdraw interoperability with a Third-Party Service at any time without liability. **17.3** Open-source software. The Service incorporates open-source software licensed under its own terms, which are identified in the Documentation. Nothing in the Agreement limits the Customer's rights under any such licence. ## 18. Warranties and disclaimers **18.1** Mutual warranties. Each party warrants that it has full power and authority to enter into and perform the Agreement, and that doing so does not breach any other agreement by which it is bound. **18.2** Infinia's warranties. Infinia warrants that (a) the Service will perform materially in accordance with the Documentation for the Service Mode concerned; (b) it will provide the Service with reasonable skill and care and in accordance with Applicable Law that applies to Infinia as a provider of the Service; (c) it will comply with Sections 6.1 to 6.4; and (d) any professional services it provides under an Order Form will be performed in a professional and workmanlike manner. The Customer's sole and exclusive remedy for breach of warranty (a) or (d) is for Infinia to use commercially reasonable efforts to correct the non-conformity or re-perform the services and, if Infinia is unable to do so within a reasonable time, for the Customer to terminate the affected Order Form and receive a refund of prepaid Fees for the unperformed portion. The remedies for breach of Sections 6.1 to 6.4 are set out in Section 20.4. **18.3** Disclaimer. EXCEPT AS EXPRESSLY SET OUT IN SECTION 18.2 AND THE SLA, THE SERVICE, THE MODELS, THE OUTPUTS, THE DOCUMENTATION AND ALL OTHER MATERIALS ARE PROVIDED "AS IS" AND "AS AVAILABLE", AND INFINIA, ITS AFFILIATES, ITS ZONE OPERATORS AND ITS LICENSORS DISCLAIM ALL OTHER WARRANTIES, CONDITIONS AND REPRESENTATIONS, WHETHER EXPRESS, IMPLIED, STATUTORY OR ARISING FROM COURSE OF DEALING OR USAGE OF TRADE, INCLUDING ANY WARRANTY OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, ACCURACY OR QUIET ENJOYMENT, TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. **18.4** No warranty as to Output. WITHOUT LIMITING SECTION 18.3, INFINIA DOES NOT WARRANT THAT ANY OUTPUT WILL BE ACCURATE, COMPLETE, CURRENT, RELIABLE, LAWFUL, NON-INFRINGING, FREE OF BIAS OR FIT FOR ANY PURPOSE, INCLUDING ANY REGULATED, HIGH-RISK, SAFETY-CRITICAL, LEGAL, MEDICAL, FINANCIAL, EMPLOYMENT OR CUSTOMER-FACING PURPOSE. OUTPUT DOES NOT CONSTITUTE PROFESSIONAL ADVICE. THE CUSTOMER ASSUMES ALL RISK ARISING FROM ITS USE OF AND RELIANCE ON OUTPUT. **18.5** No warranty as to Models or third parties. Infinia makes no representation or warranty about any Model, any Model Provider's data-handling, training, security, licensing or intellectual-property practices, the continued availability of any Model, or any Third-Party Service. **18.6** Service availability. Except as stated in the SLA, Infinia does not warrant that the Service will be uninterrupted, timely, secure or error-free, that any defect will be corrected, or that the Service will meet the Customer's requirements or achieve any particular result. **18.7** High-Risk Use. The Service is not designed, certified or warranted for High-Risk Use. Any High-Risk Use is at the Customer's sole risk and subject to the Customer's compliance with Section 8.7 and all AI Law, and Infinia disclaims all liability arising from it to the maximum extent permitted by Applicable Law. **18.8** Jurisdictional limits. Some jurisdictions do not allow the exclusion of certain warranties. To the extent a warranty cannot be excluded, it is limited in duration and scope to the minimum permitted by Applicable Law. ## 19. Indemnities **19.1** By the Customer. The Customer will defend, indemnify and hold harmless Infinia, its Affiliates, its Zone Operators, its Model Providers and each of their officers, directors, employees, contractors and agents (the "Infinia Indemnitees") from and against all claims, demands, suits and proceedings brought by a third party ("Claims"), and all resulting liabilities, damages, fines, penalties, settlements, costs and expenses (including reasonable legal fees), to the extent arising from or relating to: (a) Customer Content, including any allegation that an Input, Customer Model or the Customer's use of Output infringes or misappropriates a third party's rights or breaches Applicable Law; (b) the Customer's or its Authorised Users' or end users' use of the Service in breach of the Agreement, Annex A or the Model Terms; (c) any application, product or service the Customer offers using the Service, including any Claim by the Customer's own customers or end users; (d) any High-Risk Use; (e) any breach of Section 23; or (f) any dispute between the Customer and a third party, including a Model Provider or the Customer's end user. **19.2** By Infinia. Infinia will defend the Customer against any Claim alleging that the Service, excluding Models, Customer Content, Third-Party Services and open-source components, as provided by Infinia and used in accordance with the Agreement, infringes a patent, copyright or trade mark, or misappropriates a trade secret, of a third party enforceable in the jurisdiction of the Sovereign Zone in which the Service is used, and will pay the damages finally awarded against the Customer by a court of competent jurisdiction, or agreed by Infinia in settlement, in respect of that Claim. If the Service becomes, or in Infinia's opinion is likely to become, the subject of such a Claim, Infinia may at its option and expense procure the right for the Customer to continue using it, modify or replace it so that it is non-infringing while remaining materially equivalent, or, if neither is commercially reasonable, terminate the affected Service and refund any prepaid Fees for the unperformed period. Infinia has no obligation under this Section 19.2 for Claims arising from (i) Models, Model Terms or Customer Models, (ii) Customer Content, (iii) combination of the Service with anything not supplied by Infinia, (iv) modification of the Service by anyone other than Infinia, (v) use in breach of the Agreement or after notice to cease, or (vi) Beta Features. This Section 19.2 states Infinia's entire liability, and the Customer's exclusive remedy, for infringement or misappropriation of third-party rights. Infinia's aggregate liability under this Section 19.2 is limited to the greater of USD 1,000,000 and the amount stated in an applicable Order Form. **19.3** Procedure. The indemnified party must (a) give the indemnifying party prompt written notice of the Claim, provided that failure to do so relieves the indemnifying party only to the extent it is prejudiced; (b) grant the indemnifying party sole control of the defence and settlement, provided that the indemnifying party may not settle a Claim in a manner that imposes an admission, obligation or liability on the indemnified party without its prior written consent, not to be unreasonably withheld; and (c) provide reasonable cooperation at the indemnifying party's expense. The indemnified party may participate in the defence with counsel of its own choosing at its own expense. ## 20. Limitation of liability **20.1** Exclusion of certain losses. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NEITHER PARTY, NOR INFINIA'S AFFILIATES, ZONE OPERATORS, MODEL PROVIDERS OR LICENSORS, WILL BE LIABLE UNDER OR IN CONNECTION WITH THE AGREEMENT, WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF STATUTORY DUTY, MISREPRESENTATION OR OTHERWISE, FOR ANY (a) INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY OR PUNITIVE DAMAGES; (b) LOSS OF PROFITS, REVENUE, BUSINESS, CONTRACTS, ANTICIPATED SAVINGS OR GOODWILL; (c) LOSS OF OR CORRUPTION TO DATA OR OUTPUT, OR COST OF RECOVERING OR RE-GENERATING IT; (d) BUSINESS INTERRUPTION; OR (e) COST OF PROCUREMENT OF SUBSTITUTE SERVICES; IN EACH CASE HOWEVER ARISING AND EVEN IF THE PARTY WAS ADVISED OF THE POSSIBILITY OF SUCH LOSS. **20.2** Cap. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE AGGREGATE LIABILITY OF INFINIA, ITS AFFILIATES, ZONE OPERATORS, MODEL PROVIDERS AND LICENSORS, TAKEN TOGETHER, ARISING OUT OF OR RELATING TO THE AGREEMENT IN ANY TWELVE-MONTH PERIOD WILL NOT EXCEED THE GREATER OF (a) THE TOTAL FEES PAID OR PAYABLE BY THE CUSTOMER TO INFINIA UNDER THE AGREEMENT IN THE TWELVE MONTHS IMMEDIATELY PRECEDING THE FIRST EVENT GIVING RISE TO LIABILITY, AND (b) USD 250,000. THE AGGREGATE LIABILITY OF THE CUSTOMER, OTHER THAN FOR FEES PAYABLE AND UNDER SECTIONS 19.1 AND 20.3, IS LIMITED IN THE SAME WAY. MULTIPLE CLAIMS DO NOT ENLARGE THE CAP. **20.3** Exceptions. Sections 20.1 and 20.2 do not apply to (a) a party's liability for death or personal injury caused by its negligence, or for fraud or fraudulent misrepresentation; (b) the Customer's obligation to pay Fees; (c) the Customer's indemnity in Section 19.1 and its liability for breach of Section 9, Annex A or Section 23; (d) either party's liability for wilful misconduct or gross negligence; or (e) any liability that cannot be limited or excluded under Applicable Law. **20.4** Data protection and sovereignty liability. Notwithstanding Section 20.2, Infinia's aggregate liability for breach of Sections 6.1 to 6.4, Section 14 or Annex B is limited to the greater of (a) two times the Fees paid or payable in the twelve months preceding the first event giving rise to liability and (b) USD 2,000,000, and remains subject to Section 20.1 except for the direct costs of notifying affected individuals and regulators where required by Data Protection Law. **20.5** Basis of the bargain. The parties agree that the exclusions and limitations in this Section 20 are a fundamental basis of the bargain, are reflected in the Fees, and will apply even if any limited remedy fails of its essential purpose. Each provision of this Section 20 is severable and independent. **20.6** Claims period. To the extent permitted by Applicable Law, any claim by the Customer arising out of the Agreement must be brought within one year after the cause of action accrues, failing which it is permanently barred. ## 21. Term, suspension and termination **21.1** Term. The Agreement commences on the earlier of the date the Customer accepts these Terms and the date it first uses the Service, and continues until terminated in accordance with this Section 21 (the "Term"). Each Order Form for a Capacity Mode has the term stated in it and, unless the Order Form provides otherwise, renews automatically for successive periods equal to the initial term unless either party gives notice of non-renewal not less than thirty days before the end of the then-current term. **21.2** Termination by the Customer. The Customer may terminate the Agreement for convenience at any time by closing its Organisation through the portal or by written notice, provided that (a) all outstanding Fees are paid, (b) any Capacity Mode commitments run to the end of their term and remain payable, and (c) unspent Credit is forfeited under Section 10.7 subject to the exceptions there. The Customer may terminate for Infinia's material breach if the breach is not cured within thirty days after written notice describing it. **21.3** Suspension by Infinia. Infinia may suspend all or part of the Service, an Organisation, a project or an API Credential, immediately and without prior notice where it reasonably considers that (a) the Customer is in breach of Section 3.7, 7.3, 9, 23 or Annex A; (b) the Customer's use presents a security risk to the Service or any third party, or a credible risk of harm or of legal liability for Infinia, a Zone Operator or a Model Provider; (c) suspension is required by Applicable Law, a competent authority or a Model Provider's requirements; or (d) Fees are overdue in accordance with Section 10.13. In other cases of breach Infinia will give reasonable prior notice. Infinia will limit suspension to what is reasonably necessary, will notify the Customer as soon as practicable, and will restore the Service promptly when the cause is resolved. Fees for Capacity Modes continue to accrue during suspension. Infinia is not liable for any loss arising from a suspension permitted by this Section. **21.4** Termination by Infinia for cause. Infinia may terminate the Agreement or any Order Form immediately on written notice if (a) the Customer materially breaches the Agreement and, where the breach is capable of cure, fails to cure it within thirty days of notice, or within ten days in the case of non-payment; (b) the Customer breaches Section 3.7, 7.3, 23 or Annex A; (c) the Customer becomes insolvent, enters administration, liquidation or an analogous procedure, or ceases to trade; (d) the Customer fails identity verification or Infinia becomes unable to serve the Customer under Applicable Law; or (e) the Customer has been inactive, with no consumption and no Credit balance, for twelve consecutive months. **21.5** Termination by Infinia for convenience. Infinia may terminate the Agreement for convenience on not less than ninety days' written notice, or on shorter notice where required by a change in Applicable Law, the withdrawal of a Zone Operator or the discontinuation of the Service in the Customer's Region Scope. In that case Infinia will refund unspent Purchased Credit and prepaid Fees for the unperformed portion of any Capacity Mode within thirty days after the termination date, and this refund is the Customer's sole remedy for such termination. Committed Capacity Mode terms will be honoured to their end where Infinia is able to do so. **21.6** Effect of termination. On termination for any reason: (a) all rights granted to the Customer end and it must cease using the Service; (b) all API Credentials are revoked and all Capacity Mode deployments are torn down; (c) all Fees accrued to the termination date, and all Fees for the remainder of any Capacity Mode term terminated by the Customer or for the Customer's breach, become immediately due; (d) unspent Credit is forfeited except as provided in Sections 10.7 and 21.5; (e) the Customer may export its Usage Data and any stored objects through the portal or API for thirty days after termination, after which Infinia will delete Customer Content in accordance with Annex B, retaining only Usage Data and records required for billing, tax, legal and audit purposes; and (f) the Sections listed in Section 27.12 survive. **21.7** Closure with a balance. Closure of an Organisation does not discharge any amount owed. Outstanding invoices are settled first; unspent Credit is then forfeited subject to Section 10.7. ## 22. Changes to the Agreement **22.1** Infinia may amend these Terms, Schedule 1, the Annexes, the SLA and the Documentation from time to time. The current version, with its effective date, is published at [Terms URL] and in the portal, together with an archive of superseded versions. **22.2** Material changes. Where a change materially reduces the Customer's rights or materially increases its obligations, Infinia will give not less than thirty days' notice by email to the Owners and billing contact and by notice in the portal before the change takes effect. If the Customer objects to a material change it may terminate the Agreement under Section 21.2 before the change takes effect and, in that case only, Infinia will refund unspent Purchased Credit and prepaid Fees for the unperformed period of any Capacity Mode. Continued use of the Service after the effective date constitutes acceptance. **22.3** Other changes. Changes that are not material, that are required by Applicable Law or by a Model Provider, that add features or Models, that reduce Fees, or that are needed to address a security or safety risk may take effect on publication. **22.4** Order Forms. No change under this Section 22 alters the rates, capacity or term stated in an Order Form during its current term, except a change required by Applicable Law. **22.5** Version applicable to disputes. A dispute is governed by the version of the Agreement in force when the events giving rise to it occurred. **22.6** Changes to Section 26. If Infinia changes Section 26 (other than its notice address), the Customer may reject the change by written notice within thirty days of notification; in that case the prior version of Section 26 continues to apply to that Customer, and Infinia may terminate the Agreement on notice under Section 21.5. ## 23. Export controls, sanctions and anti-corruption **23.1** Compliance. Each party will comply with Export Control Law in performing the Agreement. The Customer acknowledges that the Service, Models and Outputs may be subject to Export Control Law and that certain Models and Sovereign Zones may be subject to additional restrictions imposed by Model Providers or by law. **23.2** Restricted Parties and territories. The Customer represents and warrants that neither it, nor any of its Affiliates, owners of twenty-five per cent or more, directors or Authorised Users, is (a) named on, or owned or controlled by a person named on, any sanctions or restricted-party list maintained by the United Arab Emirates, the United Nations Security Council, the European Union, the United Kingdom, the United States Office of Foreign Assets Control, or any other applicable authority; (b) located, organised or ordinarily resident in a country or territory that is the subject of comprehensive sanctions; or (c) otherwise a person with whom Infinia is prohibited from dealing under Export Control Law (each a "Restricted Party"). The Customer will not permit any Restricted Party to access the Service and will not use the Service for any purpose prohibited by Export Control Law, including the development of weapons of mass destruction or their delivery systems, or military end-uses prohibited by Applicable Law. **23.3** Screening. The Customer consents to Infinia screening the Customer, its Authorised Users and its payments against sanctions and restricted-party lists, and will provide the information reasonably required for that purpose. Infinia may refuse, suspend or terminate the Service, and may freeze Credit, where Applicable Law so requires or where a screening match is not resolved to Infinia's reasonable satisfaction, and will have no liability for doing so. **23.4** Anti-corruption. Each party will comply with all applicable anti-bribery and anti-corruption laws and will not offer, promise, give or receive any improper payment or advantage in connection with the Agreement. ## 24. Public-sector and regulated customers **24.1** Where the Customer is a governmental body, state-owned entity or public authority, or a regulated financial institution, healthcare provider or telecommunications operator, the following apply in addition to these Terms: (a) any provision of the Agreement that the Customer is prohibited by Applicable Law from accepting, including indemnities, governing law, arbitration, limitation periods or automatic renewal, applies only to the extent permitted, and the parties will negotiate in good faith an MSA addressing the relevant matters; (b) Infinia will cooperate reasonably, at the Customer's cost where material, with the Customer's regulator, including by providing the information and access that Applicable Law requires the Customer to secure from its outsourcing or cloud providers, subject to Section 14 and the protection of other customers; (c) Infinia will provide the exit-assistance, business-continuity and sub-contracting information that regulatory outsourcing guidelines applicable to the Customer require, as described in the Trust Centre; and (d) the Customer remains responsible for determining that the Service, the Sovereign Zone and the Service Mode it selects satisfy its regulatory obligations. **24.2** Nothing in this Section 24 obliges Infinia to accept terms that are inconsistent with the operating model of the Service, including any requirement to serve traffic outside a Sovereign Zone, to retain Customer Content, or to grant access to another customer's data or systems. **24.3** Forum for customers that cannot arbitrate. Where the Customer is prohibited by Applicable Law from agreeing to arbitration, to a foreign seat or to a foreign governing law, Section 26.3 does not apply to it and the courts of the Abu Dhabi Global Market have non-exclusive jurisdiction over any dispute, without prejudice to any court in which the Customer is required by Applicable Law to bring or defend proceedings. The Customer must notify Infinia of the prohibition and its basis when it accepts these Terms or as soon as it becomes aware of it. This Section 24.3 removes any doubt as to the forum; it does not affect Section 26.1 or Section 26.7. **24.4** Regulated financial customers. Where the Customer is subject to Regulation (EU) 2022/2554 (DORA) or to an equivalent regime that requires it to report a major incident to its regulator within a period shorter than the period in Section B.8.1, Section B.8.2 applies and the Customer must identify itself as such through the portal or to its account contact so that Infinia can record the shorter notification period against its Organisation. ## 25. Publicity **25.1** Infinia will not use the Customer's name, logo or trade marks in any press release, customer list, case study or marketing material without the Customer's prior written consent, which may be given by email by an Owner or Admin and withdrawn on thirty days' notice. **25.2** The Customer may state factually that it uses Xailon Token Factory, and may use the Infinia and Xailon Token Factory names and logos solely for that purpose in accordance with any brand guidelines Infinia publishes, but may not imply endorsement, partnership or certification by Infinia without written consent. ## 26. Governing law and dispute resolution **26.1** Governing law. The Agreement, and any dispute or claim (including non-contractual disputes or claims) arising out of or in connection with it, its subject matter or its formation, is governed by the laws of the Abu Dhabi Global Market, without regard to conflict-of-laws principles. The United Nations Convention on Contracts for the International Sale of Goods does not apply. **26.2** Informal resolution. Before commencing arbitration or proceedings, a party must give the other written notice of the dispute describing its nature, basis and the relief sought, and the parties will attempt in good faith to resolve it through discussions between senior representatives for a period of thirty days from the notice. Infinia's notice address for this purpose is set out in Section 28. **26.3** Arbitration. Any dispute not resolved under Section 26.2 shall be referred to and finally resolved by arbitration administered by the ADGM Arbitration Centre in accordance with the ICC Rules of Arbitration (the "Rules"), which are incorporated by reference into this Section. The seat of arbitration is the Abu Dhabi Global Market, Abu Dhabi, United Arab Emirates. The language of the arbitration is English. The tribunal consists of one arbitrator where the amount in dispute is below USD 1,000,000 and three arbitrators otherwise, appointed in accordance with the Rules. The tribunal must give a reasoned written award. The award is final and binding and may be entered and enforced in any court of competent jurisdiction. The parties will keep the arbitration, all submissions and the award confidential except as required to enforce it or by Applicable Law. **26.4** Exceptions. Either party may seek interim, injunctive or other equitable relief from a court of competent jurisdiction to protect its intellectual-property rights or Confidential Information, to restrain a breach of Section 9, 23 or Annex A, or to preserve the status quo pending arbitration. Infinia may bring proceedings for the recovery of undisputed Fees in the courts of the Abu Dhabi Global Market or of the Customer's place of incorporation. Small claims within the jurisdiction of a small-claims tribunal may be brought there. **26.5** No class or representative proceedings. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EACH PARTY MAY BRING CLAIMS AGAINST THE OTHER ONLY IN ITS INDIVIDUAL CAPACITY AND NOT AS A CLAIMANT OR CLASS MEMBER IN ANY PURPORTED CLASS, COLLECTIVE, CONSOLIDATED OR REPRESENTATIVE PROCEEDING. THE TRIBUNAL MAY NOT CONSOLIDATE MORE THAN ONE CUSTOMER'S CLAIMS OR PRESIDE OVER ANY FORM OF REPRESENTATIVE PROCEEDING WITHOUT THE WRITTEN CONSENT OF ALL PARTIES. If this Section 26.5 is found unenforceable in respect of a particular claim, that claim (and only that claim) must be brought in the courts identified in Section 26.4 rather than in arbitration. **26.6** Waiver of jury trial. To the extent a dispute is heard in a court in a jurisdiction that provides for jury trial, each party irrevocably waives any right to a trial by jury. **26.7** Mandatory local law. Nothing in this Section 26 deprives the Customer of the protection of provisions of the law of its place of establishment that cannot be derogated from by agreement, or of the right to bring proceedings there where that right cannot be excluded. ## 27. General provisions **27.1** Entire agreement. The Agreement constitutes the entire agreement between the parties in respect of its subject matter and supersedes all prior or contemporaneous proposals, representations, understandings and agreements, whether oral or written. Each party acknowledges that it has not relied on any statement not set out in the Agreement. Nothing in this Section limits liability for fraud. **27.2** Assignment. The Customer may not assign, novate or transfer the Agreement or any right or obligation under it without Infinia's prior written consent, except to an Affiliate or a successor to all or substantially all of its business relating to the Agreement that is not a Restricted Party and not a competitor of Infinia, on written notice. Infinia may assign, novate or transfer the Agreement, in whole or in part, to an Affiliate or to a successor to the relevant business on notice, and may subcontract its obligations, remaining responsible for its subcontractors. **27.3** Affiliate and regional entity substitution. Infinia may, on not less than thirty days' notice, designate an Affiliate incorporated in or near the jurisdiction of a Sovereign Zone as the contracting and invoicing entity for the Service in that Zone, and the Agreement will be read accordingly for that Zone from the effective date of the notice, without prejudice to any accrued rights. Where Applicable Law or the Customer's regulatory requirements require an in-region contracting entity, the parties will cooperate to document the substitution. **27.4** Force majeure. Neither party is liable for any failure or delay in performing its obligations (other than payment obligations) to the extent caused by events beyond its reasonable control, including natural disaster, epidemic, war, terrorism, civil disturbance, labour dispute, act of government, change in law, failure of a public utility or telecommunications network, failure or withdrawal of a Zone Operator's or upstream supplier's capacity for reasons beyond the affected party's control, or a cyber-attack of a kind that reasonable security measures could not have prevented, provided that the affected party notifies the other promptly and uses reasonable efforts to mitigate. If a force-majeure event prevents performance of a Capacity Mode for more than thirty consecutive days, either party may terminate the affected Order Form on notice and Infinia will refund prepaid Fees for the unperformed period. **27.5** Notices. Notices to the Customer will be sent to the email addresses of its Owners and billing contact recorded in the Organisation, or posted in the portal, and are deemed received when sent or posted. Notices to Infinia must be sent by email to [legal@●] with a copy by courier or registered post to the address in Section 28, and are deemed received on the next business day after delivery. Each party must keep its notice details current. Operational and technical notifications may be sent through the channels described in the Documentation. **27.6** Electronic contracting. The Customer consents to contract electronically and to receive all notices, disclosures, invoices and communications electronically, and agrees that electronic acceptance, click-through and electronic signatures satisfy any requirement that the Agreement or any notice be in writing or signed. **27.7** Severability. If any provision of the Agreement is held invalid or unenforceable, it will be enforced to the maximum extent permissible and the remaining provisions will continue in full force, except as provided in Section 26.5. **27.8** Waiver. No failure or delay in exercising any right or remedy constitutes a waiver of it, and no single or partial exercise precludes any further exercise. A waiver is effective only if in writing and applies only to the specific instance. **27.9** Relationship. The parties are independent contractors. Nothing in the Agreement creates a partnership, joint venture, agency, fiduciary or employment relationship, and neither party may bind the other. **27.10** Third-party rights. Except for the Infinia Indemnitees under Section 19.1, Model Providers under Section 7.6, and Infinia's Affiliates, Zone Operators and licensors in respect of Sections 18 and 20, no person other than the parties has any right to enforce any term of the Agreement. **27.11** Language. The Agreement is drafted in English. Any translation is for convenience only and the English version prevails. **27.12** Survival. Sections 2, 3.9, 4.2, 4.3, 6.3 to 6.5, 6.8, 6.11, 7.2 to 7.6, 8, 10, 14, 15, 18, 19, 20, 21.6, 21.7, 22.5, 23, 26 and 27, Annexes B and E to the extent stated in them, and any other provision that by its nature is intended to survive, survive termination or expiry of the Agreement. **27.13** Counterparts and headings. An Order Form or MSA may be executed in counterparts and electronically. Headings are for convenience only. ## 28. Contact and notices **28.1** Infinia may be contacted as follows. Legal notices: [Infinia Legal Entity Name], [registered address], attention Legal Department, [legal@●]. Support: [support@●] or through the portal. Security and vulnerability disclosure: [security@●]. Privacy, data protection and requests by individuals to exercise their rights: [privacy@●]. Data Protection Officer: [dpo@●]. Representative in the European Union for the purposes of Article 27 of the GDPR: [name and address]. Notices and complaints about content under Section 15.7, and the point of contact for authorities: [legal@●]. Reports of abuse or breach of Annex A: [abuse@●]. Copyright complaints: [legal@●]. ## Schedule 1 — Service Mode Terms This Schedule sets out the commercial and operational terms of each Service Mode. It forms part of the Agreement. Where it conflicts with Part A in respect of a Service Mode, this Schedule governs. Capitalised terms have the meanings in Part A. ### S1.1 Common terms applying to every Service Mode | Matter | Term | |---|---| | Access | The published Model Catalogue available within the Customer's Region Scope, via the published API. | | Metering | All consumption is metered to a single authoritative record maintained in the serving Zone. That record governs in any dispute absent manifest error. | | Cost transparency | Cost is reported on every request. Usage is queryable by project, model, region and API key. | | Residency | Traffic stays within the Region Scope set by the Customer's Admin. A request that cannot be served within it is refused, not served elsewhere. | | Content retention | Inputs and Outputs are never stored and are never inspected. Only Usage Data and Stored Objects are retained, for the periods in Annex C. | | Liability for consumption | The Customer is liable for all consumption generated by its API Credentials, including from misconfigured or runaway workloads and from a leaked or compromised key. | | Content responsibility | The Customer is responsible for the lawfulness and appropriateness of Inputs and Outputs. Content passes through unmoderated by default. | | Model licensing | The Customer must comply with each Model's Model Terms, including geographic restrictions. | | Abuse protection | Throttles apply to stop runaway or abusive traffic. They are safety mechanisms, not tiered, and not commercially removable. | | Commercial rate limits | None. Consumption is bounded only by Credit, spend limits and credit limits. | | Price changes | Minimum thirty days' notice of any increase. Reductions apply immediately to all Customers. Order Form rates are fixed for their term. | | Model lifecycle | Deprecation with a stated notice period and named successor where one exists. Version-pinned requests must be migrated before sunset. | | Credit validity | Credit expires twelve months from purchase. Non-use does not pause or extend validity in any Service Mode. | | Currency | All pricing, balances and invoices in US dollars. Bank conversion costs are outside Infinia's pricing. | | Refunds | Non-refundable, subject to Section 10.7. | ### S1.2 Serverless | Matter | Term | |---|---| | What the Customer receives | Best-effort access to the shared serving pool for the Models in the Model Catalogue within the Region Scope. | | Billing basis | Per unit consumed (tokens, images, seconds or other unit stated in the rate list) at Published Rates. Drawn from Credit, or invoiced monthly on postpaid. Cached-input tokens are charged at the published cached rate where prefix caching applies. | | Infinia commits to | Accurate metering at Published Rates; residency confinement. | | Infinia does not commit to | Throughput, latency or availability at any given moment, except as stated in the SLA. | | Minimum term | None. No committed volume. | | Cancellation | Stop at any time without notice. No charge accrues while no requests are made. Stopping does not extend the validity of Credit already purchased. | | Above-capacity behaviour | Under capacity pressure requests queue within the request timeout and are then refused with a retry indication. | | On Credit exhaustion | Service stops immediately. Nothing is served that cannot be billed. | | On non-payment (postpaid) | New consumption suspended per Section 10.13; restored immediately on settlement. | | Tenancy | Shared. Not isolated from other Customers. | ### S1.3 Batch | Matter | Term | |---|---| | What the Customer receives | The same Models, executed when capacity allows, at the discounted Published Rate for Batch. | | Billing basis | Published Batch rate, charged on completion of each request within a job. Failed requests within a job are not charged. | | Infinia commits to | Accurate metering; the discounted rate; that accepted jobs will be executed. | | Infinia does not commit to | Any completion time. Jobs may be deferred, interrupted and resumed as capacity moves. | | Minimum term | None. Per job. | | Cancellation | A job may be cancelled before completion. Work already performed is charged. | | Admission | A job is accepted only where sufficient Credit or postpaid headroom exists for its estimated cost. Jobs exceeding available funds are refused at submission. | | On non-payment (postpaid) | As Serverless. Queued jobs are held, not discarded, for the period stated in Annex C. | | Stored objects | Batch input and output files are Stored Objects, held within the Region Scope for the period stated in Annex C. | | Tenancy | Shared. Batch work may run on idle reserved capacity of other Customers. | ### S1.4 Reserved Throughput | Matter | Term | |---|---| | What the Customer receives | A guaranteed rate of tokens per minute for a specified Model in a specified Zone, held for the term. | | Billing basis | Fixed monthly capacity charge for the reserved rate, invoiced and payable whether or not the capacity is used, plus Published Rates for traffic above the reserved rate. | | Counting | Input tokens count as one; output tokens count at the Model's published output-to-input weighting; cached input at the published cached weighting. Weightings are published per Model. | | Infinia commits to | Throughput up to the reserved rate; the availability commitment in the SLA; that reserved traffic is served entirely within the purchased Zone. | | Infinia does not commit to | Tenancy isolation (the reservation guarantees throughput, not separation from other Customers' workloads); any guarantee for traffic above the reservation; that unused capacity is held idle. | | Available for | Text and embedding Models only. | | Minimum term | One month. Longer terms at the rates stated in the Order Form. | | Cancellation | Not available within the term. Non-renewal on notice before term end as stated in the Order Form. | | Mid-term changes | Increases subject to available capacity in the Zone, effective on confirmation, charged pro rata. Reductions take effect at renewal. | | Above-capacity behaviour | Served from the shared pool in the same Zone at Published Rates and not covered by the reservation's SLA. Where no same-zone capacity exists, or residency prevents it, such traffic is refused rather than served from another region. | | Funding | Funded Credit or postpaid headroom is required alongside a reservation for above-capacity traffic. On Credit exhaustion reserved traffic is unaffected and traffic above the reservation stops. | | On non-payment | New consumption suspended per Section 10.13. The reservation is not terminated and capacity charges continue for the balance of the term, because the capacity remains committed on the Customer's behalf. | | At end of term | Capacity released. Traffic falls back to Serverless at Published Rates automatically. Advance notice given. | | Unused throughput | Does not roll over and is not credited or refunded. | | Additional responsibility | Sizing the reservation, with Infinia's guidance and calculator. | ### S1.5 Dedicated Model Inference | Matter | Term | |---|---| | What the Customer receives | Single-tenant compute running a catalogue Model or a Customer Model, served through Infinia's API and serving stack. | | Billing basis | Per GPU-hour for every running replica, billed by the minute, regardless of traffic volume. Invoiced. | | Infinia commits to | That the compute is not shared with other Customers; that the deployment runs as configured; residency within the chosen Zone. | | Infinia does not commit to | Any throughput or latency figure. Performance is a function of the hardware selected and the Customer's traffic shape. | | Minimum term | As stated in the Order Form. | | Cancellation | The Customer may stop the deployment at any time, subject to any committed term, for which charges remain payable. Billing ends on teardown. | | Mid-term changes | Replica count changeable at any time. Charge adjusts from the point of change, pro rata. | | Above-capacity behaviour | Bounded by the hardware provisioned. Excess traffic queues or is refused per the Customer's configuration. | | On non-payment | Suspension per Section 10.13. Infinia may stop the deployment; charges cease from that point but any committed-term charges remain payable. | | At end of term | Deployment torn down, capacity released, endpoint withdrawn. | | Metering | Tokens metered for analytics and partner attribution. Billing is on hardware time, not consumption. | | Additional responsibility | Sizing and scaling configuration; licensing of any Customer Model; suitability of the selected hardware. | ### S1.6 GPU Clusters (planned — not contractually offered until generally available) | Matter | Term | |---|---| | What the Customer receives | Bare reserved GPU capacity in a Sovereign Zone for the term. The Customer supplies and operates its own serving software. | | Billing basis | Per GPU-hour for the term, at the rate applicable to the committed length. Invoiced. | | Infinia commits to | The specified hardware, in the specified Zone, for the specified term; network and physical environment; the contractual residency guarantee. | | Infinia does not commit to | Anything above the hardware. No Model, serving stack, throughput, latency or inference SLA. Infinia has no visibility into what runs on the cluster. | | Minimum term | As committed in the Order Form. | | Cancellation | Not available within the term. | | Mid-term changes | Additional capacity subject to availability. Reductions at term end only. | | On non-payment | Suspension per Section 10.13. Capacity remains committed and charges continue for the balance of the term. | | At end of term | Capacity released, access revoked. Customer data on the cluster is deleted in accordance with Annex B. | | Metering | None. Infinia does not observe usage. Billing is entirely on capacity held. | | Additional responsibility | Everything above the hardware: serving stack, model licensing, content handling, and the security of anything deployed. Sections 6.3 and 6.4 apply to Infinia only; the Customer is responsible for its own retention and training practices on the cluster. | ## Annex A — Acceptable Use Policy **A.1** Purpose and scope. This Acceptable Use Policy ("AUP") governs all use of the Service by the Customer, its Authorised Users and the end users of any application the Customer operates using the Service. It applies in addition to the Model Terms of each Model, which may be more restrictive. Where the AUP and the Model Terms differ, the Customer must comply with both. Infinia may update the AUP under Section 22 of the Terms. **A.2** Principles. The Service is built for regulated enterprises and public bodies and is provided without content moderation. That design places responsibility on the Customer to ensure that what it sends through the Service, and what it does with what comes back, is lawful, safe and consistent with this AUP. Infinia does not and cannot inspect Inputs or Outputs (Section 6.3 of the Terms). It enforces this AUP on Usage Data, on reports made under Section A.7, on information the Customer supplies, and on the two integrity controls that Section 6.10 of the Terms applies to Stored Objects. The Customer must not rely on Infinia to detect a breach of this AUP. **A.3** Prohibited uses. The Customer must not use, and must not permit any person to use, the Service or any Output to: **(a)** violate Applicable Law, or facilitate, promote or conceal a violation by any person; **(b)** generate, store, distribute or solicit child sexual abuse material or any content that sexualises, exploits or endangers minors. Infinia applies the hash-matching control in Section 6.10(a) of the Terms to stored image, audio and video files; where that control returns a match, or where Infinia otherwise becomes aware of apparent material of this kind, Infinia will preserve the material and the associated Usage Data to the extent and for the period Applicable Law requires, will report it to the competent authorities in accordance with Applicable Law, and will terminate the account immediately. Preservation and reporting under this paragraph are the only circumstances in which Infinia retains Customer Content otherwise than under Annex C, and they do not extend to Inputs or Outputs, which Infinia does not hold; **(c)** generate non-consensual intimate imagery, or sexual content depicting real identifiable persons without their consent; **(d)** create, plan, or provide material assistance for acts of terrorism, genocide, violent extremism, or violence against any person or group; **(e)** design, develop, produce, acquire or use chemical, biological, radiological or nuclear weapons, their precursors or delivery systems, or conventional weapons in breach of Export Control Law, or to provide uplift to any person seeking to do so; **(f)** develop, distribute or operate malware, ransomware, exploits or tools designed to gain unauthorised access to, disrupt, or damage any system, network or data, or to conduct unauthorised penetration testing, credential stuffing, phishing or social engineering; **(g)** engage in fraud, scams, deceptive commercial practices, impersonation of any person or organisation, or the creation of fake reviews, credentials, identity documents or records intended to be taken as genuine; **(h)** generate or distribute disinformation intended to interfere with elections, democratic processes or public-health responses, or to deceive the public about matters of public interest, or operate undisclosed coordinated inauthentic behaviour; **(i)** harass, threaten, bully, defame or intimidate any person, or generate content that incites hatred or discrimination against any person or group on the basis of a protected characteristic; **(j)** make automated decisions producing legal or similarly significant effects on individuals without the human oversight, transparency and contestability that Applicable Law, including AI Law, requires, or engage in any use prohibited by AI Law, including social scoring by public authorities, exploitation of vulnerabilities of specific groups, untargeted scraping of facial images to build recognition databases, emotion recognition in workplaces and educational institutions except where permitted, and biometric categorisation inferring protected characteristics; **(k)** conduct surveillance, tracking or profiling of individuals in violation of Applicable Law or in a manner that would reasonably be regarded as a violation of their privacy, including covert monitoring of journalists, activists, dissidents or political opponents; **(l)** infringe, misappropriate or violate the intellectual-property, privacy, publicity or other rights of any person, including by submitting Inputs the Customer has no right to use or by using Output in a manner that infringes; **(m)** provide medical, legal, financial or other regulated professional advice to third parties in a manner that misrepresents the Output as the advice of a qualified professional, or without the disclosures Applicable Law requires; **(n)** misrepresent Output as human-generated where Applicable Law or the context requires disclosure that it was generated by an AI system, or remove or obscure any provenance, watermark or disclosure mechanism the Service or a Model applies; **(o)** circumvent, disable or interfere with any security, authentication, residency, attestation, metering, spend-control or abuse-protection mechanism of the Service, or with any safety mechanism of a Model, except as authorised under Section 9.3 of the Terms; **(p)** access or attempt to access a Restricted Model in breach of its restrictions, or misrepresent the Customer's location, identity or purpose to do so; **(q)** resell or redistribute raw access to the Service or to Models as a stand-alone inference service, or use the Service to build, train or benchmark a competing inference service, except under a written agreement with Infinia; **(r)** use Output to train, fine-tune or distil a model that competes with the Model that generated it where the Model Terms prohibit this; **(s)** send unsolicited bulk communications, or generate content for that purpose; **(t)** create multiple Organisations or accounts to evade limits, suspension or verification, or provide false registration or billing information; **(u)** impose an unreasonable or disproportionate load on the Service, or interfere with any other Customer's use of it; or **(v)** submit the voice, face, likeness or other biometric characteristic of any individual to a Model that performs biometric processing without that individual's informed, specific and documented consent, or use such a Model to identify, categorise or infer characteristics about an individual in a manner Applicable Law prohibits; or **(w)** attempt, encourage or assist any of the foregoing. **A.4** Sensitive workloads. The Service is designed to process regulated and sensitive data within a Sovereign Zone. Processing of Personal Data, health data, financial data and other regulated data is permitted subject to Section 8.5 of the Terms and Annex B, and to the Customer selecting a Sovereign Zone and Service Mode appropriate for the data. Processing of payment-card data is permitted only where the Documentation confirms the relevant Zone and Service Mode are validated for it. **A.5** Biometric, voice and image processing. Where the Customer uses a Model that the Model Catalogue identifies as performing biometric processing — including voice cloning, voice conversion, speaker identification, voice-preserving translation and the generation of a likeness of a person's face — Section 8.9 of the Terms applies. In summary, and without limiting it: the Customer is the controller of the resulting Biometric Data; the Customer must hold the informed, specific and documented consent of every individual whose voice, face or likeness it submits, and must be able to evidence that consent; the Customer must observe any additional notice, consent, retention-schedule and non-disclosure requirement that Applicable Law imposes in respect of biometric identifiers; and Infinia neither extracts nor retains any biometric identifier, because it does not retain Inputs or Outputs at all. The Customer must not use such a Model to impersonate an individual, to defeat a voice or face authentication system, or to create a depiction of an identifiable individual saying or doing something they did not say or do, other than with that individual's consent and with the disclosure that Applicable Law requires. **A.6** Applications serving third parties. Where the Customer operates an application that exposes the Service to its own end users, the Customer must (a) impose terms on its end users that are no less protective of Infinia and the Model Providers than this AUP and the applicable Model Terms; (b) implement abuse-prevention, rate-limiting and content controls proportionate to the application's risk, and the Customer acknowledges that it is the only party in a position to do so; (c) provide the disclosures about AI-generated content that Applicable Law requires; (d) not represent that Infinia or a Model Provider endorses the application; and (e) be responsible to Infinia for its end users' compliance. **A.7** Reporting. Suspected violations of this AUP may be reported to [abuse@●]. Infinia will acknowledge each report, will review it in a timely, diligent and non-arbitrary manner, may act under Section 9.4 of the Terms, and will inform the reporter of the outcome. Reports are the principal means by which Infinia learns of a breach involving content, because it does not inspect content. **A.8** Enforcement. Infinia may investigate suspected violations using Usage Data, reports made under Section A.7, information the Customer supplies and the results of the integrity controls in Section 6.10 of the Terms, may request information from the Customer, may suspend or terminate access under Section 21 of the Terms, and may cooperate with competent authorities. Before suspending or terminating for a breach of this AUP, Infinia will give the Customer a statement of the reasons for its decision and a reasonable opportunity to respond, except where immediate action is required under Section 21.3 of the Terms or by Applicable Law. Infinia's decision whether a use violates this AUP is made reasonably and in good faith and is final as between the parties for the purposes of suspension. ## Annex C — Retention Schedule This Annex states every retention period referred to in the Agreement. It forms part of the Agreement. Each period runs within the Region Scope; nothing in this Annex authorises retention outside it. Where a period in this Annex conflicts with a shorter period the Customer has configured, the Customer's configuration governs. Where Applicable Law requires a longer period for a particular record, that law governs for that record only, and the row says so. Infinia will not extend a period in this Annex without giving notice under Section 22. ### C.1 Customer Content | Data | Retention period | Basis and notes | |---|---|---| | Inputs and Outputs | Not retained. Deleted on completion of the request. | Section 6.3. Not logged, not sampled, not inspected. No backup copy is taken. | | Batch input and output files | 30 days from completion of the job, or until the Customer deletes them, whichever is earlier. | Stored Object. Schedule 1, S1.3. | | Batch jobs queued during a payment suspension | 14 days from suspension, then discarded. | Schedule 1, S1.3. The Customer is notified before discard. | | Uploaded datasets and files | Until the Customer deletes them; and in any event deleted within 30 days after the export period in Section 21.6(e) ends. | Stored Object. Section 8.6. | | Customer Models and Fine-Tuned Models | Until the Customer deletes them; and in any event deleted within 30 days after the export period in Section 21.6(e) ends. | Sections 7.8, 7.9 and B.9.1. | | Material preserved under Section A.3(b) | The period Applicable Law requires, and no longer. | The only retention of Customer Content outside this Annex. Segregated, access-logged, and disclosed only to the competent authority. | ### C.2 Usage Data, logs and evidence | Data | Retention period | Basis and notes | |---|---|---| | Usage Data (request metadata: timestamps, model, token counts, latency, error codes, routing, key and region identifiers) | 13 months rolling, then irreversibly aggregated. | Section 6.5. Contains no Input or Output content. Exportable by the Customer throughout. | | Billing records derived from Usage Data | 7 years from the end of the tax year to which they relate. | Section 6.5 and Applicable Law on tax and accounting records. Retained by Infinia as controller. | | Per-request attestation records and signed residency exports | 13 months, aligned with Usage Data. | Section 6.6. Available to the Customer through the portal and API. | | Immutable audit log of administrative and configuration changes | 13 months in the portal; 7 years in the immutable archive where the Sovereign Zone offers one. | Sections 6.6 and B.12(g). The 7-year archive is available to public-sector and regulated Customers under Section 24. | | Security, authentication and personnel access logs | 12 months. | Section B.12(h). Retained by Infinia as controller for security purposes. | | Encrypted backups of Stored Objects and control-plane data | Overwritten on a rolling 35-day cycle. | Section B.9.1. Deletion of a Stored Object takes effect in active systems immediately and in backups within this cycle. | ### C.3 Personal Data for which Infinia is a controller These periods are stated here so that the Agreement and the Privacy Notice cannot diverge. The Privacy Notice describes the same processing for individuals. | Data | Retention period | Basis and notes | |---|---|---| | Account, Organisation and Authorised User records | For the Term, then 7 years from closure. | Section 6.11. Limitation periods, tax and audit. | | Identity-verification (KYC/KYB) records and supporting documents | 5 years from the end of the business relationship, or longer where Applicable Law requires. | Section 3.7 and anti-money-laundering law. Cannot be deleted on request during this period. | | Sanctions and restricted-party screening records, including resolved matches | 5 years from the screening event. | Section 23.3 and sanctions law. Cannot be deleted on request during this period. | | Credit assessment records | 2 years from the assessment, or for the Term if longer. | Section 10.12. Subject to the human-review route in Section B.14.3. | | Support correspondence and case records | 24 months from closure of the case. | Section 11.3. | | Website and portal analytics | 14 months. | Consent-based where Applicable Law requires consent. Described in the Privacy Notice. | | Marketing contact records and consent evidence | Until consent is withdrawn, then 3 years for the consent record only. | Evidence that consent was validly obtained and lawfully withdrawn. | ### C.4 Notice periods stated elsewhere in the Agreement | Matter | Period | Clause | |---|---|---| | Deprecation of a generally available Model | Not less than 30 days. | Section 7.7 | | Deprecation of a Model under an active Reserved Throughput or Dedicated Model Inference commitment | Not less than 90 days. | Section 7.7 | | New Sub-processor | Not less than 30 days. | Section B.5.2 | | Change moving operational access outside a stated jurisdiction | Not less than 30 days. | Section 6.7 | | Increase in Published Rates | Not less than 30 days. | Section 10.16 | | Material change to the Agreement | Not less than 30 days. | Section 22.2 | | Credit expiry warning | 30 days and again 7 days before expiry. | Section 10.6 | | Termination by Infinia for convenience | Not less than 90 days. | Section 21.5 | | Security Incident notification to the Customer | Within 24 hours of awareness. | Section B.8.1 | | Initial incident notification to a DORA customer | Within 4 hours of awareness. | Section B.8.2 | | Export window after termination | 30 days. | Section 21.6(e) | ## Acceptance record By clicking "I agree" at sign-up, the Customer's Authorised User confirms on behalf of the Customer that they have read and understood these Terms including Schedule 1 and Annexes A to E, that they have authority to bind the Customer, that the Customer is not a consumer and is not a Restricted Party, and that they have taken particular note of Sections 10 (Fees, Credit and payment), 18 (Warranties and disclaimers), 20 (Limitation of liability) and 26 (Governing law and dispute resolution). Infinia records the version accepted, the accepting user, the Organisation, and the date and time of acceptance, and makes that record available in the Organisation's audit log. Xailon Token Factory Terms of Service · Version 1.1 · © [year] [Infinia Legal Entity Name]. All rights reserved. --- ## Links - [Support](mailto:support@iamsaif.ai)