On this page: Quickstart
One API for OpenAI, Anthropic and content checks
withConflux is OpenAI-compatible. Point any OpenAI SDK at our base URL, use your withConflux key, and call GPT or Claude models. The same base URL checks images, video and text for moderation, AI generation and deepfakes.
Quickstart
- Create a free accountSign up at withconflux.com/get-started. No card needed.
- Copy your keyDashboard › API keys. Use a
cflx_test_key while you build, acflx_live_key in production. - Make your first callUse the official OpenAI SDK, or plain HTTP.
import OpenAI from "openai";
const ai = new OpenAI({
baseURL: "https://withconflux.com/v1",
apiKey: process.env.CONFLUX_KEY,
});
const res = await ai.chat.completions.create({
model: "claude-sonnet-5-5", // or "gpt-6-astra", any model in the list
messages: [{ role: "user", content: "Hello" }],
});
console.log(res.choices[0].message.content);Keys and authentication
Send your key as a bearer token on every request. Keys are created, paused and revoked in the dashboard; give each app or service its own key so its usage shows separately.
Authorization: Bearer cflx_live_…| Key | Use it for | Billing |
|---|---|---|
cflx_test_… | Building and testing | Not billed, and never counts toward your monthly checks or spend cap. Content checks still run for real, so test with your own sample files. |
cflx_live_… | Production traffic | Billed as normal. |
403.Base URL
https://withconflux.com/api/v1 also works. Both reach the same API.
Chat completions
Same request and response as OpenAI's chat completions, for OpenAI and Anthropic models alike. Streaming (stream: true) and tool calls work for both. Errors come back in OpenAI's format, so SDK clients show them as usual.
const stream = await ai.chat.completions.create({
model: "gpt-6-astra",
messages: [{ role: "user", content: "Write a tagline" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}Models
Returns the models on the gateway, each with the price for your account. Use the id as model. Every model and its price is also listed on withconflux.com/llm-gateway/models.
const { data } = await ai.models.list();
console.log(data.map((m) => m.id));Embeddings
Same as OpenAI's embeddings endpoint, for example text-embedding-3-small.
const emb = await ai.embeddings.create({
model: "text-embedding-3-small",
input: "withConflux docs",
});BYOK or Managed
| BYOK | Managed | |
|---|---|---|
| Provider keys | Yours, added under Provider keys | Ours, nothing to set up |
| Token price | Billed by OpenAI or Anthropic | Provider price plus a small margin, from prepaid credits |
| Code changes to switch | None. Same key, same base URL. | |
Check an asset
Send an asset and the checks you need. Each check runs on its own and returns its own result.
curl https://withconflux.com/v1/check \
-H "Authorization: Bearer $CONFLUX_KEY" \
-H "Content-Type: application/json" \
-d '{
"asset": { "url": "https://example.com/upload.jpg" },
"needs": ["moderation", "ai-image", "deepfake"]
}'Checks you can ask for
| Need | What it checks | Asset |
|---|---|---|
moderation | Nudity, violence, hate and self-harm in an image | url |
video-moderation | The same, across a video file | url |
text-moderation | Harmful or abusive text | text |
ai-image | Whether an image is AI-generated | url |
ai-video | Whether a video is AI-generated | url |
deepfake | Face swaps and manipulated faces in an image | url |
Images and videos go in asset.url (a public link to the file); text goes in asset.text.
{ "asset": { "text": "The comment to check" }, "needs": ["text-moderation"] }
{ "asset": { "url": "https://example.com/clip.mp4" },
"needs": ["video-moderation", "ai-video"] }Response
A valid key gets 200 back, with each check's outcome in results[] and its own status. Build on verdict and score: they have the same shape for every check.
{
"ok": true,
"org": "Acme Media",
"requestId": "req_q3XbT0kM1sVz9yAa",
"contentHash": "4f1c9e2ab07d63f58e21c4d09b7a6e35f2d8c1b0",
"cachedCount": 1,
"latencyMs": 412,
"keyMode": "managed",
"results": [
{ "need": "moderation", "status": 200, "cached": false, "mode": "live",
"verdict": "clean", "score": 0.03, "detail": { … } },
{ "need": "ai-image", "status": 200, "cached": false, "mode": "live",
"verdict": "ai-generated", "score": 0.94, "detail": { … } },
{ "need": "deepfake", "status": 200, "cached": true, "mode": "cache",
"verdict": "real", "score": 0.07 }
]
}| Field | Meaning |
|---|---|
requestId | Identifies this request. Quote it when you contact us. |
contentHash | The hash the asset was recognised by (see Repeat files). |
cachedCount | How many checks were answered from a repeat file. |
keyMode | managed or byok. |
results[].status | The outcome of that check: 200, or an error code from the table under Errors. |
results[].verdict | clean or flagged for the moderation checks, ai-generated or real for ai-image and ai-video, deepfake or real for deepfake. |
results[].score | 0 to 1: how sure the check is that the content is the flagged class. Can be null when the check gives no number, for example on text. |
results[].cached | true when the answer came from a repeat file (mode: "cache"). |
results[].detail | The full analysis for that check. Its fields depend on the check and can change; not present on repeat-file answers. |
results[].error | Present when the check didn't run, with the reason. |
A check that can't run comes back with its status and reason, while the other checks in the request still run:
{ "need": "ai-image", "status": 402, "cached": false, "mode": "none",
"error": "Monthly spend cap reached." }Repeat files
Every asset is recognised by a hash of what you send: the URL or the text. If you already have a content hash (for example the file's SHA-256), send it as asset.hash and the same file is recognised under any URL. If the same asset and check were seen in the last 30 days, the stored result comes back with cached: true and you are not billed again. You don't need your own cache.
Spend caps and limits
Set a monthly spend cap in the dashboard. Once it is reached, paid calls are refused with 402 and nothing is billed; repeat-file results stay free. The cap is checked as each request starts, so calls already in flight can finish a little over it.
Each key has a request limit per minute. Above it you get 429 with a Retry-After header in seconds. Each key can also be paused on its own in the dashboard.
Errors
When the whole request is refused, the status code says why. The LLM endpoints answer in OpenAI's error format ({ "error": { "message", "type", "code" } }); /v1/check answers { "ok": false, "error" }.
| Code | Meaning | What to do |
|---|---|---|
400 | Body is missing something: needs, messages or model | Fix the request body |
401 | Missing, invalid, revoked or paused key | Check the key in the dashboard |
402 | LLM gateway: credits used up, or monthly spend cap reached | Top up or raise the cap |
403 | Account suspended, key used on the other product, or model not enabled for your account | Use the right key or model, or contact us |
429 | Too many requests for this key | Retry after the seconds in Retry-After |
Per-check status in results[]
/v1/check answers 200 when the key is valid; each check then carries its own status.
| Status | Meaning | What to do |
|---|---|---|
200 | The check ran, or came from a repeat file | Read verdict and score |
402 | Spend cap or free monthly limit reached. Nothing billed | Raise the cap or upgrade |
403 | Check not enabled on your account | Ask us to turn it on |
422 | Unknown check name, or the asset can't be processed | Check the need name and the asset URL or text |
424 | BYOK only: no provider key set up for this check | Add it under Vendor keys |
5xx | The check failed on the way. Nothing billed | Retry |
Pricing
Free to start: 10,000 content checks a month, and €40 in credits for models on Managed. While you are on the free credits, the most expensive models and very long replies are held back until you add balance. BYOK is a flat €19 a month with no token markup. Full plans on withconflux.com/pricing.