AI Commons

OpenAI API Reference

The UCSB AI Commons exposes an OpenAI-compatible REST API that lets you use standard OpenAI client libraries (Python, Node.js, etc.) and third-party tools (AnythingLLM, Chatbox, Continue.dev, GitHub Copilot, etc.) backed by AWS Bedrock.

Base URL

Your API Base URL is unique to your deployment. To find it:

  1. Log in to the web app
  2. Click your profile icon (top-right) to open the Settings menu
  3. Select API Keys
  4. Your Base URL is displayed at the top of the page

It will look something like:

https://<your-api-id>.execute-api.us-east-1.amazonaws.com/v1
Both /v1/chat/completions and /chat/completions are accepted — set your client's base_url to the value shown on the API Keys page (it already includes /v1).

Authentication

All requests require an API key passed via one of two headers:

Header Format Notes
Authorization Bearer <api-key> OpenAI standard — recommended for SDK clients
x-api-key <api-key> AWS-style alternative

API keys are provisioned through the web UI. Navigate to Settings → API Keys to mint, view, and revoke keys.

POST /chat/completions

Create a chat completion. Supports synchronous and streaming (SSE) responses.

Request Body

Field Type Required Description
model string Yes Model identifier. See Model Resolution
messages array Yes Conversation history
temperature number No Sampling temperature (0.0–2.0)
max_tokens number No Maximum output tokens
stream boolean No If true, returns SSE token stream. Default: false
tools array No Function/tool definitions for client-side tool calling
Warning: Bedrock does not support tool_choice: "none". If provided, the platform silently falls back to "auto", meaning the model may still call a tool. Omit tools entirely to guarantee no tool calls.

Response (Buffered)

{
  "id": "chatcmpl-01JXYZ123456789ABCDEFGH",
  "object": "chat.completion",
  "created": 1718000000,
  "model": "claude-v4.6-sonnet",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 9,
    "total_tokens": 21
  }
}

GET /models

List all models available to the authenticated user.

Response

{
  "object": "list",
  "data": [
    {
      "id": "claude-v4.6-sonnet",
      "object": "model",
      "created": 1718000000,
      "owned_by": "bedrock"
    },
    {
      "id": "gpt-4",
      "object": "model",
      "created": 1718000000,
      "owned_by": "openai-alias"
    }
  ]
}

Model Resolution

The model field accepts multiple forms:

When resolving the model field, the platform checks the following forms in order until one matches:

  1. Composite bot/model identifier
  2. OpenAI alias
  3. Raw Bedrock model
  4. Bot ULID

Raw Bedrock Models

Talk directly to a Bedrock model:

{
  "model": "claude-v4.6-sonnet"
}

OpenAI Aliases

Drop-in compatibility for clients that hardcode OpenAI model names:

Alias Maps To
gpt-4 claude-v4.6-sonnet
gpt-4o claude-v4.6-sonnet
gpt-4-turbo claude-v4.6-sonnet
gpt-3.5-turbo claude-v4.5-haiku

Python Examples

Basic Usage

from openai import OpenAI

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

response = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[
        {"role": "user", "content": "Explain quantum computing in simple terms."}
    ],
)

print(response.choices[0].message.content)

Streaming

from openai import OpenAI

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

stream = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[
        {"role": "user", "content": "Write a haiku about programming."}
    ],
    stream=True,
)

for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
print()

With Images

import base64
from openai import OpenAI

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

# Base64-encoded image
with open("chart.png", "rb") as f:
    image_data = base64.b64encode(f.read()).decode("utf-8")

response = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "What trends do you see in this chart?"},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/png;base64,{image_data}"}
                }
            ]
        }
    ],
)

print(response.choices[0].message.content)

Using a Bot

from openai import OpenAI

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

# Use a bot's system prompt, knowledge base, and guardrails
response = client.chat.completions.create(
    model="01KQ07QSSST2TSZV5WT2YQ6BPM",   # Your bot's ULID
    messages=[
        {"role": "user", "content": "What is the university's leave policy?"}
    ],
)

print(response.choices[0].message.content)

Bot with Model Override

from openai import OpenAI

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

# Use the bot's config but force a specific underlying model
response = client.chat.completions.create(
    model="01KQ07QSSST2TSZV5WT2YQ6BPM/claude-v4.5-haiku",
    messages=[
        {"role": "user", "content": "Summarize the key points."}
    ],
)

print(response.choices[0].message.content)

Custom Parameters

from openai import OpenAI

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

response = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[
        {"role": "user", "content": "Generate a creative story opening."}
    ],
    temperature=1.6,      # 0.0–2.0 (mapped internally to 0.0–1.0 for Bedrock)
    max_tokens=500,
    top_p=0.95,
)

print(response.choices[0].message.content)

JavaScript/Node.js Examples

Basic Usage

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "YOUR_BASE_URL",
  apiKey: "your-api-key",
});

const response = await client.chat.completions.create({
  model: "claude-v4.6-sonnet",
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(response.choices[0].message.content);

Streaming

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "YOUR_BASE_URL",
  apiKey: "your-api-key",
});

