One AI gateway for every internal app you ship
Stop scattering OpenAI, Anthropic, and Azure keys across half a dozen services. Point your apps, agents, and back-office jobs at Curie and get auth, rate limits, tool forwarding, audit, and cost tracking for free — under the same policies your chat users already follow.
curl https://curie.your-org.com/v1/conversations \ -H "Authorization: Bearer curie_pat_..." \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "model": "gpt-4.1", "messages": [{"role":"user","content":"..."}], "tools": [...], "agent_profile": "lattice_assistant" }'
What the gateway gives your apps
Curie is the conversation system of record for every programmatic call
Managed credentials
Your operator configures the OpenAI / Anthropic / Azure key once on Curie. Your apps
authenticate with user-issued curie_pat_* tokens scoped to one organization.
Rotate or revoke without redeploying anything.
Shared rate & cost limits
API traffic counts against the same per-user, per-org, and per-usage-group limits as chat. One token-per-minute ceiling, one monthly cost cap — consistent whether the request came from a browser or a job.
Tool forwarding
Send your tool catalog with each request; Curie forwards it to the provider's native
tools field, streams back tool_call events over SSE, and lets
your app execute and post results. Curie never executes tools itself.
Conversation persistence
Every API conversation is durable in Curie's database. Your app stores only a
curie_conversation_id; Curie owns the transcript, the tool-call sidecar,
and the audit view at /external_interactions.
Agent profiles
Reference an agent profile by slug; Curie composes the layered prompt stack server-side. New slugs auto-provision a draft profile that admins can refine in the console — no code change to update an in-flight integration's behavior.
Read-only audit
Admins see every programmatic conversation at /external_interactions
— system prompt, tool calls, results, token usage. Same UI shape as the chat
audit; same content-free logging guarantees.
Wire shape
A small, conversation-centric protocol. Your app POSTs a conversation, streams an SSE
response, and POSTs follow-up messages or tool results to resume the stream. Full spec
at docs/curie-integration-protocol.md in the repo.
Endpoints
GET /v1/models— enabled providersPOST /v1/conversations— start, stream SSEPOST /v1/conversations/:id/messages— resume turnGET /v1/conversations— list (paginated)GET /v1/conversations/:id— full transcriptDELETE /v1/conversations/:id— soft-delete
SSE event types
message_delta— assistant content chunktool_call— buffered, complete tool callusage— token countsdone— terminal (stop / tool_call_pending)error— structured error
# 1. Start a conversation. Curie streams SSE. curl -N https://curie.your-org.com/v1/conversations \ -H "Authorization: Bearer $CURIE_PAT" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "model": "gpt-4.1", "agent_profile": "support_assistant", "messages": [ {"role": "user", "content": "Look up ticket 4421"} ], "tools": [ {"name": "get_ticket", "input_schema": {...}} ] }' # 2. On a tool_call event, your app executes the tool # and POSTs the result back to resume the same turn: curl https://curie.your-org.com/v1/conversations/$CID/messages \ -H "Authorization: Bearer $CURIE_PAT" \ -d '{"role":"tool","call_id":"curie_call_abc","result":{...}}'
import { CurieClient } from "@your-org/curie"; const curie = new CurieClient({ baseUrl: "https://curie.your-org.com", apiKey: process.env.CURIE_PAT!, }); const stream = await curie.conversations.create({ model: "gpt-4.1", agent_profile: "support_assistant", messages: [{ role: "user", content: "..." }], tools: myToolCatalog, }); for await (const event of stream) { if (event.type === "message_delta") writeChunk(event.content_delta); if (event.type === "tool_call") await handleTool(event); }
curie = Curie::Client.new( base_url: "https://curie.your-org.com", api_key: ENV.fetch("CURIE_PAT") ) curie.conversations.create( model: "gpt-4.1", agent_profile: "support_assistant", messages: [{ role: "user", content: question }], tools: tool_catalog ) do |event| case event.type when "message_delta" then stream_to_user(event.content_delta) when "tool_call" then handle_tool(event) end end
Sample SDKs are illustrative. The wire is plain HTTP + SSE; any language with an SSE
client (Python httpx, Go net/http, Node fetch) works directly.
Where customers point it
In-house agents
Embedded chat panels in line-of-business apps. The host app owns tools and approval UI; Curie owns the model call and the transcript.
Batch jobs & workers
Background classifiers, summarizers, and enrichment pipelines. One key, one cost cap, one place to audit what ran overnight.
SaaS modules you build for clients
Each tenant brings their own LLM credentials via Curie. You ship one integration; they keep their data residency posture.
Ready to wire up your apps?
The gateway is part of every Curie license — no separate procurement, no extra vendor.
See Pricing