Documentation

Authentication

All API requests (except /health and /.well-known/oauth-protected-resource) require a Bearer token in the Authorization header.

Token types

Gunni accepts two kinds of tokens:

Gunni API key — starts with gn_live_. Created at gunni.ai/keys. Recommended for server-side and CLI use.
Supabase JWT — a short-lived JWT issued after OAuth sign-in. Used by the web app and OAuth-based integrations.

Header format

Authorization: Bearer gn_live_your_key_here

curl example

curl https://api.gunni.ai/api/upload-url?filename=photo.jpg&content_type=image/jpeg \
  -H "Authorization: Bearer gn_live_your_key_here"

Base URL

All endpoints are relative to the base URL for your environment.

EnvironmentBase URL
Productionhttps://api.gunni.ai
Local devhttp://localhost:3847

GET /health

Returns the server status and current version. No authentication required.

Request

curl https://api.gunni.ai/health

Response

{
  "status": "ok",
  "version": "0.3.0"
}

GET /api/upload-url

Generates a signed upload URL for uploading a local file to Gunni's storage. Use the returned uploadUrl to PUT the file, then pass the publicUrl as an image reference in generation calls.

Requires authentication.

Query parameters

NameTypeDefaultDescription
filenamestringThe filename including extension (e.g. photo.jpg).
content_typestringMIME type of the file (e.g. image/jpeg, image/png).

curl example

curl "https://api.gunni.ai/api/upload-url?filename=photo.jpg&content_type=image%2Fjpeg" \
  -H "Authorization: Bearer gn_live_your_key_here"

Response

{
  "uploadUrl": "https://storage.example.com/signed-upload-url?...",
  "publicUrl": "https://storage.example.com/user-assets/photo.jpg"
}

Upload the file

# Step 1: Get the signed URL
RESP=$(curl -s "https://api.gunni.ai/api/upload-url?filename=photo.jpg&content_type=image%2Fjpeg" \
  -H "Authorization: Bearer gn_live_your_key_here")

UPLOAD_URL=$(echo $RESP | jq -r .uploadUrl)
PUBLIC_URL=$(echo $RESP | jq -r .publicUrl)

# Step 2: Upload the file
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg

# Step 3: Use the public URL in a generation call
echo "File available at: $PUBLIC_URL"

Node.js example

const fs = require("fs")

async function uploadFile(localPath, filename, contentType) {
  // Get signed URL
  const res = await fetch(
    `https://api.gunni.ai/api/upload-url?filename=${filename}&content_type=${encodeURIComponent(contentType)}`,
    { headers: { Authorization: "Bearer gn_live_your_key_here" } }
  )
  const { uploadUrl, publicUrl } = await res.json()

  // Upload the file
  await fetch(uploadUrl, {
    method: "PUT",
    headers: { "Content-Type": contentType },
    body: fs.readFileSync(localPath),
  })

  return publicUrl
}

const publicUrl = await uploadFile("./photo.jpg", "photo.jpg", "image/jpeg")
console.log("Uploaded:", publicUrl)

MCP over HTTP

Gunni's primary interface is the MCP protocol over HTTP (Streamable HTTP transport). Clients send JSON-RPC 2.0 requests to /mcp and receive JSON-RPC responses.

The session lifecycle: initialize (POST) → send tool calls (POST) → close (DELETE). The server returns a Mcp-Session-Id header on initialization which must be sent with every subsequent request in the session.

Endpoints

MethodPathDescription
POST/mcpInitialize session or send JSON-RPC request
DELETE/mcpClose the session

Required headers

NameTypeDefaultDescription
AuthorizationstringBearer token: your Gunni API key or Supabase JWT.
Content-TypestringMust be application/json.
Mcp-Session-IdstringSession ID returned by the initialize call. Required on all calls after initialization.

Initialize Session

Send the MCP initialize request. The server responds with its capabilities and returns a Mcp-Session-Id header — save this for all subsequent calls.

