Developers
The Dyadic API.
Use Iris from OpenAI-compatible apps and SDKs: an OpenAI-compatible subset with a published contract, one key, your credits, the same assistant as the Workspace.
Base URL
Point an OpenAI-compatible app or SDK here and use a Dyadic API key. The Dyadic API is an OpenAI-compatible subset, not all of OpenAI's API: the compatibility table below says what each endpoint supports. Requests use your account's credits and plan, like the Workspace.
https://dyadic.app/api/openai/v1Explore
Quick Start
- Create a key in the Workspace under API, and set limits if you like.
- Set your app's base URL to
https://dyadic.app/api/openai/v1and paste the key. - Pick a model:
iris, oriris-thinkfor harder questions.
curl https://dyadic.app/api/openai/v1/chat/completions \
-H "Authorization: Bearer $DYADIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "iris", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'Anthropic-Compatible API
Apps built on Anthropic's SDKs or its Messages API, Claude Code included, can use Dyadic too: set the base URL to https://dyadic.app/api/anthropic and the API key to a Dyadic key (sent as x-api-key). Requests run like the OpenAI-compatible ones, with the same key, limits, credits and activity log, and are answered by the model chosen for this API. A model name is one of your key's agents (iris, iris-think) or any Claude model name.
import anthropic
client = anthropic.Anthropic(base_url="https://dyadic.app/api/anthropic", api_key=DYADIC_API_KEY)
message = client.messages.create(model="iris", max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}])| Feature | Details |
|---|---|
| Messages | /v1/messages, streamed or not: text, images and PDFs, system prompts and stop sequences. |
| Tools | Your tools, tool_use and tool_result blocks, tool_choice (auto, any, tool, none) and disable_parallel_tool_use, streamed as input_json_delta. |
| Thinking | adaptive with output_config.effort, enabled with budget_tokens, or disabled. A thinking block carries the signature its model gave, so it can go back on the next turn; a model that gives none leaves it empty, never made up. |
| Prompt caching | cache_control on blocks, or at the top level for the last one; usage reports cache reads and writes. |
| Structured output | output_config.format with a JSON schema. |
| Counting and models | /v1/messages/count_tokens and /v1/models; the whole API is described in OpenAPI at /api/anthropic/v1/openapi.json. |
| Errors | Anthropic's shapes and statuses, with a request-id; out of credits or over a key's limit is 402 billing_error. |
| Server tools | web_search and web_fetch on Dyadic's own search and page reader, code_execution in Dyadic's containers (Python and bash; the key's Code Interpreter switch), and remote MCP servers (mcp_servers; the key's MCP switch), answered with Anthropic's blocks (server_tool_use and its result, mcp_tool_use and mcp_tool_result). max_uses is enforced and usage.server_tool_use counts the requests. Search results carry no encrypted_content, and answers carry no citations. |
| Client tools | Anthropic's bash, text editor and memory tools: your app runs them, as with Anthropic. Claude on Anthropic gets Anthropic's own definitions; another model gets them described as functions. |
| Files and batches | /v1/files keeps files with your API files (images and documents name one by file_id), and /v1/messages/batches runs up to 5,000 requests on Dyadic's batch runner, its results as Anthropic's JSONL. Both need the key's Files switch. |
| Not offered | Domain limits on web search and fetch, Anthropic's skills, the computer and browser toolsets and tool search are refused with 400. |
OpenRouter-Compatible Gateway
Apps built for OpenRouter reach its models through Dyadic: set the base URL to https://dyadic.app/api/openrouter/v1 and use a Dyadic key with the AI gateway switch. The gateway reaches any of OpenRouter's models at the platform's cost, so Dyadic turns it on for an app's key on request; you can turn it off in the key's settings at any time, and turning it back on is Dyadic's. A key without it is answered 403 gateway_not_allowed: the policy, not a fault. Requests and answers are OpenRouter's, forwarded unchanged: its model ids, models fallback lists, provider routing, reasoning, plugins and usage.cost. Each request counts against the key's limits, is billed to your account and shows in its activity, on your own OpenRouter key when the key carries one.
from openai import OpenAI
client = OpenAI(base_url="https://dyadic.app/api/openrouter/v1", api_key="dya-...")
reply = client.chat.completions.create(model="anthropic/claude-haiku-5.5",
messages=[{"role": "user", "content": "Hello"}],
extra_body={"models": ["openai/gpt-6-luna"], "usage": {"include": True}})| Path | What It Does |
|---|---|
/chat/completions, /completions, /embeddings, /audio/speech, /audio/transcriptions, /images | Forwarded to OpenRouter; the key's Images, Embeddings, Audio and Moderate switches apply. |
/models, /models/{author}/{slug}/endpoints | OpenRouter's models and their providers' prices. |
/generation | A generation's cost and tokens, for this key's own requests only. |
/key, /credits | Answered by Dyadic: the key's usage and limits, and your account's allowance, in dollars. |
Described in OpenAPI at /api/openrouter/v1/openapi.json.
Works with
Apps and SDKs that use the endpoints and fields in the compatibility table, including the official OpenAI Python SDK (tested with 3.26.0):
Supported
| Feature | Details |
|---|---|
| Streaming | Server-Sent Events with content, reasoning_content and tool-call fragments, and a final usage chunk with stream_options.include_usage. |
| Function calling | Your tools, tool_choice and parallel_tool_calls, streamed argument by argument. |
| Structured output | response_format: json_object or json_schema. |
| Generation Settings | temperature, top_p, top_k, min_p, presence_penalty, frequency_penalty, repetition_penalty, seed, logit_bias, stop, max_tokens, reasoning_effort, logprobs. |
| Responses API | /responses with streaming events, function calling, web search, stored responses, previous_response_id, conversations, background responses with cancel, input token counts, compaction and a WebSocket mode. |
| Conversations | /conversations and their items, continued with the Responses conversation parameter. |
| Stored chat completions | store: true keeps a chat completion 30 days: list, retrieve, update its metadata, delete, and read its messages. |
| Embeddings | iris-embed and iris-embed-large (Iris's names for text-embedding-3-small and -large), text-embedding-3-small, text-embedding-3-large and text-embedding-ada-002, as floats or base64. |
| Audio | Text to speech with iris-voice or OpenAI's model and voice names (mp3, wav, pcm); transcription and translation with iris-transcribe, Whisper and GPT-4o models (json, text, srt, vtt, verbose_json). In runs, Iris can speak (audio.speak) and transcribe an uploaded recording (audio.transcribe). |
| Moderations | /moderations in OpenAI's format, for text and images (iris-moderation or OpenAI's model names). |
| Decisions | /decisions in OpenAI's beta format: typed answers (a predicate's probability, a choice, a rubric score) to questions about text or images, paid on input. Needs the key's Decisions switch. |
| Images | Images in messages (https or data URLs), /images/generations, /images/edits and /images/variations. |
| Files and batches | Upload files to reference by file_id in messages, and run JSONL batches of chat, Responses, embeddings or moderation requests in the background. |
| Hosted tools | In /responses: code_interpreter and shell (Python and Bash in an offline container, files at /mnt/data), file_search over your vector stores, image_generation, and mcp for tools on a public HTTPS MCP server, with approvals unless you turn them off. Each needs its switch on the key. |
| Containers, vector stores and skills | /containers and their files (expire after 20 minutes without use), /vector_stores with files, file batches and search, and /skills with versions, mounted read-only into containers. |
| Not offered | Assistants, Assistants threads, ChatKit, content provenance checks, evals, fine-tuning, multi-part uploads (use /v1/files), safety checks, the admin API, the Live API, the organization API, the Realtime API, the retired edits endpoint, the retired engines endpoints, video generation and webhooks return 404 unsupported_endpoint. |
| Tracing and limits | Every response has an X-Request-Id (errors too); your own X-Client-Request-Id is kept in your activity log; x-ratelimit-* headers show the key's requests per minute and token limit. |
| Context | Your whole conversation is sent; the model's context window is the limit. Keys can add your memory or a Workspace library's context. |
Compatibility
The Dyadic API is an OpenAI-compatible subset. An OpenAI field an endpoint knows but can't honor is refused with 400 and code unsupported_parameter (the field in param), never silently dropped; a field OpenAI doesn't define for the endpoint is ignored and named in the X-Dyadic-Ignored-Parameters header. The same contract is machine-readable at /capabilities (version 1.1.0).
| Endpoint | Applies | Only These Values | Refused | Accepted, No Effect |
|---|---|---|---|---|
POST /chat/completions | frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, messages, metadata, min_p, model, parallel_tool_calls, presence_penalty, reasoning, reasoning_effort, repetition_penalty, response_format, safety_identifier, seed, stop, store, stream, stream_options, temperature, tool_choice, tools, top_a, top_k, top_logprobs, top_p, user, verbosity | n (1), service_tier (auto / default), modalities (text) | audio, moderation, prediction, web_search_options | prompt_cache_key, prompt_cache_options, prompt_cache_retention, stream_options.include_obfuscation |
POST /completions | frequency_penalty, logit_bias, max_completion_tokens, max_tokens, min_p, model, presence_penalty, prompt, reasoning, reasoning_effort, repetition_penalty, response_format, seed, stop, stream, stream_options, temperature, top_a, top_k, top_p, user | n (1), best_of (1), echo (false), logprobs (false), suffix ('') | none | stream_options.include_obfuscation |
POST/GET /responses | background, conversation, input, instructions, max_output_tokens, metadata, model, parallel_tool_calls, previous_response_id, reasoning, safety_identifier, store, stream, stream_options, temperature, text, tool_choice, tools, top_logprobs, top_p, user | include (message.output_text.logprobs / message.input_image.image_url / reasoning.encrypted_content / code_interpreter_call.outputs / file_search_call.results), service_tier (auto / default), truncation (disabled) | access_programs, context_management, max_tool_calls, moderation, prompt | prompt_cache_key, prompt_cache_options, prompt_cache_retention, reasoning.generate_summary, reasoning.summary, stream_options.include_obfuscation |
POST /responses/input_tokens | conversation, input, instructions, model, parallel_tool_calls, previous_response_id, reasoning, text, tool_choice, tools | truncation (disabled) | personality | none |
POST /responses/compact | input, instructions, model, previous_response_id | service_tier (auto / default) | none | prompt_cache_key, prompt_cache_options, prompt_cache_retention |
POST /conversations | items, metadata | none | none | none |
GET/POST/DELETE /conversations/{conversation_id} | metadata | none | none | none |
GET/POST /conversations/{conversation_id}/items | items | none | none | none |
GET/DELETE /conversations/{conversation_id}/items/{item_id} | none | none | none | none |
GET/POST/DELETE /chat/completions/{completion_id} | metadata | none | none | none |
GET /chat/completions/{completion_id}/messages | none | none | none | none |
POST /embeddings | dimensions, encoding_format, input, model, user | none | none | none |
POST /moderations | input, model | none | none | none |
POST /decisions | input, model, questions | none | none | none |
POST /images/generations | prompt, quality, size, user | n (1), background (auto), moderation (auto), partial_images (0), stream (false) | output_compression, output_format, style | model, response_format |
POST /images/edits | image, prompt, quality, size, user | n (1), background (auto), partial_images (0), stream (false) | input_fidelity, mask, output_compression, output_format | model, response_format |
POST /images/variations | image, size, user | n (1) | none | model, response_format |
POST /audio/speech | input, model, response_format, speed, voice | stream_format (audio) | instructions | none |
POST /audio/transcriptions | file, language, model, prompt, response_format, temperature, timestamp_granularities | stream (false) | chunking_strategy, include, known_speaker_names, known_speaker_references | none |
POST /audio/translations | file, model, prompt, response_format, temperature | none | none | none |
POST/GET /files | file, purpose | none | expires_after | none |
POST/GET /batches | completion_window, endpoint, input_file_id, metadata | none | output_expires_after | none |
POST/GET /containers | expires_after, file_ids, name, skills | memory_limit (1g), network_policy ({'type': 'disabled'}) | none | none |
POST/GET /containers/{container_id}/files | file, file_id | none | none | none |
POST/GET /vector_stores | chunking_strategy, expires_after, file_ids, metadata, name | none | none | none |
GET/POST/DELETE /vector_stores/{vector_store_id} | expires_after, metadata, name | none | none | none |
POST /vector_stores/{vector_store_id}/search | filters, max_num_results, query, ranking_options | none | ranking_options.hybrid_search | ranking_options.ranker, rewrite_query |
POST/GET /vector_stores/{vector_store_id}/files | attributes, chunking_strategy, file_id | none | none | none |
GET/POST/DELETE /vector_stores/{vector_store_id}/files/{file_id} | attributes | none | none | none |
POST /vector_stores/{vector_store_id}/file_batches | attributes, chunking_strategy, file_ids, files | none | none | none |
POST/GET /skills | files | none | none | none |
GET/POST/DELETE /skills/{skill_id} | default_version | none | none | none |
POST/GET /skills/{skill_id}/versions | default, files | none | none | none |