Skip to content
Hoody.com

This page covers configuring and calling Hoody AI from containers, AI coding assistants, and application code, with a worked example for each.


Hoody AI endpoint:

https://ai.hoody.com/api/v1

API key format:

container-{containerName}

Compatibility: OpenAI-compatible API

Model ids: examples below use placeholders of the form <...-model-id> to indicate the capability or price tier required. Replace every such placeholder with an appropriate id returned by /api/v1/ai/models; that endpoint is the authoritative catalog, and OpenAI, Anthropic and Google models are not in it.


Terminal window
# Chat completion from your container
curl -X POST "https://ai.hoody.com/api/v1/chat/completions" \
-H "Authorization: Bearer container-dev-env" \
-H "Content-Type: application/json" \
-d '{
"model": "<chat-model-id>",
"messages": [
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "Write a Python function to calculate fibonacci numbers"}
],
"max_tokens": 1024,
"temperature": 0.7
}'
# Streaming response
curl -X POST "https://ai.hoody.com/api/v1/chat/completions" \
-H "Authorization: Bearer container-dev-env" \
-H "Content-Type: application/json" \
-d '{
"model": "<chat-model-id>",
"messages": [{"role": "user", "content": "Explain quantum computing"}],
"stream": true
}'

The model-dependent request bodies above are templates, not runnable defaults. Replace each <...-model-id> placeholder with an appropriate live-catalog id before sending the request.

// Using native fetch (Node 18+)
async function askAI(prompt: string) {
const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-dev-env',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: [
{ role: 'user', content: prompt }
],
max_tokens: 2048
})
});
const data = await response.json();
return data.choices[0].message.content;
}
// Example usage
const answer = await askAI('How do I deploy a Next.js app?');
console.log(answer);
import requests
import json
def ask_ai(prompt: str) -> str:
"""Query Hoody AI with a prompt"""
response = requests.post(
'https://ai.hoody.com/api/v1/chat/completions',
headers={
'Authorization': 'Bearer container-python-app',
'Content-Type': 'application/json'
},
json={
'model': '<chat-model-id>',
'messages': [
{'role': 'user', 'content': prompt}
],
'max_tokens': 2048
}
)
data = response.json()
return data['choices'][0]['message']['content']
# Example usage
answer = ask_ai('Explain Docker containers in simple terms')
print(answer)

hoody-agent has built-in Hoody AI support:

Terminal window
# Dispatch a turn to an agent session (blocks until the turn completes)
curl -X POST "https://{project}-{container}-agent-1.node-us.containers.hoody.com/api/v1/agent/sessions/{id}/prompt:sync" \
-H "Content-Type: application/json" \
-d '{
"text": "Build a REST API for a todo app with SQLite"
}'
# Read the session's task snapshot (delivered over the session stream)
curl "https://{project}-{container}-agent-1.node-us.containers.hoody.com/api/v1/agent/sessions/{id}/tasks"

The Hoody AI gateway (base URL, API key, model) is configured on the session/agent itself, not passed inline per turn. When the agent is wired to Hoody AI it automatically uses container-{name}, so no API key is needed in the request.

Settings → Models → Add Custom Provider:

  1. Base URL: https://ai.hoody.com/api/v1
  2. API Key: container-{yourContainerName}
  3. Provider: Custom (OpenAI-compatible)
  4. Models: Select a model id returned by /api/v1/ai/models

Example .cursor/config.json:

{
"openai": {
"baseURL": "https://ai.hoody.com/api/v1",
"apiKey": "container-cursor-dev"
},
"defaultModel": "<chat-model-id>"
}

Extension settings:

  1. Open Cline settings in VS Code
  2. API Provider: Custom (OpenAI-compatible)
  3. Base URL: https://ai.hoody.com/api/v1
  4. API Key: container-{containerName}
  5. Model: a model id returned by /api/v1/ai/models

Cline then routes all AI requests through Hoody AI without exposing real keys.

config.json:

{
"models": [
{
"title": "Hoody AI",
"provider": "openai",
"model": "<chat-model-id>",
"apiKey": "container-continue-dev",
"apiBase": "https://ai.hoody.com/api/v1"
}
]
}

AI settings:

  1. Provider: Custom OpenAI-compatible
  2. Endpoint: https://ai.hoody.com/api/v1
  3. API Key: container-windsurf-env
  4. Model: any model id returned by /api/v1/ai/models

app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const { message } = await req.json();
const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-nextjs-app',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: [
{ role: 'user', content: message }
],
stream: true // Enable streaming
})
});
// Stream response back to client
return new NextResponse(response.body, {
headers: { 'Content-Type': 'text/event-stream' }
});
}
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/ask', async (req, res) => {
const { prompt } = req.body;
try {
const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-express-api',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<cheap-chat-model-id>', // Faster, cheaper model
messages: [
{ role: 'user', content: prompt }
],
max_tokens: 1024
})
});
const data = await response.json();
res.json({ answer: data.choices[0].message.content });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000);
'use client';
import { useState } from 'react';
export default function AIChat() {
const [prompt, setPrompt] = useState('');
const [response, setResponse] = useState('');
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setResponse('');
const res = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-react-app',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: [{ role: 'user', content: prompt }],
stream: true
})
});
const reader = res.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.trim());
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content || '';
setResponse(prev => prev + content);
}
}
}
setLoading(false);
}
return (
<form onSubmit={handleSubmit}>
<input
value={prompt}
onChange={e => setPrompt(e.target.value)}
placeholder="Ask me anything..."
/>
<button type="submit" disabled={loading}>
{loading ? 'Thinking...' : 'Send'}
</button>
{response && <div>{response}</div>}
</form>
);
}

