API reference

Difinity API Reference

Authenticate an application, call the Flow runtime at api.difinity.ai, and read the evidence a governed run leaves behind.

API reference

Before you start

Three things must exist before your application can make a governed call.

An organisation administrator creates an application in Hub. An application is a registered caller with its own identity and its own rate limit; it is not an agent and it is not a person.

The administrator issues an API token for that application. The token's secret is shown once, at creation, and never again.

The administrator assigns at least one use case to the application. A use case is the bounded governed context your calls run in: its policies, its guardrails, and (for the provider-compatible endpoints and POST /api/v1/chat) whose model provider account pays for the call.

You never create the application or issue the token yourself through this API. Those are Hub tasks, covered in "What an administrator sets up in Hub" below.

Hosts

Host What runs there
api.difinity.ai Flow, the runtime. Everything in this reference except the token exchange.
platform.difinity.ai The Platform API, the system of record. The token exchange, and everything an administrator manages through Hub.

Every governed action an agent takes leaves through the tool gateway, which holds the credential, decides, and executes. The tool gateway has no public host and you never call it directly; every example in this reference calls only api.difinity.ai and platform.difinity.ai.

Getting started

Five steps from setup to evidence review.

  1. An administrator creates an application in Hub and issues it an API token. This happens once per application.
  2. An administrator assigns the application at least one use case.
  3. Your application exchanges the API token for a short-lived identity token, once per session, by calling platform.difinity.ai.
  4. Your application calls api.difinity.ai with that identity token and the use case it wants to run in.
  5. An administrator reads the run in Hub's AI Trail afterwards. Your application does not read its own trail through this API.

Steps 1 and 2 are covered in "What an administrator sets up in Hub". Steps 3 and 4 are covered in "Authentication" and the endpoint sections that follow. Step 5 is covered in "Reading the evidence".

Authentication

Exchange your application's API token for a short-lived identity token. This is the one call in this reference made to platform.difinity.ai rather than api.difinity.ai, and the one call that carries no bearer token, because obtaining one is what it does.

POST https://platform.difinity.ai/api/v1/auth/exchange
Content-Type: application/json

{
  "api_token": "<your application's API token>",
  "subject": { "id": "end-user-42", "name": "Jane" }
}

subject is optional and names the end user your application is acting for. It is not verified: it is your application's own claim, carried into the identity token you receive back and shown in the trail as an attested subject, never as a signed-in person. Omit it for calls with no end user behind them, such as a scheduled job.

Response:

{
  "access_token": "<identity token>",
  "token_type": "Bearer",
  "expires_in": 900,
  "app_id": "app_...",
  "tenant_id": "org_...",
  "available_use_cases": ["uc_support_bot", "uc_billing_assistant"]
}

The identity token is valid for 15 minutes. Request a new one when it expires; there is no refresh call, because the exchange call is the refresh call. When you sent subject, it travels inside this token rather than as a separate field in this response; the run trail reads it back from there.

available_use_cases is a snapshot taken at the moment of exchange. If an administrator changes which use cases your application may reach, that change takes effect the next time you exchange, not immediately on the token you are already holding.

Send the identity token as a bearer token on every call to api.difinity.ai:

Authorization: Bearer <identity token>

Every call also names the use case it runs in, as a query parameter or a header. Use whichever fits your client:

POST https://api.difinity.ai/api/v1/chat?use_case_id=uc_support_bot

or

POST https://api.difinity.ai/api/v1/chat
X-Use-Case-ID: uc_support_bot

A missing use case, or a use case your application was not assigned, is refused. See "Errors".

Rate limits

The default rate limit is 60 requests per 60 seconds per application. An administrator can configure a different limit for a specific application in Hub.

Every response that passes authentication and the use-case check carries these headers:

Header Meaning
X-RateLimit-Limit The limit for the current window.
X-RateLimit-Remaining Requests left in the current window.
X-RateLimit-Reset When the current window resets.
Retry-After Present only on a 429. Seconds to wait before retrying.

A request over the limit is refused with HTTP 429. See "Errors" for the shape.

POST /api/v1/chat

A single governed chat turn. Runs the guardrail pipeline, calls the model, and returns the answer.

Request fields:

