Embed Companion in Proxy Mode
Overview
Deploy a Companion widget on any website with the delegation token held server-side. The browser never sees the token — only the deployment_id and surface_context travel in the request.
When to use
Prerequisites
companion_deployments record with a minted delegation token/api/companion/ask)Create the deployment
# CLI
human companion deployment create \
--name my-site \
--surface-label my-site \
--allowed-origins "https://yoursite.com"# Get the ready-to-paste snippet
human companion deployment snippet my-site
Proxy endpoint (Next.js App Router)
// app/api/companion/ask/route.ts
import { NextRequest, NextResponse } from 'next/server';const HUMAN_API_URL = process.env.HUMAN_API_URL ?? 'https://api.haio.run';
const INTERNAL_API_TOKEN = process.env.INTERNAL_API_TOKEN!;
export async function POST(req: NextRequest) {
const body = await req.json() as {
text: string;
deployment_id?: string;
surface_context?: Record;
};
// Fetch deployment record (server-side; never expose delegation_token to browser)
const depRes = await fetch(
${HUMAN_API_URL}/v1/companion/deployments/${body.deployment_id},
{ headers: { Authorization: Bearer ${INTERNAL_API_TOKEN} } }
);
const deployment = await depRes.json() as {
delegation_token: string;
allowed_origins: string[];
};
// Validate origin
const origin = req.headers.get('origin') ?? '';
if (!deployment.allowed_origins.includes(origin) && deployment.allowed_origins.length > 0) {
return NextResponse.json({ error: 'Origin not allowed' }, { status: 403 });
}
// Forward to HumanOS with the server-side token
const agentRes = await fetch(${HUMAN_API_URL}/v1/agents/call, {
method: 'POST',
headers: {
Authorization: Bearer ${deployment.delegation_token},
'Content-Type': 'application/json',
},
body: JSON.stringify({
target: 'agent://org/human/companion@0.1',
input: {
text: body.text,
surface_context: {
...(body.surface_context ?? {}),
deployment_id: body.deployment_id,
},
},
}),
});
const data = await agentRes.json();
return NextResponse.json(data);
}