OpenDeploy AI Hub is a bundled LLM gateway: one OpenAI-compatible base URL
(https://api.opendeploy.dev/v1), one bearer-token API key per project /
environment, dozens of models across providers, and one shared credit balance.
This skill manages the agent-visible surface — keys, credits, auto-recharge,
and usage views.
Invocation Preflight
If this skill is invoked directly, first run the global CLI version gate unless another OpenDeploy skill already did:
npm list -g @opendeploydev/cli --depth=0 --json
npm view @opendeploydev/cli version --json
opendeploy preflight . --json
If global is missing or older than npm latest, hand off to opendeploy-setup.
If the user skips the update, continue with the installed global CLI only when
it lists the ai-hub command group in opendeploy routes list --json. Do not
use npx as a fallback runner.
When to invoke
Auto-invoked from the deploy autoplan, in two phases. The opendeploy
First-Deploy Workflow (which both /opendeploy and /deploy run) handles
AI Hub natively across two steps backed by references/ai-hub.md:
-
Step 6.25 — decision. Uses the analyzer's
ai_config.requires_ai_api_keysignal plus direct source review. When detected and the project is not already on AI Hub, raises a singleAskUserQuestion:Use OpenDeploy AI Hub (Recommended)/Use my own AI keys/Continue without AI. This step records the choice into the deploy-attempt record but performs no mutation — the project does not exist yet. -
Step 9.5 — provision + local-file fill (only if step 6.25 chose AI Hub). After the project is created (Step 8) and dependencies are provisioned (Step 9), the autoplan runs
opendeploy ai-hub keys provision --project <id> --environment <env> --show-secrets --jsonto explicitly create the key. The response's realkeyvalue flows into a secondAskUserQuestioncovering local-file writes (write the AI Hub key into.envsopnpm devworks locally;.env.examplegets the var name + base URL only, never the real key). Service body at Step 10 still uses the{{OPENDEPLOY_AI_API_KEY}}placeholder so future key rotations propagate server-side. Provision failures stop the deploy with a clear error — there is no silent fallback to lazy backend expansion.
For deeper operations on already-deployed projects (re-provision after a rotation, top up credits, set auto-recharge, inspect usage, fully rewrite an existing app's LLM call sites), the autoplan hands off here. Treat that handoff as the primary entry point — most invocations arrive mid-deploy or post-deploy with project context already resolved.
Otherwise pick this skill when the user wants to:
- Provision a fresh AI Hub key for a project / environment
- Check the remaining AI Hub credit balance
- Top up AI Hub credits (Stripe checkout) or set up auto-recharge
- See how many credits a model burned, broken down per model or per key
- List the models available through the AI Hub
- Get the OpenAI-compatible base URL + auth header format to paste into an SDK
- Deactivate or delete an AI Hub key (e.g. suspected leak)
- Detect existing LLM call sites in the project and offer to migrate them to AI Hub (see the Migrating an existing app to AI Hub section below)
Do NOT pick this skill for:
- Setting
OPENDEPLOY_AI_API_KEYinto a service env — the deploy flow auto-injects the AI Hub key when the service env contains the placeholder{{OPENDEPLOY_AI_API_KEY}}(or the legacy{{MINIONS_AI_API_KEY}}, kept for backwards compatibility) or a known AI SDK env var. Useopendeploy-envto add the placeholder, not this skill. - Initial deploy — the deploy autoplan provisions an AI Hub key per project automatically. Only invoke this skill if the user wants to inspect or re-provision after deploy.
- Project / service CRUD → use
opendeploy/opendeploy-config. - Stripe subscription billing (plans) → not this skill. AI Hub credits are pay-as-you-go top-ups, independent of plan subscription.
Capabilities
| Operation | Preferred CLI command | API fallback | Mutation? | Confirmation? |
|---|---|---|---|---|
| List keys | opendeploy ai-hub keys list [--project <id>] [--environment <env>] [--include-deleted] --json |
opendeploy api get /v1/aihub-keys --json |
no | no |
| Get one key | opendeploy ai-hub keys get <key-id> --json |
opendeploy api get /v1/aihub-keys/<key-id> --json |
no | no |
| Provision (create / re-create) | opendeploy ai-hub keys provision --project <id> --environment <env> --json |
opendeploy api post /v1/aihub-keys/recreate --data '{"project_id":"<id>","environment":"<env>"}' --json |
yes | YES if a previous active key exists for that project/env |
| Delete key record | opendeploy ai-hub keys delete <key-id> --json |
opendeploy api delete /v1/aihub-keys/<key-id> --json |
yes | YES |
| Deactivate by token name | opendeploy ai-hub keys deactivate --token-name <name> --json |
opendeploy api delete /v1/aihub-keys/by-token?token_name=<name> --json |
yes | YES |
| Balance | opendeploy ai-hub credits balance --json |
opendeploy api get /v1/ai-hub/user/self --json |
no | no |
| Top-up history | opendeploy ai-hub credits history --json |
opendeploy api get /v1/topup/history --json |
no | no |
| Transactions ledger | opendeploy ai-hub credits transactions --json |
opendeploy api get /v1/topup/transactions --json |
no | no |
| Latest top-up status | opendeploy ai-hub credits latest --json |
opendeploy api get /v1/topup/latest --json |
no | no |
| Create Stripe checkout | opendeploy ai-hub credits checkout --amount <usd> --confirm-paid --json |
opendeploy api post /v1/topup/checkout --data '{"amount":<usd>}' --confirm-paid --json |
yes | YES (paid action) |
| Get auto-recharge | opendeploy ai-hub credits auto-recharge get --json |
opendeploy api get /v1/topup/auto-recharge --json |
no | no |
| Set auto-recharge | opendeploy ai-hub credits auto-recharge set --enabled true --threshold-amount 5 --recharge-amount 20 --json |
opendeploy api put /v1/topup/auto-recharge --body auto-recharge.json --json |
yes | YES (turning on / changing cap) |
| Usage by model | opendeploy ai-hub usage models [--start <ts>] [--end <ts>] --json |
opendeploy api get /v1/ai-hub/usage/model --json |
no | no |
| Usage by key | opendeploy ai-hub usage tokens --json |
opendeploy api get /v1/ai-hub/usage/token --json |
no | no |
| Per-call logs | opendeploy ai-hub usage logs [--limit 50] [--page 1] --json |
opendeploy api get /v1/ai-hub/log/self --json |
no | no |
| Aggregated stats | opendeploy ai-hub usage stats --json |
opendeploy api get /v1/ai-hub/log/self/stat --json |
no | no |
| List models | opendeploy ai-hub models list --json |
opendeploy api get /v1/ai-hub/models --json |
no | no |
| Model metadata | opendeploy ai-hub models metadata --json |
opendeploy api get /v1/ai-hub/model-metadata --json |
no | no |
| Pricing (public) | opendeploy ai-hub pricing --json |
opendeploy api get /v1/ai-hub/pricing --public --json |
no | no |
| Base URL + auth header | opendeploy ai-hub base-url --json |
local — no API call | no | no |
CLI route compatibility. The opendeploy ai-hub namespace is new. If
opendeploy ai-hub <op> exits with unknown command or opendeploy routes list --json does not list an ai-hub group, fall back to the API column or
hand off to opendeploy-setup to update the CLI.
DELETE policy reminder. The global opendeploy-api skill blocks DELETE.
ai-hub keys delete and ai-hub keys deactivate are the only sanctioned
exceptions in this skill, and only when the named CLI command is not yet
available on the user's installed CLI. Always ask before issuing the fallback
DELETE call.
Request body shapes for the API fallback:
// /v1/topup/checkout
{ "amount": 20, "success_url": "https://...", "cancel_url": "https://..." }
// /v1/topup/auto-recharge (PUT)
{
"enabled": true,
"threshold_amount": 5,
"recharge_amount": 20,
"max_monthly_amount": 200,
"stripe_payment_method_id": "pm_..."
}
// /v1/aihub-keys/recreate
{ "project_id": "<uuid>", "environment": "staging" }
amount, threshold_amount, recharge_amount, and max_monthly_amount are
USD (numbers, not strings). max_monthly_amount: 0 means no monthly cap.
Mandatory structured confirmation
Every operation that spends money, replaces a live key, or removes a key
record MUST get explicit user approval BEFORE calling the CLI/API. Use the
host agent's structured question / approval UI when available
(AskUserQuestion, Codex user-input popup, or equivalent). Do not print a
plain Confirm? yes/no line and wait in chat when a structured UI exists.
Never chain confirmations into the same turn — the user gets one clear
approval choice.
Provisioning a key when an active one already exists
question: "Re-provision the AI Hub key for <project> / <env>?"
header: "AI Hub key"
options:
- label: "Re-provision and continue (Recommended)"
description: "Creates a fresh AI Hub key and rotates it into the project's secrets. The old key stops working immediately — any service still using the cached value will fail until it picks up the new value on next restart/redeploy."
- label: "Show what would change first"
description: "List the affected project, environment, current key ID, and which services consume OPENDEPLOY_AI_API_KEY before changing anything."
- label: "Cancel"
description: "Stop without changing the key."
Top-up (Stripe checkout)
question: "Top up AI Hub credits with $<amount>?"
header: "Paid action"
options:
- label: "Create checkout link (Recommended)"
description: "Generates a Stripe checkout URL for $<amount> USD. No charge happens until you complete checkout in the browser."
- label: "Cancel"
description: "Stop. No checkout link is created and no charge is attempted."
The recommended option triggers opendeploy ai-hub credits checkout --amount <amount> --confirm-paid. The --confirm-paid flag is required —
the gateway's consent gate rejects the request without it.
Auto-recharge enable / change
question: "Enable auto-recharge that adds $<recharge_amount> when balance drops below $<threshold>?"
header: "Auto-recharge"
options:
- label: "Enable auto-recharge (Recommended)"
description: "OpenDeploy will charge the saved Stripe payment method up to $<max_monthly_amount>/month when the AI Hub balance falls under $<threshold>. You can disable it any time."
- label: "Adjust limits first"
description: "Show the current threshold, recharge amount, and monthly cap before enabling."
- label: "Cancel"
description: "Stop. Auto-recharge stays in its current state."
Delete / deactivate a key
question: "Deactivate AI Hub key <token_name>?"
header: "AI Hub key"
options:
- label: "Deactivate (Recommended)"
description: "The key stops accepting requests immediately. Any service still using it will fail until rotated. This cannot be reversed — re-provisioning gives a new key, not the old value."
- label: "Cancel"
description: "Stop without deactivating."
list, get, balance, history, transactions, latest, all usage
queries, models list, models metadata, pricing, and base-url proceed
without confirmation. They are all read-only.
Provisioning a key (first time on a project)
- Confirm the project ID:
opendeploy projects list --jsonand pick by name, OR pass--project <project-id>if the user already named one. - Pick the environment. AI Hub keys are scoped per-environment so staging
spend stays separate from production:
question: "Which environment is the key for?" options: - label: "production (Recommended)" description: "Use for live customer traffic. Reads/writes to the production AI Hub budget." - label: "staging" description: "Use for pre-production. Reads/writes the staging budget when staging is enabled; otherwise shares the production budget." - Provision:
opendeploy ai-hub keys provision --project <id> --environment <env> --json - The response includes:
key: the actualBearertoken. Treat as a secret — print only when the user explicitly asks. Do NOT echo it into logs or commit it.key_propagated: true|false— whether the new value was written into project secrets that reference{{OPENDEPLOY_AI_API_KEY}}(or the legacy{{MINIONS_AI_API_KEY}}). Iffalse, the deploy flow will pick it up on next deploy; warn the user that running services will fail until then.
- Surface the base URL the user needs to point their SDK at:
opendeploy ai-hub base-url --json. The response is the constant{"base_url":"https://api.opendeploy.dev/v1","protocol":"OpenAI-compatible","auth_header":"Authorization: Bearer <ai-hub-key>"}.
Topping up credits
OpenDeploy AI Hub is metered. Each top-up is a Stripe checkout: the CLI
returns a URL, the user completes payment in the browser, then credits latest flips to succeeded after Stripe webhook confirms.
- Check current balance first:
opendeploy ai-hub credits balance --json. The response includesquota(raw quota units),used_quota, and (when available)quota_dollarsderived from the AI Hub conversion (500000 quota units = $1 USD). Always report USD to the user, never raw quota. - Confirm with the structured approval above (Top-up).
- Create the checkout session:
opendeploy ai-hub credits checkout --amount <usd> --confirm-paid --json. Response includessession_url— open in browser. - After the user reports they completed checkout, poll:
opendeploy ai-hub credits latest --jsonuntilstatus: "succeeded". Do not retry checkout while the previous session is stillpending— the user may complete it shortly.
Auto-recharge
Auto-recharge keeps an app from running out of credits mid-call. It needs:
- A saved Stripe payment method (
stripe_payment_method_id). The user gets this by completing at least one manual top-up checkout first; the dashboard Account → Payment Methods page surfaces saved cards. - A threshold (e.g. $5) and a recharge amount (e.g. $20).
- Optionally a monthly cap (
max_monthly_amount); 0 means unlimited.
Enable / change via:
opendeploy ai-hub credits auto-recharge set \
--enabled true \
--threshold-amount 5 \
--recharge-amount 20 \
--max-monthly-amount 200 \
--stripe-payment-method-id pm_... \
--json
Disable:
opendeploy ai-hub credits auto-recharge set --enabled false --json
If the user doesn't have a saved payment method yet, do not invent one — ask them to complete one manual top-up first, then re-run.
Inspecting usage
For "where did my credits go?" questions:
opendeploy ai-hub usage models --json # top spend by model
opendeploy ai-hub usage tokens --json # spend per AI Hub key (per project)
opendeploy ai-hub usage stats --json # aggregate (call count, total quota)
opendeploy ai-hub usage logs --limit 50 --json # raw per-call logs
The usage logs response includes refund/system rows; filter to type: "consume" and type: "error" if the user only wants call activity.
Always convert quota to USD before reporting:
1 USD = 500000 quota units
Migrating an existing app to AI Hub
The headline AI Hub value prop: an app that already calls OpenAI / Anthropic / Gemini / Cohere / Mistral / Groq / xAI / DeepSeek / Together / OpenRouter can usually switch to AI Hub with one env value change + one line of code, and inherit shared credits, usage tracking, auto-recharge, and the existing project key lifecycle.
This section is the runbook. Do every step in order; do not skip the detect / inventory / approval stages.
Step 1 — Detect
Scan the project for LLM call sites before asking anything. Use the
Grep / Glob tools, not a fresh find shell. Run these searches in
parallel:
1. Package / module references
pattern: \b(openai|anthropic|@anthropic-ai/sdk|@google/(generative-ai|genai)|google\.generativeai|cohere(-ai)?|mistralai|groq(-sdk)?|together(-ai)?|replicate|@ai-sdk/(openai|anthropic|google|cohere|mistral|groq|perplexity|xai|deepseek|togetherai)|@langchain/(openai|anthropic|google-genai|cohere|mistralai|groq)|langchain[_-]?openai|langchain[_-]?anthropic|litellm|llama[_-]?index)\b
include globs: *.ts, *.tsx, *.js, *.jsx, *.mjs, *.cjs, *.py, *.go, *.rb, *.php, *.java, *.kt, *.cs
exclude globs: node_modules/**, .venv/**, venv/**, __pycache__/**, dist/**, build/**, .next/**, out/**, target/**, *.test.*, *.spec.*
2. Env var references in code
pattern: \b(OPENAI|ANTHROPIC|GOOGLE_GENERATIVE_AI|GEMINI|GOOGLE|COHERE|MISTRAL|GROQ|XAI|DEEPSEEK|TOGETHER|OPENROUTER|PERPLEXITY|FIREWORKS|REPLICATE)_API_(KEY|TOKEN|BASE|BASE_URL)\b
(same include / exclude as above)
3. Env files
include globs: .env, .env.*, *.env
pattern: same env-var regex as #2
4. Hardcoded provider base URLs
pattern: \b(api\.openai\.com|api\.anthropic\.com|generativelanguage\.googleapis\.com|api\.cohere\.com|api\.mistral\.ai|api\.groq\.com|api\.x\.ai|api\.deepseek\.com|api\.together\.xyz|api\.replicate\.com|openrouter\.ai|api\.perplexity\.ai|api\.fireworks\.ai)\b
5. Dependency manifests (read full file)
files: package.json, pyproject.toml, requirements.txt, Pipfile, go.mod, Gemfile, composer.json, build.gradle, pom.xml
keywords: same package names as #1
Skip any hit where the surrounding code makes it clear the caller is
already pointing at AI Hub (api.opendeploy.dev, OPENDEPLOY_AI_API_KEY,
MINIONS_AI_API_KEY,
or a comment marking it as already-migrated).
Step 2 — Categorize and present the inventory
Group hits into three buckets:
- Auto-migratable — OpenAI SDK callsites (Node
openai, Pythonopenai,@ai-sdk/openai,@langchain/openai,langchain-openai,litellmwithopenai/model prefix). These can swap to AI Hub with abaseURLchange. - Native SDK (manual) —
@anthropic-ai/sdk,@google/generative-ai,cohere-ai,@mistralai/mistralai,groq-sdk(when used as native client, not via@ai-sdk/groq),replicate, native LangChain provider classes (@langchain/anthropic, etc). AI Hub speaks OpenAI Chat Completions / Embeddings, NOT the vendor-specific wire formats. Report these but do not auto-rewrite — offer the manual recipe (switch to OpenAI SDK + AI Hub URL with the OpenAI-format model name likeclaude-3-5-sonnet-20241022). - Env-only — env var is set but no SDK callsite found (e.g. only in
.env, used by a binary the source doesn't ship). Treat as auto-migratable on the env-var side; flag that no code change is needed.
Then ask:
question: "Migrate the project's LLM calls onto OpenDeploy AI Hub?"
header: "AI Hub migration"
options:
- label: "Migrate auto-migratable callsites (Recommended)"
description: "Provision an AI Hub key for the project, point the <N> OpenAI-compatible callsites at https://api.opendeploy.dev/v1, sync the placeholder into OpenDeploy service env. Leaves the <M> native-SDK callsites alone — those need a manual switch to OpenAI SDK first."
- label: "Migrate everything (including native SDKs)"
description: "Also rewrite Anthropic / Gemini / Cohere SDK callsites to use the OpenAI SDK against AI Hub with mapped model names. Riskier — vendor-specific features (Anthropic prompt caching, Gemini safety settings, etc.) do not transfer."
- label: "Show the inventory first"
description: "Print every detected callsite with its file:line so I can choose what to migrate."
- label: "Cancel"
description: "Do not migrate. Leave source and env unchanged."
When showing the inventory, format as a short table per bucket:
Auto-migratable (3):
src/lib/openai.ts:12 new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
src/api/chat.py:5 OpenAI(api_key=os.environ["OPENAI_API_KEY"])
src/ai.ts:3 openai('gpt-4o') from @ai-sdk/openai
Native SDK — manual only (1):
src/lib/claude.ts:8 new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
Env-only (1):
.env.production ANTHROPIC_API_KEY=sk-ant-...
Hardcoded provider URLs (0): none
Step 3 — Provision the AI Hub key
If the user approved migration, provision the key for the project /
environment they pick. Use the existing Provisioning a key runbook
above. The response includes the key value and key_propagated.
If the project does not exist on OpenDeploy yet (no project ID resolvable
from opendeploy context resolve --json), pause and hand off to the
opendeploy deploy autoplan — AI Hub keys are project-scoped, you need a
project first. Do not silently create a project here.
Step 4 — Apply code edits
Default convention (per user preference): keep the existing env var
name, swap its value to the AI Hub key, add baseURL in the client
constructor. No env-var renames in code. This minimises diff size and
lets the user roll back by changing the env value back.
Apply these patterns via Edit, one file at a time. Show the diff in chat before applying when the file has more than one callsite.
OpenAI SDK — Node / TypeScript
// before
import OpenAI from 'openai'
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
// after
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: 'https://api.opendeploy.dev/v1',
})
OpenAI SDK — Python
# before
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
# after
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
base_url="https://api.opendeploy.dev/v1",
)
(Python's openai>=1.0 also auto-reads OPENAI_BASE_URL from the
environment, so an alternative is to set that env var and leave code
alone. Use whichever the codebase already prefers.)
Vercel AI SDK (@ai-sdk/openai)
// before
import { openai } from '@ai-sdk/openai'
const result = await generateText({ model: openai('gpt-4o') })
// after
import { createOpenAI } from '@ai-sdk/openai'
const aiHub = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: 'https://api.opendeploy.dev/v1',
})
const result = await generateText({ model: aiHub('gpt-4o') })
LangChain — Node
// before
import { ChatOpenAI } from '@langchain/openai'
const model = new ChatOpenAI({ apiKey: process.env.OPENAI_API_KEY, modelName: 'gpt-4' })
// after
const model = new ChatOpenAI({
apiKey: process.env.OPENAI_API_KEY,
modelName: 'gpt-4',
configuration: { baseURL: 'https://api.opendeploy.dev/v1' },
})
LangChain — Python
# before
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4")
# after
model = ChatOpenAI(model="gpt-4", base_url="https://api.opendeploy.dev/v1")
LiteLLM
# before
response = litellm.completion(model="gpt-4", messages=[...])
# after — route through the OpenAI provider against AI Hub
response = litellm.completion(
model="openai/gpt-4",
api_base="https://api.opendeploy.dev/v1",
api_key=os.environ["OPENAI_API_KEY"],
messages=[...],
)
Native SDK — DO NOT auto-rewrite, only print the manual recipe
@anthropic-ai/sdk / google.generativeai / cohere / native SDK callsite at <file:line>
→ To migrate, replace the import with `openai` (or `@ai-sdk/openai`),
update the model string to AI Hub's OpenAI-format name (check with
`opendeploy ai-hub models list --json`), and pass
baseURL: 'https://api.opendeploy.dev/v1'. The vendor SDK's
non-chat-completions features (Anthropic prompt caching, Gemini
safety settings, Cohere rerank, etc.) won't transfer — review per
callsite before migrating.
If the user picked "Migrate everything", still pause at each native-SDK callsite, show the proposed rewrite + the lost-features warning, and confirm one more time before editing.
Step 5 — Sync OpenDeploy service env
After code edits land, push the env values into the deployed service so
the runtime picks up the AI Hub key on next deploy. Use the placeholder
form — never paste the raw key into a --set flag.
- Resolve project + service + environment:
opendeploy context resolve --json. If the resolution does not include a service ID, ask the user which service the migrated code runs in. - Patch the service env additively (one call per affected env var):
Repeat for every other auth env var that the migrated callsites read (e.g.opendeploy services env patch <project-id> <service-id> \ --environment <env> \ --set OPENAI_API_KEY={{OPENDEPLOY_AI_API_KEY}} \ --jsonANTHROPIC_API_KEYif the user opted to migrate native SDKs too,GROQ_API_KEYfor@ai-sdk/groq, etc.). The backend's AI Hub expansion resolves{{OPENDEPLOY_AI_API_KEY}}to the project's current AI Hub key at deploy time — so the value rotates automatically when the key is re-provisioned, and no raw token ever lives in the env table. - (Optional) For Python apps that rely on
OPENAI_BASE_URLenv-var auto-discovery instead of in-codebase_url=:opendeploy services env patch <project-id> <service-id> \ --environment <env> \ --set OPENAI_BASE_URL=https://api.opendeploy.dev/v1 \ --json - Mirror the change in the local
.env.example(and.envif the user keeps one) so local dev matches:
Never write a real key value into# for OpenDeploy deploys the value is auto-injected; for local dev, # paste your AI Hub key from `opendeploy ai-hub keys provision ...` OPENAI_API_KEY= # uncomment to use AI Hub locally too # OPENAI_BASE_URL=https://api.opendeploy.dev/v1.env.exampleor any committed file.
Step 6 — Verify and redeploy
- Confirm the service env was updated:
opendeploy services env get <project-id> <service-id> --json— look for the new keys withvalue_redacted: true(or whatever the server returns; never echo the raw value). - Tell the user a redeploy is needed for running pods to pick up the new
key:
(Or hand off to theopendeploy deployments create --project <id> --service <id> --environment <env>opendeploydeploy autoplan if the user prefers the guided flow.) - After redeploy, recommend a sanity check call. Pick one migrated
callsite, ask the user to trigger the relevant endpoint, then:
should show the call. If usage logs stay empty, hand off toopendeploy ai-hub usage logs --limit 5 --jsonopendeploy-debugwith the deployment ID — the app is probably still pointing at the old provider URL or the env didn't propagate.
Migration-specific failure modes
| Symptom | Cause | Resolution |
|---|---|---|
401 invalid_api_key from api.opendeploy.dev after migration |
App still has the old provider key value in memory (env not refreshed) | Force a fresh deploy with deployments create; cached env on running pods does not auto-update |
404 model_not_found after migration |
App calls a model name AI Hub does not expose (e.g. a fine-tune ID, a vendor preview model) | Check opendeploy ai-hub models list --json; either pick a supported model or keep that specific callsite on the original provider |
| Some calls succeed, others 401 | Mixed env: code reads OPENAI_API_KEY in one place, ANTHROPIC_API_KEY in another, only one was synced | Run Step 5 for the other env var name(s) too |
Native SDK still hits api.anthropic.com after "Migrate everything" |
The agent rewrote one callsite but missed another import path | Re-run Step 1 (detect) and pick up the survivors |
usage logs shows the call but with quota: 0 and an error |
Model is gated behind a higher plan, or the project has $0 balance | Top up with ai-hub credits checkout, enable auto-recharge, or pick a cheaper model |
Limits
- One bundled gateway. The AI Hub speaks the OpenAI Chat Completions /
Embeddings format. Apps that need vendor-specific SDK surfaces (e.g.
Anthropic's
messagesendpoint, Google's Generative AI SDK with native Gemini auth) need to use those SDKs directly with the AI Hub key only if the SDK supports a custom base URL and OpenAI-compatible format. Confirm before assuming compatibility. - One credit pool per user. Staging and production share the same wallet
unless the user is on a plan that splits them — check
credits balanceresponse. - Soft delete.
keys deleteandkeys deactivateare reversible only via re-provisioning a NEW key — the original token value cannot be brought back. - Webhook lag.
credits checkoutreturns immediately with a session URL. Balance updates only after Stripe fires the success webhook, which is typically a few seconds but can take longer. Pollcredits latest, do not assume failure on first miss. - Pricing endpoint is public. Anyone can
GET /v1/ai-hub/pricingwithout auth — that's how the landing page renders the price table. All other endpoints require a bearer token. - Rate limits / quota errors surface as HTTP 429 or as
status: "insufficient_quota"on chat completions. The CLI does not retry — the agent should top up (or enable auto-recharge) and ask the user before retrying.
Failure modes
| Symptom | Cause | Resolution |
|---|---|---|
opendeploy ai-hub ... → unknown command |
CLI predates 0.1.15 | Hand off to opendeploy-setup to update; fall back to opendeploy api get/post/... in the meantime |
403 paid action requires consent on checkout |
--confirm-paid not passed |
Re-ask the structured question, then re-run with --confirm-paid |
409 active key exists on provision |
Project/env already has an active key | Confirm re-provision via the structured question, then call provision again |
| Balance unchanged after checkout | Stripe webhook still pending | Wait 10–30s, re-check credits latest; if still pending after 5 min, hand off to opendeploy-oncall |
auto-recharge set → payment method required |
User has never completed a top-up | Run one manual credits checkout first, then retry |
Service requests fail with 401 from api.opendeploy.dev/v1 after re-provision |
App is still using cached old key | Restart / redeploy the affected service so it picks up the rotated OPENDEPLOY_AI_API_KEY value |
usage models returns empty array |
New project with zero calls so far | Not an error — say "no AI Hub usage recorded yet" |
For deeper debugging (CLI behavior, plugin trust, broken installs), hand off
to opendeploy-debug. For platform-side issues (gateway timeouts, charge
not landing after 10+ minutes), hand off to opendeploy-oncall.