Field Type Notes
model string, required The model to use.
provider string or null One of openai, anthropic, google, deepseek, grok. Leave it null and Difinity.ai selects a model for you based on what the turn needs.
message object, required The turn's message. Has role (one of system, user, assistant, tool) and content.
history_messages array, optional Earlier turns, oldest first. Defaults to an empty list.
parameters object, required temperature, max_tokens, top_p, top_k, presence_penalty, frequency_penalty, stop, seed. All optional inside the object.
tools array or null Function-calling tool definitions, in the same shape as OpenAI's.
outputFormat object, optional type (one of raw, json, xml, markdown, html) and an optional schema. The one field on this endpoint that is camelCase rather than snake_case.
stream boolean Defaults to false. Set it to true, or send an Accept: text/event-stream header, to stream the answer. See "Streaming".
conversation_id string, optional Threads several turns into one run in the trail. Omit it and each call is its own single-turn run.
POST https://api.difinity.ai/api/v1/chat?use_case_id=uc_support_bot
Authorization: Bearer <identity token>
Content-Type: application/json

{
  "model": "gpt-5.6-terra",
  "provider": null,
  "message": { "role": "user", "content": "What is our policy on damaged-goods refunds?" },
  "parameters": { "temperature": 0.7, "max_tokens": 800 },
  "stream": false
}

Response fields:

Field Type Notes
id string An id for this turn.
content string The answer.
usage object or null prompt_tokens, completion_tokens, total_tokens, latency_ms.
model string The model that actually answered.
finish_reason string or null One of stop, length, content_filter, tool_calls, error, or END_TURN as observed; that last value has no lowercase form.
tool_calls array Empty unless the model called a tool.
status object or null A guardrail-stage status, where present: stage, label, state, and an optional detail.
transparency object or null Disclosure strings for the person using the answer, where the transparency pipeline set any.
{
  "id": "resp_8f2ac1",
  "content": "Our policy allows a replacement or refund within 30 days of receipt for goods that arrived damaged.",
  "usage": { "prompt_tokens": 142, "completion_tokens": 41, "total_tokens": 183, "latency_ms": 910 },
  "model": "gpt-5.6-terra",
  "finish_reason": "stop",
  "tool_calls": []
}

Streaming

Set stream to true, or send Accept: text/event-stream, and the response streams as server-sent events. Each event is a complete JSON object in the same shape as the non-streaming response above, sent as an unnamed data frame. There is no closing sentinel; the stream simply ends when the connection closes.

If you drop the connection, the work behind it stops. Flow cancels the model or tool call in progress at its next suspension point, rather than continuing to spend the turn after nobody is listening.

POST /api/v1/agent/runs

Starts an agent run. Not streamed: a run can pause partway through to wait for a person's approval, and answering in one response keeps that question separate from the run loop itself.

Request fields:

Field Type Notes
agent_id string, required Which agent.
version_id string, required Which published version of it.
message string, required What you are asking the agent to do.
model string, required The model the run uses.
provider string, optional One of openai, anthropic, google, deepseek, grok.
purpose string, optional A name from the closed set the agent's policy defines.
conversation_id string, optional Threads several runs into one conversation in the trail.

There is no on_behalf_of field, and this is deliberate. Who the run acts for comes from your identity token, never from a value in the request body, because a field like that would let a caller name anybody in the organisation. The token itself is verified; the subject claim inside it, if you sent one at exchange, is not (see Authentication).

POST https://api.difinity.ai/api/v1/agent/runs?use_case_id=uc_billing_assistant
Authorization: Bearer <identity token>
Content-Type: application/json

{
  "agent_id": "agt_refund_assistant",
  "version_id": "ver_14",
  "message": "Refund order 90142, it arrived damaged.",
  "model": "gpt-5.6-terra",
  "purpose": "customer_refund"
}

Response fields:

Field Type Notes
conversation_id string
outcome string One of answered, needs_approval, reached_limit, failed.
steps integer How many steps the run took.
content string or null The run's answer, once it has one.
because string or null Why the run stopped or is asking.
approval object or null Present only when outcome is needs_approval. See "Approvals and resume".
{
  "conversation_id": "conv_7a19",
  "outcome": "needs_approval",
  "steps": 2,
  "content": null,
  "because": "refunding more than the order total needs a person to confirm",
  "approval": {
    "approval_id": "apr_5c02",
    "action_digest": "sha256:9f3a...",
    "connector": "stripe",
    "tool": "refund_payment",
    "arguments": { "order_id": "90142", "amount_cents": 4200 },
    "expires_at": "2026-09-02T18:00:00Z"
  }
}

