Add customer AI models to your product
This guide adds a Settings → AI models page to a product you host, part of Local AI models. The customer connects Amazon Bedrock, Vertex AI, Azure AI Foundry, OpenAI, Anthropic, or Databricks, and your AI features run on it.
Example UI /Connected
AI models
Settings · AI
AI features run on your Amazon Bedrock
Requests from Acme use your account, your approved models, and your committed spend.
- Provider
- Amazon Bedrock
- Claude Opus 5
- Available
- Account
- 123456789012 · us-east-1
The page works like Launch the portal from your product, with one model provider instead of a deployment:
- The customer clicks Connect AI provider in your product.
- Your server creates a setup link and sends them to the deployment portal, the branded setup page Alien hosts.
- Their admin connects a provider account.
- Your server sends model requests through the AI Gateway, the API at
ai.alien.dev, and shows the connection's state.
Sequence diagram. Lanes: Your backend (your cloud), AI Gateway (ai.alien.dev), Customer provider (Bedrock, Vertex AI, …). Steps: 1 · request + external ID; 2 · call their provider; 3 · response; 4 · response.
Prompts and responses pass through Alien on the way to the customer's provider. Logs record routing metadata, never bodies. To keep prompts inside the customer's cloud, see Local AI models in a deployment.
Configure the gateway
This happens once. In the dashboard:
- Open AI Gateway in the sidebar and click Configure models.
- Add each model your product calls, choose its client API (OpenAI Chat Completions API or Anthropic Messages API), and mark it required or optional. Click Save models. The Provider coverage panel warns when no provider can serve every required model.
- Click Create a server API key. The secret is shown once. Save it as
ALIEN_AI_API_KEYon your backend.
From the CLI:
alien projects capabilities enable ai --required-model byo/claude-opus-5
alien api-keys create --for ai-gateway --description production--required-model marks a model every customer must connect; --model offers it as optional. A required model must be available before the customer's initial setup completes. Optional models don't block it. Repeat --provider (for example --provider aws-bedrock) to limit which providers customers can choose.
The AI Gateway key only sends model requests. Creating setup links and reading connection status need your regular Alien API key (ALIEN_API_KEY), used by the client from Set up a client.
Add a Connect AI provider button
This runs on your server, in the route your Connect AI provider button calls. Create a setup link with the models item and send the customer to it.
The link opens the deployment portal, where the customer connects their provider. The request is POST /v1/deployment-groups/setup-links:
import { alien, PROJECT } from "./alien"
export async function connectModelsLink(customer: { id: string; slug: string }) {
const link = await alien.setupLinks.create({
project: PROJECT,
externalId: customer.id,
name: customer.slug,
setupItems: [{ item: "models", required: true }],
})
return link.deploymentLink
}To open one provider first, add entryPoint: { item: "models", provider: "aws-bedrock" }. It only picks the first screen: the customer can still go back and choose another provider.
To allow only some providers, set deploymentSetupConfig.policy.allowedAIProviders. Provider IDs: aws-bedrock, gcp-vertex, azure-foundry, anthropic, openai, databricks. See link options.
Calling it again with the same external ID reuses the customer's record and returns a fresh link.
In the portal, the customer sees Bring your own AI models: "Choose the account or provider that supplies the required models."
| Provider | What the customer grants |
|---|---|
| OpenAI, Anthropic, Databricks | An API key from their account |
| Amazon Bedrock | An IAM role in their AWS account, created by CloudFormation or Terraform |
| Vertex AI, Azure AI Foundry | An identity in their Google Cloud project or Azure tenant |
When a model isn't usable yet, the portal tells the customer what to fix, for example "Accept the provider agreement", "Activate this model", or "Configure quota".
Send model requests
This runs on your server, wherever your product calls a model. Point your OpenAI client at https://ai.alien.dev/v1 with the AI Gateway key, and send the customer's external ID:
import OpenAI from "openai"
export function aiFor(customerId: string) {
return new OpenAI({
baseURL: "https://ai.alien.dev/v1",
apiKey: process.env.ALIEN_AI_API_KEY,
defaultHeaders: { "X-Alien-External-ID": customerId },
})
}
export async function summarize(customerId: string, text: string) {
const res = await aiFor(customerId).chat.completions.create({
model: "byo/claude-opus-5",
messages: [{ role: "user", content: `Summarize this document:\n\n${text}` }],
})
return res.choices[0]?.message.content
}The Anthropic SDK works the same way, with the base URL https://ai.alien.dev. The Python SDK takes the same base_url and default_headers:
import Anthropic from "@anthropic-ai/sdk"
export async function summarizeWithAnthropic(customerId: string, text: string) {
const anthropic = new Anthropic({
baseURL: "https://ai.alien.dev",
apiKey: process.env.ALIEN_AI_API_KEY,
defaultHeaders: { "X-Alien-External-ID": customerId },
})
const msg = await anthropic.messages.create({
model: "byo/claude-opus-5",
max_tokens: 1024,
messages: [{ role: "user", content: `Summarize this document:\n\n${text}` }],
})
return msg.content
}The API key selects your project, X-Alien-External-ID selects the customer's connection, and model selects a model from that connection. Use the exact model IDs from AI Gateway → Models.
- Resolve the external ID from the signed-in account on your backend, never from browser input.
- The gateway also takes OpenAI Responses requests at
POST /v1/responses. - When it must translate between protocols, it rejects fields it can't represent instead of dropping them.
See Requests and models for model discovery, deployment IDs, and model status.
Show the connection
This runs on your server, when your settings page loads. Read the models entry from the capability overview, GET /v1/projects/{idOrName}/project-capabilities/overview, with the capability helper from Customer-managed keys:
import { capability } from "./capability"
export async function modelStatus(customerId: string) {
const models = await capability(customerId, "models")
if (!models) return { state: "not-connected" as const }
return {
state: models.state,
provider: models.directProvider?.provider ?? models.observation?.provider,
// Each configured model: available | blocked | unknown
coverage: models.modelCoverage?.map(m => ({
id: m.publicModelId,
availability: m.availability,
})),
}
}Each state below is one ExampleUI card. state is not-connected, setting-up, connected, needs-attention, or revoked.
Not connected
Example UI /Not connected
AI models
Settings · AI
Use your own AI models
Run Acme’s AI features on your Amazon Bedrock, under your agreement and on your bill.
Show it when models.state is not-connected, or the customer has no group yet.
- Create the setup link when the customer clicks Connect AI provider, not when the page loads.
- If your product falls back to your own provider until the customer connects one, say so on the card.
- The person who clicks may not be the cloud admin. Let them send the link to someone else.
{ "externalId": "customer-1", "capabilities": { "models": { "enabled": true, "state": "not-connected", "observation": null, "modelCoverage": [] } } }Setting up
Example UI /Setting up
AI models
Settings · AI
Finish connecting Amazon Bedrock
Your admin is running the setup in your AWS account. This page updates when it’s connected.
- Account
- 123456789012 · us-east-1
Show it when models.state is setting-up.
- Reopen setup creates another link with the same external ID. The customer resumes an incomplete initial setup.
- Keep this state until the overview says
connected, even if the customer says they're done. - Model coverage can be
unknownuntil Alien has checked the provider.
{ "externalId": "customer-1", "capabilities": { "models": { "enabled": true, "state": "setting-up", "modelCoverage": [{ "publicModelId": "claude-opus-5", "required": true, "availability": "unknown", "blockerCodes": [] }] } } }Model not ready
Example UI /Model not ready
AI models
Settings · AI
Claude Opus 5 isn’t ready in Amazon Bedrock
Amazon Bedrock is connected, but a model Acme needs can’t be used yet.
Accept the provider agreement for Claude Opus 5 in your AWS account.
- Provider
- Amazon Bedrock
- Account
- 123456789012 · us-east-1
Show it when a required entry in models.modelCoverage has availability: "blocked".
- Map
blockerCodesto an action:agreement-required→ accept the provider agreement,model-activation-required→ activate the model,quota-configuration-required→ configure quota. - A connected provider doesn't guarantee every model: regions, model activation, and quotas differ per account.
- The portal shows the same fix to the customer when they open it again.
{ "modelCoverage": [{ "publicModelId": "claude-opus-5", "required": true, "availability": "blocked", "blockerCodes": ["agreement-required"], "accessTest": "not-checked" }] }Connected
The card at the top of this page. Show it when models.state is connected and every required model is available.
- Show the provider name from
directProvider.providerorobservation.provider. availablemeans the configuration includes the model. Only a real request proves it works, so surface request errors too.- To change providers, create another link with the same external ID.
{ "externalId": "customer-1", "capabilities": { "models": { "enabled": true, "state": "connected", "observation": { "provider": "aws-bedrock", "health": "healthy" }, "modelCoverage": [{ "publicModelId": "claude-opus-5", "required": true, "availability": "available", "blockerCodes": [] }] } } }Disconnect
To reconnect or change providers, create another link with the same external ID.
For an API-key provider (OpenAI, Anthropic, Databricks), your server removes the stored key. Look up the customer's group with GET /v1/deployment-groups/by-external-id, then call DELETE /v1/deployment-groups/{id}/ai/external:
import { alien, PROJECT } from "./alien"
export async function disconnectProvider(customerId: string) {
const group = await alien.deploymentGroups.getDeploymentGroupByExternalId({
project: PROJECT,
externalId: customerId,
})
await alien.deploymentGroups.deleteExternalAIBinding({ id: group.id })
}For Bedrock, Vertex AI, and Azure AI Foundry, the customer disconnects by deleting the stack or resources their initial setup created. Keep reading the capability overview until state is revoked. Requests already admitted may finish for up to 5 minutes after a disconnect.
Logs and usage
Gateway logs, usage, and static provider headers are in Requests and models.
Test it
Try the flow as your own test customer:
alien onboard "Test customer" --external-id org_test --setup-items models
export CUSTOMER_ID=org_test
alien examples ai-gateway --protocol openai-chat --model byo/claude-opus-5Open the printed link and connect a provider account you control. alien examples prints a ready-to-run request. Then, for each provider you offer:
- Call every required model through every client API you use.
- Test streaming and tool calls if you use them.
- Revoke access and confirm your UI shows it.
- Reconnect and confirm requests recover.
Next
- Requests and models: model status, protocols, logs, and usage
- Customer buckets: keep the customer's files in their bucket
- Local AI models in a deployment: model calls from a deployment in the customer's cloud, so prompts stay there
- Inside a deployment: when prompts must stay in the customer's cloud