Start here
Quickstart
Send a call through a Syderial door with the OpenAI or Anthropic SDK you already use. You change the base URL and add your credential.
A Syderial door accepts the same requests as the API your client was written for. This guide sends one call through the chat completions door, then repeats it through the messages and responses doors.
-
Save your credential.
Your credential arrives as request headers, one
Name: valuepair per line. Save them to a file namedsyderial-headers.txtand keep that file out of source control.syderial-headers.txt <header name issued to you>: <value issued to you><header name issued to you>: <value issued to you>The door identifies you from these headers, never from the request body. Your organization and identity come from the credential, so a request can’t claim to be someone else.
-
Set your door address.
export SYDERIAL_DOOR_URL="https://YOUR-DOOR-ADDRESS" -
Install the SDK you already use.
pip install openai anthropicnpm install openai @anthropic-ai/sdkcurl 7.55 or later reads headers from a file with
-H @file. Nothing to install. -
Load the credential in your client.
Both SDKs accept extra headers on every request. A small helper reads the file from step 1.
syderial_headers.py from pathlib import Pathdef load_headers(path="syderial-headers.txt"):"""Read the issued credential headers, one 'Name: value' per line."""headers = {}for line in Path(path).read_text().splitlines():if ":" in line:name, value = line.split(":", 1)headers[name.strip()] = value.strip()return headerssyderial-headers.ts import { readFileSync } from "node:fs";// Read the issued credential headers, one "Name: value" per line.export function loadHeaders(path = "syderial-headers.txt"): Record<string, string> {const headers: Record<string, string> = {};for (const line of readFileSync(path, "utf8").split("\n")) {const i = line.indexOf(":");if (i > 0) headers[line.slice(0, i).trim()] = line.slice(i + 1).trim();}return headers;}curl reads the file directly with
-H @syderial-headers.txt. -
Find a model alias.
The
modelfield takes an alias configured for your organization. List them with the standard models call.import osfrom openai import OpenAIfrom syderial_headers import load_headersclient = OpenAI(base_url=os.environ["SYDERIAL_DOOR_URL"] + "/v1",api_key="unused", # the door reads the credential headers, not this fielddefault_headers=load_headers(),)for model in client.models.list():print(model.id)import OpenAI from "openai";import { loadHeaders } from "./syderial-headers";const client = new OpenAI({baseURL: `${process.env.SYDERIAL_DOOR_URL}/v1`,apiKey: "unused", // the door reads the credential headers, not this fielddefaultHeaders: loadHeaders(),});for await (const model of client.models.list()) {console.log(model.id);}curl "$SYDERIAL_DOOR_URL/v1/models" \-H @syderial-headers.txtSet the alias you want to use:
export SYDERIAL_MODEL="<a model alias from the list>" -
Send a call.
call.py import osfrom openai import OpenAIfrom syderial_headers import load_headersclient = OpenAI(base_url=os.environ["SYDERIAL_DOOR_URL"] + "/v1",api_key="unused",default_headers=load_headers(),)completion = client.chat.completions.create(model=os.environ["SYDERIAL_MODEL"],messages=[{"role": "user", "content": "When did V-204 fail, and how do we know?"},],)print(completion.choices[0].message.content)call.ts import OpenAI from "openai";import { loadHeaders } from "./syderial-headers";const client = new OpenAI({baseURL: `${process.env.SYDERIAL_DOOR_URL}/v1`,apiKey: "unused",defaultHeaders: loadHeaders(),});const completion = await client.chat.completions.create({model: process.env.SYDERIAL_MODEL!,messages: [{ role: "user", content: "When did V-204 fail, and how do we know?" },],});console.log(completion.choices[0].message.content);curl "$SYDERIAL_DOOR_URL/v1/chat/completions" \-H @syderial-headers.txt \-H "content-type: application/json" \-d '{"model": "'"$SYDERIAL_MODEL"'","messages": [{"role": "user", "content": "When did V-204 fail, and how do we know?"}]}'
Syderial answers from the entries you’re cleared to see. If the record has nothing on the question, the door says so. If the answer is behind a boundary you aren’t cleared for, you get a refusal that names no one and reveals nothing about the withheld entries.
The same call through the other doors
Section titled “The same call through the other doors”Anthropic messages
Section titled “Anthropic messages”The Anthropic SDK adds /v1/messages to its base URL, so pass the door address without /v1.
import osimport anthropicfrom syderial_headers import load_headers
client = anthropic.Anthropic( base_url=os.environ["SYDERIAL_DOOR_URL"], api_key="unused", default_headers=load_headers(),)
message = client.messages.create( model=os.environ["SYDERIAL_MODEL"], max_tokens=1024, messages=[ {"role": "user", "content": "When did V-204 fail, and how do we know?"}, ],)
print(message.content[0].text)import Anthropic from "@anthropic-ai/sdk";import { loadHeaders } from "./syderial-headers";
const client = new Anthropic({ baseURL: process.env.SYDERIAL_DOOR_URL, apiKey: "unused", defaultHeaders: loadHeaders(),});
const message = await client.messages.create({ model: process.env.SYDERIAL_MODEL!, max_tokens: 1024, messages: [ { role: "user", content: "When did V-204 fail, and how do we know?" }, ],});
const first = message.content[0];if (first.type === "text") console.log(first.text);curl "$SYDERIAL_DOOR_URL/v1/messages" \ -H @syderial-headers.txt \ -H "content-type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "'"$SYDERIAL_MODEL"'", "max_tokens": 1024, "messages": [ {"role": "user", "content": "When did V-204 fail, and how do we know?"} ] }'OpenAI responses
Section titled “OpenAI responses”response = client.responses.create( model=os.environ["SYDERIAL_MODEL"], input="When did V-204 fail, and how do we know?",)
print(response.output_text)Here client is the OpenAI client from step 6.
const response = await client.responses.create({ model: process.env.SYDERIAL_MODEL!, input: "When did V-204 fail, and how do we know?",});
console.log(response.output_text);Here client is the OpenAI client from step 6.
curl "$SYDERIAL_DOOR_URL/v1/responses" \ -H @syderial-headers.txt \ -H "content-type: application/json" \ -d '{ "model": "'"$SYDERIAL_MODEL"'", "input": "When did V-204 fail, and how do we know?" }'Streaming
Section titled “Streaming”All three doors stream server-sent events when you set stream, in the event format each API defines.
stream = client.chat.completions.create( model=os.environ["SYDERIAL_MODEL"], messages=[{"role": "user", "content": "Summarise the V-204 record."}], stream=True,)
for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True)const stream = await client.chat.completions.create({ model: process.env.SYDERIAL_MODEL!, messages: [{ role: "user", content: "Summarise the V-204 record." }], stream: true,});
for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? "");}curl -N "$SYDERIAL_DOOR_URL/v1/chat/completions" \ -H @syderial-headers.txt \ -H "content-type: application/json" \ -d '{ "model": "'"$SYDERIAL_MODEL"'", "stream": true, "messages": [{"role": "user", "content": "Summarise the V-204 record."}] }'- Read The journal to see what the answer was drawn from.
- Connect an agent through the MCP door or a coding harness.
- See the fields each door reads in the API reference.