{"openapi":"3.1.0","info":{"title":"CompanyFabric API","version":"1.0.0","summary":"One API key for every major text and code model, plus typed decisions.","description":"CompanyFabric is a model gateway: one API key and one prepaid balance reach models from OpenAI, Anthropic, Google, DeepSeek, Moonshot, xAI, Mistral and others through an OpenAI-compatible API.\n\nHow to use it as an agent:\n- To get text from a model (write, summarize, answer, code), call createChatCompletion. Use the model `companyfabric/auto` unless the user names a model; it picks a suitable model per request.\n- To answer yes/no, classification or rating questions about a piece of text or data, call createDecision instead. It returns probabilities, not prose, costs a fraction of a chat call, and answers in about 100 ms.\n- To find pages on the web (current facts, sources to cite, documentation), call createSearch. It returns ranked links with snippets, about a tenth of a cent to half a cent per search.\n- To see which model ids exist and what they cost, call listModels (no key needed).\n\nRequests are billed per token from your CompanyFabric balance, and the exact charge comes back in usage.cost_usd. Set a per-key monthly cap in the dashboard (https://companyfabric.com/dashboard/keys) so this key cannot spend more than you allow.\n\nEvery error is JSON { error: { code, message, resolution } }. Read `resolution`: it says what to do next. Codes: invalid_key (401, missing or wrong key), invalid_request (400, malformed body), model_not_found (404, unknown model id: call listModels), model_retired (404, the resolution names the replacement model), wrong_endpoint (400, a decision or search model sent to chat or the reverse), byok_required (403, a search engine that runs only on your own provider key: the user must add it at https://companyfabric.com/dashboard/byok, or use companyfabric/search), insufficient_credits (402, balance is empty: the user must top up at https://companyfabric.com/dashboard/billing), key_budget_exceeded (402, this key hit its monthly cap: the user must raise it in the dashboard or wait for the month to roll over), provider_rejected_request (400/413/422, the model's provider refused this body, often a prompt longer than the context window: change the request or the model, retrying the same body fails the same way), upstream_error (429/502/503, the provider is rate-limiting or down: retry with backoff or pick another model). Failed requests are not billed.\n\nGet a key at https://companyfabric.com/dashboard/keys. Keys look like `sk-cf-...` and are sent as `Authorization: Bearer sk-cf-...`.","contact":{"name":"CompanyFabric","url":"https://companyfabric.com/contact"},"termsOfService":"https://companyfabric.com/terms"},"externalDocs":{"description":"CompanyFabric API documentation","url":"https://companyfabric.com/docs"},"servers":[{"url":"https://api.companyfabric.com","description":"CompanyFabric production API"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Chat","description":"Text generation through any model."},{"name":"Models","description":"The live model catalog and prices."},{"name":"Decisions","description":"Typed answers with probabilities, no text."},{"name":"Search","description":"Web search: ranked links and snippets in one shape."}],"paths":{"/v1/chat/completions":{"post":{"operationId":"createChatCompletion","tags":["Chat"],"summary":"Generate a reply from any model","description":"Send a conversation (messages) to a model and get its reply. OpenAI-compatible: the request and response have the same shape as OpenAI's chat.completions.\n\nCall this whenever the user wants text written, summarized, translated, explained, or code produced by a model. Use model `companyfabric/auto` unless the user names a model; other routed choices are companyfabric/fast, companyfabric/cheap, companyfabric/best and companyfabric/code. Any id from listModels also works, e.g. `anthropic/claude-sonnet-5-5` or `openai/gpt-5.5`.\n\nLeave `stream` false (the default) to receive one JSON reply. With `stream: true` the reply arrives as server-sent events instead.\n\nFor a yes/no or pick-a-category question, createDecision is cheaper and returns a probability instead of text to parse.\n\nRequests are billed per token from your CompanyFabric balance, and the exact charge comes back in usage.cost_usd. Set a per-key monthly cap in the dashboard (https://companyfabric.com/dashboard/keys) so this key cannot spend more than you allow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionRequest"},"example":{"model":"companyfabric/auto","messages":[{"role":"system","content":"You are a concise assistant."},{"role":"user","content":"Summarize this week's sales notes in three bullets."}],"max_tokens":400}}}},"responses":{"200":{"description":"The model's reply. `usage.cost_usd` is what this request charged to the balance. With stream: true, a text/event-stream of OpenAI-format chunks ending in `data: [DONE]`.","headers":{"x-fabric-request-id":{"description":"Identifies this request in the CompanyFabric ledger. Quote it when reporting a problem.","schema":{"type":"string"}},"x-fabric-served-model":{"description":"The concrete model that answered, useful when a meta-model such as companyfabric/auto was requested.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionResponse"}},"text/event-stream":{"schema":{"type":"string","description":"Server-sent events, one OpenAI chat.completion.chunk JSON per `data:` line, ending with `data: [DONE]`. With stream_options.include_usage, the last chunk carries usage including cost_usd."}}}},"400":{"description":"Invalid request (invalid_request, wrong_endpoint, or provider_rejected_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (invalid_key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required: insufficient_credits (balance empty) or key_budget_exceeded (this key's monthly cap is reached). Do not retry; tell the user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or retired model (model_not_found, model_retired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The provider is rate-limiting this model (upstream_error). Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The provider failed (upstream_error). Retry shortly or use another model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No provider is available for this model right now (upstream_error).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/models":{"get":{"operationId":"listModels","tags":["Models"],"summary":"List available models and prices","description":"Returns every model you can call right now, with its id, provider, modality (text, code or decision), context size and price per million tokens. The first entries are CompanyFabric's routed meta-models (companyfabric/auto and friends), billed at the rate of whichever model they route to. Call this when the user asks which models exist, what one costs, or when a request failed with model_not_found. Public: no API key needed, and it is free.","security":[],"responses":{"200":{"description":"The live catalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelList"}}}}}}},"/v1/decisions":{"post":{"operationId":"createDecision","tags":["Decisions"],"summary":"Answer typed questions about a text with probabilities","description":"Ask a decision model one or more typed questions about a piece of text or JSON (the `state`) and get one answer per question with a probability. It never writes text, so there is nothing to parse and nothing it can invent outside the options given.\n\nCall this instead of createChatCompletion for yes/no questions (type `noul`: returns the probability that the answer is yes, 0 to 1), classification into labels you define (type `choice`), and ratings on an ordered scale you define (type `score`). Ask several questions in one call; that is the intended use. Examples: is this email urgent, which department does this ticket belong to, how likely is this customer to cancel.\n\nOmit `model` or use `typesafe/jev-latest`. A probability near 0.5 means unsure; treat unsure as no.\n\nBilled on input tokens only, output is free; a typical call costs a few thousandths of a cent. Requests are billed per token from your CompanyFabric balance, and the exact charge comes back in usage.cost_usd. Set a per-key monthly cap in the dashboard (https://companyfabric.com/dashboard/keys) so this key cannot spend more than you allow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionRequest"},"example":{"model":"typesafe/jev-latest","state":"Second time this month my card was charged twice. Refund me or I cancel.","questions":{"category":{"type":"choice","instructions":"What is this ticket about?","criteria":{"billing":"Charges, refunds","technical":"Bugs, errors","other":null}},"urgent":{"type":"noul","instructions":"Does this need a reply within the hour?"},"churn_risk":{"type":"score","instructions":"How likely is this customer to leave?","criteria":["No signal","Frustrated","Threatens to cancel"]}}}}}},"responses":{"200":{"description":"One answer per question, keyed by the same names as `questions`. `usage.cost_usd` is what this request charged.","headers":{"x-fabric-request-id":{"description":"Identifies this request in the CompanyFabric ledger. Quote it when reporting a problem.","schema":{"type":"string"}},"x-fabric-served-model":{"description":"The concrete model that answered, useful when a meta-model such as companyfabric/auto was requested.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionResponse"},"example":{"model":"typesafe/jev-latest","answers":{"category":{"type":"choice","choice":"billing","confidence":0.97,"probabilities":{"billing":0.97,"technical":0.02,"other":0.01}},"urgent":{"type":"noul","noul":0.88},"churn_risk":{"type":"score","score":1.8,"confidence":0.81,"probabilities":{"0":0.03,"1":0.14,"2":0.83},"legend":{"0":"No signal","1":"Frustrated","2":"Threatens to cancel"}}},"usage":{"input_tokens":142,"output_tokens":0,"cost_usd":0.0000063}}}}},"400":{"description":"Invalid request (invalid_request, wrong_endpoint, or provider_rejected_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (invalid_key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required: insufficient_credits (balance empty) or key_budget_exceeded (this key's monthly cap is reached). Do not retry; tell the user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or retired model (model_not_found, model_retired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The provider is rate-limiting this model (upstream_error). Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The provider failed (upstream_error). Retry shortly or use another model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No provider is available for this model right now (upstream_error).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/search":{"post":{"operationId":"createSearch","tags":["Search"],"summary":"Search the web and get ranked results with snippets","description":"Search the web and get ranked results, each with a title, url, a snippet written for a model to read, and the published date when known. The same result shape whichever engine answered; `source_provider` says which one did.\n\nCall this when the user's question needs current information, sources to cite, or a page you do not already have. Then read the snippets, and cite the urls. Omit `model` (companyfabric/search, the cheapest engine that supports the fields sent) or use companyfabric/auto-search, which picks an engine per query: dated or news queries to Exa, technical and academic ones to Parallel, the rest to DeepSeek. Both modes use only engines this key can run, so they never fail for lack of one.\n\nWhich engines a key can run: deepseek/search always, billed from the balance. exa/search and parallel/search run on the user's OWN Exa or Parallel key (saved at https://companyfabric.com/dashboard/byok, 0% fee) until each provider confirms resale through CompanyFabric in writing; naming one without that key answers 403 byok_required. listModels shows each engine's `access`: credits or byok.\n\nBilled per search, not per token: about $0.0016 on DeepSeek for up to 20 results (DeepSeek's price × 2), nothing on your own key. Requests are billed per token from your CompanyFabric balance, and the exact charge comes back in usage.cost_usd. Set a per-key monthly cap in the dashboard (https://companyfabric.com/dashboard/keys) so this key cannot spend more than you allow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"},"example":{"model":"companyfabric/auto-search","query":"speculative decoding benchmarks arxiv","max_results":5}}}},"responses":{"200":{"description":"Ranked results, most relevant first. `engine` (and the x-fabric-served-model header) is the engine that answered; `usage.cost_usd` is what this request charged.","headers":{"x-fabric-request-id":{"description":"Identifies this request in the CompanyFabric ledger. Quote it when reporting a problem.","schema":{"type":"string"}},"x-fabric-served-model":{"description":"The concrete model that answered, useful when a meta-model such as companyfabric/auto was requested.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"},"example":{"model":"companyfabric/auto-search","engine":"parallel/search","results":[{"title":"Speculative decoding: a survey","url":"https://arxiv.org/abs/2601.00001","snippet":"We benchmark draft-model and self-speculative methods on four model families.","published_at":"2026-09-02","source_provider":"parallel"}],"usage":{"requests":1,"cost_usd":0}}}}},"400":{"description":"Invalid request (invalid_request, wrong_endpoint, or provider_rejected_request).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (invalid_key).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Payment required: insufficient_credits (balance empty) or key_budget_exceeded (this key's monthly cap is reached). Do not retry; tell the user.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"byok_required: this engine runs only on the user's own provider key. Do not retry; tell the user to add the key at https://companyfabric.com/dashboard/byok, or use companyfabric/search.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or retired model (model_not_found, model_retired).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"The provider is rate-limiting this model (upstream_error). Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The provider failed (upstream_error). Retry shortly or use another model.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"No provider is available for this model right now (upstream_error).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"sk-cf-...","description":"A CompanyFabric API key from https://companyfabric.com/dashboard/keys, sent as `Authorization: Bearer sk-cf-...`. Give each app or agent its own key with a monthly cap."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","resolution"],"properties":{"code":{"type":"string","description":"Machine-readable error code.","examples":["invalid_key","invalid_request","model_not_found","model_retired","wrong_endpoint","insufficient_credits","key_budget_exceeded","provider_rejected_request","upstream_error"]},"message":{"type":"string","description":"What went wrong."},"resolution":{"type":"string","description":"What to do next, written so an agent can act on it."}}}},"description":"Every error is JSON { error: { code, message, resolution } }. Read `resolution`: it says what to do next. Codes: invalid_key (401, missing or wrong key), invalid_request (400, malformed body), model_not_found (404, unknown model id: call listModels), model_retired (404, the resolution names the replacement model), wrong_endpoint (400, a decision or search model sent to chat or the reverse), byok_required (403, a search engine that runs only on your own provider key: the user must add it at https://companyfabric.com/dashboard/byok, or use companyfabric/search), insufficient_credits (402, balance is empty: the user must top up at https://companyfabric.com/dashboard/billing), key_budget_exceeded (402, this key hit its monthly cap: the user must raise it in the dashboard or wait for the month to roll over), provider_rejected_request (400/413/422, the model's provider refused this body, often a prompt longer than the context window: change the request or the model, retrying the same body fails the same way), upstream_error (429/502/503, the provider is rate-limiting or down: retry with backoff or pick another model). Failed requests are not billed."},"ChatMessage":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["system","user","assistant","tool"]},"content":{"description":"The message text. OpenAI-style content-part arrays (text and image parts) are also accepted.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"object"}}]}},"additionalProperties":true},"ChatCompletionRequest":{"type":"object","required":["model","messages"],"properties":{"model":{"type":"string","description":"Model id. Use companyfabric/auto unless the user names one. Any id from listModels works.","examples":["companyfabric/auto","companyfabric/fast","companyfabric/cheap","companyfabric/best","companyfabric/code","anthropic/claude-sonnet-5-5","openai/gpt-5.5"]},"messages":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/ChatMessage"},"description":"The conversation so far, oldest first."},"stream":{"type":"boolean","default":false,"description":"false returns one JSON reply. true returns server-sent events."},"max_tokens":{"type":"integer","minimum":1,"description":"Upper bound on the reply length in tokens. Lower values cap the cost of the reply."},"temperature":{"type":"number","minimum":0,"maximum":2,"description":"Sampling temperature. Lower is more deterministic."}},"additionalProperties":true,"description":"OpenAI chat.completions request. Other OpenAI parameters (tools, response_format, stream_options, top_p, stop) are passed through to models that support them."},"ChatCompletionResponse":{"type":"object","required":["id","object","model","choices"],"properties":{"id":{"type":"string"},"object":{"type":"string","examples":["chat.completion"]},"created":{"type":"integer"},"model":{"type":"string","description":"The model id you requested. The model that actually answered is in the x-fabric-served-model header."},"choices":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"message":{"type":"object","properties":{"role":{"type":"string","examples":["assistant"]},"content":{"type":["string","null"]}},"additionalProperties":true},"finish_reason":{"type":["string","null"]}},"additionalProperties":true}},"usage":{"$ref":"#/components/schemas/ChatUsage"}},"additionalProperties":true},"ChatUsage":{"type":"object","properties":{"prompt_tokens":{"type":"integer"},"completion_tokens":{"type":"integer"},"total_tokens":{"type":"integer"},"cost_usd":{"type":"number","description":"What this request charged to your CompanyFabric balance, in US dollars. The same figure the dashboard shows."}},"additionalProperties":true},"Model":{"type":"object","required":["id","name","provider","modality","status","pricing"],"properties":{"id":{"type":"string","description":"Pass this as `model`.","examples":["companyfabric/auto","anthropic/claude-sonnet-5-5"]},"alias":{"type":"string","description":"Branded alias that resolves to the same model."},"access":{"type":"string","enum":["credits","byok"],"description":"Search engines only. credits: billed from the balance. byok: runs only on the user's own provider key, saved at https://companyfabric.com/dashboard/byok."},"name":{"type":"string"},"provider":{"type":"string"},"modality":{"type":"string","description":"text and code models go to createChatCompletion; decision models go to createDecision; search models go to createSearch.","examples":["text","code","decision","search"]},"status":{"type":"string","examples":["live"]},"context_tokens":{"type":"integer","description":"Maximum context window in tokens."},"pricing":{"type":"object","description":"Prices in US dollars, e.g. inputUsdPerMtok and outputUsdPerMtok (per million tokens), or usdPerRequest for a search engine, whose feeMultiplier is its margin on our key (2 for DeepSeek search). Meta-models show unit: routed and are billed at the rate of the model they route to.","additionalProperties":true}},"additionalProperties":true},"ModelList":{"type":"object","required":["object","data"],"properties":{"object":{"type":"string","const":"list"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Model"}}}},"SearchRequest":{"type":"object","required":["query"],"properties":{"model":{"type":"string","enum":["companyfabric/search","companyfabric/auto-search","deepseek/search","parallel/search","exa/search"],"default":"companyfabric/search","description":"companyfabric/search: the cheapest engine that supports the fields sent. companyfabric/auto-search: an engine picked per query. Or one engine by name."},"query":{"type":"string","minLength":1,"maxLength":1000,"description":"What to search for. A few words to a sentence works best."},"max_results":{"type":"integer","minimum":1,"maximum":20,"default":10},"include_domains":{"type":"array","items":{"type":"string"},"maxItems":50,"description":"Only results from these domains. Not supported by deepseek/search."},"exclude_domains":{"type":"array","items":{"type":"string"},"maxItems":50,"description":"No results from these domains. Not supported by deepseek/search."},"published_after":{"type":"string","format":"date","description":"YYYY-MM-DD: only results published on or after this date. Not supported by deepseek/search."}},"additionalProperties":false},"SearchResult":{"type":"object","required":["title","url","snippet","source_provider"],"properties":{"title":{"type":"string"},"url":{"type":"string"},"snippet":{"type":"string","description":"Text from or about the page, written for a model to read."},"published_at":{"type":"string","format":"date","description":"Present when the engine knows it."},"source_provider":{"type":"string","examples":["deepseek","parallel","exa"]}}},"SearchResponse":{"type":"object","required":["model","engine","results","usage"],"properties":{"model":{"type":"string","description":"The model you asked for."},"engine":{"type":"string","description":"The engine that answered, e.g. deepseek/search."},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}},"usage":{"type":"object","properties":{"requests":{"type":"integer","description":"Searches billed: 1."},"cost_usd":{"type":"number","description":"What this request charged to your balance, in US dollars."}}}}},"DecisionQuestion":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["noul","choice","score"],"description":"noul: yes/no, answered with the probability of yes. choice: pick one label from `criteria`. score: place on the ordered scale in `criteria`."},"instructions":{"type":"string","description":"The question, in plain language."},"criteria":{"description":"noul: optional { true, false } describing what each side means. choice: required map of label to description (description may be null), up to 255 labels. score: required list of 2 to 10 level descriptions, lowest first, indexed from 0.","oneOf":[{"type":"object","additionalProperties":{"type":["string","null"]}},{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":10}]}},"additionalProperties":true},"DecisionRequest":{"type":"object","required":["state","questions"],"properties":{"model":{"type":"string","default":"typesafe/jev-latest","description":"Decision model id. Omit to use the default. typesafe/jev-latest (Jev), perplexity/decider-27b (Decider 27B, also reads images), microsoft/microsoft-decision-1 (Microsoft-Decision-1) and google/gemini-decision-1 (Gemini Decision 1, also reads images, audio, video and PDFs) take the same questions and return the same answers.","examples":["typesafe/jev-latest","perplexity/decider-27b","microsoft/microsoft-decision-1","google/gemini-decision-1"]},"state":{"description":"The text or JSON the questions are about: a ticket, an email, a transcript, a record.","oneOf":[{"type":"string"},{"type":"object"}]},"questions":{"type":"object","minProperties":1,"description":"Questions keyed by a name you choose; the answers come back under the same names.","additionalProperties":{"$ref":"#/components/schemas/DecisionQuestion"}}}},"DecisionAnswer":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["noul","choice","score"]},"noul":{"type":"number","description":"noul only: probability of yes, 0 to 1."},"choice":{"type":"string","description":"choice only: the chosen label."},"score":{"type":"number","description":"score only: expected position on the scale, may fall between levels."},"confidence":{"type":"number","description":"choice and score: confidence 0 to 1."},"probabilities":{"type":"object","additionalProperties":{"type":"number"},"description":"choice and score: the full distribution."},"legend":{"type":"object","additionalProperties":{"type":"string"},"description":"score only: level index to its description."}},"additionalProperties":true},"DecisionResponse":{"type":"object","required":["model","answers","usage"],"properties":{"model":{"type":"string"},"answers":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/DecisionAnswer"}},"usage":{"type":"object","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer","description":"Always 0; output is free."},"cost_usd":{"type":"number","description":"What this request charged to your balance, in US dollars."}}}}}}}}