An agent's reasoning tier, Quick or Deep, is set on the approved version, not on the request, and it decides the run's step budget and time limit. This reference does not publish those numbers. Ask the person who configured the agent about its limits.

Approvals and resume

Some agent actions need a person to confirm them before they run. Only the person the agent is acting for may answer; there is no approver group, no shared queue, and no delegation. A run nobody answers eventually expires.

A standing "always allow" answer is possible for some escalations, and it is keyed to the specific policy clause that raised the question, not to how similar a future action looks. A refusal is never standing: saying no once does not become a rule for next time.

A person answers a pending approval through the Platform API's approvals endpoint, using their own sign-in, never your application's token. That answer becomes a signed packet. Your application picks the run back up by sending that packet, exactly as issued, to the resume endpoint:

POST https://api.difinity.ai/api/v1/agent/runs/resume?use_case_id=uc_billing_assistant
Authorization: Bearer <identity token>
Content-Type: application/json

{
  "approval_id": "apr_5c02",
  "approval": "<the signed packet, exactly as issued>"
}

The response has the same fields as the response shown under "POST /api/v1/agent/runs".

An unknown approval id, an already-resumed one, and an expired one all come back as the same 404. This is deliberate: telling them apart would let a caller probe which approval ids exist.

A run started through this application surface does not currently record which person it is acting for. The approval endpoint only allows the run's own recorded person to approve it, so with no person recorded, nobody can pass that ownership check. Treat needs_approval on this surface as informational until Flow records a person for application-surface runs, and do not build a customer-facing approval flow on it yet.

POST /api/v1/audit and POST /api/v1/verify

Two guardrail-only endpoints. Each runs the configured guardrail tasks (personal-information detection, harmful and toxic content, disallowed topics, blacklisted keywords, and the rest of the checks listed below) against a message, and returns a redacted copy ready for you to send onward. Neither generates an answer and neither performs model routing. A check that needs a model's judgement calls Difinity's own evaluation service, on Difinity-hosted models, never the provider or model your request named.

The only difference is the record it leaves. POST /api/v1/audit adds a row to the run trail, the same evidence a chat turn or agent run produces. POST /api/v1/verify runs the identical checks and makes no such record, for pre-flight checks and development.

Both take the same request:

Field Type Notes
message object, required Same shape as POST /api/v1/chat's message.
history_messages array, optional Defaults to an empty list.

Both return the same shape:

Field Type Notes
overall_status string passed or failed.
checks object One entry per configured check (harmful_content, toxic_content, manipulative_behaviour, insensitive_language, social_scoring, fact_checking, fake_news, phishing, blacklisted_keywords, topic_policy, disallowed_topics, custom_ethical_rules, human_escalation), each with status (one of passed, failed, not_applied), and optionally reason, probability and threshold.
pii_redaction object or null detected, entities_found, redaction_count, mode. Null if PII redaction did not run.
redacted_content object or null message and history_messages, with any detected PII replaced. Null only if PII redaction did not run at all, or ran in detect-only mode; present otherwise, even when nothing was found.

Each entry in pii_redaction.entities_found carries both the placeholder and the original value that was detected, in plain text, so the caller can see exactly what was found. Handle this response with the same care you would handle the personal information itself.

POST https://api.difinity.ai/api/v1/audit?use_case_id=uc_support_bot
Authorization: Bearer <identity token>
Content-Type: application/json

{
  "message": { "role": "user", "content": "Please send the invoice to jane@example.com." }
}
{
  "overall_status": "failed",
  "checks": {
    "harmful_content": { "status": "passed" },
    "blacklisted_keywords": { "status": "passed" }
  },
  "pii_redaction": {
    "detected": true,
    "entities_found": [
      { "type": "EMAIL", "original_text": "jane@example.com", "redacted_as": "[EMAIL_1]" }
    ],
    "redaction_count": 1,
    "mode": "redact"
  },
  "redacted_content": {
    "message": "Please send the invoice to [EMAIL_1].",
    "history_messages": []
  }
}

The checks object above is shortened for space; a real response includes every configured check.

Provider-compatible endpoints

This surface has six paths across five providers, each following one provider's own API shape for the fields Difinity documents. The base URL changes to api.difinity.ai. The Authorization header carries your Difinity identity token, not the provider's own key. Every call also still needs use_case_id (or X-Use-Case-ID), the same as the rest of this application surface. The request and response follow the provider's shape for the fields listed below; fields outside that list are ignored or rejected as noted for each endpoint.