curl -X POST https://api.gunni.ai/mcp \
  -H "Authorization: Bearer gn_live_your_key_here" \
  -H "Content-Type: application/json" \
  -D - \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": { "name": "my-app", "version": "1.0.0" }
    }
  }'

The response headers include Mcp-Session-Id: <session-id>. Save this value.

HTTP/2 200
content-type: application/json
mcp-session-id: sess_abc123xyz

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": { "tools": {} },
    "serverInfo": { "name": "gunni", "version": "0.3.0" }
  }
}

Call a Tool

Send a tools/call request with the tool name and arguments. The response contains the tool result.

curl -X POST https://api.gunni.ai/mcp \
  -H "Authorization: Bearer gn_live_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: sess_abc123xyz" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "image",
      "arguments": {
        "prompt": "a fox in watercolor style",
        "width": 1024,
        "height": 1024
      }
    }
  }'
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Generated image: https://storage.gunni.ai/outputs/abc123.png"
      }
    ]
  }
}

Generation calls are async — the server polls the underlying model API and returns only when the result is ready. Expect latency of 5–60 seconds depending on the model and media type.

Node.js example

async function generateImage(prompt) {
  const BASE = "https://api.gunni.ai"
  const TOKEN = "gn_live_your_key_here"

  // 1. Initialize
  const initRes = await fetch(`${BASE}/mcp`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "initialize",
      params: {
        protocolVersion: "2024-11-05",
        capabilities: {},
        clientInfo: { name: "my-app", version: "1.0.0" },
      },
    }),
  })

  const sessionId = initRes.headers.get("mcp-session-id")

  // 2. Call the image tool
  const callRes = await fetch(`${BASE}/mcp`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
      "Mcp-Session-Id": sessionId,
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 2,
      method: "tools/call",
      params: {
        name: "image",
        arguments: { prompt, width: 1024, height: 1024 },
      },
    }),
  })

  const result = await callRes.json()
  return result.result.content[0].text
}

const url = await generateImage("a fox in watercolor style")
console.log(url)

Close Session

Send a DELETE request with the session ID to clean up server-side session state. This is optional but recommended for long-running applications.

curl -X DELETE https://api.gunni.ai/mcp \
  -H "Authorization: Bearer gn_live_your_key_here" \
  -H "Mcp-Session-Id: sess_abc123xyz"

Available Tools

All Gunni MCP tools are available via the HTTP API. See the MCP reference for full parameter documentation on each tool.

Generation (consume credits)

ToolDescription
imageGenerate, edit, upscale, describe, or remove background from an image
videoGenerate video from a still image or text prompt
audioConvert text to speech
lipsyncSync audio to a video or animate a portrait image to speak

Knowledge (free)

ToolDescription
learnAccess the Gunni creative knowledge base
modelsList available models and capabilities
historySearch past generations
visual_researchFind reference images and visual inspiration

Asset Management (free)

ToolDescription
refs, ref_save, upload_refReference image management
styles, style_create, style_deleteVisual style management
presets, preset_create, preset_deleteProduction preset management
templates, template_save, template_deletePrompt template management
pipelines, pipeline_save, pipeline_deleteMulti-step workflow management

OAuth Discovery

Gunni exposes RFC 9728 OAuth protected resource metadata at the well-known endpoint. MCP clients that implement OAuth discovery use this to locate the authorization server.

Request

curl https://api.gunni.ai/.well-known/oauth-protected-resource

Response

{
  "resource": "https://api.gunni.ai",
  "authorization_servers": ["https://accounts.gunni.ai"],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://gunni.ai/docs/api"
}

Errors

HTTP errors use standard status codes. MCP tool errors use JSON-RPC error objects inside an otherwise successful HTTP 200 response.

HTTP errors

StatusMeaning
401Missing or invalid Authorization header
400Malformed request body or missing required parameters
404Session not found (invalid or expired Mcp-Session-Id)
500Server error

JSON-RPC error response

{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32600,
    "message": "Invalid request",
    "data": "Missing required parameter: prompt"
  }
}

Insufficient credits

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Error: Insufficient credits. Please top up at gunni.ai/billing."
      }
    ],
    "isError": true
  }
}