# COHESIVITY - DOCUMENTATION FOR AGENTS > For complete documentation with every resource API reference in one file, see: https://cohesivity.ai/llms-full.txt Cohesivity is one HTTP API that AI coding agents (Claude Code, Cursor, Codex, Windsurf, and similar) use to build apps for the user. The agent creates a project, provisions services, builds the app on the user's behalf, and surfaces what it does as it goes. The user can claim the project and take ownership at any time. Each section relevant to a build carries the formats, endpoints, and constraints the integration depends on. **APIs available:** openweather-api | google-geocoding-api | openai-api | ai-gateway | deepgram-api | exa-api | steel-browser | inbox | postgres | redis | object-storage | vector-database | railway-hosting | cloudflare-workers | social-login | realtime. The postgres and Steel Browser offerings are public to all tenants. Per-API details, exact rate limits, and the full table of contents are under "Cohesivity's offerings" below. --- # What is Cohesivity? Cohesivity is on-the-fly infrastructure, purpose-built for AI agents like claude-code, claude-web, codex, openclaw, and hermes. One managed API for a project's backend: databases, auth, hosting, storage, AI. Provider keys stay server-side, every project is isolated, usage is metered. The agent talks to one API instead of pausing for signups on Supabase, AWS, or GCP. Cohesivity provisions resources (S3, databases, auth) and returns credentials or proxy endpoints. The workflow is HTTP. The agent requests a service, Cohesivity provisions it, and the agent gets back what it needs. Traffic routes through the edge so calls stay standardized. One place holds the provider accounts, so there are no cloud consoles and no pasted API keys. Cohesivity manages lifecycle and billing, and agents build full-stack apps autonomously. --- # How Cohesivity works **Cohesivity is agent-managed infrastructure.** Constructive actions (creating a project, provisioning services, building the app) run through the agent's API calls; there is no separate human console for them. A read-only account page at `/account` shows the human billing and recent activity and lets them claim ownership. **The shape of it:** one POST creates an ephemeral tenant, one POST provisions a service, then the agent calls the service through the Cohesivity edge. Cohesivity injects the real upstream API keys server-side. The user sees no upstream keys, visits no provider consoles, and signs up for nothing before building starts. **Rate-limit model.** Ephemeral tenants have tenant-lifetime hard caps during their 72-hour claim window; the whole tenant pauses if any authoritative hard cap is breached. Claimed tenants use account-scoped Free/Plus/Pro buckets, a shared wallet, Razorpay-backed subscription state, and per-offering burst, storage, and concurrency guards across all projects the Cohesivity user owns. OpenAI, AI Gateway, Deepgram, and Exa also have tier-gated surface access at launch. Each offering page lists its current limits and any surface restrictions. Cohesivity can beta-gate individual offerings or features. A gated resource returns `403 { "error": "beta_access_required", "scope_type", "scope_key", "target" }` on provisioning. `/api/status` `account.experiments` reports active grants and configured gated scopes. **Account creation is agent-first.** The quickstart (below) creates an ephemeral tenant for 72 hours and writes tenant credentials, `expires_at`, and claim instructions into `.cohesivity`. Keeping the tenant runs through one-click approval: `POST /api/claim/url` (with `Authorization: Bearer `) returns `approval_url` (`/c/`) for the human, and `/api/wait` with the returned wait blob resolves once the claim completes. The management key stays agent-side for Management API calls; keeping it out of the browser is the point of this flow. There is no manual claim page — an approval link only exists once the agent mints one. Tenant creation is rate-limited to **10 requests per 60 seconds per client IP**: reuse an existing `.cohesivity` rather than re-running the quickstart. **Billing API.** `GET /api/billing/plans` is public and reports configured plans plus `price_monthly_cents` and `currency` per plan (use it for programmatic price reads instead of scraping the `/pricing` page). Its `provider_usage_pricing` object publishes the current metered provider rule: successful OpenAI, AI Gateway, Deepgram, and Exa usage is billed at provider cost plus 10%, rounded up to the nearest cent per settled charge. Failed calls are not billed. Constructive billing actions are `POST /api/billing/subscription { "plan": "plus" | "pro" }` (create or upgrade) and `POST /api/billing/topup { "amount_cents": <500..50000> }`. Both accept a Cohesivity account session cookie OR `Authorization: Bearer ` from any claimed tenant on the account, so an agent acts on behalf of the tenant owner. `amount_cents` is wallet fluid in USD cents: 500 for $5, 2000 for $20, 50000 for $500. Cohesivity converts to Razorpay's merchant currency at payment-link creation. The destructive billing action `POST /api/billing/subscription/cancel` accepts the same auth shape (session OR claimed `coh_man_*`). There is no state-based gate, so an agent can cancel any non-terminal subscription on the account's behalf. It accepts `{ "cancel_at_cycle_end": false | 0 }` to cancel now (default is end-of-cycle) and `{ "cancel_all": true }` to cancel every Razorpay subscription tied to the account's customer record. Account-scoped billing endpoints require a claimed tenant. A still-ephemeral tenant runs the one-click claim flow first (`POST /api/claim/url`, then `approval_url` at `/c/`, then poll `/api/wait`). Both create responses carry `checkout_url`: `https://cohesivity.ai/u/` for subscription create/upgrade, `https://cohesivity.ai/t/` for topups. It 302-redirects to Razorpay's hosted payment page (card, UPI, netbanking, wallets, per the merchant's enabled methods). The `checkout_url` is self-sufficient: the human need not be signed in to Cohesivity to pay it. They can open it on another device, in another browser, or signed in to a different Cohesivity account; the order's notes (set server-side at create time) determine which account gets credited, not the click-time session. The API response exposes only the Cohesivity `checkout_url`, not Razorpay's underlying `short_url`. Topup responses include `{ provider, kind, payment_link_id, amount_cents, merchant_amount_minor, currency, status, checkout_url, next_steps }`. There is no `qr` block; UPI, cards, and netbanking all live on the Razorpay page that `checkout_url` redirects to. Topups credit fluid through the `payment_link.paid` webhook. An agent holding a funded Base USDC wallet can self-pay via `POST /api/billing/topup/x402`: USDC on Base, no checkout, claimed `coh_man_*` only. The payer private key comes from a shell env var, not the chat. When the rail is live, a `topup_x402_url` field appears on the 402 bodies and on the `/api/status` and `/api/observability` advisories. Subscription mandates settle via `subscription.activated` and `subscription.charged` webhooks. Razorpay sends every event to `POST /api/billing/razorpay/webhook`; Cohesivity verifies `X-Razorpay-Signature` before changing account plan state. The headline pricing summary lives at `/pricing`. "Upgrade me to plus" and "topup $20" are pass-throughs: the agent fires the request, gives the user the `checkout_url` from the response, and the hosted Razorpay page handles the rest. Notifications surfaced through `/api/status`: bucket_warn_80/95/100, fluid_warn_80/95/exhausted, tier_changed, grant_reset, topup_received, offering_paused, offering_resumed. Each is deduplicated per (user, code, metric, month). `/api/status` also exposes `account.runtime_profile`, `account.runtime_version`, `account.upgrade_available` plus `account.upgrade_target_profile` (the runtime-upgrade discovery breadcrumb; see "Runtime upgrades" below), `account.pause_reason` as a structured object `{ resource, metric, kind, recommended_action }` (with `pause_reason_legacy` as a one-version concat-string alias), and a per-offering `bucket_usage` object keyed by service. `bucket_usage` flattens metric counters and limits per window: `_used_`, `_limit_`, plus `_per_minute_limit` for the rate-limit window (no `used` counter). **Experimental access.** Beta access is grant-based and account-level in v1. Claimed tenants inherit the owner account's active grants plus any matching pre-signup email grants; ephemeral tenants read only the global ephemeral grant target. There are no tenant-scoped grants. `/api/status` exposes active, non-expired grants under `account.experiments.active_grants` and configured beta gates under `account.experiments.gated_scopes`. Revoked and expired grants are not exposed. **Account observability.** `GET /api/observability` returns one payload covering every claimed tenant on the account, the shared wallet, every offering's usage, account-wide cost rollups, notifications, and derived advisories. Auth matches constructive billing: a session cookie OR `Authorization: Bearer ` from any **claimed** tenant. Ephemeral tenants get 403. The default response covers the calling tenant plus every sibling; heavier slices opt in with `?include=events,ledger,deploy_history,end_user_sessions,poller_health,webhook_history,recent_events`. The `events` slice is a top-level SQL-backed usage-event page, also mirrored into tenant rows when `tenants` is included. Plan-aware caps (Free/Plus/Pro) bound the time window and pagination size; the response echoes the applied caps under `limits_for_this_response`. Full spec: https://cohesivity.ai/docs/observability. **Failed upstream calls cost no quota or wallet.** When an OpenAI, AI Gateway, Deepgram, Exa, Weather, or Geocoding call returns non-OK or times out, Cohesivity revokes the preflight counter reservation synchronously. OpenAI, AI Gateway, Deepgram, and Exa settle fluid only after a successful response; Weather and Geocoding stay bucket-only unless an overuse block is bought after bucket exhaustion. Retries carry no penalty. ## How to build with Cohesivity (in order) 1. An existing `.cohesivity` in the project root already holds these values; step 3 covers reading it, and step 2 is unnecessary. 2. Project setup runs through either plugin-aware installer, `npx @cohesivity/init` or the quickstart; see "Step 1: Set up the project" below for the exact command and behavior. 3. `.cohesivity` holds the keys and links to service docs (https://cohesivity.ai/offerings); each offering page documents the resource it covers. 4. Provisioning runs through the Management API, per service the build needs. 5. The application runs on the Cohesivity edge, reading keys from `.cohesivity`. `.cohesivity` is the per-project source of truth across sessions. In a later session or fresh chat, the keys, `expires_at`, and claim handoff instructions are still in the file; re-reading it is faster than re-running the quickstart and friendlier to the rate limiter. The file carries a short directive block, the canonical version, telling agents to surface the 72-hour ephemeral notice and to use `POST /api/claim/url` for the one-click claim handoff. Cohesivity is the aggregation layer: it holds the upstream provider accounts and injects their keys at the edge, so there is nothing to collect from the user. The quickstart mints the ephemeral tenant with no signup and no payment; Cohesivity records the request origin (IP, ASN, country) for abuse prevention and an opaque per-machine setup id that groups the tenants one machine creates (see the privacy policy), and the tenant self-expires in 72 hours under hard caps. The natural point to bring the user in is claim time, when they decide whether to keep the project (give them the one-click `approval_url`). --- # How to reach Cohesivity **Base URL:** https://cohesivity.ai - Docs index: https://cohesivity.ai/docs - Service docs (per-API endpoints, examples): https://cohesivity.ai/offerings - Full agent reference: https://cohesivity.ai/llms-full.txt - Onboarding: https://cohesivity.ai/onboarding - Account observability spec: https://cohesivity.ai/docs/observability - Human-facing read-only dashboard (Cohesivity-session cookie): https://cohesivity.ai/account - About, team, and public profiles: https://cohesivity.ai/about - Privacy policy: https://cohesivity.ai/privacy - Terms of service: https://cohesivity.ai/terms Cohesivity is built by a public founding team: Arag Agrawal (https://www.linkedin.com/in/aragagrawal/), Anshu Agrawal (https://www.linkedin.com/in/aanshuaggrawal120/), and Shouryamaan Jain (https://www.linkedin.com/in/shouryamaanjain/). Company LinkedIn: https://www.linkedin.com/company/cohesivity/ . Product Hunt launch: https://www.producthunt.com/products/cohesivity . Contact: accounts@cohesivity.ai ## Lifecycle rules 1. New tenants are ephemeral for 72 hours. 2. An ephemeral tenant can pause as a whole if any authoritative hard cap is breached. 3. Hosted paused tenants redirect to a generic claim-help page. 4. Infra-only paused tenants return structured API errors telling the agent to mint an approval link. 5. Claim is one-click approval: `/api/claim/url` returns `approval_url` at `/c/` for the human, and `/api/wait` with the returned wait blob resolves once the lifecycle flips to claimed. The management key stays agent-side for Management API use; it is never entered in a browser. 6. Unclaimed expired tenants are terminated. Cohesivity retains the management tenant record for audit and abuse forensics. ## Runtime upgrades (when `/api/status` reports drift) Tenants pin to whatever `runtime_profile` was stable at genesis. The platform's stable version advances as Cohesivity ships; existing tenants stay pinned until they upgrade. `GET /api/status` returns `account.upgrade_available` (boolean) and `account.upgrade_target_profile` (string) for the calling tenant. When `upgrade_available` is true: 1. `POST /api/runtime/upgrade/plan` (`Authorization: Bearer `) returns a stacked diff of every tenant-facing change between the pinned profile and current stable, filtered to the offerings in use plus a cross-cutting `runtime` bucket. Response shape: `{ tenant_id, current_runtime_profile, target_runtime_profile, already_current, releases_in_path: [{ profile, version, commit_sha, created_at }], changes: { breaking: [...], deprecations: [...], behavior_changes: [...] } }`. Each entry carries `summary`, `what_to_change` (imperative, agent-readable), `offering`, `version`, `commit_sha`, `date`. 2. Every `breaking` and `deprecation` entry's `what_to_change` describes the codebase change it requires. `behavior_change` entries describe semantic shifts and may need no code change. 3. `POST /api/runtime/upgrade/commit { "target_profile": "" }` with the same management-key auth. On success, `runtime_profile` bumps and `AUTH_CACHE` is invalidated; later requests hit the new runtime. A 409 means `stable_profile` advanced mid-flight: re-plan. Commit is idempotent: re-calling on-target returns `changed: false` and re-invalidates the cache. Skipping commit leaves the tenant on the old runtime; the agent can re-plan and commit in any later session. Cohesivity does not force-migrate. --- # The flow ## Step 1: Set up the project Both supported paths detect every installed supported client and reconcile its native or portable Cohesivity plugin before bootstrapping the project: npx @cohesivity/init Or, when Node is unavailable: curl -fsSL https://cohesivity.ai/quickstart.sh | bash Claude and Codex use their verified native marketplaces, Gemini and Antigravity use their native package formats, OpenClaw uses its Claude-marketplace adapter plus native MCP registry, Cursor uses the verified portable package, Hermes receives the verified skill and MCP records through its released native import/config surfaces, and OpenCode receives the verified portable skill and local MCP through its documented global skill and native MCP commands. Every installed client is handled independently. Artifact bytes are pinned by size and SHA-256 before extraction; OAuth and browser login are deferred until after the client restarts. The same run creates an ephemeral tenant, writes the keys to `.cohesivity` (tenant_id, coh_management_key, coh_application_key, `expires_at`, `runtime_profile`, one-click claim instructions), gitignores it, and prints next steps. It is idempotent and reuses an existing `.cohesivity`. The MCP surfaces are intentionally separate. `https://cohesivity.ai/mcp` is the public documentation MCP and needs no login. The installed `cohesivity-local` stdio server exposes six project-bootstrap tools and needs no login. `https://cohesivity.ai/mcp/manage` is the account-scoped management MCP and requires OAuth; installers save the entry but never start its browser flow. To install only the canonical standalone skill, with no plugin or MCP configuration, pass `--no-plugin`: curl -fsSL https://cohesivity.ai/quickstart.sh | bash -s -- --no-plugin Both installers accept `--no-plugin` for a user who explicitly opts out of plugin and MCP installation. Both perform the same tenant and project bootstrap, and `--dry-run` reports every planned delivery and bootstrap action without changing files, running client commands, or making network requests. The quickstart needs no headers: it identifies the calling agent itself by walking its own parent processes. On Claude Code, the command classifier blocks this shell quickstart unless the user has authorized it first; asking the user for that authorization (for example with the AskUserQuestion tool) before running it clears the block. These two installers are the only supported ways to create a tenant. Do not call the creation endpoint by hand — the installers carry the idempotency and attribution rules that make a bootstrap correct, and a hand-rolled call that sends a default HTTP-client `User-Agent` is rejected by the WAF with HTTP 403 and Cloudflare error 1010 anyway. The skill is the full agent playbook (resource catalog, claim, billing, realtime, identity, gotchas) with the exact endpoint paths and auth flows. Without it the common misses are `/gw/` vs `/edge/`, token-in-callback-URL instead of the code-exchange OAuth flow, and `/chat/completions` instead of `/v1/responses`. Keeping the tenant runs through `POST https://cohesivity.ai/api/claim/url`, which returns `approval_url` (`https://cohesivity.ai/c/`) for the human, and `/api/wait` with the returned wait blob. `coh_management_key` stays agent-side and is never entered in a browser. ## Step 2: Provision services Bulk (recommended, one call for all resources): curl -s -X POST https://cohesivity.ai/api/resources \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"resources": ["postgres", "railway-hosting", "realtime"], "social-login": {"callback_urls": ["https://*.cohesivity.app/auth/done"]}}' Cohesivity resolves internal dependencies automatically. Per-resource config is a top-level key matching the resource name. Single resource: curl -s -X POST https://cohesivity.ai/api/resources/ \ -H "Authorization: Bearer " **Resource names:** openweather-api, google-geocoding-api, openai-api, ai-gateway, deepgram-api, exa-api, steel-browser, inbox, postgres, redis, object-storage, vector-database, railway-hosting, cloudflare-workers, social-login, realtime An edge call needs its resource provisioned first; unprovisioned resources return an error. **Delete a resource:** curl -s -X DELETE https://cohesivity.ai/api/resources/ \ -H "Authorization: Bearer " ## Step 3: Use the service **Server-side only.** `coh_management_key` and `coh_application_key` are both secrets. Neither belongs in browser-loaded JS, mobile app bundles, or any client-side code. Every `/edge/*` call originates from a server-side context: a Railway-hosted server, a Cloudflare Worker (`cloudflare-workers`), or your own server tier. The `?key=` edge bootstrap is server-to-server only. SPA-only architectures need `cloudflare-workers` as the proxy tier. - Edge requests go to `https://cohesivity.ai/edge//`, sent from your server. - `coh_application_key` goes in as the **key** query parameter (server-to-server bootstrap). - Example (server-side): `https://cohesivity.ai/edge/openweather-api/data/2.5/weather?q=Bengaluru&key=` --- ### Cohesivity's offerings | Resource name | Description | | :-- | :-- | | openweather-api | OpenWeather API: current conditions and 5-day/3-hour forecast by city, ZIP, or lat/lon | | google-geocoding-api | Google Geocoding API: geocode addresses to coordinates and reverse; pairs with Weather API | | openai-api | OpenAI API: Responses text, embeddings, and gpt-image-2 image generation with strict launch tier gates; successful claimed usage costs provider cost plus 10% | | ai-gateway | AI Gateway: Cohesivity's multi-provider AI Gateway for openai, anthropic, google, and xai chat completions, embeddings, and image generation; successful claimed usage costs provider cost plus 10% | | deepgram-api | Deepgram API: English nova-3 STT, Aura-2 English TTS, and Voice Agent with flux-general-en v2 listen and linear16/wav Settings; successful claimed usage costs provider cost plus 10% | | exa-api | Exa: search, research after claim, deep-reasoning search, contents, answer, context; successful claimed usage costs provider cost plus 10% | | steel-browser | Steel Browser: standard-datacenter CDP sessions plus one-shot scrape for every tenant | | inbox | Agent Inbox: one claimed-tenant email address with inbound storage and send/list/get/reply/delete over a minimal API | | postgres | Postgres on Neon: per-tenant Postgres database; edge at /edge/postgres to run SQL over HTTP (uses $1 placeholders) | | redis | Redis key-value store: edge at /edge/redis for key-value, expiry, counters, sorted sets over HTTP | | object-storage | Object storage (R2): per-tenant namespace; edge at /edge/object-storage for PUT/DELETE, public URL for reads | | railway-hosting | Railway Hosting: primary public hosting; direct source upload through Cohesivity to a Cohesivity-owned Railway project/service/environment/domain with env vars, vanity subdomains, custom domains, and Cohesivity-managed tier CPU/RAM/replica/sleep caps; no GitHub, Railway CLI, or tenant Railway credentials | | cloudflare-workers | Cloudflare Workers: serverless JavaScript modules on the edge; create with POST, deploy code with PUT, supports cron triggers | | social-login | Social Login (Google OAuth): zero-setup Google login; OAuth flow, user storage, token management | | realtime | Realtime: channels, presence, runtime-selected managed persistence; WebSocket messaging + HTTP publish/history. Video/voice via /edge/realtimekit/* (CF RealtimeKit API proxy) | | vector-database | Vector Database (Pinecone): managed vector store for embeddings; upsert, query, fetch, delete via HTTP. Tenant isolation enforced server-side | | domain | Domain Purchase: buy an external domain (e.g. yourapp.com) on the end user's behalf. Paid one-shot via Razorpay, registered at Name.com. NOT provisioned via /api/resources; see /offerings/domain | - **Management Plane:** account creation, resource provisioning, status, account suspension, CF Workers sub-resources. - **Data Plane:** proxy to the real provider; your request is forwarded with the enterprise key injected. Same API as the provider, different base URL and key. --- Errors return in the same format as the upstream provider (e.g. Google JSON error schema); a Cohesivity error says so in its message. --- # Requirements (the working integration) 1. Either project setup command reconciles the plugin for installed clients, creates an ephemeral tenant (72-hour window), and saves credentials to `.cohesivity`. Idempotent. An explicit `--no-plugin` installs only the standalone skill. The skill keeps auth and edge paths correct. 2. The app loads credentials from `.cohesivity` rather than creating its own accounts; account creation is the agent's job, done before the app runs. 3. **coh_management_key** is the `Authorization: Bearer` header value for all management endpoints. 4. From the server tier, a short-lived edge token from `POST /edge/session?key=` is the preferred call shape, used as `Authorization: Bearer `. The raw `?key=` path is a server-to-server bootstrap only; both call shapes originate server-side. 5. A resource needs provisioning before its edge calls work. 6. **Both `coh_management_key` and `coh_application_key` are secrets** (server-side only, per Step 3); committing either to GitHub exposes the tenant. For Railway-hosted apps, `POST /api/railway/env` stores them as Railway environment variables through Cohesivity-managed auth. 7. Claim runs through `POST /api/claim/url`, which returns `approval_url` at `/c/` for the human and a wait blob to poll at `/api/wait`. It is the only claim path: paused hosted tenants redirect visitors to a generic claim-help page, and infra-only tenants surface the same guidance through API errors. --- # Key format - **tenant_id:** adjective-animal-action format (e.g. `swift-fox-sleeping`). Lowercase, hyphen-separated. - **coh_management_key:** prefix `coh_man_` + 20 alphanumeric (a-z, 0-9). No spaces or special chars. - **coh_application_key:** prefix `coh_app_` + 20 alphanumeric (a-z, 0-9). No spaces or special chars. --- ## Docs Each offering page documents prerequisites, examples, and instructions for its resource. The table above carries the per-resource summaries; the notes below add page facts not in that table. - [OpenWeather API](https://cohesivity.ai/offerings/openweather-api) - [Google Geocoding API](https://cohesivity.ai/offerings/google-geocoding-api) - [OpenAI API](https://cohesivity.ai/offerings/openai-api) - [AI Gateway](https://cohesivity.ai/offerings/ai-gateway) - [Deepgram API](https://cohesivity.ai/offerings/deepgram-api): server-side Token auth - [Exa API](https://cohesivity.ai/offerings/exa-api) - [Steel Browser](https://cohesivity.ai/offerings/steel-browser): available to every tenant - [Inbox](https://cohesivity.ai/offerings/inbox) - [Postgres](https://cohesivity.ai/offerings/postgres): public for all tenants - [Redis](https://cohesivity.ai/offerings/redis) - [Object Storage](https://cohesivity.ai/offerings/object-storage) - [Railway Hosting](https://cohesivity.ai/offerings/railway-hosting): primary public hosting with direct Cohesivity upload and Cohesivity-managed tier compute caps; no GitHub, CLI, or Railway credentials - [Cloudflare Workers](https://cohesivity.ai/offerings/cloudflare-workers) - [Social Login](https://cohesivity.ai/offerings/social-login): supports `?return_to=/path` on the login URL so apps resume deep links after OAuth - [Realtime](https://cohesivity.ai/offerings/realtime): receivers and history include both `action` and `event` with the same value, so publishing `{ "event": "message" }` delivers `{ "action": "message", "event": "message", ... }` - [Vector Database](https://cohesivity.ai/offerings/vector-database): profile-based shared indexes - [Domain Purchase](https://cohesivity.ai/offerings/domain): registered at Name.com on payment capture ### Feedback A feedback endpoint records the Cohesivity build experience: GET /api/feedback returns a structured prompt (what to include) POST /api/feedback submits feedback as { "feedback": "..." } Both require `Authorization: Bearer `. The GET response names the sections to include (GOAL, ISSUES, WHAT WORKED, TIME BREAKDOWN, DIRECTIVES, WISH). A later POST appends new text to the existing feedback document for this tenant; no need to repeat prior content. --- # Onboarding Docs index: https://cohesivity.ai/docs ## The Flow Setup, provisioning, and claim follow "How to build with Cohesivity" and "Lifecycle rules" above. ### Example: provision and use a resource curl -s -X POST https://cohesivity.ai/api/resources/postgres \ -H "Authorization: Bearer " POST https://cohesivity.ai/edge/postgres?key= { "query": "CREATE TABLE users (id BIGSERIAL PRIMARY KEY, name TEXT)" } An always-on claim CTA doesn't fit an active, in-progress tenant app: claim is a one-time handoff, not persistent UI. Every resource doc below lists the current rate limits for the ephemeral, free, plus, and pro tiers. --- # Postgres (Neon) Public SQL access to a shared tenant Neon project. Execute SQL over HTTP: no Postgres setup, no connection pool to manage, no credentials to rotate. Public for all tenants; existing `database` (D1/SQLite) tenants are unaffected. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/postgres \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/postgres \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Shared Project and Access Boundary Inbox, social-login event history, realtime history, and public Postgres can share one tenant Neon project. Creating that internal primitive does not grant `/edge/postgres`; only provisioning the public `postgres` resource enables this endpoint. If an internal dependent created the project first, public Postgres provisioning reuses it instead of creating another project. `/edge/postgres` connects with a dedicated least-privilege role restricted to the `public` schema. It cannot access the reserved `coh_inbox`, `coh_auth`, or `coh_realtime` schemas; Cohesivity uses separate owner credentials for those internal services. Deleting public Postgres drops only the public schema and revokes the resource grant while another dependent remains. The Neon project is deleted only after its last dependent is removed. ## Relationship to Legacy /database `database` (SQLite/D1) is retired for new provisioning, while tenants with an existing active D1 retain its full contract. Existing D1 and Postgres can coexist on one tenant, use different SQL dialects and metrics, and remain independent. Provisioning Postgres or upgrading a runtime never migrates, backfills, flips, or deletes D1 data; future migration requires an explicit per-tenant operator action. - Existing `database` (D1/SQLite): rows-based billing, `?` placeholders, edge-colocated, single-threaded writer, ~10 GB hard cap. - `postgres` (Neon): compute/storage/transfer billing, `$1` placeholders, regional, autoscaling compute, per-tier storage ceiling. ## SQL Dialect This is **PostgreSQL**, not SQLite. Key differences from `database`: - `BIGSERIAL` or `GENERATED ALWAYS AS IDENTITY` replaces `INTEGER PRIMARY KEY AUTOINCREMENT` - Parameter placeholders are `$1`, `$2`, ..., not `?` or `?1` - `TIMESTAMPTZ`, `JSONB`, `TEXT[]`, `ILIKE` all available - `NOW()` works in DEFAULT clauses - One statement per `query` string; multi-statement SQL in a single string is not supported. Running several statements in one request requires a batch (`statements`, see Edge Usage below) ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/postgres - **Recommended auth:** `POST https://cohesivity.ai/edge/session?key=` → `Authorization: Bearer ` - **Fallback auth:** `?key=` query parameter - **Method:** POST with a JSON body in one of two forms: sending both `query` and `statements` is a 400: - **Single:** `{ "query": "SQL", "params": [...] }` → `{ "rows": [...], "rowCount": N }` - **Batch:** `{ "statements": [{ "query": "SQL", "params": [...] }, ...] }` → `{ "results": [{ "rows": [...], "rowCount": N }, ...] }`, one result per statement in submission order - `rowCount` is the real affected-row count for INSERT/UPDATE/DELETE - A result that hits the row cap carries `"truncated": true` (see Limits below) ## Batches - All statements in a batch run in a **single atomic transaction** in one round-trip. On any failure the whole batch rolls back: nothing is applied, and the error does not identify which statement failed - Transaction-control or session statements (BEGIN/COMMIT/ROLLBACK/SAVEPOINT/RELEASE/SET/RESET) inside a batch are rejected with 400, because the batch is already a transaction - Max 50 statements per batch. **Each statement bills one postgres request**, so batch size is also bounded by the tier's per-minute request cap (e.g. 30 on ephemeral) ## Provisioning curl -s -X POST https://cohesivity.ai/api/resources/postgres \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"region":"apac"}' Accepted regions are documented in the regions block below. No beta grant is required; normal plan, quota, and Neon-region limits still apply. ## Limits - Max query length: 50,000 chars per statement - Max rows returned: 10,000 per response, cumulative across batch results: once the budget is exhausted, later slots return `rows: []` with `"truncated": true` - Max 50 statements per batch; each statement bills one postgres request against the per-minute cap - Per-tier concurrent SQL limit, statement timeout, and max compute (see Rate Limits below) - Scale-to-zero after 5 minutes of inactivity; first query after idle pays a brief cold-start cost ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - compute unit seconds: 3600 per ephemeral tenant lifetime before claim or expiry - public transfer bytes: 250 MB per ephemeral tenant lifetime before claim or expiry - written bytes: 250 MB per ephemeral tenant lifetime before claim or expiry - live storage: 100 MB max total - restore gb months: 0.05 per ephemeral tenant lifetime before claim or expiry - requests: 30 per minute **Claimed Free** - live storage: 512 MB max total - requests: 120 per minute - compute unit seconds: 18000 per month - storage gb months: 1 per month - restore gb months: 0.5 per month - public transfer bytes: 5 GB per month - written bytes: 5 GB per month **Claimed Plus** - live storage: 2 GB max total - requests: 600 per minute - compute unit seconds: 90000 per month - storage gb months: 5 per month - restore gb months: 5 per month - public transfer bytes: 25 GB per month - written bytes: 50 GB per month **Claimed Pro** - live storage: 10 GB max total - requests: 3000 per minute - compute unit seconds: 540000 per month - storage gb months: 25 per month - restore gb months: 25 per month - public transfer bytes: 100 GB per month - written bytes: 250 GB per month --- # Realtime Real-time messaging with channels, presence, and runtime-selected managed persistence. WebSocket handles live delivery; HTTP handles server-side publish and history. No third-party service is needed. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/realtime \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/realtime \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Common Mistakes - **User ID type mismatch with social-login.** Social-login returns `user.id` as a **number** (e.g., `30`). Realtime presence returns user IDs as **strings** (e.g., `"30"`). Minting realtime tokens or comparing presence data against a social-login ID without casting to `String(user.id)` produces a silent type mismatch. - **One channel per WebSocket.** The channel is chosen at connect time via `?channel=`. There is no `subscribe` or `unsubscribe` action in the Worker-based realtime API. - **Confusing `event` (publish) with `action` (legacy receive).** When publishing, you set the `event` field. Recipients and history now return both `event` and `action` with the same value. See "Event/Action Mapping" below. ## What Happens on Provision - Fresh realtime provisioning on a fresh capability runtime stores persisted history in the private `coh_realtime.realtime_messages` schema of the shared tenant Neon project. This does not provision D1 or grant `/edge/postgres`. - Existing realtime resources and missing backend state remain legacy D1. Runtime upgrades never move, backfill, or delete their data; there is no migration endpoint in this release. - History and publication shapes, ordering, cursor IDs, and numeric `created_at_ms` values remain identical on both backends. - Video/voice rooms are lazily provisioned on first room creation (powered by Cloudflare RealtimeKit): no extra setup needed - Deleting realtime drops only its configured history store and any RealtimeKit app. It never deletes an existing tenant D1; a shared Neon project is reclaimed only when realtime was its last dependent. You can choose the managed history write region when provisioning realtime: curl -s -X POST https://cohesivity.ai/api/resources/realtime \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"write_region":"us"}' `write_region` remains a compatibility input. It selects the write region for a legacy D1 backend and maps to the nearest supported Neon region for a fresh capability backend. Direct D1 regions (`wnam`, `enam`, `weur`, `eeur`, `apac`, `oc`) and aliases such as `us-west`, `us`, `eu`, `eu-east`, `apac`, and `australia` remain accepted; omitting it uses the backend default. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Concepts - **Channels**: named pub/sub topics (strings you choose: `chat:room_42`, `feed:user_5`, etc.) - **Events**: when publishing, set `event` to a message type (`message`, `typing`, `update`, etc.). Default: `message`. Recipients see both `event` and the legacy-compatible `action` on incoming frames. - **Persist**: messages are saved to the configured managed history store by default. Set `persist: false` to skip (use for typing indicators, cursor positions, etc.) - **Presence**: the server returns current occupants on connect and broadcasts join/leave events automatically for that channel ## Connection Token Minting a short-lived edge bearer token from your server first speeds up repeated edge calls: POST https://cohesivity.ai/edge/session?key= Response: { "token": "", "token_type": "Bearer", "expires_in": 60 } Mint responses are returned with `Cache-Control: private, no-store, max-age=0`. The WebSocket connection token can then be minted using that bearer token (recommended), or via the raw `?key=` bootstrap path. WebSocket connections require a short-lived token: POST https://cohesivity.ai/edge/realtime/token?key= Content-Type: application/json { "user_id": "alice", "name": "Alice", "ttl": 300 } Response: `{ "token": "", "expires_in": 300 }` Fields: - `user_id` (required): string or number identifying the user - `name` (optional): display name included in presence events - `ttl` (optional): token lifetime in seconds (integer, 60–3600). Defaults to 300 (5 minutes). Chat-style apps where users stay on a page for hours benefit from a longer `ttl`, such as `1800` or `3600`. Existing WebSocket connections do not auto-refresh, so reconnecting after expiry requires a fresh token. The Worker re-checks that the tenant and `realtime` resource are still active when the WebSocket opens. This endpoint runs server-side only: the application key it uses lives on the server, and exposing that key to the browser would let any client mint tokens directly. The resulting token is what gets passed to the client. ## Connect (WebSocket) Each WebSocket connects to exactly one channel: WS wss://cohesivity.ai/edge/realtime?token=&channel=chat:room_42 On connect you receive: { "action": "connected", "channel": "chat:room_42" } { "action": "subscribed", "channel": "chat:room_42", "presence": [...] } User identity comes from the token; it cannot be changed by the client. Watching another live channel requires a separate WebSocket. ## Publish via WebSocket Publishing sends a frame on the connected socket: { "action": "publish", "event": "message", "data": { "text": "hello" } } Recv: { "action": "published", "event": "published", "channel": "chat:room_42", "created_at_ms": 1711111111111 } All subscribers (including the sender) receive the publication: { "action": "message", "event": "message", "channel": "chat:room_42", "data": { "text": "hello" }, "created_at_ms": 1711111111111 } Setting `"persist": false` skips managed persistence, for ephemeral events like typing: { "action": "publish", "event": "typing", "data": { "user": "Alice" }, "persist": false } ## Presence Events When someone connects to or disconnects from that channel, all other connected clients in the same channel receive: { "action": "join", "channel": "chat:room_42", "user": { "id": "42", "name": "Alice" } } { "action": "leave", "channel": "chat:room_42", "user": { "id": "42", "name": "Alice" }, "last_seen": "..." } Note: realtime user IDs are always strings. Comparing them against numeric auth IDs (e.g., social-login's `id: 25`) without casting to `String(user.id)` causes a mismatch. ## Publish (HTTP) HTTP publishing works from server-side code (Railway services, CF Workers, cron jobs), without needing a WebSocket connection. Recommended fast path: POST https://cohesivity.ai/edge/session?key= Response: { "token": "", "token_type": "Bearer", "expires_in": 60 } POST https://cohesivity.ai/edge/realtime Authorization: Bearer Content-Type: application/json { "channel": "chat:room_42", "event": "message", "data": { "text": "hello" } } Fallback bootstrap path: POST https://cohesivity.ai/edge/realtime?key= Content-Type: application/json { "channel": "chat:room_42", "event": "message", "data": { "text": "hello" } } Response: `{ "channel": "chat:room_42", "action": "message", "event": "message", "created_at_ms": 1711111111111 }` Setting `"persist": false` in the body skips managed persistence. ## History Persisted messages for a channel can be fetched from the configured managed history store, for scrollback or reconnection catch-up. Recommended fast path: GET https://cohesivity.ai/edge/realtime/history?channel=chat:room_42&after=100&limit=50 Authorization: Bearer Fallback bootstrap path: GET https://cohesivity.ai/edge/realtime/history?channel=chat:room_42&after=100&limit=50&key= Response: `{ "messages": [{ "action": "message", "event": "message", "data": {...}, "id": 101, "created_at_ms": ... }] }` All reads are strongly consistent and use the primary configured history store. Parameters: - `channel` (required): the channel name - `after`: return messages with id greater than this (default: 0, meaning all) - `limit`: max messages to return (default: 50, max: 100) Legacy D1-backed realtime tenants keep their existing direct `realtime_messages` table access through the existing-tenants-only `database` offering. Fresh capability tenants use the backend-independent history endpoint above; the reserved `coh_realtime` schema is not exposed through `/edge/postgres`. ## Event/Action Mapping When publishing, you set `event`. When receiving (via WebSocket or history), Cohesivity now returns both `action` and `event` with the same value for compatibility: Publishing: { "event": "reaction", ... } → Subscribers receive: { "action": "reaction", "event": "reaction", ... } → History returns: { "action": "reaction", "event": "reaction", ... } If `event` is omitted, it defaults to `"message"`. For new code, key off `event` if you want the most explicit cross-surface field. `action` remains fully supported for backward compatibility. ## Token Lifecycle - The connection token (JWT) is validated only when the WebSocket opens - A new WebSocket connect also re-checks that the tenant and `realtime` resource are still active - An established WebSocket connection survives past token expiry: the server does not disconnect you when the token expires - Token expiry controls the window during which the token can be used to open a new WebSocket connection, not the lifetime of an already-open connection - Disconnecting or reconnecting requires a **fresh token**, since the old one may have expired. A longer validity window (up to 3600s) is available via `ttl`. - `GET /edge/realtime/history?after=` catches up on messages missed during disconnection ## WebSocket Errors WebSocket protocol errors are sent as `{ "error": "..." }` frames, not Google-style HTTP error envelopes. Common frames: - `{ "error": "Invalid JSON" }` - `{ "error": "Unknown action: \"...\". Use: publish" }` - `{ "error": "publish requires \"data\"" }` - `{ "error": "Failed to persist message: ..." }` ## Common Patterns - **Chat messages**: one WebSocket per open room, persist (default) - **Typing indicator**: `event: "typing"`, `persist: false` - **Online status**: Presence (`action: "join"` / `action: "leave"`, automatic) - **Last seen**: `msg.last_seen` in `leave` action - **Read receipts**: `event: "read"`, `persist: false` - **Notifications**: keep a dedicated `user:` WebSocket open and publish from the server over HTTP - **Live dashboard**: one socket per visible feed, `persist: false` - **Reconnect catch-up**: GET /history?after= on reconnect ## DM Pattern Realtime is channel-based and each WebSocket joins exactly one channel. Recommended pattern: **Channel naming:** `dm:_` with sorted IDs lets both users compute the same channel name. Example: users 3 and 17 → `dm:3_17`. 1. Each user keeps one WebSocket open to a personal channel: `user:` 2. When User A starts a DM with User B, a notification goes to `user:` with the DM channel name 3. User B receives the notification, opens another WebSocket to the DM channel, and fetches history 4. Both users publish messages to the DM channel normally Example notification (HTTP publish from your server): POST /edge/realtime?key= { "channel": "user:42", "event": "dm_request", "data": { "from": "alice", "dm_channel": "dm:1_42" } } The recipient receives: `{ "action": "dm_request", "event": "dm_request", "channel": "user:42", "data": { "from": "alice", "dm_channel": "dm:1_42" } }`, then opens a second WebSocket to `dm:1_42`. ## Video and Voice (RealtimeKit) Realtime includes video/voice via Cloudflare RealtimeKit: once `realtime` is provisioned, video is available through `/edge/realtimekit/*`. The `/edge/realtimekit/*` endpoint proxies directly to the Cloudflare RealtimeKit REST API for your tenant. All CF RTK API paths work: POST https://cohesivity.ai/edge/realtimekit/meetings Create a meeting POST https://cohesivity.ai/edge/realtimekit/meetings//participants Add participant → returns the upstream participant token GET https://cohesivity.ai/edge/realtimekit/meetings/ Meeting status PUT https://cohesivity.ai/edge/realtimekit/meetings/ Update meeting GET https://cohesivity.ai/edge/realtimekit/presets List presets The proxy maps `/edge/realtimekit/` to Cloudflare's `api.cloudflare.com/.../realtime/kit/{your_app}/`, with account ID and app ID injected automatically. Responses follow Cloudflare's `{ success, data: {...} }` envelope format. For participant creation, `data.token` from the response is the participant auth token. ### Eventual consistency after meeting creation Meeting creation is eventually consistent: calling participant endpoints right after creation, with the freshly-returned `id`, may briefly return `ResourceNotFound`. Polling `GET /edge/realtimekit/meetings/:id` until it resolves, before minting participants, avoids the race. A loop of 5 attempts at 200ms apart is sufficient. On the frontend, the Cloudflare RealtimeKit SDK takes the participant token from `data.token`. **Frontend packages:** - React: `@cloudflare/realtimekit-react` + `@cloudflare/realtimekit-react-ui` - Web Components: `@cloudflare/realtimekit-web` + `@cloudflare/realtimekit-ui` - Angular: `@cloudflare/realtimekit-angular` + `@cloudflare/realtimekit-angular-ui` - React Native: `@cloudflare/realtimekit-react-native-ui` The UI Kit provides a complete meeting experience out of the box (video grid, controls, setup screen). Quick start with React: ``` import { RtkMeeting } from '@cloudflare/realtimekit-react-ui'; ``` **Docs:** - RealtimeKit overview: https://developers.cloudflare.com/realtime/realtimekit/ - UI Kit (pre-built components): https://developers.cloudflare.com/realtime/realtimekit/ui-kit - Core SDK (build custom UI): https://developers.cloudflare.com/realtime/realtimekit/core - Examples: https://github.com/cloudflare/realtimekit-web-examples - Live demo: https://demo.realtime.cloudflare.com ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. ### Realtime chat / presence / history **Ephemeral** - token mints: 100 per ephemeral tenant lifetime before claim or expiry - published messages: 25000 per ephemeral tenant lifetime before claim or expiry - history reads: 2500 per ephemeral tenant lifetime before claim or expiry - concurrent sockets: 5 max at once - requests: 30 per minute - token mints: 5 per minute **Claimed Free** - concurrent sockets: 50 max at once - requests: 120 per minute - token mints: 30 per minute - token mints: 5000 per month - published messages: 500000 per month - history reads: 25000 per month **Claimed Plus** - concurrent sockets: 500 max at once - requests: 600 per minute - token mints: 150 per minute - token mints: 50000 per month - published messages: 5000000 per month - history reads: 250000 per month **Claimed Pro** - concurrent sockets: 2500 max at once - requests: 3000 per minute - token mints: 750 per minute - token mints: 250000 per month - published messages: 25000000 per month - history reads: 1250000 per month ### RealtimeKit media **Ephemeral** - participant tokens: 10 per ephemeral tenant lifetime before claim or expiry **Claimed Free** - No bucket cap is published for this tier; this surface is fluid-only after any tier-gating check. **Claimed Plus** - No bucket cap is published for this tier; this surface is fluid-only after any tier-gating check. **Claimed Pro** - No bucket cap is published for this tier; this surface is fluid-only after any tier-gating check. ### Notes - RealtimeKit media transport and claimed participant usage are not published hard buckets at launch. Ephemeral RealtimeKit participant-token issuance is capped to keep abandoned tenants bounded. --- # Steel Browser Steel Browser runs short-lived browser automation through Cohesivity. Every tenant may provision it without an experimental access grant; actual use remains subject to the returned readiness, tenant limits, and fleet guards. The internal `offering:browser` policy key is ignored if an operator lists that scope. Cohesivity manages the Steel credentials, project organization, and provider connection URLs. The production integration uses Steel.dev's **published Scale plan** without a custom SLA or DPA: **$250/month** with **$100 monthly usage credit**, **$0.08/hour** for browser sessions, **$5 per 1,000** Browser Tool calls, one-hour maximum sessions, and up to 14 days of provider-side data retention. Deployments fail closed with `503 browser_not_configured` unless the required server key and stable ephemeral-admission HMAC secret are present. The current surface includes only **standard datacenter** sessions controlled over CDP and one stateless Browser Tool: scrape. Steel credentials, identifiers, viewers, debug links, and connection URLs stay internal. Tenant responses contain only Cohesivity URLs and safe local session state. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/steel-browser \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/steel-browser \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. The canonical provisioning route is `POST https://cohesivity.ai/api/resources/steel-browser`. It performs ephemeral identity admission before marking the resource active and returns Cohesivity-owned `edge_url`, `sessions_url`, `tools_url`, `docs_url`, `session_limits`, and `admission` fields under `/edge/steel-browser` and `/offerings/steel-browser`. Read `admission.state`, `admission.capabilities.sessions`, and `admission.capabilities.scrape`: each is `ready` or `limited`, with remaining shared capacity and `reset_at` included for ephemeral tenants. The readiness response never returns a raw IP, identity hash, Steel key, project identifier, provider session identifier, viewer, debug URL, or raw provider WebSocket URL. The older `browser` resource name, `/api/resources/browser`, `/edge/browser/*`, and `/offerings/browser` remain compatibility aliases backed by the same internal resource, policy, usage, session, and cleanup state. ## Sessions > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ### Create curl -s -X POST https://cohesivity.ai/edge/steel-browser/sessions?key= \ -H "Content-Type: application/json" \ -H "Idempotency-Key: browser-session-job-42" \ -d '{}' Only `timeout_seconds` and `inactivity_timeout_seconds` are accepted, and both are optional. Omit them as shown to use plan-safe defaults. Maximum/default hard and inactivity timeouts are **Ephemeral: 300/60 seconds**, **Claimed Free: 900/120 seconds**, **Claimed Plus: 1,800/300 seconds**, and **Claimed Pro: 3,600/600 seconds**. Inactivity must also be lower than the hard timeout. The provisioning response returns the applicable values in `session_limits`. `Idempotency-Key` is required and must contain 1–255 visible ASCII characters. Cohesivity stores only its SHA-256 hashed value plus the normalized timeout request; the raw key is never stored, queried, echoed, or logged. Reusing the key with the same request returns the existing safe local projection without another reservation or provider call, including while creation is in progress or after termination. Reusing it with changed fields returns `409`. The maximum requested duration is reserved in whole minutes before a session starts; terminal settlement keeps only actual provider-rounded minutes and returns unused reserved minutes plus the concurrency slot exactly once. ### List and inspect - `GET https://cohesivity.ai/edge/steel-browser/sessions?key=` lists safe local rows owned by the authenticated tenant. - `GET https://cohesivity.ai/edge/steel-browser/sessions/:id?key=` returns one safe local projection. - Neither route performs an organization-wide provider listing. Tenant ownership comes from Cohesivity auth and the local `tenant_id` mapping, never an upstream project or namespace. ### Connect over CDP 1. `POST https://cohesivity.ai/edge/steel-browser/sessions/:id/connect-token?key=`. 2. Connect to the returned Cohesivity URL: `wss://cohesivity.ai/edge/steel-browser/sessions/:id/cdp?token=`. The connect token is tenant-bound, session-bound, **60-second**, and **one-use**. Cohesivity retains at most one live connect token per session: minting another atomically replaces and invalidates the previous row. The WebSocket URL contains no application key. On upgrade Cohesivity consumes the token atomically, re-reads fresh tenant state and local ownership, opens CDP server-side, and relays opaque frames. A server-owned **8 MiB** default message ceiling applies independently in both directions; an oversized message closes both sockets with code `1009` and schedules exact-session release. Close, error, and hard timeout also release and settle the exact session. ### Release `DELETE https://cohesivity.ai/edge/steel-browser/sessions/:id?key=` explicitly releases that one session and is idempotent. Teardown and lifecycle maintenance also release sessions one by one; there is no account-wide release operation. ## Steel Browser scrape > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. - `POST https://cohesivity.ai/edge/steel-browser/tools/scrape?key=` — body `{ "url":"https://example.com", "format":["markdown"], "delay":0 }`. Every scrape reserves one `tool_calls` unit before dispatch and revokes it if the operation or response validation fails. Cohesivity forces standard networking with proxies disabled and sends `screenshot:false` plus `pdf:false`. Syntactic checks reject obvious loopback, private, link-local, metadata, credential-bearing, non-HTTP(S), and IP-literal targets before dispatch. Request streams are read incrementally and canceled as soon as the byte ceiling is crossed, including when `Content-Length` is absent or understated; responses are also size-capped. Identifier/debug/viewer/WebSocket fields are removed from JSON responses. **Hosted artifacts are unavailable:** the upstream convenience screenshot and PDF tools return hosted objects documented as public and durable, while no supported exact-object deletion or retention contract has been verified for those outputs. Their Cohesivity routes return `404 browser_tool_not_found` before quota reservation or provider dispatch. Raw CDP sessions remain available: call `Page.captureScreenshot` or `Page.printToPDF` and receive the bytes directly through the private connection. Convenience screenshot/PDF endpoints remain a follow-up requiring private artifact delivery plus verified upstream deletion/retention semantics. Adding another operator grant around known-public artifacts is not an acceptable substitute. **Network security boundary:** those top-level URL checks are defense in depth, not complete gateway network isolation. Cohesivity does not resolve arbitrary DNS names to prove every destination remains public, detect DNS rebinding, inspect page subresources, or inspect navigation carried in opaque raw CDP frames, and the public provider documentation does not promise those controls. Cohesivity accepts that residual network risk. Tenants are prohibited from targeting private, link-local, or metadata destinations; Cohesivity also retains low fleet/provider-cost ceilings, exact-session cleanup, and an operator kill switch, but those controls do not turn opaque CDP into network enforcement. ## Explicitly unavailable Stateless screenshot/PDF convenience tools, profiles, persisted profiles, auth contexts, cookies supplied as credentials, managed proxies, bring-your-own proxies, CAPTCHA solving, extensions, custom executables, custom launch arguments, files, downloads, download persistence, raw viewers, and premium provider fields are not implemented. Passing an unsupported scrape field returns `400 unsupported_browser_fields`; unavailable tools return `404 browser_tool_not_found`. ## Fluid overflow Claimed accounts may buy overflow in the existing small-block model: **20 browser minutes for $0.05** ($0.15/hour) and **50 browser scrape calls for $0.50** ($0.01/call). Blocks remain under $1. Ephemeral tenants cannot buy overflow. These prices use the public Scale rate of $0.08/browser-hour and $5 per 1,000 scrape calls; the Launch plan's 15-minute maximum does not support the published Plus/Pro surface. ## Fleet guard Database-backed global guards default to 8 active sessions overall, at most 5 ephemeral sessions, 15 browser scrape calls per minute, and a durable **$5 per UTC day** provider-cost safety ceiling at the contracted assumptions above. Session timeout reservations and terminal provider durations are **rounded up to whole provider minutes** before applying the hourly rate, capped by the reserved minute cost; undispatched and confirmed create-not-found paths reconcile to zero. The cost counter reserves atomically before provider dispatch and reconciles failed pre-provider session paths plus terminal session duration; it is an operator kill switch, **not customer billing**. Capacity responses are `429` or `503` with `Retry-After`; Cohesivity never silently overbooks. Ephemeral provisioning atomically records an opaque HMAC of the stored authoritative exact genesis IPv4 address or IPv6 `/64` plus bounded network-origin signals. Tenant ids mapped to one identity share an anchored 24-hour aggregate budget of **30 browser minutes**, **9 session starts**, **9 scrape calls**, and **3 concurrent sessions**. This bounds tenant churn without rejecting a fresh tenant merely because another tenant id used the same egress. Each tenant still has its stricter 10-minute, 3-start, 3-scrape, and 1-concurrent lifetime caps, and claimed accounts bypass identity budgeting. Identity-limit responses return `browser_ephemeral_identity_usage_limit`, the exhausted metric, current/requested/limit values, `retry_at`, and a `claim_tenant` remediation. Missing origin or HMAC configuration fails closed during provisioning before the resource is marked active. The tables store no additional raw IP, User-Agent, key, or token; opaque mapping and counter history survives tenant and resource teardown and is removed after 30 days. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - browser minutes: 10 per ephemeral tenant lifetime before claim or expiry - browser scrape calls: 3 per ephemeral tenant lifetime before claim or expiry - session starts: 3 per ephemeral tenant lifetime before claim or expiry - concurrent sessions: 1 max at once - session starts: 1 per minute - browser scrape calls: 1 per minute **Claimed Free** - concurrent sessions: 1 max at once - session starts: 2 per minute - browser scrape calls: 2 per minute - session starts: 20 per day - browser minutes: 60 per month - browser scrape calls: 20 per month **Claimed Plus** - concurrent sessions: 2 max at once - session starts: 5 per minute - browser scrape calls: 5 per minute - session starts: 100 per day - browser minutes: 600 per month - browser scrape calls: 200 per month **Claimed Pro** - concurrent sessions: 5 max at once - session starts: 10 per minute - browser scrape calls: 10 per minute - session starts: 500 per day - browser minutes: 3000 per month - browser scrape calls: 1000 per month ### Notes - Steel Browser is available to every tenant and does not require an experimental access grant. It uses Steel.dev's published Scale plan with no custom SLA or DPA; that plan advertises up to 14 days of provider-side data retention. - The provider does not publish an enforceable guarantee against private/link-local/metadata egress across DNS rebinding and subresources. Cohesivity accepts that residual network risk and bounds it with top-level target validation, low fleet and provider-cost ceilings, exact-session cleanup, and an operator kill switch. - The current surface is standard-datacenter CDP sessions plus one-shot scrape. Scrape always sends `screenshot:false` and `pdf:false`; every plan uses the same scrape-only tool surface. - Convenience screenshot/PDF endpoints remain unavailable because their upstream outputs are public and durable and exact deletion/retention semantics are not verified. Use `Page.captureScreenshot` or `Page.printToPDF` over the private CDP connection instead. - A durable provider-cost safety ceiling defaults to $5 per UTC day using Scale assumptions of $0.08/browser-hour, billed by each started minute rounded up, and $5 per 1,000 scrape calls. This is an operator kill switch, not customer billing. - Ephemeral Steel Browser provisioning records an opaque exact-IP-derived identity and returns readiness plus session limits. Tenant ids sharing that identity consume one 24-hour aggregate budget of 30 browser minutes, 9 session starts, 9 scrape calls, and 3 concurrent sessions instead of blocking one another merely because another tenant id was seen. Claimed accounts are unaffected. - Profiles, auth contexts, credentials, managed or bring-your-own proxies, CAPTCHA solving, extensions, custom executables or launch arguments, file/download persistence, raw viewers, and provider-specific fields are rejected. --- # Inbox A purpose-built email inbox for agents, backed by Cloudflare Email Service. Every active tenant can provision one inbox with the deterministic canonical address `@inbox.cohesivity.app`. Account owners can inspect claimed projects in a read-only dashboard viewer; composing and mutation remain agent/API work, with no SMTP or IMAP surface. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/inbox \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/inbox \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. > **Ephemeral contract.** The 72-hour tenant gets only its canonical address, send/receive/list/read/reply/delete, one recipient per outbound message, and five outbound emails total. Vanity and webhooks unlock after claim. There is no per-minute Inbox send cap. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Storage - Provisioning Inbox ensures a shared tenant Neon project exists and returns the compatibility object `postgres: { required, auto_provisioned, schema }`; `postgres.auto_provisioned` reports whether that internal project was created by this call. This storage dependency does not provision or grant the public `postgres` resource; `/edge/postgres` remains unavailable until that offering is provisioned separately. - Normalized messages live under the private `coh_inbox.messages` schema. It is not accessible through `/edge/postgres`: that endpoint uses a dedicated public role restricted to the `public` schema, while Cohesivity keeps owner access for reserved `coh_*` schemas. - Complete inbound MIME, including attachments, remains in private Cohesivity R2. Attachment bytes are available only through the authenticated Inbox attachment endpoint. - Inbox, public Postgres, social-login event history, and realtime history can reuse the same project without granting one another's API surfaces. Inbox teardown drops only `coh_inbox`; the Neon project is deleted only when the last dependent is removed. ## Addresses and Vanity - Canonical address: `@inbox.cohesivity.app`. It remains an alias while the Inbox is active. - Claimed tenants may add one optional vanity: check with `GET https://cohesivity.ai/api/vanity/check?name=acme`, then claim once with `POST https://cohesivity.ai/api/vanity` and `{ "name": "acme" }` using the management key. The Inbox address becomes `acme@inbox.cohesivity.app`. - The same vanity is shared with hosting as `acme.cohesivity.app`. Inbox and hosting inherit whichever claim was created first. - A vanity is immutable: it cannot be renamed, released, or transferred. Reposting the same name is idempotent; a different name or any DELETE returns `409 vanity_immutable`. Account deletion tombstones the canonical and vanity identities so neither can ever be reassigned. ## Send ``` POST https://cohesivity.ai/edge/inbox/messages?key= Idempotency-Key: order-123-confirmation Content-Type: application/json { "to": ["person@example.com"], "cc": [], "bcc": [], "subject": "Hello", "text": "Plain text body", "html": "