Use different models for different tasks:

async function orchestrateTask(userRequest: string) {
// Fast model for classification
const classification = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-orchestrator',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<cheap-chat-model-id>', // Fast & cheap
messages: [
{ role: 'user', content: `Classify this request: "${userRequest}".
Categories: code, creative, data, general. Reply with one word.` }
]
})
});
const category = await classification.json();
// Choose model based on category
const modelMap = {
'code': '<code-chat-model-id>',
'creative': '<creative-chat-model-id>',
'data': '<data-chat-model-id>',
'general': '<cheap-chat-model-id>'
};
const selectedModel = modelMap[category.choices[0].message.content.trim()];
// Execute with optimal model
const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-orchestrator',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: selectedModel,
messages: [{ role: 'user', content: userRequest }]
})
});
return response.json();
}

Execute multiple AI requests simultaneously:

async function parallelAnalysis(text: string) {
const [summary, sentiment, keywords] = await Promise.all([
// Summarization
fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-analyzer',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<cheap-chat-model-id>',
messages: [
{ role: 'user', content: `Summarize in one sentence: ${text}` }
]
})
}).then(r => r.json()),
// Sentiment analysis
fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-analyzer',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<cheap-chat-model-id>',
messages: [
{ role: 'user', content: `Sentiment (positive/negative/neutral): ${text}` }
]
})
}).then(r => r.json()),
// Keyword extraction
fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-analyzer',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<cheap-chat-model-id>',
messages: [
{ role: 'user', content: `Extract 5 keywords: ${text}` }
]
})
}).then(r => r.json())
]);
return { summary, sentiment, keywords };
}
class HoodyAI {
private apiKey: string;
private baseURL = 'https://ai.hoody.com/api/v1';
private maxRetries = 3;
constructor(containerName: string) {
this.apiKey = `container-${containerName}`;
}
async chat(messages: any[], model: string = '<chat-model-id>') {
let lastError;
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
try {
const response = await fetch(`${this.baseURL}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ model, messages })
});
if (response.status === 429) {
// Rate limited - exponential backoff
const waitTime = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, waitTime));
continue;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
return await response.json();
} catch (error) {
lastError = error;
if (attempt < this.maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
}
}
}
throw lastError;
}
}
// Usage
const ai = new HoodyAI('production-api');
const result = await ai.chat([
{ role: 'user', content: 'Hello!' }
]);

Build a production API that uses AI without key exposure:

// hoody-exec script: api/analyze-code.ts (deploy via scripts/write)
// @mode serverless
// @cors reflective
const { code, language } = req.body;
const aiResponse = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-code-analyzer',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: [
{
role: 'system',
content: 'You are a code review expert. Analyze code and provide constructive feedback.'
},
{
role: 'user',
content: `Review this ${language} code:\n\n${code}`
}
]
})
});
const analysis = await aiResponse.json();
return {
feedback: analysis.choices[0].message.content,
language,
timestamp: new Date().toISOString()
};

Accessible at:

https://{project}-{container}-exec-1.node-us.containers.hoody.com/api/analyze-code
// Maintain conversation history
class AIChatSession {
private messages: any[] = [];
private containerKey: string;
constructor(containerName: string) {
this.containerKey = `container-${containerName}`;
}
async send(userMessage: string) {
// Add user message to history
this.messages.push({
role: 'user',
content: userMessage
});
// Get AI response
const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.containerKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: this.messages
})
});
const data = await response.json();
const assistantMessage = data.choices[0].message;
// Add to history
this.messages.push(assistantMessage);
return assistantMessage.content;
}
reset() {
this.messages = [];
}
}
// Usage
const chat = new AIChatSession('chatbot-prod');
await chat.send('What is Hoody?');
await chat.send('How does it work?'); // Has context from previous message

Use cheaper models for:

  • Classification tasks
  • Simple Q&A
  • Keyword extraction
  • Quick validations
const cheapModel = '<cheap-chat-model-id>'; // Fast & economical

Use expensive models for:

  • Complex reasoning
  • Code generation
  • Creative writing
  • Multi-step analysis
const powerModel = '<premium-chat-model-id>'; // Most capable

Return a fallback instead of throwing, so one failed call does not take down the request:

async function safeAICall(prompt: string) {
try {
const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-app',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: [{ role: 'user', content: prompt }]
})
});
if (!response.ok) {
// Log error for monitoring
console.error('AI Error:', response.status, await response.text());
return { error: 'AI service unavailable' };
}
return await response.json();
} catch (error) {
console.error('Network error:', error);
return { error: 'Network error' };
}
}

Always use streaming for user-facing applications:

const response = await fetch('https://ai.hoody.com/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer container-frontend',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: '<chat-model-id>',
messages: [{ role: 'user', content: prompt }],
stream: true // Enable streaming
})
});

Users see results as they are generated instead of waiting for the complete response.