Path Mimics
POST /openai/v1/chat/completions OpenAI's Chat Completions API
POST /anthropic/v1/messages Anthropic's Messages API
POST /deepseek/chat/completions DeepSeek's OpenAI-compatible chat API
POST /gemini/v1/models/{model}:generateContent Google's Gemini API
POST /gemini/v1/models/{model}:streamGenerateContent Google's Gemini API, streaming
POST /grok/v1/chat/completions xAI's OpenAI-compatible chat API

Model names follow whichever provider you are pointed at; Flow reads the model your existing integration already sends and maps it to its own catalogue.

OpenAI-compatible

The request accepts these fields: model, messages (each with role and content), temperature, max_tokens, top_p, frequency_penalty, presence_penalty, stop, stream, stream_options, seed, response_format, tools, tool_choice and user. Fields outside this list are ignored. The response carries id, object, created, model, choices (each with an OpenAI-shaped message and finish_reason), and usage.

POST https://api.difinity.ai/openai/v1/chat/completions?use_case_id=uc_support_bot
Authorization: Bearer <identity token>
Content-Type: application/json

{
  "model": "gpt-5.6-terra",
  "messages": [{ "role": "user", "content": "Summarise this ticket in one sentence." }],
  "temperature": 0.7
}

Streaming sends chat.completion.chunk frames in OpenAI's own delta shape, and closes with a literal [DONE] frame, exactly as the real OpenAI streaming API does.

Anthropic-compatible

The request accepts model, max_tokens, messages, system, temperature, top_p, top_k, stop_sequences, stream, metadata, tools and tool_choice. The response carries id, type, role, a content array of typed blocks, model, stop_reason and usage in input_tokens/output_tokens form. Fields outside the request list are ignored or rejected; check the request against a real call before relying on an edge case.

POST https://api.difinity.ai/anthropic/v1/messages?use_case_id=uc_support_bot
Authorization: Bearer <identity token>
Content-Type: application/json

{
  "model": "claude-sonnet-5",
  "max_tokens": 800,
  "messages": [{ "role": "user", "content": "Summarise this ticket in one sentence." }]
}

Streaming sends a sequence of typed frames: message_start, content_block_start, content_block_delta, content_block_stop, message_delta and message_stop. Do not assume this exactly reproduces Anthropic's own streaming sequence; for example, a message_start and content_block_start pair can be sent more than once in one response. Confirm the exact sequence against a real streaming call before building strict event-sequence handling.

DeepSeek-compatible, Gemini-compatible, Grok-compatible

DeepSeek and Grok mimic the OpenAI Chat Completions shape, because both providers' own APIs are themselves OpenAI-compatible. Gemini mimics Google's own generateContent and streamGenerateContent request and response shape, including its contents array and generationConfig object. Field-level behaviour for these three follows the provider's own API shape; verify against your provider's documentation before relying on an edge case.

Errors

Four different error shapes exist across this API, depending on which layer and which host refuses the request. None of them is a normalised, single format; document and handle all four.

The authentication envelope

Returned by the layer that checks your bearer token, your use case, and your rate limit, before any endpoint logic runs.

{
  "error": "missing_use_case",
  "error_description": "Use case ID is required"
}
HTTP status error When
401 missing_authorization No Authorization header.
401 invalid_token The token does not verify.
403 missing_use_case No use_case_id or X-Use-Case-ID was sent.
403 unauthorized_use_case The use case is not one your application was assigned.
401 authentication_error An unexpected failure while authenticating, distinct from an invalid token.
429 rate_limit_exceeded Over the rate limit. Carries a Retry-After header.

The token exchange envelope

The Platform API returns this envelope for the token exchange, the one call in this reference made to platform.difinity.ai. It uses the RFC 9457 Problem Details format (application/problem+json) and differs from the three Flow envelopes below.

{
  "type": "https://difinity.ai/problems/validation-failed",
  "title": "Bad Request",
  "status": 400,
  "detail": "the request could not be accepted; see errors",
  "instance": "/api/v1/auth/exchange",
  "correlation_id": "...",
  "errors": [ { "field": "api_token", "detail": "is required" } ]
}

The errors field is present only on a validation failure; other failures on this call carry type, title, status, detail, instance and correlation_id with no errors array.

The handler envelope

Returned by endpoint logic once authentication has passed: a bad request body, a policy failure, a provider error, or an unexpected fault.