Optional HTML body

" } ``` `Idempotency-Key` is required and may be 1-128 characters. Reusing it returns the original message without another provider call. The API ignores any caller-supplied `from`; Cohesivity forces the active tenant vanity address, or the canonical address when no vanity exists. `to`, `cc`, and `bcc` may be a string or string array, with at most 10 unique recipients combined. At least one of `text` or `html` is required. Outbound attachments are not included. A successful send returns HTTP 202 with `message.status: "accepted"` and the Cloudflare message id. `accepted` means Cloudflare accepted the message, not that the destination mailbox delivered it. If Cloudflare returns an ambiguous internal failure, Cohesivity stores `unknown`; reuse the same idempotency key and do not retry with a new one. ## Reply Reply to an inbound message through the same send endpoint. Cohesivity derives the recipient, sender alias, thread, `In-Reply-To`, and `References`; callers cannot override them. ``` POST https://cohesivity.ai/edge/inbox/messages?key= Idempotency-Key: reply-message-42 Content-Type: application/json { "reply_to_message_id": "", "text": "Thanks — received." } ``` Replies target inbound messages only. `subject` is optional for replies; absent values inherit `Re: `. ## Read and Delete - List newest first: `GET https://cohesivity.ai/edge/inbox/messages?key=&limit=50`. `limit` is 1-100. Continue with the opaque `next_cursor` as `cursor=...`; optionally filter one conversation with `thread_id=`. - Get one: `GET https://cohesivity.ai/edge/inbox/messages/?key=`. - Download an inbound attachment: `GET https://cohesivity.ai/edge/inbox/messages//attachments/?key=`. Attachment indexes come from the message `attachments` array. Attachments and HTML are untrusted email content; do not execute or render them without your own safety controls. - Delete one: `DELETE https://cohesivity.ai/edge/inbox/messages/?key=` → HTTP 204. This removes normalized data and private raw MIME immediately and decrements live storage. Messages expose `received`, `pending`, `accepted`, `failed`, or `unknown` status. Inbound messages are `received`. List/get never claim final outbound delivery. ## Inbound Webhook Claimed Inbox can send one signed `message.received` event after an inbound message and its durable tenant-Postgres outbox row exist. Ephemeral tenants cannot configure a webhook. Provisioning a claimed Inbox accepts optional `{ "webhook_url": "https://agent.example.com/hooks/inbox" }`; the provision response includes `webhook.secret` once. An existing claimed Inbox uses these management-key endpoints: - `GET https://cohesivity.ai/api/inbox/webhook` returns the active URL and event, never the secret. - `PUT https://cohesivity.ai/api/inbox/webhook` with `{ "url": "https://agent.example.com/hooks/inbox" }` creates or replaces the URL. A newly created configuration returns its secret once; URL replacement preserves the secret. - `POST https://cohesivity.ai/api/inbox/webhook/rotate-secret` returns a replacement secret once and invalidates queued events signed for the older configuration. - `DELETE https://cohesivity.ai/api/inbox/webhook` returns HTTP 204 and prevents queued events for that configuration from being delivered. Delivery is an HTTP POST with `Content-Type: application/json` and body `{ "type": "message.received", "message_id": "" }`. The headers are `webhook-id`, `webhook-timestamp`, and `webhook-signature`. The signature is `v1,` over `..` using the `whsec_...` secret. A receiver can reject timestamps outside its own tolerance before verifying the HMAC. Delivery is at-least-once. `webhook-id` is stable for a message, so receivers can de-duplicate it. A 2xx response acknowledges the event; redirects, timeouts, and other status codes retry at approximately 1, 5, 20, and 60 minutes, then move to Cohesivity's dead-letter queue after the fifth failed attempt. Inbox exposes no delivery-history or replay API; `GET /edge/inbox/messages` is the recovery feed. Webhook URLs must be public HTTPS URLs without embedded credentials. ## Limits and Retention - Inbound and outbound message ceiling: 5 MiB each. Cloudflare accepts larger inbound mail, but Cohesivity rejects it at 5 MiB to keep the API symmetric and bounded. - Outbound recipients: 1 per message while ephemeral; 10 total across to/cc/bcc after claim. - Private raw-MIME storage: Free 100 MB, Plus 1 GB, Pro 5 GB per inbox. Mail is rejected once the active inbox reaches its storage ceiling. - Retention: fixed 30 days for every tier. The lifecycle sweep removes normalized rows and raw MIME in bounded batches. - Ephemeral tenants get five outbound emails and five recipients for the entire tenant lifetime with no Inbox per-minute cap. Claimed monthly and per-minute buckets are shown below. Inbound mail does not consume outbound buckets. ## Error Contract - `400 idempotency_key_required`, `invalid_json`, `invalid_cursor`, or `invalid_thread_id`: repair the request; do not retry it unchanged. - `403 tenant_must_be_claimed`: claim the tenant before configuring Inbox vanity or a webhook. - `404 message_not_found`, `attachment_not_found`, or `reply_target_not_found`: the target does not exist for this tenant. - `409 inbox_not_active`, `inbox_storage_migrating`, or `reply_target_invalid`: Inbox must be active and on its tenant Postgres data plane; replies target inbound messages. - `413 message_too_large`: reduce the text/HTML payload below the 5 MiB ceiling. - `422 invalid_recipients`, `too_many_recipients`, `invalid_subject`, `invalid_content`, `invalid_reply_to_message_id`, or `invalid_webhook_url`: the submitted message or webhook URL is invalid. - `429`: a Cohesivity per-minute/monthly bucket or Cloudflare account send limit denied the call. Respect `Retry-After` when present. - `502 send_failed`: Cloudflare definitively rejected the send. `502 send_outcome_unknown` is ambiguous; retry only with the same `Idempotency-Key`. - `503 inbox_storage_not_configured` or `email_sending_not_configured`: Cohesivity provider configuration is unavailable; retry later. ## Dashboard Viewer Signed-in account owners can open `https://cohesivity.ai/account/inbox` to search and inspect projected sent/received message content across their claimed Inbox projects. The viewer renders plain text only, blocks HTML formatting and remote content, shows attachment metadata without downloads, and exposes no send, reply, forward, delete, or configuration controls. ## Deliberately Not Included No dashboard composer or message mutation controls, drafts, webhook delivery-history/replay API, SMTP, IMAP, custom mail domains, batch sends, arbitrary headers, arbitrary sender addresses, outbound attachments, or per-tenant Cloudflare routing rules. Cloudflare has one operational catch-all for `inbox.cohesivity.app`; provisioning and deprovisioning only change Cohesivity data. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - emails: 5 per ephemeral tenant lifetime before claim or expiry - recipients: 5 per ephemeral tenant lifetime before claim or expiry **Claimed Free** - requests: 3 per minute - emails: 100 per month - recipients: 200 per month **Claimed Plus** - requests: 15 per minute - emails: 1000 per month - recipients: 2000 per month **Claimed Pro** - requests: 60 per minute - emails: 5000 per month - recipients: 10000 per month ### Notes - Ephemeral tenants get the canonical @inbox.cohesivity.app address, send/receive/list/read/reply/delete, one recipient per outbound message, and five outbound emails across the tenant lifetime. Ephemeral Inbox has no vanity address or webhook. - Claimed Inbox messages are capped at 5 MiB and each outbound send supports at most 10 total to/cc/bcc recipients. Private raw-MIME storage is capped at 100 MB on Free, 1 GB on Plus, and 5 GB on Pro; all tiers use fixed 30-day retention. - Outbound status `accepted` means Cloudflare Email Service accepted the message. It is not a final-delivery guarantee. --- # Railway Hosting Primary public hosting option for Cohesivity tenants. Cohesivity creates and owns the Railway project/service/environment/domain, and tenant agents deploy by uploading source files directly to Cohesivity. No GitHub repo, Railway CLI install, or tenant Railway credentials are required. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/railway-hosting \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/railway-hosting \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## What Happens on Provision - Creates a Railway project `coh-` under Cohesivity-owned Railway credentials - Creates an empty `web` service and `production` environment - Attaches an existing tenant vanity, or otherwise the automatic `.cohesivity.app` host, and returns it as the single `deployment_url` - Applies Cohesivity tier compute caps to the Railway service instance and stores the applied cap/status metadata in Cohesivity - Stores Railway project, service, environment, domain, compute cap, and last deployment metadata in Cohesivity The provisioning response is intentionally small: `success`, `resource`, `status`, `deployment_url`, `compute_limits`, `already_provisioned`, `deploy_endpoint`, and `docs_url`. It does not return provider ids, provider domains, generated scripts, or repeated next-step prose. ## Compute Caps Railway topology and CPU/RAM/replica/sleep limits are Cohesivity-managed and not tenant-callable in v1. The service is always `web` and the environment is always `production`; bodies containing `service_name` or `environment_name` return `400 railway_topology_managed`. Re-provisioning `railway-hosting` idempotently reapplies the active tier cap, and claiming an ephemeral tenant reconciles its Railway cap to the claimed account tier in the background. - Ephemeral: 0.5 vCPU, 1 GB RAM, 1 replica, sleep enabled - Claimed Free: 0.5 vCPU, 1 GB RAM, 1 replica, sleep enabled - Plus: 1 vCPU, 2 GB RAM, 1 replica, sleep disabled - Pro: 2 vCPU, 4 GB RAM, 1 replica, sleep disabled ## Runtime metering A minute-level poller reads Railway `usage` for the UTC month (`CPU_USAGE`, `MEMORY_USAGE_GB`, `NETWORK_TX_GB`) and records deltas as `vcpu_seconds`, `memory_gb_seconds`, and `egress_bytes`. Railway reports CPU and memory in its per-minute billing units, which Cohesivity normalizes to seconds on ingest; egress is a volume and is converted from GB to bytes. Claimed overage auto-purchases fluid blocks at Railway's published rates plus 10%. When fluid is exhausted the offering pauses; the poller then sets `sleepApplication: true`, and after a grace window (immediate for Ephemeral/Free, 24h for Plus/Pro) stops the running deployment. Stopping halts compute billing without destroying anything — the service, its source, variables, domains, and TLS certificate all survive. Hosting is never deleted for non-payment. Topup or a new monthly grant redeploys the stopped deployment, restores tier compute caps, and resumes the offering. Metering starts when a project first becomes observable to the poller: a project provisioned mid-month is metered from provisioning onward, and consumption that predates metering is never billed retroactively. ## How to Deploy POST `multipart/form-data` to `/api/railway/deploy`: each project file is a `files` part. The endpoint validates paths/count/size, creates the gzip tarball Railway expects, and uploads it to Railway server-side with Cohesivity-owned auth. Append `?wait=ready` to block until Railway reports SUCCESS and the public application route answers without a 5xx response for an additional stability window. This application probe catches immediate crashes and Railway restart loops even when Railway continues to label the deployment SUCCESS. If the 60-second wait expires while the provider or application is not ready, the endpoint returns HTTP 202 with `success: false`, `wait_timed_out: true`, and `readiness_state: "pending"|"unhealthy"`; poll the deployment detail route. A normal response contains only `success`, `deployment_id`, `state`, the Cohesivity `deployment_url`, and the Cohesivity `logs_url`. Railway provider URLs and dashboard links remain internal. ### curl ``` curl -F "files=@package.json" \ -F "files=@src/index.js" \ -H "Authorization: Bearer " \ https://cohesivity.ai/api/railway/deploy?wait=ready ``` ### JSON shape ``` POST https://cohesivity.ai/api/railway/deploy?wait=ready Authorization: Bearer Content-Type: application/json { "files": [ { "file": "package.json", "data": "{...}" }, { "file": "src/index.js", "data": "console.log(process.env.PORT)" }, { "file": "public/logo.png", "data": "", "encoding": "base64" } ] } ``` ### Response ```json { "success": true, "deployment_id": "", "state": "SUCCESS", "deployment_url": "https://.cohesivity.app", "logs_url": "https://cohesivity.ai/api/railway/deployments//logs" } ``` ## Deployment Management All endpoints require `Authorization: Bearer `. - **List deployments:** `GET https://cohesivity.ai/api/railway/deployments` returns deployment ids, one `state`, the Cohesivity `deployment_url`, the Cohesivity `logs_url`, and timestamps - **Deployment details:** `GET https://cohesivity.ai/api/railway/deployments/:id` returns the same public fields plus a curated `last_error` for terminal failures - **Deployment logs:** `GET https://cohesivity.ai/api/railway/deployments/:id/logs?type=build|runtime|http` (Cohesivity proxies Railway build, deployment, and HTTP log queries; build/runtime severity is normalized from message prefixes, with a differing raw value preserved as `provider_severity`) - **Domain/status detail:** `GET https://cohesivity.ai/api/railway/domain` or `GET https://cohesivity.ai/api/railway/status` ## Environment Variables Set env vars before deploying when your build reads them. Railway does not return decrypted values on list, so list responses include names/metadata only. The account env manager uses Railway for `railway-hosting` tenants. - **List env vars:** `GET https://cohesivity.ai/api/railway/env` - **Upsert env var:** `POST https://cohesivity.ai/api/railway/env` with `{ "key": "MY_SECRET", "value": "abc123" }`. Values are limited to 32,768 UTF-8 bytes. Writes always use `skip_deploys: true`; `skip_deploys: false` returns `400 provider_deploy_not_allowed`, and callers must use the explicit deploy endpoint. - **Delete env var:** `DELETE https://cohesivity.ai/api/railway/env/:key`; a missing key returns `404 env_var_not_found`. Environment responses expose variable names, sealed/write-only state, update timestamps, and create-time fingerprints only. They never include Railway variable or service ids. ## Tenant Vanity (claimed tenants) A tenant may claim one immutable vanity such as `acme`. Hosting uses `acme.cohesivity.app`; an active Inbox uses `acme@inbox.cohesivity.app`. Whichever offering is provisioned second inherits the first offering's existing claim automatically. Cohesivity manages the Railway and Cloudflare bindings, including Railway's required routing CNAME and ownership TXT records; tenants never provide provider tokens. Multi-record Cloudflare changes are transactional: if a later record fails, earlier records are restored or deleted before the Railway domain is rolled back. - **Check availability** (no auth): `GET https://cohesivity.ai/api/vanity/check?name=acme`. - **Get current** (auth): `GET https://cohesivity.ai/api/vanity`. Railway-backed vanities refresh provider verification status before responding and persist the latest verification records/certificate metadata. `verified` becomes true only after both DNS ownership and TLS certificate issuance are complete; the issued certificate is the authoritative readiness signal when Railway's auxiliary DNS flag remains false behind proxied DNS. - **Claim once** (auth): `POST https://cohesivity.ai/api/vanity` with `{ "name": "acme" }`. For Railway hosting, `https://acme.cohesivity.app` becomes the hosting `deployment_url` immediately; no re-provision is required. Reposting `acme` is idempotent. Posting a different name returns `409 vanity_immutable`. - **No release or transfer:** `DELETE https://cohesivity.ai/api/vanity` returns `409 vanity_immutable`. Account deletion leaves a permanent tombstone, so the identity is never reassigned. Legacy `/api/railway/custom-subdomain` remains a compatibility alias for the shared vanity handlers. ## Custom Domains Claimed Railway tenants can attach their own apex domain with `POST /api/railway/domains`. Cohesivity keeps provider ids and managed-DNS bookkeeping internally in `railway_custom_domains`; tenant responses contain only the domain state and actions the caller can take. - **Cohesivity-purchased domain**: `POST https://cohesivity.ai/api/railway/domains` with `{ "domain_name": "yourapp.com", "source": "cohesivity-purchased", "include_www": true }`. Cohesivity attaches the domain, creates the required Name.com DNS records, and polls readiness. Purchased-domain responses never expose provider ids or managed DNS ids. Omitting `source` keeps this default. - **Bring-your-own-domain (BYOD)**: `POST https://cohesivity.ai/api/railway/domains` with `{ "domain_name": "yourapp.com", "source": "byod", "include_www": true }`. Cohesivity returns BYOD `records_to_add` containing only `host`, `type`, `value`, and `ttl`. Add every routing record and every ownership TXT record at the external registrar. The required CNAME value may name Railway because it is DNS configuration, never an app or dashboard URL. - **Status**: `GET https://cohesivity.ai/api/railway/domains/yourapp.com` refreshes current readiness. `verified` is true only when TLS certificates are issued for the apex and requested `www` host; issuance proves ownership even if Railway's auxiliary DNS flag remains false behind proxied DNS. - **Detach**: `DELETE https://cohesivity.ai/api/railway/domains/yourapp.com` removes Cohesivity-created Railway custom domains. Purchased domains also remove only tracked Name.com records; BYOD returns `records_to_remove` for the external registrar. Hosting teardown and account deletion perform the same binding cleanup before deleting the Railway project. A binding that references an already-deleted project is reconciled locally instead of remaining a permanent conflict. - `source` is strict (`byod` or `cohesivity-purchased`) and `include_www` must be a JSON boolean; invalid values return 400 instead of being silently coerced. ## Security Tenant agents never send Railway tokens. The only auth they use is Cohesivity `coh_management_key`; Cohesivity injects Railway auth server-side and records only ownership/metadata needed for lifecycle, status, and teardown. Railway service URLs, project links, topology ids, custom-domain ids, DNS bookkeeping ids, and environment-variable ids are excluded from tenant-facing responses. The sole provider hostname a tenant may see is an actionable BYOD CNAME value that must be copied into external DNS. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - projects: 1 max total - deploys: 20 per ephemeral tenant lifetime before claim or expiry - uploaded bytes: 250 MB per ephemeral tenant lifetime before claim or expiry - vCPU-seconds: 50000 per ephemeral tenant lifetime before claim or expiry - memory GB-seconds: 150000 per ephemeral tenant lifetime before claim or expiry - egress bytes: 2 GB per ephemeral tenant lifetime before claim or expiry - deploys: 3 per minute **Claimed Free** - projects: 10 max total - deploys: 3 per minute - deploys: 60 per month - uploaded bytes: 2 GB per month - vCPU-seconds: 100000 per month - memory GB-seconds: 400000 per month - egress bytes: 5 GB per month **Claimed Plus** - projects: 30 max total - deploys: 10 per minute - deploys: 200 per month - uploaded bytes: 10 GB per month - vCPU-seconds: 400000 per month - memory GB-seconds: 1500000 per month - egress bytes: 50 GB per month **Claimed Pro** - projects: 100 max total - deploys: 10 per minute - deploys: 600 per month - uploaded bytes: 50 GB per month - vCPU-seconds: 1500000 per month - memory GB-seconds: 6000000 per month - egress bytes: 250 GB per month ### Notes - Railway deploys and upload bytes remain Cohesivity-side hard counters. Runtime vCPU-seconds, memory GB-seconds, and egress bytes are polled from Railway each minute into the same counter tables; claimed overage draws from fluid at provider cost plus 10%. When fluid is exhausted the offering pauses and the graduated ladder sleeps then stops the Railway service. --- # Domain Purchase Buys an external domain (e.g. `yourapp.com`) **in the end user's name**. Cohesivity is the reseller, owning the wholesale account at Name.com; the end user is the ICANN registrant of record. Name.com's CORE API backs the v1 implementation. Cohesivity adds a 10% markup over Name.com's retail price, and the user pays in INR via Razorpay. Unlike the rest of the offerings catalog, this is **not** provisioned through `/api/resources`. It's a paid one-shot: agents quote candidates, the human pays through a Razorpay-hosted checkout, and Cohesivity registers the domain server-side after payment captures. ## Lifecycle 1. `POST /api/domains/check`: agent submits up to 20 candidate names; Cohesivity returns availability + price + a 10-minute signed quote token for each available one. 2. **The registrant contact is collected** (full name + email default from the Cohesivity Google-OAuth profile; address1, city, state, zip, country, and phone have no default and are required). 3. `POST /api/domains/purchase`: agent picks one (passing the quote token AND the registrant contact); Cohesivity validates the contact, mints a Razorpay Payment Link, returns a `cohesivity.ai/t/` checkout URL. 4. Human pays via the URL (no Cohesivity sign-in required to pay). 5. Razorpay webhook → Cohesivity re-checks availability, calls Name.com to register **with the user's contact**, inserts a row in `tenant_domains`. If registration fails or the domain was taken in between, the payment is automatically refunded. 6. **Name.com emails the user** to verify their email address. The user must click the link within 15 days or ICANN policy requires their domain to stop resolving. Cohesivity does not send this email: Name.com does, directly. 7. `GET /api/domains` / `GET /api/domains/`: agent inspects what's on file, including the registrar order id, expiry, and `registrant_email`. ## Pricing - Wholesale comes from Name.com's `:checkAvailability` endpoint (USD). - Cohesivity applies a flat **10% markup** on the wholesale USD cost, then converts to INR paise at the configured FX rate (`DOMAIN_USD_INR_RATE_PAISE_PER_CENT`, default 83 paise per USD cent ≈ ₹83/$1). - The user-visible INR price is shown in the `/api/domains/check` response and embedded in the signed quote token. The token expires 10 minutes after issue, and `/api/domains/purchase` rejects an expired one, so a decision that takes longer needs a fresh check. - **Registration term**: most TLDs are 1-year; `.ai` is 2-year minimum per the registry. The `years` field in the check response tells you which; the quoted price covers the full term. - **v1 is one-shot, with no renewals.** Cohesivity disables Name.com's auto-renew right after registration, and the domain expires naturally at the end of the term. A user-billed renewal flow is planned for v2; until then, nothing in the API reminds the user before the domain lapses. ## Supported TLDs Name.com supports 300+ TLDs including `.com`, `.dev`, `.app`, `.net`, `.org`, `.xyz`, `.ai`, `.io`, `.uk`, `.co`. **Not supported in this iteration:** `.in` (Name.com's API drops it from the response, confirmed against both sandbox and production). **Premium / aftermarket domains** are also gated out for v1; check responses surface them with `reason: "premium_not_supported"`. ## Endpoint reference ### Check availability curl -s -X POST https://cohesivity.ai/api/domains/check \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"domains":["yourapp.com","yourapp.dev","yourapp.app"]}' Returns one entry per requested name: { "registrar": "namecom", "currency": "INR", "markup_percent": 10, "quote_validity_ms": 600000, "domains": [ { "name": "yourapp.com", "available": true, "years": 1, "purchase_inr_paise": 118591, "currency": "INR", "quote_token": "eyJh...HS256.eyJ...exp", "quote_valid_until_ms": 1715543700000, "renewal_supported": false }, { "name": "yourapp.dev", "available": false, "reason": "taken" }, { "name": "best.com", "available": false, "reason": "premium_not_supported" } ] } `reason` values when unavailable: `taken`, `tld_unavailable`, `premium_not_supported`, `invalid_format`, `unknown`. ### Purchase The user's ICANN registrant contact is required in the body. firstName / lastName / email default from the Cohesivity Google-OAuth profile when omitted; the address fields and phone have no default. curl -s -X POST https://cohesivity.ai/api/domains/purchase \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "quote_token": "eyJh...HS256.eyJ...exp", "registrant": { "firstName": "Asha", /* optional, defaults to OAuth name */ "lastName": "Patel", /* optional */ "email": "a@example.com", /* optional, defaults to OAuth email */ "phone": "+919876543210", /* REQUIRED, E.164 (dots/spaces/dashes are stripped) */ "address1": "12 MG Road", /* REQUIRED */ "address2": "Apt 4B", /* optional */ "city": "Bangalore", /* REQUIRED */ "state": "Karnataka", /* REQUIRED */ "zip": "560001", /* REQUIRED */ "country": "IN", /* REQUIRED, ISO 3166-1 alpha-2 */ "companyName": "Patel Inc" /* optional */ } }' Returns a Razorpay-backed checkout link and a `wait` blob. The `checkout_url` is what the human uses to pay; no Cohesivity sign-in is required. Cohesivity registers the domain automatically on receipt of the Razorpay `payment_link.paid` webhook. If the registrant contact is missing fields or malformed, the response is `400 invalid_registrant_contact` with per-field errors and no Razorpay link is minted. ### Wait for payment + registration The `/api/domains/purchase` response includes a `wait` blob identical in shape to the topup + subscription wait blobs. The bash one-liner in `wait.command` long-polls `GET /api/wait` (55s per call, looped) until the Razorpay webhook fires and Name.com confirms registration; it's meant to run once the checkout URL has reached the user. Terminal statuses: - `completed`: domain registered. Payload includes `domain_name`, `expires_at`, `registrar_order_id`, `registered_at`. - `denied`: registration failed (availability race after payment, or Name.com 4xx). Payment is auto-refunded. Payload includes `reason: "registration_failed_or_unavailable"`. - `expired`: the 30-minute wait-token TTL elapsed without the human paying; a fresh purchase call is needed. - `pending`: still waiting; the 5×55s loop in `command` handles re-running automatically. ### List domains owned by this tenant curl -s https://cohesivity.ai/api/domains \ -H "Authorization: Bearer " Returns every `tenant_domains` row keyed to the calling tenant, newest-first. The full registrant contact is **not** in the list response (PII redaction); only `registrant_email` is surfaced. ### Get a single domain curl -s https://cohesivity.ai/api/domains/yourapp.com \ -H "Authorization: Bearer " Returns 404 if the domain is not owned by this tenant (whether it doesn't exist or belongs to someone else: the response shape is intentionally the same). ## Common Mistakes - **The 15-day verification email has no reminder built in.** ICANN requires the registrant to confirm their email by clicking a link Name.com sends after registration; if they don't click within 15 days, their domain stops resolving. Nothing in the API prompts the user about this after purchase, so a UX that skips it risks a silent expiry. **Note**: the Name.com sandbox doesn't actually send the verification email (the sandbox is a functional API simulator: registrations, prices, and orders are realistic, but real-world side effects like emails, DNS propagation, and ICANN registry updates are mocked). Production registrations send the real ICANN verification email to the registrant within ~5 minutes. - **Holding a quote token longer than 10 minutes.** The signed token carries an `exp`; `/api/domains/purchase` rejects past it. Re-call `/api/domains/check` for a fresh quote. - **Sending phone in the wrong format.** Name.com's E.164 regex is strict: `^\+[1-9]\d{7,14}$`: no dots, no parens, no spaces. We strip those before validating, but the trailing must be all digits with a `+` shape. `+1.555.123.4567` becomes `+15551234567`; `(555) 123-4567` (no country code) is rejected. - **`.in` isn't supported.** Name.com's API doesn't support it; a check for it returns `tld_unavailable` rather than a quote, so retrying the same TLD in a loop won't succeed. - **Re-trying purchase after webhook success.** If `GET /api/domains/` returns `200`, the domain is registered. A second `POST /api/domains/purchase` for the same domain will reject with `409 already_registered`. The webhook is the authoritative settlement path. - **The domain does not auto-renew.** v1 disables auto-renew on Name.com's side right after registration, so Cohesivity's wholesale balance isn't silently consumed. The user needs to rebuy before the term ends; the API surfaces the expiry date but not a renewal reminder. ## Limitations (v1) - `.in` is silently dropped by Name.com's API. Surfaces as `tld_unavailable`. - Premium / aftermarket domains are refused at `/check`, surfacing as `premium_not_supported`. - **No renewals.** Auto-renew is disabled at Name.com post-register; domains expire at the end of the term. A user-billed renewal flow is on the v2 roadmap. - Transfers (in and out) are not yet exposed through Cohesivity: handled manually via the Name.com dashboard. - The verification email goes to the user from Name.com directly. We don't pre-verify on their behalf (would require approved-reseller status at Name.com plus 2-year log retention: out of v1 scope). ## DNS records The DNS endpoints manage A / AAAA / CNAME / MX / TXT / etc. records on a domain you own. All four require a claimed tenant and ownership of the domain (the `tenant_domains` row must exist for the calling tenant with `status=active`). **Supported types**: `A`, `AAAA`, `ANAME`, `CNAME`, `MX`, `NS`, `SRV`, `TXT`, `CAA`. **TTL** must be 300–86400 seconds; default 300. **priority** is required for `MX` and `SRV` records. ### List records curl -s https://cohesivity.ai/api/domains/yourapp.com/dns \ -H "Authorization: Bearer " Returns `{ tenant_id, domain_name, records: [...] }`: full Name.com record shape per entry (id, host, type, answer, ttl, priority). ### Create record curl -s -X POST https://cohesivity.ai/api/domains/yourapp.com/dns \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"host":"www","type":"CNAME","answer":"yourapp.com.","ttl":300}' Returns `201` with the created record (includes the assigned `id`: keep it; you need it for update/delete). ### Update record curl -s -X PUT https://cohesivity.ai/api/domains/yourapp.com/dns/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"host":"@","type":"A","answer":"192.0.2.42","ttl":300}' Full record body, same shape as create. Returns the updated record. ### Delete record curl -s -X DELETE https://cohesivity.ai/api/domains/yourapp.com/dns/ \ -H "Authorization: Bearer " Returns `{ deleted: true, record_id }`. ### DNS common mistakes - **Apex records**: `"host": "@"` is the correct value, not `""` or the bare domain name; Name.com rejects the latter two. - **Wildcards**: `"host": "*"` covers `*.yourapp.com`; `"host": "*.sub"` covers `*.sub.yourapp.com`. - **TTL minimum** is 300 seconds. Anything lower is rejected. - **MX / SRV priority** is required and must be 0–65535. Typical MX uses 10/20/30 for primary/secondary/tertiary. - **Propagation**: DNS is eventually consistent at recursive resolvers. Allow ~5 minutes after a write before testing with `dig`. ## Nameservers Repoints a Cohesivity-purchased domain to a DNS host other than Name.com (e.g. Cloudflare DNS, an external registrar's NS, your own resolver), via two endpoints: read and write. Setting nameservers transfers DNS authority off Name.com, so the records under `/api/domains//dns` stop being authoritative until/unless you repoint back to `dns1.name.com`/`dns2.name.com`. ### Get current nameservers curl -s https://cohesivity.ai/api/domains/yourapp.com/nameservers \ -H "Authorization: Bearer " Returns `{ tenant_id, domain_name, nameservers: [...], propagation_hint }`. ### Set nameservers curl -s -X POST https://cohesivity.ai/api/domains/yourapp.com/nameservers \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"nameservers":["ns1.dns.cloudflare.com","ns2.dns.cloudflare.com"]}' Body: a non-empty array of valid hostnames (max 13 entries, per registry policy). Idempotent: sending the same list again is a no-op at Name.com. Each entry is validated as a hostname; invalid entries surface as `400 invalid_nameservers` with per-index errors. **Propagation**: nameserver changes usually take 5–30 minutes; some recursive resolvers may cache for up to 48 hours. ## One-call Railway attach (`/api/railway/domains`) For tenants using the primary `railway-hosting` path, attach either a Cohesivity-purchased domain or a bring-your-own-domain to the tenant's Cohesivity-owned Railway service. Tenants do not supply Railway credentials, use Railway CLI, or connect GitHub; Cohesivity uses its own Railway auth and stores the binding in `railway_custom_domains`. - **`source: "cohesivity-purchased"`** (default): Cohesivity owns the DNS relationship at Name.com. We attach the host, create the required DNS records, poll readiness for ~10 s, and return the public domain state. Provider and managed-DNS ids stay internal. - **`source: "byod"`**: bring-your-own-domain. Your DNS is at an external registrar. We attach the host and return `records_to_add` for the human to configure; Cohesivity never writes external registrar state. Same caller account scoping for both paths: a domain in tenant A can be attached to tenant B's Railway service if both tenants are owned by the same Cohesivity account and tenant B has active `railway-hosting`. ### Attach (Cohesivity-purchased, Railway) curl -s -X POST https://cohesivity.ai/api/railway/domains \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"domain_name":"yourapp.com","include_www":true}' Returns `201` on fresh attach or `200 idempotent:true` on re-attach. The response contains `domain_name`, `source`, `include_www`, and `verified`; DNS and SSL hints appear only while the domain is pending. Provider topology and DNS record ids stay internal. ### Attach (bring-your-own-domain, Railway) curl -s -X POST https://cohesivity.ai/api/railway/domains \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"domain_name":"yourapp.com","source":"byod","include_www":true}' Returns `records_to_add` with only actionable DNS rows (`host`, `type`, `value`, `ttl`) to create at the external registrar. The list includes both routing `A`/`CNAME` rows and ownership `TXT` rows when required. A Railway hostname may appear only as the required DNS value, never as the application URL. ### Status + detach (Railway) curl -s https://cohesivity.ai/api/railway/domains/yourapp.com \ -H "Authorization: Bearer " curl -s -X DELETE https://cohesivity.ai/api/railway/domains/yourapp.com \ -H "Authorization: Bearer " `GET` refreshes readiness and returns the same small public domain shape. `verified` requires issued TLS certificates for every attached host; certificate issuance is authoritative when Railway's auxiliary DNS flag remains false behind proxied DNS. `DELETE` removes the internal provider bindings; purchased domains also remove only Cohesivity-created Name.com records, while BYOD returns `records_to_remove` for the human to clean up externally. ### Railway attach common errors - **`409 no_railway_project`**: the target tenant does not have active `railway-hosting`; provision it with `POST /api/resources/railway-hosting`. - **`409 domain_already_attached`**: the domain is already attached to another Railway service on this account. Detach that binding first. - **`409 domain_already_attached_other_account`**: another Cohesivity account already has a Railway binding for this domain. - **`409 domain_is_cohesivity_purchased`** (BYOD path only): omit `source` and use the purchased path because this account owns the domain through Cohesivity. - **`404 domain_not_owned`** (purchased path only): the domain is not an active Cohesivity-purchased domain on this account; use `source:"byod"` if it lives at an external registrar. - **`403 cross_account_forbidden`**: the body's `tenant_id` resolves to a different Cohesivity account. Domains attach to Railway services. Tenants on a sunset hosting path keep their existing domain bindings and the endpoints that manage them; nothing here changes for them. ## Auth All endpoints require a `coh_management_key` (`Authorization: Bearer ...`). **Lifecycle gate**: `/api/domains/check` is open to ephemeral tenants, since browsing pricing is harmless and useful as a "should I claim before I commit?" signal. Every other domain endpoint (purchase, list, get, the four DNS routes) requires a **claimed** tenant. Ephemeral tenants auto-terminate at the 72-hour claim window and the `tenant_domains` row would CASCADE-delete on termination, but the Name.com registration would survive: the user would have paid for a domain with no remaining record of it. Gating purchase and management to claimed tenants closes that hole. A `403 tenant_must_be_claimed` response means the tenant needs claiming first: mint an approval link with `POST /api/claim/url` and hand it to the user. --- # Object Storage Blob store backed by Cloudflare R2. Upload via the edge proxy (authenticated), read directly via permanent public URL (no key). ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/object-storage \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/object-storage \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Common Mistakes - **Using the original filename for download/delete.** Upload adds a random hash to the filename for security (e.g., `cat.jpg` → `cat-a3f8b2c1d4e5f6g7.jpg`), so the `path` from the upload response, not the original filename, is the value to persist and reuse. - **Putting `?key=` URLs in browser-visible HTML.** An `` tag exposes the application key to anyone who views the page source. The `url` from the upload response is the safe alternative: a permanent public link with no key. - **Trying to GET via the edge proxy.** Reads go directly through the `url` returned by upload; the edge proxy only handles PUT (upload) and DELETE. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. **CORS:** edge endpoints set `Access-Control-Allow-Origin: *` so a server-side proxy can be hit from any origin (the proxy itself stays on your server tier; only the response is browser-visible). **Upload response:** `{ ok, path, url, requested_path, path_was_mutated }`. `requested_path` echoes the raw input you sent; `path` is the stored hashed key (which is what you must persist for DELETE / DB indexing). `path_was_mutated` is always `true` for PUT today because every upload gets a random hash suffix: the field is wire-stable for callers that want a single check rather than comparing strings. ## How It Works Object storage is a blob store: write by path, read by path. 1. **Upload** goes through the edge proxy (authenticated): `PUT /edge/object-storage/?key=` 2. The response returns `{ok, path, url}`: `path` has a random hash suffix, `url` is a permanent public link 3. **`path`** persists in the database alongside the related data, forming the file index 4. **Reads** use the `url` directly (e.g., ``), served from Cloudflare's CDN with no key and no proxy 5. **Delete** goes through the edge proxy (authenticated): `DELETE /edge/object-storage/?key=` ## Upload PUT https://cohesivity.ai/edge/object-storage/?key= Content-Type: image/jpeg Body: Response: { "ok": true, "path": "photos/cat-a3f8b2c1d4e5f6g7.jpg", "url": "https://storage.cohesivity.ai//photos/cat-a3f8b2c1d4e5f6g7.jpg", "requested_path": "photos/cat.jpg", "path_was_mutated": true } - `path`: the stored filename (with random hash). Use this for DELETE and store it in your database. - `url`: permanent public URL. Use this in ``, ``, etc. Served from Cloudflare's CDN. - `requested_path`: the raw path you sent, echoed back for confirmation/logging. - `path_was_mutated`: `true` whenever the stored `path` differs from `requested_path` (always today, since every upload gets a hash suffix). Lets agents detect mutation in one boolean check rather than diffing strings. - Content-Type is preserved: whatever you set on upload is served on read. ## Delete DELETE https://cohesivity.ai/edge/object-storage/?key= Use the `path` from the upload response (the hashed filename). ## Behavior - **Filenames get a random hash suffix.** `photos/cat.jpg` becomes `photos/cat-a3f8b2c1d4e5f6g7.jpg`. This makes URLs unguessable: knowing the original filename is not enough to access the file. - **Reads go direct to CDN.** The `url` from upload points to Cloudflare's CDN. No Cohesivity proxy, no key in the URL. - **Blob store model.** Upload returns the file path: store it in your database alongside your data. This is your file index, just like any CDN or blob store. - **Path rules:** no `..` (directory traversal blocked); leading/trailing slashes are trimmed; backslashes are normalized to `/`. Paths are case-sensitive. - **Upload size:** governed by the Rate Limits section below and the account bucket/fluid model. - **Egress:** Free: no bandwidth charges for reads. - **For images:** client-side compression and resizing before upload keeps files small; a max of 1200px on the longest side at JPEG quality 0.8 is a reasonable default. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - uploads: 250 per ephemeral tenant lifetime before claim or expiry - uploaded bytes: 2 GB per ephemeral tenant lifetime before claim or expiry - live storage: 1 GB max total - uploads: 10 per minute **Claimed Free** - live storage: 10 GB max total - uploads: 30 per minute - uploads: 10000 per month - uploaded bytes: 50 GB per month **Claimed Plus** - live storage: 100 GB max total - uploads: 150 per minute - uploads: 100000 per month - uploaded bytes: 500 GB per month **Claimed Pro** - live storage: 500 GB max total - uploads: 750 per minute - uploads: 500000 per month - uploaded bytes: 2500 GB per month ### Notes - Public reads on `storage.cohesivity.ai` bypass the main Worker today. Upload/delete/storage limits above are hard; public read abuse is monitored and coarse-limited separately. --- # Vector Database Managed vector store backed by Pinecone. Store, search, and retrieve embeddings over HTTP. Tenant isolation is enforced server-side: agents never see or control the underlying isolation mechanism. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/vector-database \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/vector-database \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Common Mistakes - **Not specifying dimensions at provision time.** Dimensions are required and must match your embedding model output size. - **Dimensions can't change after provisioning.** Profile (dimensions + metric) is immutable once set; a different profile requires deleting and re-provisioning the resource. - **Passing a namespace field.** Tenant isolation is handled server-side. Any namespace field in your request body is ignored. ## Provision with Profile Vector database requires a profile (dimensions + metric) at provision time: curl -s -X POST https://cohesivity.ai/api/resources/vector-database \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"dimensions": 768, "metric": "cosine"}' **Supported dimensions:** 384, 768, 1024, 1536, 3072 **Supported metrics:** cosine (default), euclidean, dotproduct Dimensions should match the embedding model in use: - 3072: OpenAI text-embedding-3-large - 1536: OpenAI text-embedding-3-small - 1024: Cohere embed-v3 - 384: all-MiniLM-L6-v2 > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/vector-database - **Auth:** `coh_application_key` as the **key** query parameter - **Method:** POST only (all endpoints) ### Upsert Vectors POST https://cohesivity.ai/edge/vector-database?key= { "vectors": [ { "id": "doc-1", "values": [0.1, 0.2, ...], "metadata": { "title": "My Doc", "category": "faq" } }, { "id": "doc-2", "values": [0.3, 0.4, ...], "metadata": { "title": "Other Doc" } } ] } Response: `{ "upsertedCount": 2 }` ### Query (Similarity Search) POST https://cohesivity.ai/edge/vector-database/query?key= { "vector": [0.1, 0.2, ...], "topK": 5, "includeMetadata": true, "filter": { "category": { "$eq": "faq" } } } Response: { "matches": [ { "id": "doc-1", "score": 0.95, "metadata": { "title": "My Doc", "category": "faq" } }, { "id": "doc-2", "score": 0.82, "metadata": { "title": "Other Doc" } } ] } ### Fetch by IDs POST https://cohesivity.ai/edge/vector-database/fetch?key= { "ids": ["doc-1", "doc-2"] } Response: `{ "vectors": { "doc-1": { "id": "doc-1", "values": [...], "metadata": {...} }, ... } }` ### Delete POST https://cohesivity.ai/edge/vector-database/delete?key= Delete by IDs: `{ "ids": ["doc-1", "doc-2"] }` Delete by filter: `{ "filter": { "category": { "$eq": "old" } } }` Delete all your vectors: `{ "deleteAll": true }` Response: `{ "success": true }` ## Tenant Isolation Every tenant's vectors are isolated by a server-injected Pinecone namespace. The isolation boundary is injected on every request: agents cannot read, set, or override it. Tenants sharing the same profile (dimensions + metric) share underlying infrastructure, while requests are scoped to that tenant namespace. ## End-to-End RAG Workflow Build a retrieval-augmented generation pipeline using OpenAI embeddings + vector-database + OpenAI generation: ### 1. Provision resources curl -s -X POST https://cohesivity.ai/api/resources \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"resources": ["openai-api", "vector-database"], "vector-database": {"dimensions": 1536}}' ### 2. Generate embeddings (text-embedding-3-small, 1536 dimensions) POST https://cohesivity.ai/edge/openai-api/v1/embeddings?key= { "model": "text-embedding-3-small", "input": "Your document text here" } → { "data": [{ "embedding": [0.1, 0.2, ...] }] } // 1536-dimensional vector ### 3. Store in vector database POST https://cohesivity.ai/edge/vector-database?key= { "vectors": [{ "id": "doc-1", "values": [0.1, 0.2, ...], "metadata": { "text": "Your document text", "source": "faq" } }] } ### 4. Query with user question embedding // First embed the question using step 2, then: POST https://cohesivity.ai/edge/vector-database/query?key= { "vector": [], "topK": 3, "includeMetadata": true } ### 5. Generate answer with retrieved context POST https://cohesivity.ai/edge/openai-api/v1/responses?key= { "model": "gpt-5-nano", "input": "Context: \n\nQuestion: \nAnswer:" } ## Metadata Filtering Query and delete support Pinecone metadata filter syntax: { "category": { "$eq": "faq" } } { "price": { "$gt": 10, "$lte": 100 } } { "$and": [{ "category": { "$eq": "faq" } }, { "lang": { "$eq": "en" } }] } See Pinecone docs for full filter operator reference. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - read units: 50000 per ephemeral tenant lifetime before claim or expiry - write units: 100000 per ephemeral tenant lifetime before claim or expiry - requests: 30 per minute **Claimed Free** - requests: 120 per minute - read units: 1000000 per month - write units: 500000 per month **Claimed Plus** - requests: 600 per minute - read units: 10000000 per month - write units: 5000000 per month **Claimed Pro** - requests: 3000 per minute - read units: 50000000 per month - write units: 25000000 per month --- # Social Login (Google OAuth) Zero-setup Google login for your app. Cohesivity handles the entire OAuth flow, stores users, issues tokens, and manages sessions. No Google Cloud Console setup needed. ## Prerequisites Edge auth calls return errors until this resource is provisioned. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/social-login \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"callback_urls": ["https://yourapp.com/auth/done", "http://localhost:5173/auth/done"]}' **Required body:** `{ "callback_urls": ["..."] }`: array of allowed redirect URLs. Also accepts `{ "callback_url": "..." }` (single string, converted to array). Subdomain wildcards are supported (e.g. `https://*.cohesivity.app/auth/callback`). **Replace, not merge:** `POST /api/resources/social-login` **replaces** the entire `callback_urls` list: include every URL you want active in every call. URLs omitted from a re-provision are dropped. Pre-register both production and any localhost callbacks before first deploy so you don't have to re-POST mid-development. Re-provisioning to update callbacks does NOT delete existing users or sessions. **Response:** `{ login_url, callback_urls, events_table, events_endpoint }`: the login URL, registered callback URLs, the stable `social_login_events` compatibility label, and the management event endpoint. Storage is runtime-selected managed storage and does not change this public response. **Event storage compatibility:** fresh social-login provisioning on a fresh capability runtime stores event history in the private `coh_auth.social_login_events` schema of the shared tenant Neon project. It does not provision D1 and does not grant `/edge/postgres`; the dedicated public Postgres role cannot access `coh_auth`. Existing social-login resources and missing backend state remain legacy D1, byte-for-byte. Runtime upgrades never move, backfill, or delete their data; there is no migration endpoint in this release. Both backends return the exact same public response shape, ordering, numeric `id`/`user_id`/`created_at_ms` types, and parsed JSON `metadata`. ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/social-login \ -H "Authorization: Bearer " **Deleting social-login is irreversible:** it permanently removes all users, sessions, auth events, and configuration. ## Common Mistakes - **Verify response fields are nested.** Response shape: `{ valid, user: { id, email, name, picture } }`. Correct access is `data.user.email` and `data.user.id`, not `data.email`, `data.sub`, or `data.user_id`. - **Locally verified JWTs skip revocation checks.** The verify endpoint is the supported path; local JWT validation is unsupported. - **`router.push("/")` after storing callback tokens causes a Next.js double-login bug.** `router.push` is a client-side navigation: the layout stays mounted, so AuthProvider never re-runs and the user appears logged out. `window.location.href = "/"` forces a full page reload, which remounts AuthProvider, reads the newly-stored tokens, and completes login in one step. The same applies to any framework where an auth-checking wrapper component only initializes on mount. - **localStorage tokens written from the callback page can vanish silently.** Safari's Intelligent Tracking Prevention (ITP) discards localStorage writes on pages reached via cross-origin redirects, which is exactly what OAuth triggers. A server-side route handler that sets httpOnly cookies avoids this; see "Recommended: Server-Side Callback" below. - **`sameSite: "strict"` on auth cookies drops them on the OAuth redirect.** OAuth callbacks are cross-origin navigations, and `strict` blocks cookies across origins. `sameSite: "lax"` works for auth cookies. - **Losing the deep link after login.** If the user opened a page like `/meeting/abc`, append `?return_to=/meeting/abc` to the login URL. Cohesivity preserves it through the OAuth flow and returns it to your callback URL as `return_to`. - **User ID type mismatch with realtime.** Social-login returns `user.id` as a **number** (e.g., `30`); realtime presence returns user IDs as **strings** (e.g., `"30"`). Comparing or storing them together requires converting with `String(user.id)` first. ## How It Works 1. User clicks "Sign in with Google" → link to `login_url` (optionally append `?redirect_uri=` to select which registered callback URL to use; defaults to first). To resume a deep link after login, also append `&return_to=/path/inside/your/app`. 2. Google authenticates the user 3. Cohesivity receives the result, shows a consent screen, stores the user, issues tokens 4. User is redirected to your callback URL with two query parameters, plus optional `return_to` if you provided it: - `access_token`: short-lived JWT (1 hour). Use this for all authenticated requests. - `refresh_token`: long-lived opaque token (30 days). Use this to get a new `access_token` when it expires. 5. Your app saves both tokens. Parse them from the callback URL query string. ## Callback URL Format https://yourapp.com/auth/done?access_token=&refresh_token= On error (user denied consent): https://yourapp.com/auth/done?error=access_denied ## Client-Side Callback Fallback The server-side callback below suits production apps better. A client-side callback page reads tokens from the URL query params, persists them, then hard-navigates to `return_to` if present, otherwise home, using `window.location.href` rather than `router.push` or other client-side navigation. localStorage below is a simple fallback; Safari can discard it after OAuth redirects, so httpOnly cookies are the safer choice. ``` // Example: Next.js App Router callback page (src/app/auth/done/page.tsx) "use client"; import { useEffect } from "react"; import { useSearchParams } from "next/navigation"; export default function AuthCallback() { const params = useSearchParams(); useEffect(() => { const access_token = params.get("access_token"); const refresh_token = params.get("refresh_token"); const return_to = params.get("return_to") || "/"; if (access_token) { localStorage.setItem("access_token", access_token); localStorage.setItem("refresh_token", refresh_token || ""); window.location.href = return_to; // hard navigation remounts AuthProvider; router.push does not } }, [params]); return