const stream = await client.chat.completions.create({
  model: "claude-v4.6-sonnet",
  messages: [{ role: "user", content: "Tell me a short story." }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) process.stdout.write(content);
}
console.log();

Multimodal (Image)

import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
  baseURL: "YOUR_BASE_URL",
  apiKey: "your-api-key",
});

// Read image and convert to base64
const imageData = fs.readFileSync("chart.png", "base64");

const response = await client.chat.completions.create({
  model: "claude-v4.6-sonnet",
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "What trends do you see?" },
        {
          type: "image_url",
          image_url: { url: `data:image/png;base64,${imageData}` },
        },
      ],
    },
  ],
});

console.log(response.choices[0].message.content);

Document Attachment

import OpenAI from "openai";
import fs from "fs";

const client = new OpenAI({
  baseURL: "YOUR_BASE_URL",
  apiKey: "your-api-key",
});

// Read PDF and convert to base64
const pdfData = fs.readFileSync("report.pdf", "base64");

const response = await client.chat.completions.create({
  model: "claude-v4.6-sonnet",
  messages: [
    {
      role: "user",
      content: [
        { type: "text", text: "Summarize this report." },
        {
          type: "input_file",
          filename: "report.pdf",
          file_data: `data:application/pdf;base64,${pdfData}`,
        },
      ],
    },
  ],
});

console.log(response.choices[0].message.content);

cURL Examples

Basic Request

curl YOUR_BASE_URL/chat/completions \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-v4.6-sonnet",
    "messages": [
      {"role": "user", "content": "What is 2 + 2?"}
    ]
  }'

Streaming Request

curl YOUR_BASE_URL/chat/completions \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -N \
  -d '{
    "model": "claude-v4.6-sonnet",
    "messages": [
      {"role": "user", "content": "Tell me a joke."}
    ],
    "stream": true
  }'

List Models

curl YOUR_BASE_URL/models \
  -H "Authorization: Bearer your-api-key"

Multimodal (Image)

# First, encode your image to base64
IMAGE_B64=$(base64 -i chart.png)

curl YOUR_BASE_URL/chat/completions \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-v4.6-sonnet",
    "messages": [
      {
        "role": "user",
        "content": [
          {"type": "text", "text": "Analyze this chart"},
          {
            "type": "image_url",
            "image_url": {"url": "data:image/png;base64,'"$IMAGE_B64"'"}
          }
        ]
      }
    ]
  }'

Persistent Conversations

Supply a conversation_id (26-character ULID) to continue a server-side conversation. Only the new user message is needed — prior history is stored in the database.

Python

from openai import OpenAI
from ulid import ULID

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

# Generate a conversation ID once and reuse it
conv_id = str(ULID())

# Turn 1
response1 = client.chat.completions.create(
    model="01KQ07QSSST2TSZV5WT2YQ6BPM",  # bot ULID
    messages=[{"role": "user", "content": "My name is Alice."}],
    extra_body={"conversation_id": conv_id},
)
print(response1.choices[0].message.content)

# Turn 2 — server remembers Alice from turn 1
response2 = client.chat.completions.create(
    model="01KQ07QSSST2TSZV5WT2YQ6BPM",
    messages=[{"role": "user", "content": "What is my name?"}],
    extra_body={"conversation_id": conv_id},
)
print(response2.choices[0].message.content)  # "Your name is Alice."

Function Calling

The API supports the full OpenAI function-calling pattern. Both buffered (stream: false) and streaming (stream: true) modes are supported.

Example: Buffered Function Calling

from openai import OpenAI
import json

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather for a location.",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "City name, e.g. Santa Barbara"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Temperature unit"
                    }
                },
                "required": ["location"]
            }
        }
    }
]

response = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[{"role": "user", "content": "What is the weather in Santa Barbara?"}],
    tools=tools,
    tool_choice="auto",
)

choice = response.choices[0]
print(f"finish_reason: {choice.finish_reason}")  # "tool_calls"

if choice.finish_reason == "tool_calls":
    for tc in choice.message.tool_calls:
        print(f"Tool: {tc.function.name}")
        print(f"Args: {tc.function.arguments}")

# Execute your actual tool logic
tool_result = {"temperature": "72°F", "condition": "Sunny"}

# Send back the full history with the tool result
response2 = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[
        {"role": "user", "content": "What is the weather in Santa Barbara?"},
        {
            "role": "assistant",
            "content": None,
            "tool_calls": [
                {
                    "id": choice.message.tool_calls[0].id,
                    "type": "function",
                    "function": {
                        "name": "get_weather",
                        "arguments": choice.message.tool_calls[0].function.arguments,
                    },
                }
            ],
        },
        {
            "role": "tool",
            "tool_call_id": choice.message.tool_calls[0].id,
            "content": json.dumps(tool_result),
        },
    ],
    tools=tools,
)

print(response2.choices[0].message.content)
# "The weather in Santa Barbara is 72°F and sunny."

Example: Streaming Function Calling

stream = client.chat.completions.create(
    model="claude-v4.6-sonnet",
    messages=[{"role": "user", "content": "What is the weather in Santa Barbara?"}],
    tools=tools,
    tool_choice="auto",
    stream=True,
)

