# Antigravity Unified Gateway API Specification **Version:** 1.0 **Last Updated:** December 13, 2025 **Status:** Verified by Direct API Testing --- ## Overview Antigravity is Google's **Unified Gateway API** for accessing multiple AI models (Claude, Gemini, GPT-OSS) through a single, consistent Gemini-style interface. It is NOT the same as Vertex AI's direct model APIs. ### Key Characteristics - **Single API format** for all models (Gemini-style) - **Project-based access** via Google Cloud authentication - **Internal routing** to model backends (Vertex AI for Claude, Gemini API for Gemini) - **Unified response format** (`candidates[]` structure for all models) --- ## Endpoints | Environment | URL | Status | |-------------|-----|--------| | **Daily (Sandbox)** | `https://daily-cloudcode-pa.sandbox.googleapis.com` | ✅ Active | | **Production** | `https://cloudcode-pa.googleapis.com` | ✅ Active | | **Autopush (Sandbox)** | `https://autopush-cloudcode-pa.sandbox.googleapis.com` | ❌ Unavailable | ### API Actions | Action | Path | Description | |--------|------|-------------| | Generate Content | `/v1internal:generateContent` | Non-streaming request | | Stream Generate | `/v1internal:streamGenerateContent?alt=sse` | Streaming (SSE) request | | Load Code Assist | `/v1internal:loadCodeAssist` | Project discovery | | Onboard User | `/v1internal:onboardUser` | User onboarding | --- ## Authentication ### OAuth 2.0 Setup ``` Authorization URL: https://accounts.google.com/o/oauth2/auth Token URL: https://oauth2.googleapis.com/token ``` ### Required Scopes ``` https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/cclog https://www.googleapis.com/auth/experimentsandconfigs ``` ### Required Headers ```http Authorization: Bearer {access_token} Content-Type: application/json User-Agent: antigravity/1.15.8 windows/amd64 X-Goog-Api-Client: google-cloud-sdk vscode_cloudshelleditor/0.1 Client-Metadata: {"ideType":"ANTIGRAVITY","platform":"MACOS","pluginType":"GEMINI"} ``` For streaming requests, also include: ```http Accept: text/event-stream ``` --- ## Available Models | Model Name | Model ID | Type | Status | |------------|----------|------|--------| | Claude Sonnet 4.6 | `claude-sonnet-4-6` | Anthropic | ✅ Verified | | Claude Opus 4.6 Thinking | `claude-opus-4-6-thinking` | Anthropic | ✅ Verified | | Gemini 3 Pro High | `gemini-3-pro-high` | Google | ✅ Verified | | Gemini 3 Pro Low | `gemini-3-pro-low` | Google | ✅ Verified | | GPT-OSS 120B Medium | `gpt-oss-120b-medium` | Other | ✅ Verified | --- ## Request Format ### Basic Structure ```json { "project": "{project_id}", "model": "{model_id}", "request": { "contents": [...], "generationConfig": {...}, "systemInstruction": {...}, "tools": [...] }, "userAgent": "antigravity", "requestId": "{unique_id}" } ``` ### Contents Array (REQUIRED) **⚠️ IMPORTANT: Must use Gemini-style format. Anthropic-style `messages` array is NOT supported.** ```json { "contents": [ { "role": "user", "parts": [ { "text": "Your message here" } ] }, { "role": "model", "parts": [ { "text": "Assistant response" } ] } ] } ``` #### Role Values - `user` - Human/user messages - `model` - Assistant responses (NOT `assistant`) ### Generation Config ```json { "generationConfig": { "maxOutputTokens": 1000, "temperature": 0.7, "topP": 0.95, "topK": 40, "stopSequences": ["STOP"], "thinkingConfig": { "thinkingBudget": 8000, "includeThoughts": true } } } ``` | Field | Type | Description | |-------|------|-------------| | `maxOutputTokens` | number | Maximum tokens in response | | `temperature` | number | Randomness (0.0 - 2.0) | | `topP` | number | Nucleus sampling threshold | | `topK` | number | Top-K sampling | | `stopSequences` | string[] | Stop generation triggers | | `thinkingConfig` | object | Extended thinking config | ### System Instructions **⚠️ Must be an object with `parts`, NOT a plain string.** ```json // ✅ CORRECT { "systemInstruction": { "parts": [ { "text": "You are a helpful assistant." } ] } } // ❌ WRONG - Will return 400 error { "systemInstruction": "You are a helpful assistant." } ``` ### Tools / Function Calling ```json { "tools": [ { "functionDeclarations": [ { "name": "get_weather", "description": "Get weather for a location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "City name" } }, "required": ["location"] } } ] } ] } ``` ### Google Search Grounding Gemini models support Google Search grounding, but **it cannot be combined with function declarations** in the same request. This plugin implements a dedicated `google_search` tool that makes separate API calls. #### How the `google_search` Tool Works The model can call `google_search(query, urls?, thinking?)` which: 1. Makes a **separate API call** to Antigravity with only `{ googleSearch: {} }` (no function declarations) 2. Parses the `groundingMetadata` from the response 3. Returns formatted markdown with sources and citations **Tool Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `query` | string | ✅ | The search query or question | | `urls` | string[] | ❌ | URLs to analyze (adds `urlContext` tool) | | `thinking` | boolean | ❌ | Enable deep thinking (default: true) | **Example Response:** ```markdown ## Search Results Spain won Euro 2024, defeating England 2-1 in the final... ### Sources - [UEFA Euro 2024](https://uefa.com/...) - [Al Jazeera](https://aljazeera.com/...) ### Search Queries Used - "UEFA Euro 2024 winner" ``` #### Raw API Format (for reference) The underlying API uses these tool formats: **New API (Gemini 2.0+ / Gemini 3):** ```json { "tools": [ { "googleSearch": {} } ] } ``` **Legacy API (Gemini 1.5 only - deprecated):** ```json { "tools": [ { "googleSearchRetrieval": { "dynamicRetrievalConfig": { "mode": "MODE_DYNAMIC", "dynamicThreshold": 0.3 } } } ] } ``` **Response includes `groundingMetadata`:** ```json { "groundingMetadata": { "webSearchQueries": ["query1", "query2"], "searchEntryPoint": { "renderedContent": "..." }, "groundingChunks": [{ "web": { "uri": "...", "title": "..." } }], "groundingSupports": [{ "segment": {...}, "groundingChunkIndices": [...] }] } } ``` > **Important:** `googleSearch` and `urlContext` tools **cannot be combined with `functionDeclarations`** in the same request. This is why the plugin uses a separate API call. ``` ### Function Name Rules | Rule | Description | |------|-------------| | First character | Must be a letter (a-z, A-Z) or underscore (_) | | Allowed characters | `a-zA-Z0-9`, underscores (`_`), dots (`.`), colons (`:`), dashes (`-`) | | Max length | 64 characters | | Not allowed | Slashes (`/`), spaces, other special characters | **Examples:** - ✅ `get_weather` - Valid - ✅ `mcp:mongodb.query` - Valid (colons and dots allowed) - ✅ `read-file` - Valid (dashes allowed) - ❌ `mcp/query` - Invalid (slashes not allowed) - ❌ `123_tool` - Invalid (must start with letter or underscore) ### JSON Schema Support | Feature | Status | Notes | |---------|--------|-------| | `type` | ✅ Supported | `object`, `string`, `number`, `integer`, `boolean`, `array` | | `properties` | ✅ Supported | Object properties | | `required` | ✅ Supported | Required fields array | | `description` | ✅ Supported | Field descriptions | | `enum` | ✅ Supported | Enumerated values | | `items` | ✅ Supported | Array item schema | | `anyOf` | ✅ Supported | Converted to `any_of` internally | | `allOf` | ✅ Supported | Converted to `all_of` internally | | `oneOf` | ✅ Supported | Converted to `one_of` internally | | `additionalProperties` | ✅ Supported | Additional properties schema | | `const` | ❌ NOT Supported | Use `enum: [value]` instead | | `$ref` | ❌ NOT Supported | Inline the schema instead | | `$defs` / `definitions` | ❌ NOT Supported | Inline definitions instead | | `$schema` | ❌ NOT Supported | Strip from schema | | `$id` | ❌ NOT Supported | Strip from schema | | `default` | ❌ NOT Supported | Strip from schema | | `examples` | ❌ NOT Supported | Strip from schema | | `title` (nested) | ⚠️ Caution | May cause issues in nested objects | **⚠️ IMPORTANT:** The following features will cause a 400 error if sent to the API: - `const` - Convert to `enum: [value]` instead - `$ref` / `$defs` - Inline the schema definitions - `$schema` / `$id` - Strip these metadata fields - `default` / `examples` - Strip these documentation fields ```json // ❌ WRONG - Will return 400 error { "type": { "const": "email" } } // ✅ CORRECT - Use enum instead { "type": { "enum": ["email"] } } ``` **Note:** The plugin automatically handles these conversions via the `schema-transform.ts` module. --- ## Response Format ### Non-Streaming Response ```json { "response": { "candidates": [ { "content": { "role": "model", "parts": [ { "text": "Response text here" } ] }, "finishReason": "STOP" } ], "usageMetadata": { "promptTokenCount": 16, "candidatesTokenCount": 4, "totalTokenCount": 20 }, "modelVersion": "claude-sonnet-4-6", "responseId": "msg_vrtx_..." }, "traceId": "abc123..." } ``` ### Streaming Response (SSE) Content-Type: `text/event-stream` ``` data: {"response": {"candidates": [{"content": {"role": "model", "parts": [{"text": "Hello"}]}}], "usageMetadata": {...}, "modelVersion": "...", "responseId": "..."}, "traceId": "..."} data: {"response": {"candidates": [{"content": {"role": "model", "parts": [{"text": " world"}]}, "finishReason": "STOP"}], "usageMetadata": {...}}, "traceId": "..."} ``` ### Response Fields | Field | Description | |-------|-------------| | `response.candidates` | Array of response candidates | | `response.candidates[].content.role` | Always `"model"` | | `response.candidates[].content.parts` | Array of content parts | | `response.candidates[].finishReason` | `STOP`, `MAX_TOKENS`, `OTHER` | | `response.usageMetadata.promptTokenCount` | Input tokens | | `response.usageMetadata.candidatesTokenCount` | Output tokens | | `response.usageMetadata.totalTokenCount` | Total tokens | | `response.usageMetadata.thoughtsTokenCount` | Thinking tokens (Gemini) | | `response.modelVersion` | Actual model used | | `response.responseId` | Request ID (format varies by model) | | `traceId` | Trace ID for debugging | ### Response ID Formats | Model Type | Format | Example | |------------|--------|---------| | Claude | `msg_vrtx_...` | `msg_vrtx_01UDKZG8PWPj9mjajje8d7u7` | | Gemini | Base64-like | `ypM9abPqFKWl0-kPvamgqQw` | | GPT-OSS | Base64-like | `y5M9aZaSKq6z2roPoJ7pEA` | --- ## Function Call Response When the model wants to call a function: ```json { "response": { "candidates": [ { "content": { "role": "model", "parts": [ { "functionCall": { "name": "get_weather", "args": { "location": "Paris" }, "id": "toolu_vrtx_01PDbPTJgBJ3AJ8BCnSXvUqk" } } ] }, "finishReason": "OTHER" } ] } } ``` ### Providing Function Results ```json { "contents": [ { "role": "user", "parts": [{ "text": "What's the weather?" }] }, { "role": "model", "parts": [{ "functionCall": { "name": "get_weather", "args": {...}, "id": "..." } }] }, { "role": "user", "parts": [{ "functionResponse": { "name": "get_weather", "id": "...", "response": { "temperature": "22C" } } }] } ] } ``` --- ## Thinking / Extended Reasoning ### Thinking Config For thinking-capable models (`*-thinking`), use: ```json { "generationConfig": { "maxOutputTokens": 10000, "thinkingConfig": { "thinkingBudget": 8000, "includeThoughts": true } } } ``` **⚠️ IMPORTANT: `maxOutputTokens` must be GREATER than `thinkingBudget`** ### Thinking Response (Gemini) Gemini models return thinking with signatures: ```json { "parts": [ { "thoughtSignature": "ErADCq0DAXLI2nx...", "text": "Let me think about this..." }, { "text": "The answer is..." } ] } ``` ### Thinking Response (Claude) Claude thinking models may include `thought: true` parts: ```json { "parts": [ { "thought": true, "text": "Reasoning process...", "thoughtSignature": "..." }, { "text": "Final answer..." } ] } ``` --- ## Error Responses ### Error Structure ```json { "error": { "code": 400, "message": "Error description", "status": "INVALID_ARGUMENT", "details": [...] } } ``` ### Common Error Codes | Code | Status | Description | |------|--------|-------------| | 400 | `INVALID_ARGUMENT` | Invalid request format | | 401 | `UNAUTHENTICATED` | Invalid/expired token | | 403 | `PERMISSION_DENIED` | No access to resource | | 404 | `NOT_FOUND` | Model not found | | 429 | `RESOURCE_EXHAUSTED` | Rate limit exceeded | ### Rate Limit Response ```json { "error": { "code": 429, "message": "You have exhausted your capacity on this model. Your quota will reset after 3s.", "status": "RESOURCE_EXHAUSTED", "details": [ { "@type": "type.googleapis.com/google.rpc.RetryInfo", "retryDelay": "3.957525076s" } ] } } ``` --- ## NOT Supported The following Anthropic/Vertex AI features are **NOT supported**: | Feature | Error | |---------|-------| | `anthropic_version` | Unknown field | | `messages` array | Unknown field | | `max_tokens` | Unknown field | | Plain string `systemInstruction` | Invalid value | | `system_instruction` (snake_case at root) | Unknown field | | JSON Schema `const` | Unknown field (use `enum: [value]`) | | JSON Schema `$ref` | Not supported (inline instead) | | JSON Schema `$defs` | Not supported (inline instead) | | Tool names with `/` | Invalid (use `_` or `:` instead) | | Tool names starting with digit | Invalid (must start with letter/underscore) | --- ## Complete Request Example ```json { "project": "my-project-id", "model": "claude-sonnet-4-6", "request": { "contents": [ { "role": "user", "parts": [ { "text": "Hello, how are you?" } ] } ], "systemInstruction": { "parts": [ { "text": "You are a helpful assistant." } ] }, "generationConfig": { "maxOutputTokens": 1000, "temperature": 0.7 } }, "userAgent": "antigravity", "requestId": "agent-abc123" } ``` --- ## Response Headers | Header | Description | |--------|-------------| | `x-cloudaicompanion-trace-id` | Trace ID for debugging | | `server-timing` | Request duration | --- ## Comparison: Antigravity vs Vertex AI Anthropic | Feature | Antigravity | Vertex AI Anthropic | |---------|-------------|---------------------| | Endpoint | `cloudcode-pa.googleapis.com` | `aiplatform.googleapis.com` | | Request format | Gemini-style `contents` | Anthropic `messages` | | `anthropic_version` | Not used | Required | | Model names | Simple (`claude-sonnet-4-6`) | Versioned (`claude-4-5@date`) | | Response format | `candidates[]` | Anthropic `content[]` | | Multi-model support | Yes (Claude, Gemini, etc.) | Anthropic only | --- ## Changelog - **2025-12-14**: Added function calling quirks, JSON Schema support matrix, tool name rules - **2025-12-13**: Initial specification based on direct API testing