# Experiential Labs: machine-readable reference Experiential Labs is an OpenAI-compatible model gateway: one base URL in front of every model, spanning hosted providers, your own provider keys (bring-your-own-key), our platform-funded credits, and self-hosted or custom models, plus a public model catalog, per-model provider waterfalls, organization API keys, and usage and credits. Point any OpenAI client at this gateway and change nothing else. This file is the complete agent-facing reference. Human docs: https://pr-1132.preview.experientiallabs.ai/docs. OpenAI-shared semantics: https://platform.openai.com/docs/api-reference ## Base URLs (local vs platform) This deployment: API base URL: https://api-pr-1132.preview.experientiallabs.ai Web app: https://pr-1132.preview.experientiallabs.ai This is a self-hosted or local deployment. The hosted platform is https://api.experientiallabs.ai (web: https://platform.experientiallabs.ai). OpenAI clients use base_url = "https://api-pr-1132.preview.experientiallabs.ai/v1". The management API is under https://api-pr-1132.preview.experientiallabs.ai/api. ## Authentication - One header, always: Authorization: Bearer . No X-Api-Key, no query param. - Keys look like xpl_ + 40 lowercase hex chars and are scoped to ONE organization. The secret is shown once, at creation. - Mint a key by signing in to https://pr-1132.preview.experientiallabs.ai/settings/api-keys; the plaintext appears exactly once. Key creation and revocation are web-session actions, not API-key actions. - One key reaches BOTH the inference surface (/v1/*) and the management API an agent needs: catalog reads, custom-model and waterfall writes, BYOK provider connections, usage reads, and the org's key list. It CANNOT mint or revoke keys, change another key's limits, or reach platform-admin routes. - Bad or missing key: 401 with {"error":{"code":"invalid_key", "type":"authentication_error"}}. The 401 does not distinguish absent, malformed, revoked, or expired. - Verify a key works: GET https://api-pr-1132.preview.experientiallabs.ai/v1/models with the key -> 200 and the org's callable models. ## Two lanes (how a call is paid for) - pass_through: your own provider key (BYOK). No markup: the provider bills you directly. Connect keys at https://pr-1132.preview.experientiallabs.ai/settings. - platform_funded: our credits. Public-catalog models are priced from the launch catalog; each call draws down your credit balance. No markup. Which lane a model uses is decided by its provider waterfall. Either way, the gateway adds zero markup. ## Models - GET https://api-pr-1132.preview.experientiallabs.ai/v1/models lists the model slugs your key can call: the public catalog plus your organization's own custom and local models. Each id is a slug, e.g. "claude-opus-5", "gpt-5.5", "gemini-3.7-flash". - A slug resolves through a provider waterfall: an ordered list of ways to reach the model (provider + provider model id). The gateway tries each rung in order, fails over on capacity and transport errors, and returns the first success. Organizations may override the default chain. - The full catalog with pricing, context window, and modalities is a public, keyless read: GET https://api-pr-1132.preview.experientiallabs.ai/api/models (no key returns the public rows; send your key to also see your org's own custom and local models). One model: GET https://api-pr-1132.preview.experientiallabs.ai/api/models/; its deployments: .../providers. Note the split: the /api/models* catalog reads are keyless, while GET https://api-pr-1132.preview.experientiallabs.ai/v1/models (the OpenAI-compatible list) requires your key. ## OpenAI-compatible inference API GET https://api-pr-1132.preview.experientiallabs.ai/v1/models -> {"object": "list", "data": [{"id": "", "object": "model", "created": 0, "owned_by": "exp"}]} Scoped to the key's organization. POST https://api-pr-1132.preview.experientiallabs.ai/v1/chat/completions Standard OpenAI Chat Completions. "model" MUST be a slug from /v1/models. "stream": true streams as SSE. POST https://api-pr-1132.preview.experientiallabs.ai/v1/responses Standard OpenAI Responses. "stream": true streams as SSE. "previous_response_id" continues a prior response on any worker instance; continuations are retained for 24 hours. An unknown or expired id -> 400 code=previous_response_not_found: resend the full conversation. "store" is gateway retention: "store": false skips retention, so that response cannot be continued from; nothing is ever stored with the upstream provider either way. On native OpenAI routes the reasoning passthrough ships end to end: include=["reasoning.encrypted_content"] returns the encrypted reasoning carrier and accepts it back as input, and "reasoning": {"summary": ...} is honored. Non-function tool types in "tools" (custom, namespace, web_search, tool_search) are carried to the provider verbatim on native OpenAI routes; on any other route the request answers a 400 naming "tools" rather than silently dropping a tool. The same path also serves a WebSocket transport: upgrade GET https://api-pr-1132.preview.experientiallabs.ai/v1/responses (wss) with the same Authorization header; frames cross unchanged and "previous_response_id" works the same. A deployment without the transport answers the upgrade 426 code=upgrade_required: POST over HTTP instead (the Codex client falls back on exactly this status). POST https://api-pr-1132.preview.experientiallabs.ai/v1/messages Anthropic Messages API, translated onto the same chat surface and models. Auth: x-api-key OR Authorization: Bearer (same xpl_ key). "stream": true streams Anthropic SSE events. Extended thinking works on all-Anthropic routes: the thinking config passes through verbatim and thinking/ redacted_thinking blocks round-trip with signatures intact; on any other route, thinking config and history blocks -> 400 (nothing is silently dropped). Limits of the translation lane: image/document blocks -> 400 (the chat surface is text-only), tool_result.is_error=true requires a native Anthropic-only route (otherwise 400), Idempotency-Key is not honored here, /v1/messages/count_tokens answers an explicit 404 not_found_error (estimate locally), and errors use Anthropic's envelope {"type":"error","error":{"type","message"}} at the same statuses as below. Anthropic server tools: web_search tool types serve end to end on Anthropic routes (server_tool_use and web_search_tool_result blocks and citation-bearing text stream through intact and round-trip as history); other Anthropic-defined server tool types are rejected by name, never half-served. Prompt caching works: block-level cache_control markers on system blocks, message text, and tool_result reach Anthropic routes verbatim, and usage reports cache_creation_input_tokens / cache_read_input_tokens with input_tokens excluding both, exactly like the provider. - Reasoning effort ("reasoning_effort" on chat, "reasoning": {"effort": ...} on Responses, "output_config": {"effort": ...} on Messages) is served, never rejected: a recognized level passes through when the route supports it, snaps to the nearest supported level otherwise (a tie prefers the lower, so a substitution never spends more reasoning than you asked for), and on a model with no reasoning support the request serves without reasoning. "max" is the top tier. Every substitution or drop is disclosed on OpenAI-shaped responses in a top-level "x-experiential-ignored-parameters" array; nothing is silently rewritten. - Idempotency-Key is honored on the OpenAI routes: an exact retry with the same key replays the original result; the same key with a different body -> 409 idempotency_conflict. - Every other /v1/* path answers 404 code=not_found in the OpenAI error envelope. ## Error envelope (all /v1 routes; /v1/messages wraps the same statuses and ## meanings in Anthropic's envelope instead) Errors are {"error": {"message", "type", "code", "param"?}}. Stable codes (code -> HTTP status -> meaning -> recovery): invalid_json 400 body is not valid JSON -> fix the request. invalid_request 400 malformed request -> read message, fix, resend. invalid_parameter 400 a field is invalid ("param" names it) -> fix it. unsupported_capability 400 a whole capability the route does not expose (a tool, a modality, reasoning) -> pick a capable model; check supported_params and modalities in /api/models. unsupported_parameter 400 a specific field the route rejects, e.g. temperature on a reasoning-only route or top_k / frequency_penalty / presence_penalty / top_logprobs where unaccepted ("param" names it) -> remove the field or pick a model that lists it. previous_response_not_found 400 previous_response_id is unknown or expired -> resend the full conversation. invalid_key 401 missing/bad/expired/revoked key -> fix Authorization. model_not_granted 403 your org cannot call this slug -> use one from /v1/models. idempotency_conflict 409 same Idempotency-Key, different body -> new key. idempotency_replay_unavailable 409/500 original keyed result gone after a restart -> resend with a new Idempotency-Key. insufficient_quota 429 a spend limit or your credit balance is exhausted (message says which: a daily org or per-model cap, or credits) -> add credits or raise limits at https://pr-1132.preview.experientiallabs.ai/credits (platform-funded lane). unavailable_route 429/503 throttled or no healthy route right now -> retry with backoff. gateway_overloaded 429 -> retry with backoff. request_cancelled 499 the client disconnected before completion. all_routes_failed 502 every provider in the waterfall failed -> retry; if BYOK, check your provider key. provider_output_too_large 502 -> lower max output tokens. gateway_draining 503 instance is draining -> retry (hits another). deadline_exceeded 504 request ran past the deadline -> shorten or retry. internal_error 500 -> retry with backoff. Retry 429 (throttled)/502/503/504 with backoff. Do NOT blindly retry 400/401/403/409: fix the request first. Retries can double-bill a provider (at-least-once); pass an Idempotency-Key to dedupe. ## Management API (same Bearer key; https://api-pr-1132.preview.experientiallabs.ai/api) Catalog reads (keyless-public: public rows without a key, plus your org's own rows when you send your key): GET /api/models[?modality=&category=&provider=&min_context=&sort=&limit=&offset=] GET /api/models/ GET /api/models//providers Custom and local models and waterfalls (the key acts for its own org): POST /api/models create a custom model: {slug, display_name, providers:[{provider, provider_model_id, base_url?, ...}]} POST /api/models//providers add a deployment or local variant GET/PUT /api/models//waterfall read or replace the ordered chain ({model_provider_ids:[...]}) BYOK provider connections: GET /api/orgs//provider-connections PUT /api/orgs//provider-connections/ connect/rotate ({secret, config}) POST /api/orgs//provider-connections//check verify Usage and keys: GET /api/gateway/usage/daily?org_id=&scope=&group_by= rollup (day|model|member) GET /api/gateway/usage/events?org_id=... per-request stream GET /api/gateway/catalog?org_id=... aliases as your org resolves them, with each one's lane GET /api/keys the org's keys (never secrets) Archived Projects: The trace-backed optimizer, router-building, and Project-serving surfaces are retained for historical data and operator recovery only. Their UI routes and customer-key build paths are closed; do not use them as an inference API or as an alternate modeling implementation. Trace telemetry (bring your traces in as telemetry only; never a router build; the key acts for its org): POST /api/orgs//telemetry/traces/pull live pull from a provider: {transport_kind, source_kind, source_label, credential, config?}. transport_kind is one of: braintrust, langsmith, langfuse, posthog, mastra, postgres. The credential is used once and never stored on the row. POST /api/orgs//telemetry/traces/upload JSON {source_kind, source_label} returns a short-lived signed Storage URL/token (PUT exact raw bytes; 2h; path-bound; no overwrite). POST /api/orgs//telemetry/traces//finalize idempotent 202 accepted; the worker verifies the object then projects. Same formats and <=50MB as before. Arize/Phoenix have no live pull yet, use this with source_kind phoenix (or otlp). GET /api/orgs//telemetry/traces the org's landed telemetry traces plus total_ingests and total_traces (the verify-count). Humans see these at https://pr-1132.preview.experientiallabs.ai/telemetry. This path never creates a Project, preparation, or optimize job. Providers accepted on a deployment: openai, anthropic, gemini, azure_openai, openrouter, bedrock, local, fireworks, modal, zai, qwen, tencent, vertex, experiential_cloud. Experiential Cloud is a curated collection of models, hosted and optimized by Experiential Labs. Call it with an xpl_ key. Organizations do not connect their own credentials to it. BYOK connections: openai, anthropic, gemini, azure_openai, openrouter, bedrock, fireworks, modal, vertex, zai, qwen, tencent. ## Coding agents (Claude Code, Conductor, Codex, OpenCode, Cline, Cursor, and ## any OpenAI-compatible tool) Any coding agent that can target an OpenAI-compatible endpoint works against this gateway: set its base URL to https://api-pr-1132.preview.experientiallabs.ai/v1, supply an xpl_ key as the Bearer token, and name models by slug (from GET https://api-pr-1132.preview.experientiallabs.ai/v1/models). Tools that read the standard OpenAI SDK environment variables need only: export OPENAI_BASE_URL="https://api-pr-1132.preview.experientiallabs.ai/v1" export OPENAI_API_KEY="xpl_..." The variables must be EXPORTED: a plain KEY=... line in a shell file is invisible to child processes (the tool errors "Missing environment variable" while echo $KEY looks fine in the same shell). Claude Code connects through the Anthropic Messages lane: export ANTHROPIC_BASE_URL="https://api-pr-1132.preview.experientiallabs.ai" export ANTHROPIC_API_KEY="xpl_..." export ANTHROPIC_MODEL="" Use ANTHROPIC_API_KEY, never ANTHROPIC_AUTH_TOKEN: an existing claude.ai sign-in can outrank AUTH_TOKEN and send the gateway an OAuth token (a 401). Claude Code reads auth at startup, so start a new session after switching. Any catalog slug works as the model, not just Claude models, and the [1m] model suffix (ANTHROPIC_MODEL="[1m]") runs the 1M-token context window on models that serve one. See the /v1/messages limits above (extended thinking on all-Anthropic routes only, no images). Conductor (parallel Claude Code agents) injects the env it captured from your login shell plus its own settings into every agent, and passes the per-chat picker choice as --model, so remap a picker alias only with ANTHROPIC_DEFAULT_*_MODEL (ANTHROPIC_MODEL does not apply to picker selections). Recommended per-repo placement: base URL + picker remaps committed in the repo's .claude/settings.json env block (it overrides anything captured from the shell), with only the key machine-local in .conductor/settings.local.toml under [environment_variables] (git-ignored). App-wide instead: the same variables in Settings -> Environment, with Settings -> Harnesses -> Claude Code on the API key, not a claude.ai CLI login. The key in ANTHROPIC_API_KEY also stops Claude Code from authenticating with Anthropic directly; settings reach newly created workspaces, not running ones. Codex targets the Responses API. In ~/.codex/config.toml: model = "" model_provider = "explabs" [model_providers.explabs] name = "Experiential Labs" base_url = "https://api-pr-1132.preview.experientiallabs.ai/v1" env_key = "EXPLABS_API_KEY" wire_api = "responses" Export EXPLABS_API_KEY (the xpl_ key) in the shell that launches Codex, and leave requires_openai_auth unset: setting it forces a ChatGPT login instead of your gateway key. Codex rides the Responses WebSocket transport when the deployment serves it and falls back to HTTP on 426 by itself. Prove any lane in isolation before touching real config (and again when rotating a key), and exercise the REAL tool surface: agents send every MCP/plugin tool schema with each request, and tool schemas are where gateway/provider strictness differences bite, so a bare hello can pass while the first real session fails. The pass criterion is a session that completes with tools listed and one real tool round-trip. Codex: copy ~/.codex into CODEX_HOME=$(mktemp -d) (real mcp_servers ride the test) and run one tool-using prompt; Claude Code: an env-prefixed one-shot (claude -p) from a project with MCP servers and plugins enabled, asking it to list tools and read a file. Cheapest key-only pre-flight: GET /v1/models plus one tiny completion. Rotate keys new-first: mint the new key, verify it with the isolated tool-exercising test, swap it into the config, THEN revoke the old one. Never revoke or overwrite before the replacement is proven. Cursor (paid plans only: the Free plan serves only Auto and refuses named models, which custom models are) takes the same pair in its settings UI: Cursor Settings -> Models -> API Keys -> "OpenAI API Key" = the xpl_ key, "Override OpenAI Base URL" = https://api-pr-1132.preview.experientiallabs.ai/v1, then add catalog slugs as custom model names. Cursor relays every request through its own servers and keeps Tab completion on its own models; if it sends a sampling value a model's route pins, the request answers 400 invalid_parameter naming the field (see the error table above). Use a different slug there. Verified per-agent configs (Claude Code, OpenAI Codex CLI over the Responses API, OpenCode, Cline, Cursor) are maintained at https://pr-1132.preview.experientiallabs.ai/docs/coding-agents. ## Self-hosted CLI Self-hosters run the open-source Experiential gateway from the terminal with "exp run". The hosted platform manages the catalog, keys, and usage for you in the web app. ## Typical agent flow 1. A human signs in and mints an API key at https://pr-1132.preview.experientiallabs.ai/settings/api-keys, and (for the pass-through lane) connects provider keys at https://pr-1132.preview.experientiallabs.ai/settings. 2. The agent receives the key. It calls GET https://api-pr-1132.preview.experientiallabs.ai/v1/models to see callable slugs. 3. The agent calls POST https://api-pr-1132.preview.experientiallabs.ai/v1/chat/completions (or /v1/responses) with model="" exactly as it would call OpenAI, streaming or not. 4. The agent reads its own usage and spend at GET https://api-pr-1132.preview.experientiallabs.ai/api/gateway/usage/daily; humans see the same at https://pr-1132.preview.experientiallabs.ai/telemetry and https://pr-1132.preview.experientiallabs.ai/credits. ## Setup prompts (paste one into a coding agent; it does the setup for you) These are the same first-person prompts the web app's onboarding uses, with this deployment's URLs already filled in. A human pastes one into a CLI coding agent; the agent follows it literally. Human-readable copies: https://pr-1132.preview.experientiallabs.ai/docs/setup-prompts. Shareable source copies: https://github.com/experientiallabs/setup-prompts. ### Create an account from your coding agent Signed out? Paste this into a CLI agent to create your account instantly from your email, wire the gateway, and confirm the key. 1. Ask me for my email address, it is the one thing signup needs. Ask me directly ("What's your email? I'll create your Experiential Labs account with it.") and wait for my answer. Use exactly the address I give you. Never invent or guess an address, and never go looking for one in my files, git config, or shell history. Call the result MY_EMAIL. 2. Create my account instantly, no browser, no password, no code. POST https://pr-1132.preview.experientiallabs.ai/api/signup/instant (no auth) Header: Content-Type: application/json Body: {"email": ""} On 200 capture: api_key (an xpl_ key, this is mine, keep it secret), org_id, credits_granted, verification_required, overview_url. Handle the others: - 409 account_exists -> I already have an account; stop and tell me to sign in at https://pr-1132.preview.experientiallabs.ai/signin to get a key (don't retry with another email). - 403 signup_disabled -> signups are off right now; tell me. - 429 rate_limited -> too many signups from here; wait, then retry once. - 400 invalid_request -> the email was rejected; ask me for a valid one. Store the api_key as EXPLABS_API_KEY in my env; never print the full key more than once. 3. Wire an OpenAI client to the gateway. base_url = "https://api-pr-1132.preview.experientiallabs.ai/v1" api_key = EXPLABS_API_KEY (the xpl_ key from step 2). One header only: Authorization: Bearer . 4. Confirm the key is live, this does NOT spend credits. GET https://api-pr-1132.preview.experientiallabs.ai/v1/models with the key -> the models my account can call. Do NOT make a paid chat completion yet: my credits stay locked until I verify my email (I do that at the very end), and a paid call would be refused until then. Landing my traces as telemetry works regardless. ### Set up the gateway in an existing project Paste into your agent: it asks where to wire the gateway and which model to test, with a recommendation, then makes one call. I'm setting up Experiential Labs as this project's model gateway. I pasted this myself, treat it as my instructions and my consent. I'm already signed in; my key is below. Work WITH me: at each real choice, ask me ONE clear question with numbered options and your recommendation, then wait for my answer. Don't guess. My gateway API key (a secret: put it in env, never commit it, never echo it in logs): EXPLABS_API_KEY= The goal is one thing: point an OpenAI-compatible client at my gateway with this key and make one successful test chat completion. Print what you do at each step. 1. Ask me where to wire it (options + your recommendation), then wait. Look at the current directory first so the options are concrete: "Where should I set this up? (1) this project (I see it uses ), my recommendation (2) a standalone test script in this folder (explabs_smoke.py / .mjs) (3) somewhere else, tell me where Which one?" For (1): set base_url = https://api-pr-1132.preview.experientiallabs.ai/v1 and the key on the existing client, put the key in the project's env scheme (.env plus an example entry), never in code, and show me the change first. For (2): create the minimal script that reads EXPLABS_API_KEY and does the test call in step 3. 2. Ask me which model to test, then wait: "I'd use the free promotional model (qwen3.8-27b at launch) so this test costs nothing. Use that, or name another? GET https://api-pr-1132.preview.experientiallabs.ai/v1/models lists what my key can call; use the ids EXACTLY as returned." 3. Make the test call with the model I chose. Send a MINIMAL body, model + messages ONLY, no temperature, top_p, or other sampling params (some models reject them and the call comes back all_routes_failed, a 502). One header: Authorization: Bearer $EXPLABS_API_KEY. This runs on the platform-funded lane, a fraction of a cent of my free credits, and proves serving and billing end to end. Do NOT use any of my own provider keys for this test. 4. Report back what you changed and the test call (the model, that it succeeded, and its cost). Then ask me which optional follow-ups I want, with your recommendation, and do only the ones I pick: (a) Connect my own provider keys (BYOK) for free pass-through (b) Import my Claude Code / Codex usage history (metadata only) (c) Review my credits and spend BYOK and import are quick for me to do at https://pr-1132.preview.experientiallabs.ai/settings; balances live at https://pr-1132.preview.experientiallabs.ai/credits. Read the contract if you build further: https://pr-1132.preview.experientiallabs.ai/docs (human docs) and https://pr-1132.preview.experientiallabs.ai/llms.txt (machine-readable: honored and refused parameters, error codes, streaming caveats). Follow it literally. ### Wire your coding agent to the gateway Paste into Claude Code, Codex, OpenCode, Cline, Conductor, Cursor, or any OpenAI-SDK tool: the agent identifies itself and applies its own verified integration. I pasted this into you myself, wire THIS coding agent up to my Experiential Labs gateway, so my model calls route through https://api-pr-1132.preview.experientiallabs.ai and show up in my usage. Ground rules: never print my full key (first 8 characters at most), ask me before you edit any config file or shell profile, and if you cannot do a step (no file access, settings live in a GUI), print the exact manual steps for me instead. Print what you're doing at each step. 1. Get my key. Use EXPLABS_API_KEY from my environment if it is set; otherwise ask me to paste one (I can mint it at https://pr-1132.preview.experientiallabs.ai/settings/api-keys). It looks like xpl_ followed by 40 hex characters. 2. Prove the key works before touching any config: GET https://api-pr-1132.preview.experientiallabs.ai/v1/models with header "Authorization: Bearer " -> 200 and the model slugs I can call. Remember the list; I'll pick models from it. 3. Identify which agent you are and apply YOUR integration: - Claude Code: You cannot repoint yourself mid-session, and switching auth under a running session causes mixed-state errors. First prove the lane without touching any config, via one isolated subprocess run from this project (so my MCP servers and plugins load and their tool schemas ride the request. Tool schemas are where provider strictness bites, so a bare hello proves too little): ANTHROPIC_BASE_URL="https://api-pr-1132.preview.experientiallabs.ai" ANTHROPIC_API_KEY="" \ ANTHROPIC_MODEL="" \ claude -p "list your available tools, then read ./README.md and reply with its first heading" Pass only if that run completes with tools listed and a real tool use. Then offer me two placements and apply the one I pick: (a) append to my shell profile, or (b) print for one-off use: export ANTHROPIC_BASE_URL="https://api-pr-1132.preview.experientiallabs.ai" # no /v1 suffix export ANTHROPIC_API_KEY="" export ANTHROPIC_MODEL="" Use ANTHROPIC_API_KEY, never ANTHROPIC_AUTH_TOKEN: an existing claude.ai sign-in can outrank AUTH_TOKEN and send the gateway an OAuth token instead. Then tell me to start a NEW session from a shell with those set. Warn me plainly: image pastes are rejected (the lane is text-only), and extended thinking flows only when the slug routes to Anthropic end to end. - Conductor: Conductor launches Claude Code with the environment it captured from my login shell (interactive login shell, cached per workspace) merged with its own settings, and passes the per-chat picker choice as --model — so the model is routed by remapping picker aliases with ANTHROPIC_DEFAULT_*_MODEL variables (ANTHROPIC_MODEL does not apply to picker selections). Offer me two placements and apply the one I pick: (a) per-repo, recommended: merge into this repo's .claude/settings.json "env" block (committed; Claude Code applies it in every workspace, and it overrides anything Conductor captured from my shell), one ANTHROPIC_DEFAULT__MODEL entry per picker alias I use: { "env": { "ANTHROPIC_BASE_URL": "https://api-pr-1132.preview.experientiallabs.ai", "ANTHROPIC_DEFAULT_OPUS_MODEL": "" } } then put ONLY the key machine-local in .conductor/settings.local.toml under [environment_variables] (make sure that file is git-ignored before writing my key into it): ANTHROPIC_API_KEY = "" (b) app-wide: print the same variables for me to add by hand in Conductor -> Settings -> Environment, and remind me to check that Settings -> Harnesses -> Claude Code uses my API key, not a claude.ai CLI login. Warn me either way: settings reach newly created workspaces, not running ones, and if my shell profile exports a direct Anthropic ANTHROPIC_API_KEY, Conductor's capture hands it to every agent — placement (a) overrides it; otherwise I should remove the export. The key rides ANTHROPIC_API_KEY, never ANTHROPIC_AUTH_TOKEN: an existing claude.ai sign-in can outrank AUTH_TOKEN, and a set API key also stops Claude Code from trying to authenticate with Anthropic directly. - OpenAI Codex CLI: First prove the lane in a throwaway home so my real ~/.codex (config, sessions, auth) stays untouched, and test with my REAL tool surface: you send every mcp_servers/plugin tool schema on each request, and tool schemas are where provider strictness bites, so a bare hello proves too little. Copy ~/.codex into a fresh "$(mktemp -d)", append ONLY the [model_providers.explabs] table below to that copy's config.toml, then run CODEX_HOME= codex exec -m "" \ -c model_provider=explabs -c model_reasoning_effort=max \ "list your available tools, then read ./README.md and reply with its first heading" Pass only if it completes with my MCP/plugin tools listed and a real tool use. Then add the whole block to ~/.codex/config.toml (create it if missing, show me the diff before writing) and tell me to start a new Codex session: model = "" model_provider = "explabs" model_reasoning_effort = "max" # reasoning models; "max" is the top tier [model_providers.explabs] name = "Experiential Labs" base_url = "https://api-pr-1132.preview.experientiallabs.ai/v1" env_key = "EXPLABS_API_KEY" wire_api = "responses" Leave requires_openai_auth unset, and make sure EXPLABS_API_KEY is exported where I launch you (export KEY=..., a plain KEY=... line is invisible to child processes like me). - OpenCode: Write the provider block into this project's opencode.json (or ~/.config/opencode/opencode.json if I prefer global, ask): {"provider": {"explabs": {"npm": "@ai-sdk/openai-compatible", "name": "Experiential Labs", "options": {"baseURL": "https://api-pr-1132.preview.experientiallabs.ai/v1", "apiKey": "{env:EXPLABS_API_KEY}"}, "models": {"": {"name": ""}}}} Fill limit.context/limit.output and cost ({"input": $/1M, "output": $/1M}, i.e. the catalog's *_micro_usd_per_million / 1000000) for each slug from GET https://api-pr-1132.preview.experientiallabs.ai/api/models/ so my context window and spend display are right. - Cline: Your settings live in the VS Code UI, so print these for me to set by hand: API Provider "OpenAI Compatible"; Base URL https://api-pr-1132.preview.experientiallabs.ai/v1; API Key = my key (no Bearer prefix); Model ID = a slug from step 2; and per-model context window / max output tokens from https://api-pr-1132.preview.experientiallabs.ai/api/models/. - Cursor: Your gateway settings live in the Cursor Settings UI, so print these for me to set by hand. First tell me the prerequisite: a paid Cursor plan (the Free plan serves only Auto and refuses named models, which custom models are). Then: in Cursor Settings -> Models -> API Keys, put my key in "OpenAI API Key" (no Bearer prefix), enable "Override OpenAI Base URL" and set it to https://api-pr-1132.preview.experientiallabs.ai/v1. Add each slug I pick as a custom model name in the models list; custom models ride the override in OpenAI format, so any catalog slug works. Warn me plainly: Cursor relays requests through its own servers, Tab autocomplete stays on Cursor's models, and if a chat answers 400 invalid_parameter naming temperature or top_p, that model pins its sampling (the Claude 5 family pins temperature to 1). Pick a different slug. - Any other OpenAI-compatible tool: Export OPENAI_BASE_URL="https://api-pr-1132.preview.experientiallabs.ai/v1" and OPENAI_API_KEY="" wherever I launch you, and name models by slug. If your own config wants the values instead, it needs the same three: base URL https://api-pr-1132.preview.experientiallabs.ai/v1, my key, and a slug. 4. Verify end to end. Make one tiny completion with the surface you configured ("reply with the single word: ok", small max output), via your own next model call if you now route through the gateway, otherwise via curl with my key. Then tell me it landed and that I can watch every call at https://pr-1132.preview.experientiallabs.ai/telemetry. 5. Recap exactly what you changed (files and values, key shown as xpl_ prefix only) so I can undo it later. ## Web app URL map (where to send a human) https://pr-1132.preview.experientiallabs.ai/ sign in, then land on your key and credits https://pr-1132.preview.experientiallabs.ai/models the catalog: search, detail, compare https://pr-1132.preview.experientiallabs.ai/playground chat with any model in the browser https://pr-1132.preview.experientiallabs.ai/settings/api-keys mint and revoke API keys https://pr-1132.preview.experientiallabs.ai/settings connect BYOK provider keys https://pr-1132.preview.experientiallabs.ai/credits balance, spend, and adding credits https://pr-1132.preview.experientiallabs.ai/telemetry usage by model and by agent