collected_tool_calls = {}
for chunk in stream:
    delta = chunk.choices[0].delta
    finish = chunk.choices[0].finish_reason

    # Accumulate streamed tool call fragments
    if delta.tool_calls:
        for tc in delta.tool_calls:
            idx = tc.index
            if idx not in collected_tool_calls:
                collected_tool_calls[idx] = {"id": tc.id, "name": tc.function.name, "arguments": ""}
            if tc.function.arguments:
                collected_tool_calls[idx]["arguments"] += tc.function.arguments

    if finish == "tool_calls":
        for idx, tc in collected_tool_calls.items():
            print(f"Tool: {tc['name']}, Args: {tc['arguments']}")

Tool Choice Options

Value Behavior
"auto" Model decides whether to call a tool (default)
"required" Model must call at least one tool
"none" Model will not call any tools
{"type": "function", "function": {"name": "get_weather"}} Force a specific tool

Error Handling

All errors follow the OpenAI error envelope format:

{
  "error": {
    "message": "Invalid API key.",
    "type": "authentication_error",
    "param": null,
    "code": null
  }
}

Error Types

HTTP Status Type Common Cause
400 invalid_request_error Missing/malformed fields, empty messages, invalid conversation_id
401 authentication_error Missing or invalid API key
403 permission_error Insufficient access rights
404 not_found_error Bot not found or no access
429 rate_limit_error Monthly token quota exceeded
500 api_error Internal server error

Rate Limit Response

When your monthly token quota is exceeded, the 429 response includes quota details:

{
  "error": {
    "message": "You have exceeded your token limit. Please upgrade your plan or wait until your quota resets.",
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "limit": 1000000,
    "remaining": 0,
    "reset_at": "2026-07-01T00:00:01Z"
  }
}

Error Handling Example

from openai import OpenAI, AuthenticationError, RateLimitError, BadRequestError

client = OpenAI(
    base_url="YOUR_BASE_URL",
    api_key="your-api-key",
)

try:
    response = client.chat.completions.create(
        model="claude-v4.6-sonnet",
        messages=[{"role": "user", "content": "Hello"}],
    )
    print(response.choices[0].message.content)

except AuthenticationError as e:
    print(f"Invalid API key: {e}")

except RateLimitError as e:
    print(f"Quota exceeded: {e}")

except BadRequestError as e:
    print(f"Bad request: {e}")

Limitations

Constraint Value/Note
Request timeout ~30 seconds (API Gateway limit)
Token quota Per-user monthly limit (configurable by admin)
Image formats JPEG, PNG, GIF, WebP supported
Document formats Any format supported by Bedrock Converse (PDF, DOCX, TXT, CSV, XLSX, etc.)
Audio content Not supported — audio parts are silently ignored
System messages Sent to the model's system prompt; a bot's instruction leads and the caller's system text follows. developer is a synonym; multiple are concatenated in order. Ignored in conversation_id mode.
conversation_id format Must be a valid 26-character ULID (Crockford base32)
Bot access Caller must own the bot or have shared access; 404 returned otherwise
CORS restrictions Per-key origin allow-lists can be configured via the API keys UI. Keys without restrictions accept requests from any origin.
Prompt caching Supported automatically on compatible models; token counts include cacheReadInputTokens and cacheWriteInputTokens from Bedrock

Advanced Features

Per-Key CORS Allow-Lists

API keys can be configured with origin restrictions for browser-based clients. When configured:

  • The OPTIONS preflight is handled permissively by the global CORS middleware
  • The authenticated request checks the Origin header against the key's allow-list
  • Wildcard patterns are supported (e.g., https://*.example.com)
  • A bare * in the allow-list permits any origin
  • Requests without an Origin header (server-side SDKs) are not subject to CORS enforcement

Configure allow-lists via Settings → API Keys in the web UI.

Token Usage Accounting

Token counts in the usage response object include:

Field Description
prompt_tokens Total input tokens including cache reads and writes
completion_tokens Output tokens generated
total_tokens Sum of prompt and completion tokens

Prompt Caching: When Bedrock's prompt caching is active (automatically for supported models), prompt_tokens includes:

  • inputTokens — newly processed tokens
  • cacheReadInputTokens — tokens retrieved from cache
  • cacheWriteInputTokens — tokens written to cache

This ensures accurate billing and quota tracking even when cache hits significantly reduce processing.

Example Response with Caching

{
  "id": "chatcmpl-01JXYZ123456789ABCDEFGH",
  "object": "chat.completion",
  "created": 1718000000,
  "model": "claude-v4.6-sonnet",
  "choices": [...],
  "usage": {
    "prompt_tokens": 2500,
    "completion_tokens": 150,
    "total_tokens": 2650
  }
}

In this example, prompt_tokens (2500) is the sum of all input token types. On a subsequent cached request, the breakdown might be:

  • 50 newly processed tokens (inputTokens)
  • 2450 cache-read tokens (cacheReadInputTokens)
  • Total prompt_tokens: 2500

The API abstracts these details — clients see only the aggregate values, while the server tracks the detailed breakdown for billing and performance optimization.