Signing in...

; } ``` **Why `window.location.href` and not `router.push`:** In Next.js App Router, layouts persist across client-side navigations. If AuthProvider lives in the layout and already ran its initialization effect (finding no tokens), `router.push` won't re-trigger it. `window.location.href` forces a full page reload, so AuthProvider starts fresh and finds the tokens. ## Recommended: Server-Side Callback A server-side route handler avoids a Safari compatibility issue that a client-side page has: Safari's ITP silently discards localStorage writes after cross-origin redirects, which is exactly what OAuth triggers. ``` // Next.js App Router route handler (src/app/auth/done/route.ts) import { NextRequest, NextResponse } from "next/server"; export async function GET(req: NextRequest) { const access_token = req.nextUrl.searchParams.get("access_token"); const refresh_token = req.nextUrl.searchParams.get("refresh_token"); const return_to = req.nextUrl.searchParams.get("return_to") || "/"; const res = NextResponse.redirect(new URL(return_to, req.url)); if (access_token) { res.cookies.set("access_token", access_token, { httpOnly: true, secure: true, sameSite: "lax", path: "/", maxAge: 3600 }); res.cookies.set("refresh_token", refresh_token || "", { httpOnly: true, secure: true, sameSite: "lax", path: "/", maxAge: 30 * 86400 }); } return res; } ``` Then read tokens from cookies in your server components or API routes (`req.cookies.get("access_token")`) instead of localStorage. ## Auth Helper Pattern (Next.js) A shared auth helper centralizes token verification for API routes. Cohesivity auth calls happen server-side, which is why httpOnly cookies work: the token never needs to leave the server. ``` // lib/auth.ts: call from API route handlers and server components export async function getAuthUser(cookieStore) { const accessToken = cookieStore.get("access_token")?.value; const refreshToken = cookieStore.get("refresh_token")?.value; if (!accessToken) return null; // 1. Verify access token const verify = await fetch(`${COHESIVITY}/edge/auth/${TENANT}/verify`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ access_token: accessToken }), }).then(r => r.json()); if (verify.valid) return { user: verify.user }; // 2. Access token expired: refresh if (!refreshToken) return null; const tokens = await fetch(`${COHESIVITY}/edge/auth/${TENANT}/refresh`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refresh_token: refreshToken }), }).then(r => r.json()); if (!tokens.access_token) return null; // 3. Return user + new tokens (caller sets cookies on response) const re = await fetch(`${COHESIVITY}/edge/auth/${TENANT}/verify`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ access_token: tokens.access_token }), }).then(r => r.json()); if (!re.valid) return null; return { user: re.user, newTokens: tokens }; } ``` In your API route, call `getAuthUser(cookies())`. If `newTokens` is set, update cookies on the response. ## Auth Endpoints These are public endpoints: no management key needed. - **Start login:** `GET https://cohesivity.ai/edge/auth/:tenantId/google`: browser redirect to Google. Optional query param `redirect_uri` to select which registered callback URL to use (must match one of your `callback_urls`). Defaults to first registered URL. Optional `return_to=/path` preserves a deep link inside your app and sends it back on the callback URL. - **Verify access token:** `POST https://cohesivity.ai/edge/auth/:tenantId/verify` with `{ "access_token": "" }`: returns `{ valid, user: { id, email, name, picture } }` - **Refresh tokens:** `POST https://cohesivity.ai/edge/auth/:tenantId/refresh` with `{ "refresh_token": "" }`: returns new `{ access_token, refresh_token }` (old refresh invalidated) - **Logout:** `POST https://cohesivity.ai/edge/auth/:tenantId/logout` with `{ "refresh_token": "" }`: deletes session ## User Management Endpoints These require `Authorization: Bearer ` header. - **Account insights:** claimed project owners can open the `/account` Insights tab or call `GET https://cohesivity.ai/account/insights/data` with their Cohesivity account session to see owner-safe social-login users, active refresh-session counts, and recent login/logout/failure activity. This dashboard surface does not expose Google IDs, tokens, refresh hashes, IPs, user agents, or raw event metadata. - **List users:** `GET https://cohesivity.ai/api/social-login/users` Response: `{ "users": [{ "id": number, "email": string, "name": string|null, "picture": string|null, "first_seen": ISO8601, "last_seen": ISO8601 }] }` - **Get user by ID:** `GET https://cohesivity.ai/api/social-login/users/:id` Response: `{ "user": { "id": number, "email": string, "name": string|null, "picture": string|null, "first_seen": ISO8601, "last_seen": ISO8601 } }` Returns 404 if user not found. - **List auth events:** `GET https://cohesivity.ai/api/social-login/events?after=0&limit=50` Response: `{ "events": [{ "id": number, "event": "login_started"|"consent_denied"|"authorize"|"login_failed"|"token_refreshed"|"logout", "user_id": number|null, "email": string|null, "redirect_uri": string|null, "return_to": string|null, "metadata": object, "created_at_ms": number }] }` Optional filters: `user_id=` and `event=`. `GET /api/social-login/events` is the backend-independent event-history surface. Legacy D1-backed tenants retain their existing `social_login_events` table and direct database contract; fresh capability tenants keep the reserved Neon schema private. `login_failed` carries `metadata.stage` (`token_exchange` | `userinfo` | `user_upsert`) and `metadata.error_code`. For an account-wide tail across all offerings (auth, API requests, webhooks, deploys), see `GET https://cohesivity.ai/api/observability?include=recent_events`. ## Verify Response Format The verify endpoint is the supported path for checking a token; local JWT verification is unsupported. Response is nested: Valid: { "valid": true, "user": { "id": 1, "email": "...", "name": "...", "picture": "..." } } Invalid: { "valid": false } Access fields as `data.user.email`, NOT `data.email`. User ID is `data.user.id`, NOT `data.sub` or `data.user_id`. ## Examples **Verify an access token:** curl -s -X POST https://cohesivity.ai/edge/auth//verify \ -H "Content-Type: application/json" \ -d '{"access_token": ""}' **Refresh tokens:** curl -s -X POST https://cohesivity.ai/edge/auth//refresh \ -H "Content-Type: application/json" \ -d '{"refresh_token": ""}' **Logout:** curl -s -X POST https://cohesivity.ai/edge/auth//logout \ -H "Content-Type: application/json" \ -d '{"refresh_token": ""}' ## Token Details - **`access_token`:** JWT, expires in 1 hour. Contains id, email, name, picture, tenant_id. Send this to the verify endpoint to authenticate requests. - **`refresh_token`:** Opaque string, expires in 30 days. Single-use: each refresh invalidates the old pair and returns a new `access_token` + `refresh_token`. - **Verify endpoint:** Stateless JWT check, no database call. It's the supported path; local JWT verification is unsupported. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - auth events: 100 per ephemeral tenant lifetime before claim or expiry - stored users: 1000 max total - auth events: 5 per minute **Claimed Free** - stored users: 5000 max total - auth events: 30 per minute - auth events: 5000 per month **Claimed Plus** - stored users: 50000 max total - auth events: 150 per minute - auth events: 50000 per month **Claimed Pro** - stored users: 250000 max total - auth events: 750 per minute - auth events: 250000 per month --- # Cloudflare Workers Serverless JavaScript modules on Cloudflare's global edge. Create a worker with POST, deploy code with PUT: instant deploy, no git needed. Supports cron triggers. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/cloudflare-workers \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/cloudflare-workers \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Worker Management All endpoints require `Authorization: Bearer ` header. - **List workers:** `GET https://cohesivity.ai/api/workers` - **Create worker:** `POST https://cohesivity.ai/api/workers` with `{ "name": "my-worker" }` - **Worker status:** `GET https://cohesivity.ai/api/workers/:name` - **Deploy code:** `PUT https://cohesivity.ai/api/workers/:name` with `{ "code": "...", "cron": "*/5 * * * *" }` - **Delete worker:** `DELETE https://cohesivity.ai/api/workers/:name` ## Examples **Create:** curl -s -X POST https://cohesivity.ai/api/workers \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"name":"my-worker"}' **Deploy:** curl -s -X PUT https://cohesivity.ai/api/workers/my-worker \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"code":"export default { async fetch(request) { return new Response(\\"Hello!\\"); } };"}' ## Writing Worker Code Worker code uses ES module syntax (Cloudflare Workers V8 runtime): ``` export default { async fetch(request) { return new Response("Hello from my worker!"); }, async scheduled(event, env, ctx) { // Handle cron triggers }, }; ``` Worker names: 2-50 chars, lowercase alphanumeric + hyphens, must start/end with alphanumeric. ## Limits - Worker inventory, deploys, and cron schedules are governed by the Rate Limits section below. - Runtime request analytics are not a published hard cap at launch; Cohesivity will document them when the poller is in the enforcement path. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - workers: 3 max total - deploys: 10 per ephemeral tenant lifetime before claim or expiry - cron schedules: 3 max total - deploys: 3 per minute **Claimed Free** - workers: 10 max total - cron schedules: 5 max total - deploys: 3 per minute - deploys: 30 per month **Claimed Plus** - workers: 30 max total - cron schedules: 20 max total - deploys: 10 per minute - deploys: 100 per month **Claimed Pro** - workers: 100 max total - cron schedules: 100 max total - deploys: 10 per minute - deploys: 300 per month ### Notes - Launch hard caps cover worker inventory, deploys, and cron schedules. Tenant-worker runtime traffic is not yet published as a per-tier hard cap because that analytics poller is not in the launch path yet. --- # Redis Key-value store with expiry, counters, and sorted sets. Execute commands over HTTP: no Redis client or TCP connection needed. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/redis \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/redis \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/redis - **Auth:** `coh_application_key` as the **key** query parameter - **Method:** POST with JSON body `{ "command": "SET", "args": ["key", "value"] }` - **Response:** `{ "result": ... }` ## Supported Commands - **Key-value:** GET, SET, DEL, MGET, MSET, EXISTS, KEYS, TTL, TYPE - **Expiry:** EXPIRE, PEXPIRE, EXPIREAT, PERSIST - **Counters:** INCR, DECR, INCRBY, DECRBY, INCRBYFLOAT - **Sorted sets:** ZADD, ZREM, ZSCORE, ZRANK, ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZCARD, ZCOUNT, ZINCRBY All other commands are blocked. ## Examples - Set a key: `POST https://cohesivity.ai/edge/redis?key=` with `{ "command": "SET", "args": ["mykey", "myvalue"] }` - Get a key: `{ "command": "GET", "args": ["mykey"] }` → `{ "result": "myvalue" }` - Set with expiry: `{ "command": "SET", "args": ["session", "data"] }` then `{ "command": "EXPIRE", "args": ["session", "3600"] }` - Counter: `{ "command": "INCR", "args": ["visits"] }` → `{ "result": 1 }` - Sorted set: `{ "command": "ZADD", "args": ["leaderboard", "100", "alice", "200", "bob"] }` → `{ "result": 2 }` - Get range: `{ "command": "ZRANGE", "args": ["leaderboard", "0", "-1", "WITHSCORES"] }` → `{ "result": ["alice", "100", "bob", "200"] }` - Multiple keys: `{ "command": "MSET", "args": ["a", "1", "b", "2"] }` → `{ "result": "OK" }` - Get multiple: `{ "command": "MGET", "args": ["a", "b"] }` → `{ "result": ["1", "2"] }` - List keys: `{ "command": "KEYS", "args": ["*"] }` → `{ "result": ["mykey", "session", "visits", ...] }` - Delete: `{ "command": "DEL", "args": ["mykey"] }` → `{ "result": 1 }` ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - commands: 100000 per ephemeral tenant lifetime before claim or expiry - commands: 120 per minute **Claimed Free** - commands: 600 per minute - commands: 500000 per month **Claimed Plus** - commands: 3000 per minute - commands: 5000000 per month **Claimed Pro** - commands: 15000 per minute - commands: 25000000 per month --- # OpenAI API Proxies OpenAI Responses, embeddings, and image generation through Cohesivity. Cohesivity injects the upstream OpenAI API key server-side; tenant requests authenticate with the existing Cohesivity application key or an edge session token. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/openai-api \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/openai-api \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Official Docs https://platform.openai.com/docs covers Responses, embeddings, and image generation behavior in more detail. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/openai-api - **Auth:** `coh_application_key` as the **key** query parameter, or a Cohesivity edge session token in `Authorization: Bearer ` - **Format:** OpenAI-compatible JSON request and response shapes for the supported endpoints below - **Upstream auth safety:** an OpenAI key is unnecessary. Cohesivity removes tenant-supplied `Authorization`, cookies, `key`, and OpenAI organization/project/beta headers before adding platform upstream auth. ## Supported Endpoints - `GET /v1/models`: returns a Cohesivity-synthesized OpenAI-compatible model list for the tenant tier - `GET /v1/models/`: returns a synthesized model object only when that model is available to the tenant tier - `POST /v1/responses`: text Responses with allowed GPT-5-family models, function tools, and the gated `image_generation` hosted tool - `POST /v1/embeddings`: `text-embedding-3-small` and, after claim, `text-embedding-3-large` - `POST /v1/images/generations`: direct JSON image generation with `model: "gpt-image-2"` after claim Endpoint calls outside this list are rejected before upstream. POST endpoints require a JSON object body so Cohesivity can inspect model, tool, streaming, and service-tier fields before forwarding. ## Launch Model Access - **Ephemeral:** Responses text `gpt-5-nano` only; embeddings `text-embedding-3-small` only; no image generation. Each allowed OpenAI family has a 100-request tenant-lifetime hard cap and 5/minute burst cap. - **Claimed Free:** Responses text `gpt-5-nano`, `gpt-5-mini`, `gpt-5.4-nano`, `gpt-5.4-mini`; embeddings `text-embedding-3-small` and `text-embedding-3-large`; image generation `gpt-image-2`. - **Claimed Plus / Pro:** all Free OpenAI models plus `gpt-5`, `gpt-5.1`, `gpt-5.2`, `gpt-5.4`, and `gpt-5.5`, with higher per-minute limits. - `GET /v1/models` is the source of truth for base model IDs available to the current tenant. Exact dated snapshots of an allowed text slug, such as `gpt-5-nano-YYYY-MM-DD`, may also pass policy. - Denied model families stay denied for every tier: IDs containing `pro`, `codex`, `chat`, `chatgpt`, `search`, `realtime`, `audio`, `tts`, `transcrib`, `moderation`, or `video` are blocked unless a future Cohesivity launch contract explicitly adds them. - Non-default `service_tier`, `priority`, and `data_residency` variants are blocked for launch. Omit those fields, or leave `service_tier` as `auto` / `default`. ## Common Mistakes - **Calling Chat Completions or another OpenAI endpoint.** This offering supports only the five endpoint shapes listed above. Text generation is available through `POST /v1/responses`. - **Using a model not returned by `GET /v1/models` or an exact dated snapshot of one of those text models.** The model catalog is tier-filtered. A model that is valid on Plus or Pro may still be blocked on Ephemeral or Claimed Free tenants. - **Sending a non-JSON POST body.** Responses, embeddings, and image generation requests require a JSON object body so Cohesivity can enforce model, tool, streaming, and billing policy. - **Putting `gpt-image-2` in the top-level Responses `model`.** The top-level model must be an allowed text model; `gpt-image-2` belongs only inside the `image_generation` tool. - **Omitting the image tool model.** Responses `image_generation` tools must explicitly set `model: "gpt-image-2"` so upstream default-model changes cannot alter cost or behavior. - **Streaming images or embeddings.** Streaming is supported only for `POST /v1/responses` with `stream: true`; image-generation and embeddings streaming are blocked. - **Requesting transparent image backgrounds.** `background: "transparent"` is blocked for `gpt-image-2`; use an opaque background or omit the field. ## Responses API Tools - The top-level `model` must be an allowed text model for the tenant tier. `gpt-image-2` is never valid as the top-level Responses model. - Function tools (`type: "function"`) may pass through because they do not create OpenAI-hosted external cost by themselves. - Hosted `image_generation` tools are allowed only after claim and must set exactly `model: "gpt-image-2"`. Omitted image tool models are blocked to avoid upstream default-model roulette. - `background: "transparent"` is blocked for `gpt-image-2`; use an opaque background or omit the field. - Hosted web search, file search, code interpreter, computer use, shell/container, MCP/connectors, and all other hosted tools are blocked for launch. ## Examples - List allowed models: `GET https://cohesivity.ai/edge/openai-api/v1/models?key=` - Model detail: `GET https://cohesivity.ai/edge/openai-api/v1/models/gpt-5-nano?key=` - Responses text: `POST https://cohesivity.ai/edge/openai-api/v1/responses?key=` with body `{ "model": "gpt-5-nano", "input": "Write one haiku about infrastructure." }` - Responses streaming: `POST https://cohesivity.ai/edge/openai-api/v1/responses?key=` with body `{ "model": "gpt-5-nano", "input": "Stream a short answer.", "stream": true }` - Embeddings: `POST https://cohesivity.ai/edge/openai-api/v1/embeddings?key=` with body `{ "model": "text-embedding-3-small", "input": "Cohesivity gives agents infrastructure." }` - Direct image generation after claim: `POST https://cohesivity.ai/edge/openai-api/v1/images/generations?key=` with body `{ "model": "gpt-image-2", "prompt": "A polished product dashboard for usage analytics", "size": "1024x1024", "quality": "medium", "n": 1 }` - Responses image tool after claim: `POST https://cohesivity.ai/edge/openai-api/v1/responses?key=` with body `{ "model": "gpt-5-mini", "input": "Create one product hero image.", "tools": [{ "type": "image_generation", "model": "gpt-image-2", "size": "1024x1024" }] }` ## Image Generation Image generation is available only after claim. Direct image requests go through `POST /v1/images/generations` with `model: "gpt-image-2"`; the alternate path is a Responses `image_generation` tool with the same exact tool model. Ephemeral tenants must claim before either image path works. Transparent backgrounds and image-generation streaming are blocked. ## Streaming Streaming is supported only on `POST /v1/responses` with `stream: true`. Cohesivity forwards the OpenAI SSE stream as-is and watches for a terminal `response.completed` or `response.incomplete` event with `response.usage`. If the stream ends, errors, or is canceled before terminal usage arrives, the preflight reservation is revoked and no wallet debit is finalized. ## Response Format Non-streaming billable responses are returned in OpenAI-compatible JSON shapes. Responses text may appear in `output_text` when OpenAI includes it; otherwise inspect the `output` content parts. Embeddings responses return `data[]` entries with vectors. Direct image-generation responses return the upstream image payload in `data[]`; Responses image-tool output stays in the Responses output structure. ## Structured Output Responses request fields supported by OpenAI can pass through when they do not conflict with Cohesivity policy. Structured-output options are therefore allowed behind the same model, tool, endpoint, streaming, and service-tier checks described above. ## Billing and Usage - Claimed OpenAI usage is fluid-only after tier and rate checks. There is no fixed monthly OpenAI token bucket. - Successful usage is billed at provider cost plus 10%, rounded up to the nearest cent per settled charge. - Cohesivity records request counters, endpoint/model-family counters, input/output token counters, cached token counters, and image token counters when OpenAI returns them. Prompts, images, and raw request bodies are not stored in usage events. - Wallet debit is finalized only from parseable OpenAI returned `usage`. A successful billable non-streaming response without parseable usage returns a Cohesivity settlement error after revoking the preflight reservation instead of guessing a charge. - Failed upstream responses do not burn quota or fluid; Cohesivity revokes the preflight counters synchronously before returning the upstream failure. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - GPT-5 nano Responses requests: 100 per ephemeral tenant lifetime before claim or expiry - text-embedding-3-small requests: 100 per ephemeral tenant lifetime before claim or expiry - GPT-5 nano Responses requests: 5 per minute - text-embedding-3-small requests: 5 per minute **Claimed Free** - GPT-5 nano Responses requests: 30 per minute - GPT-5.4 nano Responses requests: 30 per minute - GPT-5 mini Responses requests: 15 per minute - GPT-5.4 mini Responses requests: 15 per minute - text-embedding-3-small requests: 60 per minute - text-embedding-3-large requests: 20 per minute - image generation requests: 5 per minute **Claimed Plus** - GPT-5 nano Responses requests: 100 per minute - GPT-5.4 nano Responses requests: 100 per minute - GPT-5 mini Responses requests: 60 per minute - GPT-5.4 mini Responses requests: 60 per minute - GPT-5 / 5.1 / 5.2 Responses requests: 30 per minute - GPT-5.4 Responses requests: 20 per minute - GPT-5.5 Responses requests: 10 per minute - text-embedding-3-small requests: 300 per minute - text-embedding-3-large requests: 100 per minute - image generation requests: 20 per minute **Claimed Pro** - GPT-5 nano Responses requests: 300 per minute - GPT-5.4 nano Responses requests: 300 per minute - GPT-5 mini Responses requests: 200 per minute - GPT-5.4 mini Responses requests: 200 per minute - GPT-5 / 5.1 / 5.2 Responses requests: 100 per minute - GPT-5.4 Responses requests: 60 per minute - GPT-5.5 Responses requests: 40 per minute - text-embedding-3-small requests: 1000 per minute - text-embedding-3-large requests: 300 per minute - image generation requests: 60 per minute ### Notes - OpenAI is fluid-only for claimed accounts after tier and per-minute checks. Ephemeral tenants get only GPT-5 nano Responses text and text-embedding-3-small, each capped to 100 lifetime requests and 5/minute. - Streaming Responses are allowed and settle only from terminal OpenAI usage events. Image streaming, chat completions, audio, realtime, files, vector stores, assistants, batch, fine-tuning, web search, code interpreter, computer use, MCP/connectors, and shell/container tools are blocked for launch. --- # AI Gateway Cohesivity's own AI Gateway gives agents one Cohesivity endpoint for approved OpenAI, Anthropic, Google, and xAI models. Tenant requests authenticate only with a Cohesivity application key or edge session token; Cohesivity handles provider routing, credential isolation, rate enforcement, usage logs, and wallet settlement. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/ai-gateway \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/ai-gateway \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Provider Model Docs Provider model docs cover model behavior, prompting, context windows, and image options. Cohesivity's documented routes and model aliases below define the supported interface; provider URLs are outside it. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/ai-gateway - **Auth:** `coh_application_key` as the **key** query parameter, or a Cohesivity edge session token in `Authorization: Bearer ` - **Format:** OpenAI-compatible JSON request and response shapes for the supported endpoints below. Provider-specific responses may be normalized where Cohesivity needs consistent embeddings, image, usage, and billing data. - **Credential safety:** provider keys are unnecessary. Cohesivity removes tenant `Authorization`, cookies, `key`, OpenAI headers, `x-api-key`, Google key headers, Anthropic headers, xAI auth headers, and gateway auth headers before applying Cohesivity-managed provider credentials. ## Supported Endpoints - `POST /v1/chat/completions`: chat completions for allowlisted openai, anthropic, google, and xai text models. Requests must set `max_tokens` or `max_completion_tokens`. - `POST /v1/embeddings`: embeddings for allowlisted openai and google embedding models. - `POST /v1/images/generations`: image generation for allowlisted openai, google, and xai image models after claim. Every other endpoint or method is rejected before model execution: model listing, `/responses`, `/messages`, `/ai/run`, audio, files, batches, realtime, edits, variations, vector stores, assistants, provider-native passthrough, arbitrary paths, and hidden-cost hosted features are blocked for launch. ## Launch Model Access - **Tenant model aliases:** every request model must use the company prefix `openai/`, `anthropic/`, `google/`, or `xai/`. Bare model names and provider-native route slugs are blocked on the tenant surface. - **Ephemeral:** request-capped, no wallet billing. Chat models: `openai/gpt-5-nano`, `anthropic/claude-haiku-4.5`, `google/gemini-3.1-flash-lite`, and `xai/grok-4.3`. Embeddings: `openai/text-embedding-3-small`, `openai/text-embedding-3-large`, and `google/gemini-embedding-2-preview`. Images are not available before claim. Lifetime caps are 100/25/100/25/100/25/25 respectively, with 5/2/5/2/5/2/2 per-minute bursts. - **Claimed Free:** wallet-fluid with per-minute bursts. Chat: `openai/gpt-5-nano`, `openai/gpt-5.4-nano`, `openai/gpt-5.4-mini`, `openai/gpt-5.4`, `anthropic/claude-haiku-4.5`, `anthropic/claude-sonnet-4.6`, `google/gemini-3.1-flash-lite`, `google/gemini-3.5-flash`, and `xai/grok-4.3`. Embeddings: `openai/text-embedding-3-small`, `openai/text-embedding-3-large`, `google/gemini-embedding-2-preview`, and `google/gemini-embedding-2`. Images: `openai/gpt-image-2`, `google/gemini-3.1-flash-image-preview`, `google/nano-banana-2`, and `xai/grok-imagine-image`. - **Claimed Plus / Pro:** wallet-fluid with higher per-minute bursts and the full launch allowlist. Adds `openai/gpt-5.5`, `anthropic/claude-opus-4.7`, `google/gemini-3.1-pro-preview`, `google/gemini-3-pro-image-preview`, `google/nano-banana-pro`, and `xai/grok-imagine-image-quality`. - Claimed AI Gateway has no lifetime or monthly request bucket; wallet balance plus the per-account UTC-minute burst policy are the governing limits. ## Common Mistakes - **Using provider-native endpoints or model slugs.** This offering supports only the three endpoint shapes above under `/edge/ai-gateway`, and model names require Cohesivity's company-prefixed aliases. - **Omitting bounded output on chat.** Chat completions need `max_tokens` or `max_completion_tokens` set; Cohesivity rejects unbounded text output before provider execution so wallet guard capacity stays meaningful. - **Sending provider keys.** Tenant-supplied provider auth headers are stripped, so provider secrets serve no purpose in tenant apps. - **Streaming embeddings or images.** Streaming is enabled only for allowlisted chat-completions models with terminal usage; embeddings, images, and partial image delivery must be non-streaming for launch. - **Sizing xai images.** `xai/grok-imagine-image` and `xai/grok-imagine-image-quality` use their documented default image shape at launch; `size` and `resolution` are not supported yet. - **Requesting hidden-cost features.** Audio, files, hosted web/file search, code interpreter, computer use, batches, realtime, assistants, vector stores, edits, variations, and provider passthrough fields are blocked. ## Examples - openai chat: `POST https://cohesivity.ai/edge/ai-gateway/v1/chat/completions?key=` with body `{ "model": "openai/gpt-5-nano", "messages": [{"role":"user","content":"Write one haiku about infrastructure."}], "max_completion_tokens": 80 }` - streaming chat: same endpoint with any allowlisted chat model, for example `{ "model": "openai/gpt-5-nano", "messages": [{"role":"user","content":"Stream a short answer."}], "max_completion_tokens": 80, "stream": true }`. Cohesivity ensures terminal usage is included before finalizing billing. - anthropic chat: `POST https://cohesivity.ai/edge/ai-gateway/v1/chat/completions?key=` with body `{ "model": "anthropic/claude-haiku-4.5", "messages": [{"role":"user","content":"Summarize this release note."}], "max_tokens": 120 }` - google embeddings: `POST https://cohesivity.ai/edge/ai-gateway/v1/embeddings?key=` with body `{ "model": "google/gemini-embedding-2", "input": "Cohesivity gives agents infrastructure." }` - openai image after claim: `POST https://cohesivity.ai/edge/ai-gateway/v1/images/generations?key=` with body `{ "model": "openai/gpt-image-2", "prompt": "A polished product dashboard for usage analytics", "size": "1024x1024", "quality": "medium", "n": 1 }` - google image after claim: same image endpoint with body `{ "model": "google/nano-banana-2", "prompt": "A polished product dashboard for usage analytics", "n": 1 }` - xai image after claim: same image endpoint with body `{ "model": "xai/grok-imagine-image", "prompt": "A polished product dashboard for usage analytics", "n": 1 }` ## Streaming Allowlisted chat-completions models across openai, anthropic, google, and xai may stream at launch. Cohesivity forwards OpenAI-compatible SSE chunks to tenants and watches for terminal provider usage; if the stream ends, errors, or is canceled before usage arrives, the preflight reservation is revoked and no wallet debit is finalized. Embeddings, images, and partial image delivery stay non-streaming. ## Billing and Usage - Claimed AI Gateway usage is fluid-only after model-tier and rate checks. There is no fixed monthly AI Gateway request or token bucket. - Successful usage is billed at provider cost plus 10%, rounded up to the nearest cent per settled charge. - Cohesivity records request counters, per-model burst counters, input/output token counters, cached token counters, image token counters, and recent-event metadata when providers return billable usage. Prompts, images, and raw request bodies are not stored in usage events. - Wallet debit is finalized only from parseable successful provider usage or cost. A successful billable response without parseable usage/cost returns a Cohesivity settlement error after revoking the preflight reservation instead of guessing a charge. - Failed provider responses do not burn quota or fluid; Cohesivity revokes the preflight counters synchronously before returning the provider failure. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - openai/gpt-5-nano requests: 100 per ephemeral tenant lifetime before claim or expiry - anthropic/claude-haiku-4.5 requests: 25 per ephemeral tenant lifetime before claim or expiry - google/gemini-3.1-flash-lite requests: 100 per ephemeral tenant lifetime before claim or expiry - xai/grok-4.3 requests: 25 per ephemeral tenant lifetime before claim or expiry - openai/text-embedding-3-small requests: 100 per ephemeral tenant lifetime before claim or expiry - openai/text-embedding-3-large requests: 25 per ephemeral tenant lifetime before claim or expiry - google/gemini-embedding requests: 25 per ephemeral tenant lifetime before claim or expiry - openai/gpt-5-nano requests: 5 per minute - anthropic/claude-haiku-4.5 requests: 2 per minute - google/gemini-3.1-flash-lite requests: 5 per minute - xai/grok-4.3 requests: 2 per minute - openai/text-embedding-3-small requests: 5 per minute - openai/text-embedding-3-large requests: 2 per minute - google/gemini-embedding requests: 2 per minute **Claimed Free** - openai/text-embedding-3-small requests: 60 per minute - openai/text-embedding-3-large requests: 20 per minute - openai/gpt-5-nano requests: 30 per minute - openai/gpt-5.4-nano requests: 30 per minute - openai/gpt-5.4-mini requests: 15 per minute - openai/gpt-5.4 requests: 5 per minute - openai/gpt-image-2 requests: 5 per minute - anthropic/claude-haiku-4.5 requests: 15 per minute - anthropic/claude-sonnet-4.6 requests: 5 per minute - google/gemini-embedding requests: 20 per minute - google/gemini-3.1-flash-lite requests: 30 per minute - google/gemini-3.5-flash requests: 10 per minute - google/gemini-flash-image requests: 5 per minute - xai/grok-4.3 requests: 15 per minute - xai/grok-imagine-image requests: 5 per minute **Claimed Plus** - openai/text-embedding-3-small requests: 300 per minute - openai/text-embedding-3-large requests: 100 per minute - openai/gpt-5-nano requests: 100 per minute - openai/gpt-5.4-nano requests: 100 per minute - openai/gpt-5.4-mini requests: 60 per minute - openai/gpt-5.4 requests: 20 per minute - openai/gpt-image-2 requests: 20 per minute - openai/gpt-5.5 requests: 10 per minute - anthropic/claude-haiku-4.5 requests: 60 per minute - anthropic/claude-sonnet-4.6 requests: 20 per minute - anthropic/claude-opus-4.7 requests: 10 per minute - google/gemini-embedding requests: 100 per minute - google/gemini-3.1-flash-lite requests: 100 per minute - google/gemini-3.5-flash requests: 30 per minute - google/gemini-flash-image requests: 20 per minute - google/gemini-3.1-pro-preview requests: 20 per minute - google/gemini-pro-image requests: 10 per minute - xai/grok-4.3 requests: 60 per minute - xai/grok-imagine-image requests: 20 per minute - xai/grok-imagine-image-quality requests: 10 per minute **Claimed Pro** - openai/text-embedding-3-small requests: 1000 per minute - openai/text-embedding-3-large requests: 300 per minute - openai/gpt-5-nano requests: 300 per minute - openai/gpt-5.4-nano requests: 300 per minute - openai/gpt-5.4-mini requests: 200 per minute - openai/gpt-5.4 requests: 60 per minute - openai/gpt-image-2 requests: 60 per minute - openai/gpt-5.5 requests: 40 per minute - anthropic/claude-haiku-4.5 requests: 200 per minute - anthropic/claude-sonnet-4.6 requests: 60 per minute - anthropic/claude-opus-4.7 requests: 40 per minute - google/gemini-embedding requests: 300 per minute - google/gemini-3.1-flash-lite requests: 300 per minute - google/gemini-3.5-flash requests: 100 per minute - google/gemini-flash-image requests: 60 per minute - google/gemini-3.1-pro-preview requests: 60 per minute - google/gemini-pro-image requests: 30 per minute - xai/grok-4.3 requests: 200 per minute - xai/grok-imagine-image requests: 60 per minute - xai/grok-imagine-image-quality requests: 30 per minute ### Notes - AI Gateway is fluid-only for claimed accounts after model-tier and per-minute checks. Ephemeral tenants get only the listed openai, anthropic, google, and xai starter models with lifetime and burst caps. - Cohesivity exposes only POST /v1/chat/completions, POST /v1/embeddings, and POST /v1/images/generations through Cohesivity AI Gateway. Allowlisted OpenAI, Anthropic, Google, and xAI chat models may stream when terminal usage is returned; model listing, provider-native passthrough, tenant-supplied provider keys, audio, files, batches, realtime, vector stores, assistants, edits, variations, embeddings streaming, and image streaming are blocked before model execution. --- # Deepgram API Proxies the approved Deepgram launch surface through Cohesivity. Cohesivity injects the upstream Deepgram Token server-side, so tenant requests authenticate only with the Cohesivity application key or an edge session token; a Deepgram key is never required. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/deepgram-api \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/deepgram-api \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Official Docs https://developers.deepgram.com/docs covers speech-to-text, text-to-speech, and Voice Agent behavior in more detail. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/deepgram-api - **Auth:** `coh_application_key` as the **key** query parameter, or a Cohesivity edge session token in `Authorization: Bearer ` - **Upstream auth safety:** a Deepgram API key is unnecessary. Cohesivity strips tenant `Authorization`, cookies, and `key` before adding `Authorization: Token ` upstream. - **Claimed billing:** fluid-only after rate, duration, and concurrency checks. Successful usage is billed at provider cost plus 10%, rounded up to the nearest cent per settled charge. There is no fixed monthly Deepgram STT/TTS/Voice Agent bucket. ## Supported Endpoints - `POST /v1/listen`: prerecorded English nova-3 speech-to-text. `model` and `language` accept only their defaults, `nova-3` and `en`, whether omitted or set explicitly; other query options are blocked. - `WS /v1/listen`: streaming English nova-3 speech-to-text. Requires `encoding=linear16`, explicit integer `sample_rate`, and mono audio (`channels=1` or omitted) so duration can be metered exactly. - `POST /v1/speak`: Aura-2 English text-to-speech. JSON body is exactly `{ "text": "..." }`; max 2000 characters. Omitted model defaults to `aura-2-thalia-en`; other allowed models are Aura-2 English voices. - `WS /v1/agent/converse`: Deepgram Voice Agent with Deepgram `flux-general-en` listen using `version: "v2"`, Aura-2 English speak, OpenAI think provider from the launch allowlist, and first Settings audio declared as linear16 input/output with wav output. Everything else is rejected before upstream, including `/v2/listen`, standalone Flux outside Voice Agent, model/admin/key/token/billing endpoints, BYO provider keys, custom provider endpoints, custom headers, callbacks/webhooks, and tool/function/MCP connector surfaces. ## Voice Agent Policy - First client message must be a JSON `Settings` message. Cohesivity validates it before opening the upstream Voice Agent socket. - First Settings must include top-level `audio.input` and `audio.output` objects using `encoding: "linear16"` with explicit integer sample rates from 8000 through 48000; `audio.output.container` must be `"wav"`. - Listen provider must include explicit `type: "deepgram"`, model `flux-general-en`, and `version: "v2"`. `language` is unneeded for `flux-general-en` because the model carries English selection. `nova-3`, `flux-general-multi`, and every other listen model are blocked for Voice Agent. - Speak provider must include explicit `type: "deepgram"` and an approved Aura-2 English voice: `aura-2-amalthea-en`, `aura-2-andromeda-en`, `aura-2-apollo-en`, `aura-2-arcas-en`, `aura-2-aries-en`, `aura-2-asteria-en`, `aura-2-athena-en`, `aura-2-atlas-en`, `aura-2-aurora-en`, `aura-2-callista-en`, `aura-2-cora-en`, `aura-2-cordelia-en`, `aura-2-delia-en`, `aura-2-draco-en`, `aura-2-electra-en`, `aura-2-harmonia-en`, `aura-2-helena-en`, `aura-2-hera-en`, `aura-2-hermes-en`, `aura-2-hyperion-en`, `aura-2-iris-en`, `aura-2-janus-en`, `aura-2-juno-en`, `aura-2-jupiter-en`, `aura-2-luna-en`, `aura-2-mars-en`, `aura-2-minerva-en`, `aura-2-neptune-en`, `aura-2-odysseus-en`, `aura-2-ophelia-en`, `aura-2-orion-en`, `aura-2-orpheus-en`, `aura-2-pandora-en`, `aura-2-phoebe-en`, `aura-2-pluto-en`, `aura-2-saturn-en`, `aura-2-selene-en`, `aura-2-thalia-en`, `aura-2-theia-en`, `aura-2-vesta-en`, or `aura-2-zeus-en`. - Think provider must include explicit `type: "open_ai"` with exactly one of `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o`, or `gpt-4o-mini`. - Session duration caps are Ephemeral STT 120s / Voice Agent 180s, Claimed Free 600s / 600s, Plus 1800s / 1800s, and Pro 1800s / 1800s. - Google/Gemini, Anthropic, Groq, Bedrock, custom providers, custom endpoints, custom headers, BYO credentials (including bare `key` fields), multiple think providers, callbacks, and tools/functions are blocked on initial Settings and on later UpdateSpeak/UpdateThink messages. ## Examples - Prerecorded STT: `POST https://cohesivity.ai/edge/deepgram-api/v1/listen?key=` with audio body and `Content-Type: audio/wav` - Streaming STT: `wss://cohesivity.ai/edge/deepgram-api/v1/listen?encoding=linear16&sample_rate=16000&key=` - TTS: `POST https://cohesivity.ai/edge/deepgram-api/v1/speak?key=` with body `{ "text": "Cohesivity gives agents speech APIs without provider keys." }` - Voice Agent: `wss://cohesivity.ai/edge/deepgram-api/v1/agent/converse?key=` then send this first `Settings` JSON message before audio/control frames: ```json { "type": "Settings", "audio": { "input": { "encoding": "linear16", "sample_rate": 24000 }, "output": { "encoding": "linear16", "sample_rate": 24000, "container": "wav" } }, "agent": { "listen": { "provider": { "type": "deepgram", "model": "flux-general-en", "version": "v2" } }, "think": { "provider": { "type": "open_ai", "model": "gpt-5-nano" }, "prompt": "Reply briefly." }, "speak": { "provider": { "type": "deepgram", "model": "aura-2-thalia-en" } }, "greeting": "Hello." } } ``` ## Billing and Usage - Successful usage is billed at provider cost plus 10%, rounded up to the nearest cent per settled charge. - Failed upstream responses and upstream connection failures synchronously revoke the preflight reservation and do not burn quota or fluid. - Prerecorded STT settles only when Deepgram returns a successful response with parseable duration metadata; otherwise Cohesivity revokes the reservation and returns a settlement error instead of guessing. - Streaming STT duration is calculated from forwarded binary linear16 audio bytes and sample rate. Text/control frames are not counted as audio. - TTS settles from the validated request text character count only after a successful upstream response. - Voice Agent settles from accepted socket wall-clock duration, capped by the tier session limit. - Runtime availability follows Cohesivity runtime release and promotion. This page documents the candidate behavior in code; it does not imply a candidate is stable/default-serving before promotion. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - stt requests: 10 per ephemeral tenant lifetime before claim or expiry - tts requests: 10 per ephemeral tenant lifetime before claim or expiry - tts characters: 5000 per ephemeral tenant lifetime before claim or expiry - voice agent sessions: 3 per ephemeral tenant lifetime before claim or expiry - concurrent stt sessions: 1 max at once - concurrent tts requests: 1 max at once - concurrent voice agent sessions: 1 max at once - stt requests: 2 per minute - tts requests: 2 per minute - voice agent sessions: 1 per minute **Claimed Free** - concurrent stt sessions: 2 max at once - concurrent tts requests: 2 max at once - concurrent voice agent sessions: 1 max at once - stt requests: 10 per minute - tts requests: 5 per minute - voice agent sessions: 1 per minute **Claimed Plus** - concurrent stt sessions: 10 max at once - concurrent tts requests: 5 max at once - concurrent voice agent sessions: 5 max at once - stt requests: 60 per minute - tts requests: 20 per minute - voice agent sessions: 5 per minute **Claimed Pro** - concurrent stt sessions: 25 max at once - concurrent tts requests: 10 max at once - concurrent voice agent sessions: 10 max at once - stt requests: 150 per minute - tts requests: 60 per minute - voice agent sessions: 10 per minute ### Notes - Deepgram is fluid-only for claimed accounts after strict endpoint, rate, duration, and concurrency checks. Ephemeral tenants get small STT/TTS/Voice Agent lifetime caps during the 72-hour claim window. - Cohesivity exposes only English nova-3 standalone STT, Aura-2 English TTS, and Voice Agent over POST/WS /v1/listen, POST /v1/speak, and WS /v1/agent/converse. Voice Agent listen is restricted to flux-general-en with provider version v2, and first Settings must use linear16 input/output audio with wav output. Standalone Flux and /v2/listen remain blocked. Admin/model/key/billing surfaces, BYO credentials, custom providers, callbacks, and tool/function endpoints are blocked. --- # Exa API Proxies the Exa API. Ephemeral tenants get the Exa core retrieval surface, Claimed Free adds research plus deep search and deep-reasoning search, and Plus/Pro unlock websets and monitors. No Exa API key is needed. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/exa-api \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/exa-api \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Official Docs https://exa.ai/docs/reference covers full request and response details. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/exa-api - **Auth:** `coh_application_key` as the **key** query parameter - **Method:** mostly POST with JSON body; Plus/Pro may also use Exa's webset and monitor endpoints through the same proxy - **Format:** matches Exa's API: same paths, same request/response shapes ## Launch Access Policy - **Ephemeral core surface:** `POST /search`, `POST /contents`, `POST /findSimilar`, `POST /answer`, and `POST /context` - **Ephemeral search-type restriction:** `/search` only allows `instant`, `fast`, `auto`, and `neural` - **Blocked on Ephemeral:** `/research*`, `/websets*`, `/monitors*`, and `/search` types `deep-lite`, `deep`, `deep-reasoning` - **Claimed Free:** core retrieval, `/research*`, and deep search types including `deep-lite`, `deep`, and `deep-reasoning`; websets and monitors require Plus or Pro - **Claimed Plus and Claimed Pro:** claimed search types plus websets and monitors ## Products - **Search** (`POST /search`): find web pages matching a query. Claimed tenants can use deep search types, including `deep-reasoning`, through the same endpoint. Returns titles, URLs, dates, and optionally text/summaries. - **Contents** (`POST /contents`): extract text, highlights, or summaries from a list of URLs. - **Find Similar** (`POST /findSimilar`): find pages similar to a URL or document. - **Context** (`POST /context`): return compact context payloads for downstream reasoning. - **Answer** (`POST /answer`): ask a question and get the final synthesized answer with citations. This is the LLM answer surface, not just raw retrieval. - **Research** (`/research*`): claimed tenants can use Exa research surfaces. Ephemeral tenants must claim first. - **Monitors** (`/monitors*`): watch topics or pages over time. Plus/Pro only. - **Websets** (`/websets/v0/websets*`): build structured datasets from web data asynchronously. Plus/Pro only. ## Examples - Search: `POST https://cohesivity.ai/edge/exa-api/search?key=` with body `{ "query": "...", "numResults": 10, "type": "auto" }` - Contents: `POST https://cohesivity.ai/edge/exa-api/contents?key=` with body `{ "urls": ["https://example.com"], "text": true }` - Answer: `POST https://cohesivity.ai/edge/exa-api/answer?key=` with body `{ "query": "..." }` - Context: `POST https://cohesivity.ai/edge/exa-api/context?key=` with body `{ "query": "..." }` - Research (claimed tiers): `POST https://cohesivity.ai/edge/exa-api/research?key=` with the body shape from Exa's current docs - Deep reasoning search (claimed tiers): `POST https://cohesivity.ai/edge/exa-api/search?key=` with body `{ "query": "...", "numResults": 1, "type": "deep-reasoning" }` - Websets (Plus/Pro): `POST https://cohesivity.ai/edge/exa-api/websets/v0/websets?key=` with body `{ "search": { "query": "...", "count": 10 }, "enrichments": [{ "description": "...", "format": "text" }] }` ## Response Format Search and deep-reasoning search: `{ results: [{ title, url, publishedDate?, author?, text?, summary? }], requestId?, autopromptString? }` Contents: `{ results: [{ title, url, text, highlights?, summary? }] }` Answer: `{ answer: "...", citations: [{ url, title, text? }] }` Websets: `{ id: "...", status: "...", dashboardUrl: "..." }` (async: results require polling) ## Billing and Usage - Claimed Exa usage is fluid-only. Successful usage is billed at provider cost plus 10%, rounded up to the nearest cent per settled charge. - Failed upstream responses do not burn quota or fluid; Cohesivity revokes the preflight counters before returning the failure. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - requests: 50 per ephemeral tenant lifetime before claim or expiry - requests: 5 per minute **Claimed Free** - requests: 15 per minute **Claimed Plus** - requests: 60 per minute **Claimed Pro** - requests: 300 per minute ### Notes - Exa is fluid-only for claimed accounts. Ephemeral tenants get core retrieval only, Claimed Free adds research plus deep search and deep-reasoning search, and Plus/Pro unlock websets and monitors. --- # OpenWeather API Proxies the OpenWeather Data 2.5 API. Current weather and 5-day/3-hour forecast: no separate OpenWeather key. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/openweather-api \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/openweather-api \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Official Docs https://openweathermap.org/current and https://openweathermap.org/forecast5 cover the full parameter and response reference. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/openweather-api - **Auth:** `coh_application_key` as the **key** query parameter - **Format:** same as OpenWeather: same paths, same response shapes - **Units are forced to `metric`** (Celsius, m/s) and **`mode` is forced to `json`**. Any `units=`/`mode=` you pass is ignored. The default OpenWeather unit is Kelvin; this prevents accidental Kelvin temperatures in your UI. ## Three input modes (same endpoint) OpenWeather resolves places internally: no separate geocoding step needed. - By city: `GET https://cohesivity.ai/edge/openweather-api/data/2.5/weather?q=London&key=` - By city, country: `GET https://cohesivity.ai/edge/openweather-api/data/2.5/weather?q=New+Delhi,IN&key=` - By coordinates: `GET https://cohesivity.ai/edge/openweather-api/data/2.5/weather?lat=51.5074&lon=-0.1278&key=` - By ZIP code: `GET https://cohesivity.ai/edge/openweather-api/data/2.5/weather?zip=94040,US&key=` - 5-day / 3-hour forecast: `GET https://cohesivity.ai/edge/openweather-api/data/2.5/forecast?q=London&key=` ## Response Shape (current weather) { "coord": { "lon": -0.1278, "lat": 51.5074 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "main": { "temp": 11.51, // Celsius (units forced to metric) "feels_like": 10.73, "temp_min": 10.88, "temp_max": 12.79, "pressure": 1012, // hPa "humidity": 77 // percent }, "visibility": 10000, // meters "wind": { "speed": 2.57, "deg": 70 }, // m/s, degrees (no cardinal) "clouds": { "all": 100 }, // percent "rain": { "1h": 0.5 }, // ONLY present when raining "snow": { "1h": 0.2 }, // ONLY present when snowing "dt": 1778019904, // Unix seconds (UTC) "sys": { "country": "GB", "sunrise": 1777955116, "sunset": 1778009345 }, "timezone": 3600, // shift in seconds from UTC "name": "London", "cod": 200 } **Common access patterns:** - Temperature: `data.main.temp` (Celsius) - Feels-like: `data.main.feels_like` - Description: `data.weather[0].description` (e.g. `"overcast clouds"`) - Icon: `https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png` - Wind speed (m/s): `data.wind.speed`; direction (degrees): `data.wind.deg` - Rain in last hour (mm): `data.rain?.["1h"] ?? 0` ## Place-name grammar The `q=` parameter expects gazetteer names, optionally with `,STATE,COUNTRY` (ISO 3166). Informal regional terms (e.g. `delhi ncr`, `the Bay Area`, `downtown LA`) and street addresses do not resolve; a canonical name such as `New Delhi` succeeds where `delhi ncr` returns a 404. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - requests: 2000 per ephemeral tenant lifetime before claim or expiry - requests: 10 per minute **Claimed Free** - requests: 30 per minute - requests: 10000 per month **Claimed Plus** - requests: 120 per minute - requests: 100000 per month **Claimed Pro** - requests: 600 per minute - requests: 500000 per month --- # Google Geocoding API Proxies Google Maps Geocoding API. Convert addresses to coordinates and reverse. Pairs with Weather API when you want canonical coordinates before calling weather by lat/lon. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/google-geocoding-api \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/google-geocoding-api \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## Official Docs https://developers.google.com/maps/documentation/geocoding covers the full parameter and response reference. > **Server-side only.** `coh_application_key` is a secret; browser JS, mobile bundles, and other client-side code cannot hold it safely. This call belongs in a Railway-hosted server, `cloudflare-workers`, or your own server tier. See the canonical key-secrecy directive in `.cohesivity` for details. ## Edge Usage - **Base URL:** https://cohesivity.ai/edge/google-geocoding-api - **Auth:** `coh_application_key` as the **key** query parameter - **Format:** same as Google's Geocoding API ## Examples - Geocode: `GET https://cohesivity.ai/edge/google-geocoding-api/maps/api/geocode/json?address=San+Francisco,CA&key=` - Reverse: `GET https://cohesivity.ai/edge/google-geocoding-api/maps/api/geocode/json?latlng=37.7749,-122.4194&key=` ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - requests: 100 per ephemeral tenant lifetime before claim or expiry - requests: 5 per minute **Claimed Free** - requests: 15 per minute - requests: 500 per month **Claimed Plus** - requests: 60 per minute - requests: 5000 per month **Claimed Pro** - requests: 300 per minute - requests: 25000 per month