{
  "error_code": "DIF-ERR31",
  "message": "the message did not pass a configured guardrail check"
}
HTTP status error_code When
400 DIFF-ERR01 The request body is not valid JSON of the expected shape.
400 INVALID_ARGUMENT A bad argument value.
422 DIF-ERR11 / DIF-ERR12 The requested provider is not allowed, or not configured, for this use case.
422 DIF-ERR21 / DIF-ERR22 The requested model is not allowed, or not configured, for this use case.
422 DIF-ERR31 A guardrail or policy check failed. Carries an additional failure_details array.
401 UNAUTHORIZED An upstream session Flow depends on has expired or was never valid.
500 PROCESSING_ERROR An internal failure after the model answered.
500 INTERNAL_ERROR An unhandled fault.
(passed through) PROVIDER_ERROR or the provider's own code The model provider itself returned an error.

Field names in this shape are snake_case: error_code, message, and, where present, model and finish_reason.

Running out of credits returns HTTP 402 with error_code OUT_OF_CREDITS. This applies to the chat and agent workspace at chat.difinity.ai; this application surface does not return it.

The streaming envelope

The streaming envelope has two shapes, depending on when the failure happens.

A failure before your stream opens, such as a bad request body or a policy failure caught before any answer starts, arrives as a named error event, in the flat handler shape:

event: error
data: {"error_code": "DIF-ERR31", "message": "the message did not pass a configured guardrail check"}

A failure partway through an already-open stream arrives as one more unnamed data frame, with the error nested under an error key:

{
  "error": {
    "error_code": "DIF-ERR31",
    "message": "the message did not pass a configured guardrail check"
  }
}

A provider error nested either way also carries model and finish_reason alongside error_code and message.

An unhandled fault mid-stream carries error_code DIF-ERR00, the streaming equivalent of the handler envelope's INTERNAL_ERROR. An upstream session expiring mid-stream nests the same way, with error_code UNAUTHORIZED:

{
  "error": {
    "error_code": "UNAUTHORIZED",
    "message": "..."
  }
}

Reading the evidence

Every governed run leaves a record: the guardrail checks that ran, the model call, any tool calls and approvals, and the final answer. Hub calls this the AI Trail.

An organisation administrator reads it in Hub. This API does not expose a route for your application to read its own trail; that is a deliberate separation between the party that made a call and the party that reviews the evidence of it.

What an administrator sets up in Hub

Everything below is configured by a signed-in person in Hub, using their own sign-in. None of it is something your application calls with its API token; naming it here is so you know what exists and who owns it.

  • Applications and API tokens: creating an application, issuing and revoking its tokens.
  • Use-case assignment: which governed contexts an application may call.
  • Approvals: a person answers an agent's pending approval from the chat and agent workspace or from Hub.
  • MCP servers: registering the organisation's own tool servers, by name, endpoint, and a reference to a stored credential. The credential itself is never returned once set.
  • Connectors: turning a gallery connector on or off, or wiring a registered MCP server in as one, and choosing which of its tools an agent may call.
  • Provider keys: where an organisation's own provider keys are enabled for it, its provider keys are write-only. An administrator can set, replace or remove a key, and can see that one is set, but nothing in the platform can read the key's value back.
  • AI Trail: reading the evidence described above.

Not in this API

These families are real but not part of this application surface, and this reference does not document them. Who can reach each one differs:

  • /api/v1/conversations/** on Flow. Reachable by a signed-in person; this is the product surface for chat.difinity.ai, the chat and agent workspace. An application's token is refused on it.
  • /api/v1/compliance/** on Flow. Reachable by a signed-in person through Hub's compliance screens; not an integration point.
  • Roles, groups, permissions and the activity and access logs, and /api/v1/admin/** on the Platform API. Reachable by a signed-in organisation administrator, or, for /admin, by Difinity staff with the right directory role; not reachable with your application's token.
  • /api/v1/internal/** on the Platform API. Service-authenticated only, between Flow, the tool gateway and Platform. Not reachable by any person's sign-in or by an application's token at all.

Compliance evidence

Difinity's guardrail and evidence controls can contribute operational evidence to a wider EU AI Act, ISO/IEC 42001, risk and audit programme. Difinity does not determine that an organisation or an AI system is compliant, and does not provide ISO/IEC 42001 certification.

See the platform documentation for the architecture and controls this API sits on top of.

Platform documentation