Status: OAuth support is integrated and covered by local tests. The Google API Key / Interactions path is experimental and disabled for ordinary dispatch by default until its complete request, streaming, tool-call, usage, and error wire contract is verified against a real upstream or an authoritative SDK/protocol sample.
Codex2API can manage Google Antigravity accounts as a dedicated upstream channel and expose their models through the OpenAI-compatible /v1/responses, /v1/chat/completions, /v1/messages, and /v1/models surfaces. Antigravity accounts are isolated from Codex and Grok account groups and can be selected explicitly with an API key whose upstream channel is antigravity.
Two credential shapes are supported:
| Credential | Upstream | Authentication | Status |
|---|---|---|---|
| Google OAuth | cloudcode-pa.googleapis.com/v1internal:* (with the configured fallback hosts) |
Authorization: Bearer ... and x-goog-user-project |
Integrated |
| Google API Key | generativelanguage.googleapis.com/v1beta/interactions |
x-goog-api-key |
Experimental; dispatch disabled by default; real wire contract not yet verified |
When an account has no explicit or synchronized model list, the channel exposes this conservative fallback catalog:
gemini-3-pro-previewgemini-2.5-progemini-2.5-flash
An API Key account can declare its own model list and optional model mapping. The management models endpoint returns the declared list or these defaults; it does not claim to have remotely verified the catalog.
OAuth uses the official Antigravity Desktop client when nothing else is configured, so Start authorization works out of the box. Custom clients override that fallback. Precedence is environment variable > admin settings > built-in official client:
- Admin settings page (Settings → Antigravity): add
key/client_id/client_secretentries and optionally pick the default active client. Changes are stored insystem_settings.antigravity_oauth_config, take effect immediately without a restart, and the secret is never echoed back by the API (leave the secret blank when editing to keep the stored value). - Environment variables:
ANTIGRAVITY_OAUTH_CLIENTSis a semicolon-separated list ofkey|client_id|client_secretentries;ANTIGRAVITY_OAUTH_CLIENT_KEYoptionally selects the active key. - Built-in official Desktop client (
key=official): used only when both sources above are empty. This is the same public installed-app credential shipped in the Antigravity desktop app and reused bysub2api. Google may revoke a shared client; add your own entry if authorization starts returninginvalid_client.
ANTIGRAVITY_OAUTH_CLIENTS='primary|your-client-id|your-client-secret'
ANTIGRAVITY_OAUTH_CLIENT_KEY='primary'Environment entries take precedence for a matching key, and the active key resolves as environment variable > admin setting > first configured entry > built-in official, so a deployment-level override always wins over a misconfigured database value. Keep custom credentials in the deployment secret store or the admin settings page rather than source control.
Open Accounts → Antigravity → Google OAuth, start authorization, and complete Google's consent flow. If the loopback callback cannot complete automatically, paste the full callback URL back into the dialog.
OAuth accounts synchronize a Google subject, project, permissions, quota snapshots, and model metadata when the upstream supplies them. A 401 may trigger one OAuth token refresh/retry. This recovery is never used for API Key accounts.
The Antigravity account dialog accepts a single credential JSON / refresh token or multiple JSON files. Imported credentials are synchronized before normal use when possible. Account-list responses never return access tokens, refresh tokens, client secrets, or API keys.
Choose API Key in the add-account dialog and provide:
- Google API Key;
- an optional explicit model list;
- an optional JSON model-mapping object;
- optional proxy and Antigravity account groups.
API Key accounts use plan_type=api, do not require an OAuth project ID, and do not expose OAuth token/quota refresh actions in the admin UI. They remain visible to administration and explicit capability probes while ordinary /v1/responses scheduling stays fail-closed unless the operator sets ANTIGRAVITY_ENABLE_EXPERIMENTAL_INTERACTIONS=true.
When creating or editing a Codex2API downstream API key, choose Antigravity as its upstream channel to restrict dispatch to compatible Antigravity accounts. auto keys may route by model across Codex, Grok, and OAuth Antigravity accounts according to the available scoped model catalog. Experimental API-key accounts join either catalog only after the explicit environment opt-in above.
Account groups are channel-isolated: an Antigravity account can only join an Antigravity group.
Antigravity inference is admitted through /v1/responses, /v1/chat/completions, and /v1/messages; its models are exposed through /v1/models and the Codex model manifest. Chat and Messages translate their inbound body into a Responses payload before dispatch, which is exactly what the v1internal adapter consumes, so all three transports share one admission gate and one adapter. /v1/responses/compact still excludes Antigravity accounts because no compaction adapter exists, and the official Codex executors reject Antigravity credentials before any network request as a provider-boundary safeguard.
Antigravity OAuth accounts also support the native Gemini REST shape for clients that speak generativelanguage.googleapis.com/v1beta directly (for example ADK, LangChain Gemini adapters, or MCP stacks that post raw Gemini JSON).
| Endpoint | Method | Notes |
|---|---|---|
/v1beta/models |
GET | Lists scoped Antigravity model IDs in Gemini models[] shape (name, displayName, supportedGenerationMethods). |
/v1beta/models/{model} |
GET | Returns one model record; accepts IDs with or without the models/ prefix. |
/v1beta/models/{model}:generateContent |
POST | Non-streaming generate; unwraps the Cloud Code envelope back to Gemini JSON. |
/v1beta/models/{model}:streamGenerateContent |
POST | SSE stream; supports ?alt=sse. |
/v1beta/models/{model}:countTokens |
POST | Local token estimate (no upstream round trip). |
Requirements and behavior:
- OAuth only. API Key / Interactions accounts are rejected; bind a downstream key to the
antigravityupstream channel (or use anautokey whose scoped catalog includes Antigravity models). - Authentication. Send the Codex2API downstream key as
Authorization: Bearer <key>,x-api-key: <key>, orx-goog-api-key: <key>. The last form is what thegoogle-genaiSDK, ADK, and the Gemini channel of aggregator gateways send by default, so pointing their base URL at Codex2API works without a custom header. The?key=query-string form is not accepted (the key would land in URLs and access logs). - Model IDs match the published Antigravity catalog (
gemini-3.7-flash-high,gemini-3.8-flash-low, Claude tiers exposed by the channel, and so on), not raw Google wire IDs. - Tool schemas are normalized before upstream dispatch: orphan
requiredentries are dropped,anyOf/oneOf/allOfunions are flattened, invalid function names are sanitized with response-name restore, and schemas are sent asparametersJsonSchema(Cloud Code expectation). - Multi-turn tools:
functionResponseturns are regrouped for the adapter; leadinginlineDataparts in the same user turn are attached under the matchingfunctionResponse.parts. - Thinking: client-supplied
generationConfig.thinkingConfigis preserved; when absent, the gateway applies the tier default for the requested public model. - Thought signatures: missing stubs are injected on outbound
functionCallparts; stale signatures on inboundfunctionResponseparts are stripped to avoid replay failures. - countTokens: OAuth accounts call upstream
/v1internal:countTokenswith the same request normalization as generateContent (minusproject,model,sessionId,toolConfig, and safety settings). When no account is available or upstream returns 404, the gateway falls back to a local token estimate. - Tool schema cleanup: MCP-style bare property maps, boolean
requiredflags, local$ref/$defsJSON Pointer inline (including nested pointers,anyOf$refbranches, sibling overrides, and cycle fallback), orphanrequiredentries, union flattening, missing arrayitems, and unsupported enums are normalized before upstream dispatch.
Example:
curl -s http://127.0.0.1:2004/v1beta/models/gemini-3.7-flash-high:generateContent \
-H "Authorization: Bearer $CODEX2API_KEY" \
-H "Content-Type: application/json" \
-d '{"contents":[{"role":"user","parts":[{"text":"Say hello"}]}]}'Gemini-native clients authenticate with the same key through x-goog-api-key; for example with the google-genai Python SDK:
import os
from google import genai
client = genai.Client(api_key=os.environ["CODEX2API_KEY"], http_options={"base_url": "http://127.0.0.1:2004"})
client.models.generate_content(model="gemini-3.7-flash-high", contents="Say hello")Not yet implemented on this surface: batch/embed APIs, Imagen predict, and full parity with every generativelanguage metadata field on model list responses.
Responses function tools are bridged into Gemini functionDeclarations. Dropping them is not a safe degradation: the upstream still receives the system instruction describing those tools, answers with a call it was never allowed to declare, and terminates the turn as MALFORMED_FUNCTION_CALL. Built-in Codex tools (web search, image generation, computer use) have no v1internal equivalent and are still ignored. Operators can pin the bridge off for diagnostics, which makes a tool_choice that forces a function fail closed with a 400 instead:
ANTIGRAVITY_FUNCTION_TOOLS_ENABLED=falseProduction dispatch through this executor requires the explicit process environment opt-in:
ANTIGRAVITY_ENABLE_EXPERIMENTAL_INTERACTIONS=trueWithout that setting, API-key accounts are excluded from scheduling and model discovery. Explicit admin capability probes still work so an operator can test a key without silently enabling customer traffic.
The current experimental executor sends:
POST https://generativelanguage.googleapis.com/v1beta/interactions
Content-Type: application/json
Accept: application/json | text/event-stream
x-goog-api-key: <google-api-key>It currently preserves the OpenAI Responses-style request body and injects:
{
"model": "gemini-*",
"agent": "antigravity-preview-05-2026",
"stream": true
}Unit tests use an httptest endpoint override and verify that Codex2API constructs this endpoint, header set, and payload. They do not prove that Google accepts this body or returns OpenAI Responses-compatible JSON/SSE.
An opt-in production-executor integration test is available:
ANTIGRAVITY_INTERACTIONS_TEST_API_KEY='...' go test ./proxy -run TestAntigravityInteractionsRealUpstream -vOptional safe controls are ANTIGRAVITY_INTERACTIONS_TEST_MODEL, ANTIGRAVITY_INTERACTIONS_TEST_INPUT (maximum 512 bytes), and ANTIGRAVITY_INTERACTIONS_TEST_STREAM=true for the separately gated SSE subtest. The test skips with an explicit message when the key is absent, never prints the key, caps/redacts failure diagnostics, checks the actual status and content type, and parses the native JSON/SSE envelope without assuming OpenAI compatibility. A successful real-upstream run is required before production certification. A local skip is not a pass and does not remove this experimental warning.
Before production use, verify at least:
- whether
inputuses OpenAI Responses semantics; - whether
agentbelongs in the body, a header, or a query parameter; - whether streaming requires
alt=sseor another parameter; - JSON and SSE event envelopes;
- usage/token fields;
- function/tool call request and response shapes;
- error envelopes and retry semantics;
- accepted model IDs for
/v1beta/interactions.
Until that verification exists, keep the default disabled policy for production traffic. Enabling the environment flag is an explicit acceptance of the incomplete wire adapter and its compatibility risk.
- Credentials are persisted by the gateway. In the current account credential design, a Google API Key is stored in plaintext in the database credential payload. Protect database backups, admin access, logs, and host filesystem permissions; restrict the key at Google Cloud and rotate it regularly.
- Do not paste production credentials into a demo or untrusted deployment.
- Use of Google services must comply with the applicable Google terms, product policies, quotas, and account restrictions. Automated or unsupported access can result in throttling, suspension, or account loss.
- Preview models and estimated prices may change. Confirm current Google pricing before using the values for billing or customer charging.
- Credential exports contain live OAuth tokens/client secrets or a plaintext Google API Key. The admin-only export route uses attachment responses with
Cache-Control: no-store,Pragma: no-cache, andX-Content-Type-Options: nosniff; browsers, reverse proxies, backups, and operators must still treat the downloaded JSON/ZIP as a high-value secret. Delete or encrypt it after use. - Duplicate checks for manual add/import are serialized inside one process, but the database does not yet enforce a cross-replica uniqueness constraint for every OAuth/API-key credential family. Multiple gateway replicas sharing one database can therefore race if operators submit the same new credential concurrently. Serialize credential creation through one administrative replica (or otherwise avoid concurrent duplicate submissions) and audit/merge duplicates before enabling multi-replica writes.
GET /api/admin/accounts/antigravity/export requires normal admin authorization. With one matching account it returns application/json; charset=utf-8; with multiple accounts it returns application/zip containing one sanitized-filename JSON member per account. ids=1,2 limits the selection; omitting ids exports all active Antigravity accounts. A selection containing only missing or wrong-channel accounts returns 404.
OAuth documents include the access/refresh/ID tokens, project, OAuth client selection/client credentials, scope, and expiry needed by the existing Antigravity importer. API-key documents explicitly use auth_kind: "api_key" and api_key; they also preserve declared models and model mapping. Ordinary list/state endpoints never include these secret fields.
GET /api/admin/accounts/:id/antigravity/stateis read-only and never calls Google. It returns credential kind, catalog source/verification, identity/project status, sanitized permissions/quota, generation-fenced capability observations, timestamps, and warnings.POST /api/admin/accounts/:id/antigravity/syncrefreshes the Google identity/control plane for OAuth accounts. For API-key accounts it only persists the declared/default local catalog withverified: false; it does not fabricate remote catalog, quota, permission, project, or Interactions verification.POST /api/admin/accounts/:id/antigravity/capabilities/probeperforms one bounded non-stream request against the first configured/default model with a one-token output limit. This explicit action consumes a small amount of generation quota. Only a successful HTTP/JSON response persistsverified: true; transport, status, content-type, or envelope failures remain unverified. State reads and sync never silently run it.GET /api/admin/accounts/:id/test?model=<published-id>is the shared connection test, now wired for Antigravity. It sends the configured test prompt through the same Cloud Code executor as live dispatch (endpoint fallback, 429/503 quota mapping, one refresh-and-retry on 401 for OAuth accounts) and streamstest_start/content/test_completeorerrorSSE events, so the account page's "Test connection" button shows the model's actual reply. The model must be one of the account's published IDs; withoutmodelit prefers the global test model, then the cheapest*-flash-*-lowtier. A success clears cooldown/error state; 429/503 record a per-model cooldown instead of marking the account failed. Batch and recycle-bin tests use the same path.GET/PUT /api/admin/settings/channel-testsstores per-channel connection-test defaults for Antigravity and Claude ({"antigravity":{"test_model","test_content","test_concurrency"},"claude":{...}}, persisted insystem_settings.channel_test_config).test_concurrency(0~200, 0 = global) is applied to batch tests whose accounts all belong to that channel; mixed batches keep the global concurrency. The model must be in the tested account's catalog or automatic selection is used; empty content falls back to the global test content. The response also carriesmodel_choices(union of the pool's catalogs) anddefault_test_contentfor the settings page. Grok and Codex keep using the globaltest_model/test_content.GET/PUT /api/admin/settings/antigravitystores channel settings insystem_settings.antigravity_config.model_redirectsmaps a bare logical model to one of its own fixed tiers ({"gemini-3.8-flash":"gemini-3.8-flash-high"}); a request that names the bare model withoutreasoning.effortthen runs as that tier on every entry point (Responses fold, Chat Completions, Messages, API-key Interactions).redirect_overrides_effortmakes the redirect win even when the request carries an explicit effort. Suffixed tier IDs are never redirected, and the response listschoices(each logical model with its default level and tier IDs) for the settings page.
POST /api/admin/accounts/antigravityPATCH /api/admin/accounts/:id/antigravityPOST /api/admin/accounts/antigravity/importPOST /api/admin/accounts/antigravity/modelsPOST /api/admin/accounts/antigravity/batch-modelsGET /api/admin/accounts/antigravity/exportPOST /api/admin/accounts/antigravity/oauth/startPOST /api/admin/accounts/antigravity/oauth/completePOST /api/admin/accounts/:id/antigravity/refreshPOST /api/admin/accounts/:id/antigravity/quotaGET /api/admin/accounts/:id/antigravity/statePOST /api/admin/accounts/:id/antigravity/syncPOST /api/admin/accounts/:id/antigravity/capabilities/probePOST /api/admin/accounts/antigravity/clean-bannedPOST /api/admin/accounts/antigravity/clean-error
All endpoints above use the existing /api/admin authorization middleware. Export responses are secret-bearing; state/sync/probe responses are sanitized.