Develop
Quickstart
How to send your first request with an API key, protect inputs with Noirdoc Shield and read the answering provider from the response.
The gateway is available at https://api.noirdoc.de/v1 and speaks the format of the OpenAI API. You need access to the Noirdoc portal and curl, Python or Node.js.
-
Create a key
Sign in to the portal; without an account, the page takes you through registration via Create an account. Self-registration requires a connection from the EU or EFTA without a VPN, Private Relay, proxy or data centre IP. When you first sign in, the Getting started page guides you through the setup. Answer the question on § 203 German Criminal Code (StGB) there first, then create your first key. Your answer on § 203 StGB determines which providers are available to your organisation (see Professional secrecy).
You create further keys under Models → API keys with New key. The portal shows a key once, right after it is created.
Store the key in an environment variable. All examples in this documentation read it from
NOIRDOC_API_KEY. The value starts withpx-.Result: the environment variable
NOIRDOC_API_KEYcontains your key.export NOIRDOC_API_KEY="px-..." -
Choose a model
GET /v1/modelslists the models your key may call. Each item in the list has anid. You use this model ID asmodelin requests.The examples use
qwen3.8-27b. If this ID is not in your list, use another model ID from the list.Result: you know a model ID that your key may call.
curl https://api.noirdoc.de/v1/models \ -H "Authorization: Bearer $NOIRDOC_API_KEY" -
Send your first request
Send a request to
POST /v1/chat/completions. In the OpenAI SDK, setbase_urltohttps://api.noirdoc.de/v1andapi_keyto your key.Result: the response has the format of the OpenAI API. The text is in
choices[0].message.content.curl https://api.noirdoc.de/v1/chat/completions \ -H "Authorization: Bearer $NOIRDOC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3.8-27b", "messages": [{"role": "user", "content": "Hello"}] }'import os from openai import OpenAI client = OpenAI( base_url="https://api.noirdoc.de/v1", api_key=os.environ["NOIRDOC_API_KEY"], ) response = client.chat.completions.create( model="qwen3.8-27b", messages=[{"role": "user", "content": "Hello"}], ) print(response.choices[0].message.content)import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.noirdoc.de/v1", apiKey: process.env.NOIRDOC_API_KEY, }); const response = await client.chat.completions.create({ model: "qwen3.8-27b", messages: [{ role: "user", content: "Hello" }], }); console.log(response.choices[0].message.content); -
Protect a request with Noirdoc Shield
Send the
X-Noirdoc-Shield: onheader. Noirdoc Shield inspects the inbound request statelessly before it reaches the model: by default, it redacts personal data with markers such as[PERSON]and blocks detected secrets and prompt injections with status code 403 (shield_violation). Responses are not reverse-translated.You send
Reply to the email from Jonas Becker.
The model sees
Reply to the email from [PERSON].
New organisations start with the default setting
default_off: Shield inspects a request when it sends the header withonor when the API key or request tag enables Shield. All settings are described in Set up Noirdoc Shield.Result: the model receives sanitised input without the redacted original values, and the response reports the inspection outcome in the
X-Noirdoc-Shieldheader.curl https://api.noirdoc.de/v1/chat/completions \ -H "Authorization: Bearer $NOIRDOC_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Noirdoc-Shield: on" \ -d '{ "model": "qwen3.8-27b", "messages": [{ "role": "user", "content": "Reply to the email from Jonas Becker." }] }'response = client.chat.completions.create( model="qwen3.8-27b", messages=[{ "role": "user", "content": "Reply to the email from Jonas Becker.", }], extra_headers={"X-Noirdoc-Shield": "on"}, ) print(response.choices[0].message.content)const response = await client.chat.completions.create( { model: "qwen3.8-27b", messages: [ { role: "user", content: "Reply to the email from Jonas Becker.", }, ], }, { headers: { "X-Noirdoc-Shield": "on" } }, ); console.log(response.choices[0].message.content); -
Read the provider from the response
In curl,
-ishows the headers. In the SDKs you read headers from the raw response. The rule by which the gateway picks the provider is described in Providers & routing.Result: the response contains the
X-Noirdoc-Providerheader. It names the provider that answered the request.curl -i https://api.noirdoc.de/v1/chat/completions \ -H "Authorization: Bearer $NOIRDOC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3.8-27b", "messages": [{"role": "user", "content": "Hello"}] }'raw = client.chat.completions.with_raw_response.create( model="qwen3.8-27b", messages=[{"role": "user", "content": "Hello"}], ) print(raw.headers.get("x-noirdoc-provider")) response = raw.parse()const { data, response } = await client.chat.completions .create({ model: "qwen3.8-27b", messages: [{ role: "user", content: "Hello" }], }) .withResponse(); console.log(response.headers.get("x-noirdoc-provider"));
Troubleshooting
Every error response from the gateway has an error object with type, code and message. A missing or invalid key results in status code 401 with the code invalid_api_key. These codes are the most common when getting started:
| Status | Code | Meaning |
|---|---|---|
| 400 | model_required | The request names no model (model in the body, or in the form field for /v1/audio/transcriptions). |
| 402 | insufficient_credit | The organisation's credit is used up or lower than the estimated maximum cost of the request (including Noirdoc Shield, also on BYOK routes when Shield is active). |
| 403 | model_not_allowed_for_key | The model is not one of this key's allowed models. |
| 403 | starter_model_not_allowed | The organisation is still using only its starter credit, and the model or its managed provider is not included in starter credit. |
| 403 | shield_violation | Noirdoc Shield blocked the request before forwarding to the provider because an active protection filter configured with block matched; message names the triggered rule (secrets, pii, ner, injection or custom). |
| 404 | model_not_available | The model ID is not available to the organisation. |
| 429 | starter_rate_limit_exceeded | The organisation is still using only its starter credit and has reached the limit of 5 requests per minute or 25 requests per day (UTC) for managed providers. The response contains Retry-After with the wait in seconds. |
All codes are listed under Error codes.
Next steps
- OpenAI SDK: set up the client, set headers and handle errors
- Anthropic SDK & Claude Code:
/v1/messagesand Claude Code through the gateway - Streaming: receive responses as SSE without sentence buffering
- Set up Noirdoc Shield: configure protection filters per request, key or organisation