From 1643197740574a3e993a9315d956f68f22fefb5e Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 11 Aug 2026 09:50:42 -0700 Subject: [PATCH 01/50] feat: add Claimable Postgres agent flow --- .env.example | 2 + content/docs/reference/claimable-postgres.md | 413 ++++++++-------- .../claimable-database-integration.md | 4 +- src/app/api/claimable-postgres/route.js | 306 ++++++++++++ src/app/api/claimable-postgres/route.test.js | 232 +++++++++ src/app/claimable-postgres/page.jsx | 14 + .../claimable-postgres/claimable-postgres.jsx | 439 ++++++++++++++++++ src/constants/links.js | 3 +- src/constants/seo-data.js | 6 + src/scripts/llms-index-config.js | 6 +- 10 files changed, 1233 insertions(+), 192 deletions(-) create mode 100644 src/app/api/claimable-postgres/route.js create mode 100644 src/app/api/claimable-postgres/route.test.js create mode 100644 src/app/claimable-postgres/page.jsx create mode 100644 src/components/pages/claimable-postgres/claimable-postgres.jsx diff --git a/.env.example b/.env.example index baacfc7776a..413102c4f32 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,8 @@ NEXT_PUBLIC_GITHUB_PATH=https://github.com/neondatabase/website/tree/main/ NEXT_PUBLIC_NEON_STATUS_API=https://7687492087503394.hostedstatus.com/1.0/status/6878fc85709daa75be6c7e3c +CLAIMABLE_NEON_ORIGIN=https://claimable.neon.tech + INKEEP_ORGANIZATION_ID=neon INKEEP_INTEGRATION_ID= INKEEP_INTEGRATION_API_KEY= diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index ef4aaa7796b..2b1bb29955f 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -1,281 +1,318 @@ --- title: Claimable Postgres by Neon -subtitle: Launch an instant Lakebase Postgres database on Neon with zero configuration +subtitle: Let an agent create a temporary database before a human creates an account summary: >- - Claimable Postgres provisions an instant database via a single - unauthenticated API call, CLI command, Node.js SDK, or Vite plugin with no - account required. Unclaimed databases expire after 72 hours and are capped - at 100 MB storage and 1 GB transfer. Claiming to a Neon account removes - expiration and raises limits to the Free plan. Use this page for the REST - API reference, neon-new CLI options, and SDK integration for building - instant-database or launchpad experiences. + Claimable Postgres lets agents provision Lakebase Postgres on Neon through + auth.md, the Neon CLI, or a REST API without a Neon account. The service + issues scoped agent credentials for one temporary project, can add Data API + and Managed Better Auth, and transfers the project into a Neon organization + when a human claims it. enableTableOfContents: true redirectFrom: - /docs/reference/neon-launchpad - /docs/reference/instagres -updatedOn: '2026-08-04T05:05:30.414Z' --- -Claimable Postgres gives you an instant Postgres database with a single API call. No account required. +Claimable Postgres lets an agent create a temporary database on Neon before a human signs up. The agent receives credentials scoped to one project, builds with standard Postgres tools, and gives the human a claim link when the project is worth keeping. -Your database expires after 72 hours unless you claim it to your Neon account. Databases are provisioned on AWS us-east-2 running Postgres 17. - -Access it at [neon.new](https://neon.new/). +Start in the browser at [neon.com/claimable-postgres](/claimable-postgres), or give an agent the service's [`auth.md`](https://claimable.neon.tech/auth.md) document. ## Quick start - + +Create a project and write its credentials to `.env`: + ```bash -curl -X POST https://neon.new/api/v1/database \ - -H 'Content-Type: application/json' \ - -d '{"ref": "your-app-name"}' +npx neon@latest claim create --env-pull ``` -Example response: +Request Data API or Managed Better Auth when the app needs them: -```json -{ - "id": "01abc123-def4-5678-9abc-def012345678", - "status": "UNCLAIMED", - "neon_project_id": "cool-breeze-12345678", - "connection_string": "postgresql://neondb_owner:npg_xxxx@ep-cool-breeze-pooler...", - "claim_url": "https://neon.new/claim/01abc123-def4-5678-9abc-def012345678", - "expires_at": "2026-02-01T12:00:00.000Z", - "created_at": "2026-01-29T12:00:00.000Z", - "updated_at": "2026-01-29T12:00:00.000Z" -} +```bash +npx neon@latest claim create \ + --service data-api \ + --service auth \ + --env-pull ``` -See [API reference](#api) for details. +The CLI saves the project's identity assertion in its secure credential store. Existing commands then use the claimable project automatically: + +```bash +neon branches list +neon psql --role-name neondb_owner +``` +An agent starts with the protocol document: + ```bash -npx neon-new --yes +curl https://claimable.neon.tech/auth.md ``` -Writes to your `.env` file (partial output): +Register an anonymous agent identity: -``` -DATABASE_URL=postgresql://neondb_owner:npg_xxxxxxxxxxxx@ep-cool-breeze-a1b2c3d4-pooler.c-2.us-east-2.aws.neon.tech/neondb?channel_binding=require&sslmode=require +```bash +curl --request POST https://claimable.neon.tech/v1/agent/identity \ + --header 'Content-Type: application/json' \ + --data '{ + "type": "anonymous", + "capabilities": ["postgres", "data_api"], + "source": "example-agent" + }' ``` -See [CLI reference](#command-line-interface) for details. +The response contains an `identity_assertion`, project metadata, and one decision for every requested capability. Exchange the assertion for a short-lived access token before calling project endpoints. -## Limits +## Agent discovery -Unclaimed databases have stricter quotas. Claiming resets limits to your Neon plan. +Claimable Postgres implements [auth.md](https://auth.md/). Agents can discover the complete authentication and provisioning flow without a vendor-specific integration. -| | Unclaimed | Claimed (Free plan) | -| ---------- | --------- | ------------------- | -| Storage | 100 MB | 512 MB | -| Transfer | 1 GB | ~5 GB | -| Branches | No | Yes | -| Expiration | 72 hours | None | +| Document or endpoint | Purpose | +| --------------------------------------------- | ------------------------------------------------------------ | +| `GET /auth.md` | Human-readable agent instructions and examples | +| `GET /.well-known/oauth-protected-resource` | Resource server metadata | +| `GET /.well-known/oauth-authorization-server` | Token, revocation, identity, and skill endpoint discovery | +| `GET /.well-known/jwks.json` | Public keys for verifying Claimable Neon tokens | +| `POST /v1/agent/identity` | Provision a project and issue a durable identity assertion | +| `POST /v1/oauth2/token` | Exchange the assertion for a short-lived bearer access token | +| `POST /v1/oauth2/revoke` | Revoke an access token or identity assertion | +| `GET /v1/databases/{project_id}/credentials` | Read scoped project and service credentials | +| `POST /v1/databases/{project_id}/claim` | Create a short-lived human claim code | +| `GET /v1/databases/{project_id}/claim` | Read claim and reconciliation status | +| `DELETE /v1/databases/{project_id}` | Delete an unclaimed project | +| `/v1/projects/{project_id}/...` | Use supported Neon Management API operations before claiming | -## Claiming a database +The identity assertion is a secret. Store it like an API key. There are no refresh tokens. Exchange the assertion again when an access token expires. -Claiming is **optional**. Your database works immediately. Claim only if you want to keep it beyond 72 hours. +## Register and provision -**To claim:** +```http +POST /v1/agent/identity +Content-Type: application/json +``` -1. Visit the `claim_url` from the API response -2. Sign in to Neon (or create an account) -3. Choose an organization for the database -4. Complete the transfer +```json +{ + "type": "anonymous", + "capabilities": ["postgres", "data_api", "auth"], + "source": "example-agent" +} +``` -After claiming, the database appears in your Neon console with expiration removed. The `status` changes to `CLAIMED` and `connection_string` becomes `null` in the API (use Neon console instead). +`postgres` is always requested. Add `data_api` or `auth` only when the app needs them. -## API +The response has this shape: -The Claimable Postgres API provides programmatic database provisioning. No authentication required. +```json +{ + "registration_id": "reg_...", + "identity_assertion": "eyJ...", + "project": { + "id": "quiet-fog-12345678", + "branch_id": "br-...", + "expires_at": "2026-08-14T12:00:00.000Z" + }, + "capabilities": [ + { + "capability": "postgres", + "granted": true, + "scopes": ["postgres.read", "postgres.write"] + }, + { + "capability": "data_api", + "granted": true, + "scopes": ["data_api.query"] + } + ] +} +``` -**Base URL:** `https://neon.new/api/v1` +Check every capability decision. A successful registration can contain denied optional capabilities. -### Create database +### Exchange the identity assertion +```bash +curl --request POST https://claimable.neon.tech/v1/oauth2/token \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \ + --data-urlencode 'assertion=YOUR_IDENTITY_ASSERTION' \ + --data-urlencode 'resource=https://claimable.neon.tech/' ``` -POST /api/v1/database -``` - -| Parameter | Required | Description | -| ---------------------------- | -------- | ------------------------------------------- | -| `ref` | Yes | Referrer identifier (for tracking) | -| `enable_logical_replication` | No | Enable logical replication (default: false) | -### Get database +The response contains a bearer `access_token`, its scope, and its expiration: -``` -GET /api/v1/database/:id +```json +{ + "access_token": "eyJ...", + "token_type": "Bearer", + "expires_in": 900, + "scope": "postgres.read postgres.write data_api.query" +} ``` -Returns the same response schema. +### Pull credentials -### Response fields +```bash +curl https://claimable.neon.tech/v1/databases/quiet-fog-12345678/credentials \ + --header "Authorization: Bearer $ACCESS_TOKEN" +``` -| Field | Type | Description | -| ------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------- | -| `id` | string | Database identifier (UUID v7) | -| `status` | string | `UNCLAIMED`, `CLAIMING`, or `CLAIMED` | -| `neon_project_id` | string | Underlying Neon project ID | -| `connection_string` | string \| null | Postgres connection URL with pooling (null after claimed). For direct connections, remove `-pooler` from the hostname. | -| `claim_url` | string | URL to claim the database | -| `expires_at` | string | ISO 8601 expiration timestamp | -| `created_at` | string | ISO 8601 creation timestamp | -| `updated_at` | string | ISO 8601 last update timestamp | +```json +{ + "project_id": "quiet-fog-12345678", + "branch_id": "br-...", + "database_url": "postgresql://neondb_owner:...@ep-...-pooler.../neondb?sslmode=require", + "expires_at": "2026-08-14T12:00:00.000Z", + "services": { + "data_api": { + "url": "https://ep-....apirest.c-2.us-east-2.aws.neon.tech/neondb/rest/v1" + }, + "auth": { + "base_url": "https://ep-....neonauth.c-2.us-east-2.aws.neon.tech/api/auth", + "jwks_url": "https://ep-....neonauth.c-2.us-east-2.aws.neon.tech/.well-known/jwks.json" + } + } +} +``` -
-Error responses +Only requested and granted services appear under `services`. -| Condition | HTTP | Message | -| ---------------------- | ---- | ------------------------------------ | -| Missing or empty `ref` | 400 | `Missing referrer` (includes `hint`) | -| Invalid database ID | 400 | `Database not found` | -| Invalid JSON body | 500 | `Failed to create the database.` | -| Invalid parameter type | 500 | `Failed to create the database.` | +## Capabilities -
+| Capability | Available before claim | Environment variable | +| ---------- | ---------------------- | ----------------------------- | +| Postgres | Yes | `DATABASE_URL` | +| Data API | When requested | `NEON_DATA_API_URL` | +| Auth | When requested | `NEON_AUTH_BASE_URL` | +| Functions | No | Requires claiming the project | +| Storage | No | Requires claiming the project | +| AI Gateway | No | Requires claiming the project | -## Command-line interface +Unsupported pre-claim capabilities return the `capability_requires_claim` error code. Agents should preserve that code and give the user the claim URL instead of retrying. -The `neon-new` CLI creates a database and writes credentials to your `.env` file: +## Use the Neon CLI - +`neon claim` and its `neon claimable` alias manage anonymous projects: - -```bash -npx neon-new -``` - - ```bash -yarn dlx neon-new -``` - - -```bash -pnpm dlx neon-new -``` - - -```bash -bunx neon-new -``` - - -```bash -deno run -A neon-new -``` - - +# Create a Postgres project +neon claim create --env-pull -**Options:** - -| Option | Alias | Description | Default | -| ----------------------- | ----- | ------------------------------------- | -------------- | -| `--yes` | `-y` | Skip prompts and use defaults | | -| `--env ` | `-e` | Path to the .env file | `./.env` | -| `--key ` | `-k` | Env var for connection string | `DATABASE_URL` | -| `--prefix ` | `-p` | Prefix for generated public vars | `PUBLIC_` | -| `--seed ` | `-s` | Path to SQL file to seed the database | | -| `--logical-replication` | `-L` | Enable logical replication | `false` | -| `--ref ` | `-r` | Referrer ID for affiliates program | | -| `--help` | `-h` | Show help message | | - -**Example output in `.env`:** - -```txt -DATABASE_URL=postgresql://neondb_owner:npg_xxxxxxxxxxxx@ep-cool-breeze-a1b2c3d4-pooler.c-2.us-east-2.aws.neon.tech/neondb?channel_binding=require&sslmode=require -DATABASE_URL_DIRECT=postgresql://neondb_owner:npg_xxxxxxxxxxxx@ep-cool-breeze-a1b2c3d4.c-2.us-east-2.aws.neon.tech/neondb?channel_binding=require&sslmode=require -# Claimable DB expires at: Sat, 01 Feb 2026 12:00:00 GMT -# Claim it now to your account using the link below: -PUBLIC_POSTGRES_CLAIM_URL=https://neon.new/claim/01abc123-def4-5678-9abc-def012345678 -``` +# Request services explicitly +neon claim create --service data-api --service auth --env-pull + +# Check the current human-claim state +neon claim status -- `DATABASE_URL` is a pooled connection (hostname contains `-pooler`). Use this for application queries. -- `DATABASE_URL_DIRECT` is a direct connection (no pooler). Use this for migrations (e.g. Prisma). +# Generate and open a human claim link +neon claim accept -To claim, visit the URL in the comments above or run `npx neon-new claim` to open it in your browser. +# List claimable projects saved on this machine +neon claim list -## SDK +# Delete the unclaimed project and local credentials +neon claim delete --yes +``` -The `neon-new` package also exports an SDK for programmatic provisioning in Node.js scripts: +After `claim create`, regular Neon CLI commands exchange the saved identity assertion and route supported management operations through Claimable Neon. Explicit Neon account credentials take precedence when you pass them. -```javascript -import { instantPostgres } from 'neon-new'; +If a `neon.ts` file is present, `claim create` requests its declared services automatically: -const { databaseUrl, databaseUrlDirect, claimUrl, claimExpiresAt } = await instantPostgres({ - referrer: 'your-app-name', +```typescript filename="neon.ts" +import { defineConfig } from '@neon/config'; + +export default defineConfig({ + auth: true, + dataApi: true, }); ``` -The `referrer` parameter is required. The function returns: +Services that require human ownership still run through normal Config-as-Code planning. Claimable Neon returns `capability_requires_claim` for those operations, so the agent can ask for a claim instead of silently omitting part of the configuration. -| Field | Type | Description | -| ------------------- | ------ | --------------------------------------------------- | -| `databaseUrl` | string | Pooled connection URL (use for application queries) | -| `databaseUrlDirect` | string | Direct connection URL (use for migrations) | -| `claimUrl` | string | URL to claim the database to a Neon account | -| `claimExpiresAt` | Date | Expiration timestamp as a JavaScript Date object | +## Claim a project -You can also pass a seed option to run SQL on creation: +Create a claim code with the API: -```javascript -const result = await instantPostgres({ - referrer: 'your-app-name', - seed: { type: 'sql-script', path: './schema.sql' }, -}); +```bash +curl --request POST \ + https://claimable.neon.tech/v1/databases/quiet-fog-12345678/claim \ + --header "Authorization: Bearer $ACCESS_TOKEN" +``` + +```json +{ + "user_code": "ABCD-2345", + "verification_uri": "https://claimable.neon.tech/claim", + "verification_uri_complete": "https://claimable.neon.tech/claim?user_code=ABCD-2345", + "expires_in": 900, + "interval": 5 +} ``` -## Vite plugin +Open `verification_uri_complete`, sign in to Neon, and choose the destination organization. The project transfer then moves through these states: + +1. `pending`: the claim code exists, but the transfer has not completed. +2. `accepted`: the project has left the unclaimed-project organization. +3. `reconciled`: Claimable Neon has revoked its project key, agent tokens, and derived credentials. -Add automatic database provisioning to Vite projects with `vite-plugin-neon-new`: +Poll claim status at the server-provided `interval`: ```bash -npm install -D vite-plugin-neon-new +curl https://claimable.neon.tech/v1/databases/quiet-fog-12345678/claim \ + --header "Authorization: Bearer $ACCESS_TOKEN" ``` -```js -import { postgres } from 'vite-plugin-neon-new'; -import { defineConfig } from 'vite'; +Stop using agent credentials when the state reaches `accepted`. Use credentials from the destination Neon organization after reconciliation. -export default defineConfig({ - plugins: [ - postgres({ referrer: 'your-app-name' }), - ], -}); +## Errors + +Errors use one JSON shape across provisioning, token, credential, and management endpoints: + +```json +{ + "error": { + "code": "capability_requires_claim", + "origin": "service", + "message": "Functions require claiming this project.", + "retryable": false, + "request_id": "req_..." + } +} ``` -The plugin provisions a database on first `vite dev` if `DATABASE_URL` is missing. It's inactive during `vite build`. +Use `error.code` for control flow and show `error.message` to the user. Retry only when `retryable` is `true`. -**Additional options:** +Common codes include: -```js -// Inside defineConfig({ plugins: [...] }) -postgres({ - referrer: 'your-app-name', // Required - dotEnvFile: '.env.local', // Default: .env - dotEnvKey: 'DATABASE_URL', // Default: DATABASE_URL - envPrefix: 'VITE_', // For public env vars - seed: { - type: 'sql-script', - path: './schema.sql', - }, -}) -``` +| Code | Meaning | +| --------------------------- | ------------------------------------------------------------- | +| `invalid_request` | The request body or parameter is invalid | +| `invalid_grant` | The identity assertion cannot be exchanged | +| `invalid_token` | The access token is invalid, expired, or revoked | +| `insufficient_scope` | The access token does not permit the operation | +| `capability_requires_claim` | The requested service or operation requires human ownership | +| `claim_in_progress` | Credential issuance is frozen while a claim is pending | +| `project_claimed` | The project transferred and agent credentials no longer apply | +| `upstream_unavailable` | A Neon API or service dependency is temporarily unavailable | + +## Legacy `neon.new` clients + +The `neon-new` package and `vite-plugin-neon-new` target the earlier `https://neon.new/api/v1/database` interface. They do not implement auth.md or scoped agent tokens. New agent integrations should use `neon claim`, `auth.md`, or the `/v1` API described on this page. ## Resources -- [Claimable Postgres website](https://neon.new/) -- [neon-new CLI on GitHub](https://github.com/neondatabase/neondb-cli/tree/main/packages/neon-new) -- [Vite Plugin on GitHub](https://github.com/neondatabase/neondb-cli/tree/main/packages/vite-plugin-neon-new) -- [Claimable database integration](/docs/workflows/claimable-database-integration) (build your own claimable Postgres experience) +- [Create a database in the browser](/claimable-postgres) +- [Claimable Neon auth.md](https://claimable.neon.tech/auth.md) +- [Claimable Neon source](https://github.com/neondatabase/claimable-neon) +- [Neon CLI reference](/docs/cli) +- [Claimable database integration](/docs/workflows/claimable-database-integration) diff --git a/content/docs/workflows/claimable-database-integration.md b/content/docs/workflows/claimable-database-integration.md index bf3cafc9c49..fd18bb9d8dd 100644 --- a/content/docs/workflows/claimable-database-integration.md +++ b/content/docs/workflows/claimable-database-integration.md @@ -10,7 +10,7 @@ summary: >- creation. The feature is in private preview and does not support transferring projects into Vercel-managed Neon organizations. enableTableOfContents: true -updatedOn: '2026-07-15T00:58:07.525Z' +updatedOn: '2026-08-11T16:50:44.197Z' --- ## Overview @@ -242,7 +242,7 @@ Without the `org_id` parameter, the project transfers to the user's personal acc - **Demo environments** - Create ready-to-use demo databases that prospects can claim - **Team environments** - Provision project databases for team members to claim into their organization -For a working implementation of claimable databases, try [Claimable Postgres by Neon](https://neon.new/). This service demonstrates the complete flow: users receive a Postgres connection string immediately without creating an account, and databases remain active for 72 hours. To retain the database beyond this period, users claim it by creating a Neon account using the provided transfer URL. See the [Claimable Postgres documentation](/docs/reference/claimable-postgres) for implementation details. This same pattern enables SaaS providers to offer instant database provisioning while allowing users to take ownership when ready. +For a working implementation, try [Claimable Postgres by Neon](/claimable-postgres). Agents can provision a temporary database through auth.md, receive project-scoped credentials, and hand a claim link to a human. The registration response provides the project expiration time. See the [Claimable Postgres reference](/docs/reference/claimable-postgres) for the agent protocol, API, and CLI commands. ## Troubleshooting diff --git a/src/app/api/claimable-postgres/route.js b/src/app/api/claimable-postgres/route.js new file mode 100644 index 00000000000..75d21676605 --- /dev/null +++ b/src/app/api/claimable-postgres/route.js @@ -0,0 +1,306 @@ +const DEFAULT_CLAIMABLE_NEON_ORIGIN = 'https://claimable.neon.tech'; +const ALLOWED_SERVICES = new Map([ + ['auth', 'auth'], + ['data-api', 'data_api'], +]); + +const API_INFO = { + endpoint: 'POST /api/claimable-postgres', + description: + 'Create a temporary database on Neon with optional Managed Better Auth and Data API.', + body: { + services: ['auth', 'data-api'], + }, +}; + +class ClaimableResponseError extends Error { + constructor(status, body) { + super('Claimable Neon rejected the request.'); + this.name = 'ClaimableResponseError'; + this.status = status; + this.body = body; + } +} + +class ClaimableConnectionError extends Error { + constructor(cause) { + super('Claimable Neon could not be reached.', { cause }); + this.name = 'ClaimableConnectionError'; + } +} + +class ClaimableContractError extends Error { + constructor(message) { + super(message); + this.name = 'ClaimableContractError'; + } +} + +const jsonResponse = (body, status = 200) => + Response.json(body, { + status, + headers: { + 'Cache-Control': 'no-store', + }, + }); + +const claimableOrigin = () => { + const configured = process.env.CLAIMABLE_NEON_ORIGIN || DEFAULT_CLAIMABLE_NEON_ORIGIN; + const url = new URL(configured); + if (url.protocol !== 'https:' && url.protocol !== 'http:') { + throw new Error('CLAIMABLE_NEON_ORIGIN must use http or https.'); + } + return url.origin; +}; + +const requestClaimable = async (path, init = {}) => { + let response; + try { + response = await fetch(`${claimableOrigin()}${path}`, { + ...init, + cache: 'no-store', + }); + } catch (error) { + throw new ClaimableConnectionError(error); + } + + const text = await response.text(); + let body = null; + if (text) { + try { + body = JSON.parse(text); + } catch { + throw new ClaimableContractError( + `Claimable Neon returned non-JSON content with HTTP ${response.status}.` + ); + } + } + + if (!response.ok) { + throw new ClaimableResponseError(response.status, body); + } + return body; +}; + +const exchangeAssertion = (assertion) => + requestClaimable('/v1/oauth2/token', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion, + resource: `${claimableOrigin()}/`, + }), + }); + +const requireRegistration = (body) => { + if ( + !body || + typeof body.identity_assertion !== 'string' || + !body.project || + typeof body.project.id !== 'string' || + typeof body.project.branch_id !== 'string' || + typeof body.project.expires_at !== 'string' || + !Array.isArray(body.capabilities) + ) { + throw new ClaimableContractError('Claimable Neon returned an invalid registration.'); + } + return body; +}; + +const requireToken = (body) => { + if (!body || typeof body.access_token !== 'string') { + throw new ClaimableContractError('Claimable Neon returned an invalid access token.'); + } + return body.access_token; +}; + +const requireCredentials = (body, project) => { + if ( + !body || + body.project_id !== project.id || + body.branch_id !== project.branch_id || + typeof body.database_url !== 'string' || + !body.services || + typeof body.services !== 'object' + ) { + throw new ClaimableContractError( + 'Claimable Neon returned invalid credentials for the provisioned project.' + ); + } + return body; +}; + +const requireClaim = (body) => { + if ( + !body || + typeof body.verification_uri_complete !== 'string' || + typeof body.expires_in !== 'number' + ) { + throw new ClaimableContractError('Claimable Neon returned an invalid claim link.'); + } + return body; +}; + +const errorFor = (error) => { + if (error instanceof ClaimableResponseError) { + return jsonResponse( + error.body ?? { + error: { + code: 'claimable_request_failed', + message: `Claimable Neon returned HTTP ${error.status}.`, + }, + }, + error.status + ); + } + if (error instanceof ClaimableConnectionError) { + return jsonResponse( + { + error: { + code: 'claimable_service_unavailable', + message: error.message, + }, + }, + 502 + ); + } + if (error instanceof ClaimableContractError) { + return jsonResponse( + { + error: { + code: 'invalid_claimable_response', + message: error.message, + }, + }, + 502 + ); + } + console.error('[claimable-postgres] Unexpected error:', error); + return jsonResponse( + { + error: { + code: 'internal_error', + message: 'The database could not be created.', + }, + }, + 500 + ); +}; + +export async function GET() { + return jsonResponse(API_INFO); +} + +export async function POST(request) { + let input; + try { + input = await request.json(); + } catch { + return jsonResponse( + { + error: { + code: 'invalid_request', + message: 'Request body must be valid JSON.', + }, + }, + 400 + ); + } + + const services = input?.services ?? []; + if ( + !Array.isArray(services) || + services.some((service) => typeof service !== 'string' || !ALLOWED_SERVICES.has(service)) + ) { + return jsonResponse( + { + error: { + code: 'invalid_request', + message: 'services may contain only auth and data-api.', + }, + }, + 400 + ); + } + + const requestedServices = new Set(services); + const capabilities = [ + 'postgres', + ...['data-api', 'auth'] + .filter((service) => requestedServices.has(service)) + .map((service) => ALLOWED_SERVICES.get(service)), + ]; + + let registration; + let accessToken; + try { + registration = requireRegistration( + await requestClaimable('/v1/agent/identity', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + type: 'anonymous', + capabilities, + source: 'neon_website', + }), + }) + ); + + accessToken = requireToken(await exchangeAssertion(registration.identity_assertion)); + const authorization = { Authorization: `Bearer ${accessToken}` }; + const credentials = requireCredentials( + await requestClaimable(`/v1/databases/${registration.project.id}/credentials`, { + headers: authorization, + }), + registration.project + ); + const claim = requireClaim( + await requestClaimable(`/v1/databases/${registration.project.id}/claim`, { + method: 'POST', + headers: authorization, + }) + ); + + return jsonResponse( + { + project: registration.project, + capabilities: registration.capabilities, + credentials, + claim: { + verification_uri_complete: claim.verification_uri_complete, + expires_in: claim.expires_in, + }, + }, + 201 + ); + } catch (error) { + if (registration) { + try { + if (!accessToken) { + accessToken = requireToken(await exchangeAssertion(registration.identity_assertion)); + } + await requestClaimable(`/v1/databases/${registration.project.id}`, { + method: 'DELETE', + headers: { Authorization: `Bearer ${accessToken}` }, + }); + } catch (cleanupError) { + console.error( + `[claimable-postgres] Cleanup failed for project ${registration.project.id}:`, + cleanupError + ); + return jsonResponse( + { + error: { + code: 'claimable_cleanup_failed', + message: + 'Database setup failed and the temporary project could not be deleted. It will expire automatically.', + }, + }, + 500 + ); + } + } + return errorFor(error); + } +} diff --git a/src/app/api/claimable-postgres/route.test.js b/src/app/api/claimable-postgres/route.test.js new file mode 100644 index 00000000000..284f0ed5e21 --- /dev/null +++ b/src/app/api/claimable-postgres/route.test.js @@ -0,0 +1,232 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +global.fetch = vi.fn(); + +const ORIGIN = 'https://claimable.example'; + +const registration = { + registration_id: 'reg_test', + identity_assertion: 'assertion-secret', + project: { + id: 'quiet-fog-12345678', + branch_id: 'br-main', + expires_at: '2026-08-14T12:00:00.000Z', + }, + capabilities: [ + { capability: 'postgres', granted: true }, + { capability: 'data_api', granted: true }, + { capability: 'auth', granted: true }, + ], +}; + +const token = { + access_token: 'access-secret', + token_type: 'Bearer', + expires_in: 900, + scope: 'postgres.read postgres.write data_api.query auth.configure', +}; + +const credentials = { + project_id: registration.project.id, + branch_id: registration.project.branch_id, + database_url: 'postgresql://example', + expires_at: registration.project.expires_at, + services: { + data_api: { url: 'https://data-api.example/rest/v1' }, + auth: { + base_url: 'https://auth.example/api/auth', + jwks_url: 'https://auth.example/.well-known/jwks.json', + }, + }, +}; + +const claim = { + user_code: 'ABCD-2345', + verification_uri: `${ORIGIN}/claim`, + verification_uri_complete: `${ORIGIN}/claim?user_code=ABCD-2345`, + expires_in: 900, + interval: 5, +}; + +const jsonResponse = (body, init) => + new Response(JSON.stringify(body), { + status: init?.status ?? 200, + headers: { 'Content-Type': 'application/json' }, + }); + +describe('/api/claimable-postgres', () => { + let GET; + let POST; + + beforeEach(async () => { + vi.clearAllMocks(); + vi.resetModules(); + process.env.CLAIMABLE_NEON_ORIGIN = ORIGIN; + ({ GET, POST } = await import('./route.js')); + }); + + afterEach(() => { + delete process.env.CLAIMABLE_NEON_ORIGIN; + }); + + it('documents the browser provisioning interface', async () => { + const response = await GET(); + + expect(response.status).toBe(200); + expect(await response.json()).toMatchObject({ + endpoint: 'POST /api/claimable-postgres', + body: { + services: ['auth', 'data-api'], + }, + }); + }); + + it('provisions requested services and returns credentials plus a claim URL', async () => { + global.fetch + .mockResolvedValueOnce(jsonResponse(registration)) + .mockResolvedValueOnce(jsonResponse(token)) + .mockResolvedValueOnce(jsonResponse(credentials)) + .mockResolvedValueOnce(jsonResponse(claim)); + + const response = await POST( + new Request('https://neon.com/api/claimable-postgres', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: ['auth', 'data-api'] }), + }) + ); + + expect(response.status).toBe(201); + expect(response.headers.get('Cache-Control')).toBe('no-store'); + expect(await response.json()).toEqual({ + project: registration.project, + capabilities: registration.capabilities, + credentials, + claim: { + verification_uri_complete: claim.verification_uri_complete, + expires_in: claim.expires_in, + }, + }); + + expect(global.fetch).toHaveBeenNthCalledWith( + 1, + `${ORIGIN}/v1/agent/identity`, + expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ + type: 'anonymous', + capabilities: ['postgres', 'data_api', 'auth'], + source: 'neon_website', + }), + }) + ); + expect(global.fetch).toHaveBeenNthCalledWith( + 3, + `${ORIGIN}/v1/databases/${registration.project.id}/credentials`, + expect.objectContaining({ + headers: expect.objectContaining({ Authorization: `Bearer ${token.access_token}` }), + }) + ); + expect(global.fetch).toHaveBeenNthCalledWith( + 4, + `${ORIGIN}/v1/databases/${registration.project.id}/claim`, + expect.objectContaining({ method: 'POST' }) + ); + }); + + it('rejects unknown services before provisioning', async () => { + const response = await POST( + new Request('https://neon.com/api/claimable-postgres', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: ['functions'] }), + }) + ); + + expect(response.status).toBe(400); + expect(await response.json()).toEqual({ + error: { + code: 'invalid_request', + message: 'services may contain only auth and data-api.', + }, + }); + expect(global.fetch).not.toHaveBeenCalled(); + }); + + it('preserves structured Claimable Neon errors', async () => { + const upstreamError = { + error: { + code: 'rate_limit_exceeded', + origin: 'service', + message: 'Too many projects were created from this client.', + retryable: true, + request_id: 'req_test', + }, + }; + global.fetch.mockResolvedValueOnce(jsonResponse(upstreamError, { status: 429 })); + + const response = await POST( + new Request('https://neon.com/api/claimable-postgres', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: [] }), + }) + ); + + expect(response.status).toBe(429); + expect(await response.json()).toEqual(upstreamError); + }); + + it('returns 502 when Claimable Neon cannot be reached', async () => { + global.fetch.mockRejectedValueOnce(new Error('ECONNREFUSED')); + + const response = await POST( + new Request('https://neon.com/api/claimable-postgres', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: [] }), + }) + ); + + expect(response.status).toBe(502); + expect(await response.json()).toEqual({ + error: { + code: 'claimable_service_unavailable', + message: 'Claimable Neon could not be reached.', + }, + }); + }); + + it('deletes the remote project when setup fails after registration', async () => { + const upstreamError = { + error: { + code: 'upstream_unavailable', + message: 'Credentials are temporarily unavailable.', + }, + }; + global.fetch + .mockResolvedValueOnce(jsonResponse(registration)) + .mockResolvedValueOnce(jsonResponse(token)) + .mockResolvedValueOnce(jsonResponse(upstreamError, { status: 503 })) + .mockResolvedValueOnce(new Response(null, { status: 204 })); + + const response = await POST( + new Request('https://neon.com/api/claimable-postgres', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: [] }), + }) + ); + + expect(response.status).toBe(503); + expect(await response.json()).toEqual(upstreamError); + expect(global.fetch).toHaveBeenNthCalledWith( + 4, + `${ORIGIN}/v1/databases/${registration.project.id}`, + expect.objectContaining({ + method: 'DELETE', + headers: expect.objectContaining({ Authorization: `Bearer ${token.access_token}` }), + }) + ); + }); +}); diff --git a/src/app/claimable-postgres/page.jsx b/src/app/claimable-postgres/page.jsx new file mode 100644 index 00000000000..36440c822db --- /dev/null +++ b/src/app/claimable-postgres/page.jsx @@ -0,0 +1,14 @@ +import ClaimablePostgres from 'components/pages/claimable-postgres/claimable-postgres'; +import Layout from 'components/shared/layout'; +import SEO_DATA from 'constants/seo-data'; +import getMetadata from 'utils/get-metadata'; + +export const metadata = getMetadata(SEO_DATA.claimablePostgres); + +const ClaimablePostgresPage = () => ( + + + +); + +export default ClaimablePostgresPage; diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx new file mode 100644 index 00000000000..6a42de847ef --- /dev/null +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -0,0 +1,439 @@ +'use client'; + +import PropTypes from 'prop-types'; +import { useState } from 'react'; + +import Button from 'components/shared/button'; +import Container from 'components/shared/container'; +import Link from 'components/shared/link'; +import useCopyToClipboard from 'hooks/use-copy-to-clipboard'; + +const SERVICES = [ + { + id: 'data-api', + title: 'Data API', + description: 'Query the database over HTTPS.', + }, + { + id: 'auth', + title: 'Managed Better Auth', + description: 'Add authentication that shares the project lifecycle.', + }, +]; + +const CODE_EXAMPLES = { + agent: `GET https://claimable.neon.tech/auth.md + +POST https://claimable.neon.tech/v1/agent/identity +Content-Type: application/json + +{ + "type": "anonymous", + "capabilities": ["postgres", "data_api"] +}`, + cli: `npx neon@latest claim create \\ + --service data-api \\ + --env-pull + +neon branches list +neon claim accept`, + config: `import { defineConfig } from '@neon/config'; + +export default defineConfig({ + dataApi: true, + auth: false, +});`, +}; + +const CopyButton = ({ value, label = 'Copy' }) => { + const { isCopied, handleCopy } = useCopyToClipboard(1600); + + return ( + + ); +}; + +CopyButton.propTypes = { + value: PropTypes.string.isRequired, + label: PropTypes.string, +}; + +const Credential = ({ label, value }) => ( +
+
+ {label} + +
+ + {value} + +
+); + +Credential.propTypes = { + label: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, +}; + +const Capability = ({ name, granted }) => ( + + {name.replaceAll('_', ' ')} + +); + +Capability.propTypes = { + name: PropTypes.string.isRequired, + granted: PropTypes.bool.isRequired, +}; + +const Provisioner = () => { + const [selectedServices, setSelectedServices] = useState(new Set()); + const [state, setState] = useState({ status: 'idle' }); + + const toggleService = (service) => { + setSelectedServices((current) => { + const next = new Set(current); + if (next.has(service)) next.delete(service); + else next.add(service); + return next; + }); + }; + + const createDatabase = async () => { + setState({ status: 'loading' }); + try { + const response = await fetch('/api/claimable-postgres', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: [...selectedServices] }), + }); + const body = await response.json(); + if (!response.ok) { + throw new Error( + body?.error?.message || + body?.error || + `Database creation failed with HTTP ${response.status}.` + ); + } + setState({ status: 'success', result: body }); + } catch (error) { + setState({ + status: 'error', + message: error instanceof Error ? error.message : 'The database could not be created.', + }); + } + }; + + if (state.status === 'success') { + const { capabilities, claim, credentials, project } = state.result; + const expiresAt = new Intl.DateTimeFormat(undefined, { + dateStyle: 'medium', + timeStyle: 'short', + }).format(new Date(project.expires_at)); + + return ( +
+
+
+
+

Database ready

+

Connect your agent

+
+ + {project.id} + +
+ +
+ {capabilities.map(({ capability, granted }) => ( + + ))} +
+ +
+ + {credentials.services.data_api?.url && ( + + )} + {credentials.services.auth?.base_url && ( + + )} +
+ +
+

+ This project expires on {expiresAt} unless you claim it. The claim link expires in{' '} + {Math.round(claim.expires_in / 60)} minutes. +

+ +
+
+ ); + } + + return ( +
+
+
+

Provision from this page

+

Choose your backend

+
+ +
+
+
+

Lakebase Postgres

+

+ A temporary database is always included. +

+
+ + Included + +
+ + {SERVICES.map((service) => { + const isSelected = selectedServices.has(service.id); + return ( + + ); + })} +
+ + {state.status === 'error' && ( +
+ {state.message} +
+ )} + + + +

+ No account or payment details required. Claim the project to keep it. +

+
+ ); +}; + +const InterfaceCard = ({ eyebrow, title, description, code }) => ( +
+

{eyebrow}

+

{title}

+

{description}

+
+
+ +
+
+        {code}
+      
+
+
+); + +InterfaceCard.propTypes = { + eyebrow: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, + code: PropTypes.string.isRequired, +}; + +const ClaimablePostgres = () => ( + <> +
+
+ +
+
+
+ + Claimable Postgres +
+

+ A database when your agent needs one. +

+

+ Agents can provision Lakebase Postgres on Neon before a human creates an account. + Start building, then transfer the project into a Neon organization when it matters. +

+
+ + + Read the agent guide + +
+
+ {[ + ['01', 'Provision', 'Get scoped credentials'], + ['02', 'Build', 'Use standard Postgres tools'], + ['03', 'Claim', 'Transfer to your organization'], + ].map(([number, title, description]) => ( +
+ {number} +

{title}

+

{description}

+
+ ))} +
+
+
+ +
+
+
+
+ +
+ +
+

+ One service, three interfaces +

+

+ Agents discover the protocol. Developers use the CLI. Projects keep their config. +

+

+ The same scoped agent credential works through the Claimable Neon API, Neon CLI, and{' '} + neon.ts. +

+
+
+ + + +
+
+
+ +
+ +
+
+

+ The ownership boundary +

+

+ Temporary infrastructure, explicit handoff +

+
+
+ {[ + [ + 'Scoped from the first request', + 'The service creates one project and issues credentials for that project. It does not expose the provisioning account.', + ], + [ + 'Human ownership starts at claim', + 'A short-lived claim link starts a project transfer into the Neon organization selected by the human.', + ], + [ + 'Unclaimed projects expire', + 'Every response includes an expiration time. Claim the project before then to keep the database and its services.', + ], + ].map(([title, description], index) => ( +
+ 0{index + 1} +
+

{title}

+

{description}

+
+
+ ))} +
+
+
+
+ +); + +export default ClaimablePostgres; diff --git a/src/constants/links.js b/src/constants/links.js index 3471b21c39b..4d7faa56c93 100644 --- a/src/constants/links.js +++ b/src/constants/links.js @@ -7,6 +7,7 @@ export default { careers: 'https://www.databricks.com/company/careers/open-positions?department=engineering&location=all&itm_source=www&itm_category=company&itm_page=engineering-at-databricks&itm_location=body&itm_component=hero&itm_offer=open-positions', caseStudies: '/case-studies', + claimablePostgres: '/claimable-postgres', cli: '/cli', contactSales: '/contact-sales', enterprise: '/enterprise', @@ -118,5 +119,5 @@ export default { apiReference: '/docs/reference/api', bugBounty: 'https://hackerone.com/neon_bbp', bookMeeting: 'https://calendly.com/d/cm8j-8yw-fq8', - claimablePostgres: 'https://neon.new/', + claimablePostgresApi: 'https://claimable.neon.tech', }; diff --git a/src/constants/seo-data.js b/src/constants/seo-data.js index 98a836b5c87..596ec4ed1ba 100644 --- a/src/constants/seo-data.js +++ b/src/constants/seo-data.js @@ -51,6 +51,12 @@ export default { pathname: LINKS.caseStudies, imagePath: '/images/social-previews/case-studies.jpg', }, + claimablePostgres: { + title: 'Claimable Postgres for agents — Neon', + description: + 'Let agents provision temporary Lakebase Postgres, Data API, and Managed Better Auth before a human creates a Neon account.', + pathname: LINKS.claimablePostgres, + }, cli: { title: 'Your Neon workflow lives in the terminal', description: 'The Neon CLI brings the Neon backend platform to your terminal.', diff --git a/src/scripts/llms-index-config.js b/src/scripts/llms-index-config.js index 127056a3643..d2c32517116 100644 --- a/src/scripts/llms-index-config.js +++ b/src/scripts/llms-index-config.js @@ -17,7 +17,7 @@ module.exports = { tagline: - 'Neon is the backend for apps and agents. Services include Neon Postgres, Neon Auth, Data API, Neon Functions, Object Storage, and AI Gateway. Every service is agent-ready: instant, branchable, and serverless. Neon Postgres includes autoscaling, instant restore, and scale-to-zero, and is fully compatible with any language, framework, or ORM that supports Postgres.', + 'Neon is a complete set of cloud backend primitives built around Lakebase Postgres, for developers, startups, and agent platforms, from Databricks. Services include Lakebase Postgres, Managed Better Auth, Data API, Functions, Object Storage, and AI Gateway. Lakebase Postgres on Neon includes autoscaling, instant restore, and scale-to-zero, and works with any language, framework, or ORM that supports Postgres.', intro: [ 'Neon docs are available as markdown.', @@ -27,6 +27,10 @@ module.exports = { // Quick-reference links emitted as "## Common Queries" before the section list. commonQueries: [ + { + label: 'Provision a temporary database for an agent (Claimable Postgres)', + url: 'https://neon.com/docs/reference/claimable-postgres.md', + }, { label: 'Pricing and plans', url: 'https://neon.com/pricing.md' }, { label: 'Choose a connection method (drivers, pooling, serverless)', From 6461795d4a359180374328f25a35ce97a1fb2500 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 11 Aug 2026 09:52:18 -0700 Subject: [PATCH 02/50] docs: run claim commands from the latest CLI --- content/docs/reference/claimable-postgres.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 2b1bb29955f..013769ba916 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -206,22 +206,22 @@ Unsupported pre-claim capabilities return the `capability_requires_claim` error ```bash # Create a Postgres project -neon claim create --env-pull +npx neon@latest claim create --env-pull # Request services explicitly -neon claim create --service data-api --service auth --env-pull +npx neon@latest claim create --service data-api --service auth --env-pull # Check the current human-claim state -neon claim status +npx neon@latest claim status # Generate and open a human claim link -neon claim accept +npx neon@latest claim accept # List claimable projects saved on this machine -neon claim list +npx neon@latest claim list # Delete the unclaimed project and local credentials -neon claim delete --yes +npx neon@latest claim delete --yes ``` After `claim create`, regular Neon CLI commands exchange the saved identity assertion and route supported management operations through Claimable Neon. Explicit Neon account credentials take precedence when you pass them. From 396b066c368ea7d319606eb0e2df387b91802e5a Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Thu, 13 Aug 2026 09:00:39 -0700 Subject: [PATCH 03/50] docs: remove neon.new from Claimable Postgres launch The reference page is Claimable Neon only: no legacy neon.new clients section, resource paths are /v1/projects, and current guides/pricing/FAQ point at /claimable-postgres and neon claim create. Drop the published claimable-postgres skill copy from neon.com. --- config/skills.json | 1 - content/docs/ai/agent-skills.md | 15 +- .../guides/platform-integration-overview.md | 4 +- content/docs/guides/tanstack-start.md | 26 +- content/docs/introduction/roadmap.md | 4 +- content/docs/reference/claimable-postgres.md | 19 +- ...ses-instantly-spin-up-postgres-instance.md | 2 +- .../isolated-subagents-neon-branching.md | 4 +- content/pages/use-cases/ai-agents.md | 2 +- public/.well-known/agent-skills/index.json | 7 - public/.well-known/skills/index.json | 7 - .../docs/.well-known/agent-skills/index.json | 7 - public/docs/.well-known/skills/index.json | 7 - .../ai/skills/claimable-postgres/SKILL.md | 260 ------------------ public/pricing.md | 2 +- .../.well-known/ai-catalog.json/catalog.json | 6 - src/scripts/generate-skills-index.test.js | 10 +- 17 files changed, 32 insertions(+), 351 deletions(-) delete mode 100644 public/docs/ai/skills/claimable-postgres/SKILL.md diff --git a/config/skills.json b/config/skills.json index 0144d22792c..58d1fea2051 100644 --- a/config/skills.json +++ b/config/skills.json @@ -2,7 +2,6 @@ "primary": "neon-postgres", "skills": [ { "name": "neon-postgres", "ref": "main" }, - { "name": "claimable-postgres", "ref": "main" }, { "name": "neon-postgres-egress-optimizer", "ref": "main" }, { "name": "neon-postgres-branches", "ref": "main" }, { "name": "neon", "ref": "main" }, diff --git a/content/docs/ai/agent-skills.md b/content/docs/ai/agent-skills.md index 5200bb1cf24..4703aa7d370 100644 --- a/content/docs/ai/agent-skills.md +++ b/content/docs/ai/agent-skills.md @@ -125,18 +125,17 @@ Start here for platform overview and Postgres development. | Skill | Description | | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -| [`neon`](https://skills.sh/neondatabase/agent-skills/neon) | Platform overview for apps and agents: Postgres, Auth, Data API, Functions, Storage, and AI Gateway, and how to get started | +| [`neon`](https://skills.sh/neondatabase/agent-skills/neon) | Platform overview for apps and agents: Postgres, Auth, Data API, Functions, Storage, and AI Gateway, including Claimable Neon when there is no account yet | | [`neon-postgres`](https://skills.sh/neondatabase/agent-skills/neon-postgres) | Full index of Neon Serverless Postgres documentation and best practices | ### Database workflows -Provision, branch, and optimize Postgres projects. +Branch and optimize Postgres projects. -| Skill | Description | -| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| [`claimable-postgres`](https://skills.sh/neondatabase/agent-skills/claimable-postgres) | Instant temporary Postgres via [Claimable Postgres](/docs/reference/claimable-postgres), no login or credit card required | -| [`neon-postgres-branches`](https://skills.sh/neondatabase/agent-skills/neon-postgres-branches) | Choose and create the right branch type for migrations, schema-only branches, and reset-from-parent workflows | -| [`neon-postgres-egress-optimizer`](https://skills.sh/neondatabase/agent-skills/neon-postgres-egress-optimizer) | Diagnose and fix excessive Postgres egress and query overfetching | +| Skill | Description | +| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| [`neon-postgres-branches`](https://skills.sh/neondatabase/agent-skills/neon-postgres-branches) | Choose and create the right branch type for migrations, schema-only branches, and reset-from-parent workflows | +| [`neon-postgres-egress-optimizer`](https://skills.sh/neondatabase/agent-skills/neon-postgres-egress-optimizer) | Diagnose and fix excessive Postgres egress and query overfetching | ### Neon Platform @@ -178,7 +177,7 @@ With the full skill set installed, your assistant can guide you across the Neon **Database workflows** -- **Disposable databases** via Claimable Postgres for agents and tests +- **Disposable databases** via Claimable Postgres in the `neon` skill, for agents and tests - **Branch types and workflows** for migrations, schema-only branches, and reset-from-parent - **Egress optimization** for high transfer costs and query anti-patterns diff --git a/content/docs/guides/platform-integration-overview.md b/content/docs/guides/platform-integration-overview.md index d0413d86362..b94d2edfe7d 100644 --- a/content/docs/guides/platform-integration-overview.md +++ b/content/docs/guides/platform-integration-overview.md @@ -13,7 +13,7 @@ redirectFrom: - /docs/guides/partner-intro - /docs/guides/platform-integration-intro - /docs/guides/platform-integration-get-started -updatedOn: '2026-08-07T17:19:40.308Z' +updatedOn: '2026-08-13T16:00:41.084Z' --- Neon provides multiple options for integrating Postgres into your platform, application, or service. Choose the integration approach that best matches your use case. @@ -79,7 +79,7 @@ Use this approach when you want to create databases for your users without requi - [TanStack](/blog/neon-joins-tanstack-instant-postgres-integration-for-faster-javascript-development): Official database partner offering instant Postgres through their Vite plugin and create-tanstack CLI - [Netlify DB](https://www.netlify.com/blog/netlify-db-database-for-ai-native-development/): One-click Postgres databases for Netlify projects, built on the claimable database flow -- [Claimable Postgres by Neon](https://neon.new/): Try instant Postgres provisioning without signup using `npx neon-new` or at [neon.new](https://neon.new/) +- [Claimable Postgres](/claimable-postgres): instant Postgres without signup, via the Neon CLI or [auth.md](https://claimable.neon.tech/auth.md) diff --git a/content/docs/guides/tanstack-start.md b/content/docs/guides/tanstack-start.md index 0e4c04d1e42..cdaf53955a4 100644 --- a/content/docs/guides/tanstack-start.md +++ b/content/docs/guides/tanstack-start.md @@ -12,7 +12,7 @@ enableTableOfContents: true redirectFrom: - /docs/quickstart/tanstack-start - /docs/integrations/tanstack-start -updatedOn: '2026-08-04T05:18:26.469Z' +updatedOn: '2026-08-13T16:00:41.084Z' --- - -```bash - npm neon-new -``` - -```bash - yarn dlx neon-new -``` +If you haven't created a database yet, [Claimable Postgres](/claimable-postgres) provisions a temporary database on Neon without an account: ```bash - pnpm neon-new +npx neon@latest claim create --env-pull ``` -```bash - bunx neon-new -``` - -```bash - deno run -A neon-new -``` - - +That writes `DATABASE_URL` to `.env`. A human can claim the project into a Neon account before it expires. See [Claimable Postgres](/docs/reference/claimable-postgres). ## Configure the Postgres client diff --git a/content/docs/introduction/roadmap.md b/content/docs/introduction/roadmap.md index 44a0fd12a83..15a5ee7d8a4 100644 --- a/content/docs/introduction/roadmap.md +++ b/content/docs/introduction/roadmap.md @@ -124,12 +124,12 @@ We're accelerating work on improving and scaling the core database on Neon as we - **Neon CircleCI orb**: A community-contributed orb provisions a Neon database branch per job for isolated, production-like CI. [Learn more](/docs/guides/branching-circleci). - **Add organization members by domain**: Verify email domains on the organization People page so users with matching addresses automatically join your org. [Learn more](/docs/manage/orgs-add-members-by-domain). - **TimescaleDB on Postgres 18**: The timescaledb extension is now supported on Postgres 18. [Learn more](/docs/extensions/timescaledb). -- **Claimable Postgres agent skill**: A new agent skill lets your AI assistant provision instant temporary databases via Claimable Postgres (neon.new) with no signup or credit card. [Learn more](/docs/reference/claimable-postgres). +- **Claimable Postgres**: Agents can provision a temporary database on Neon with no signup or credit card, then a human claims the project. [Learn more](/docs/reference/claimable-postgres). - **Neon MCP Server documentation tools**: The MCP Server now includes `list_docs_resources` and `get_doc_resource` so your agent can fetch Neon docs on demand. [Learn more](/docs/ai/connect-mcp-clients-to-neon). - **Compute autoscaling report**: A published report compares Neon autoscaling to provisioned compute using real production workloads, with methodology and key findings. [Learn more](https://neon.com/autoscaling-report). - **Consumption history API**: The consumption history API is now available on all paid plans. You can use it with Neon's usage-based pricing plans to track usage programmatically: query compute, storage, and data transfer at hourly, daily, or monthly granularity for custom dashboards and alerts. [Learn more](/docs/guides/consumption-metrics). - **Simpler MCP Server setup**: Configure the Neon MCP Server for all detected AI agents and editors with a single command: `npx add-mcp https://mcp.neon.tech/mcp`. OAuth opens in your browser to authorize access. For full setup (MCP plus agent skills and VS Code extension), use `npx neon@latest init`. [Learn more](/docs/ai/connect-mcp-clients-to-neon). -- **Claimable Postgres REST API**: Claimable Postgres now offers a REST API for programmatic database provisioning, making it easy to integrate Postgres into platforms, CI/CD, and automation. [Learn more](/docs/reference/claimable-postgres). +- **Claimable Postgres REST API**: Claimable Postgres offers an auth.md agent API for programmatic project provisioning. [Learn more](/docs/reference/claimable-postgres). - **Managed Better Auth SDK simplified**: The server-side Managed Better Auth SDK for Next.js now uses a unified `createNeonAuth()` API, explicit configuration, and session caching that reduces Auth Server API calls by 95–99%. [Learn more](/docs/auth/migrate/from-auth-v0.1). - **Agent Skills**: Install [Agent Skills](https://github.com/neondatabase/agent-skills) to teach your AI assistant about Neon best practices. The Neon MCP Server can also provision the Data API with optional Managed Better Auth or external auth. [Learn more](/docs/ai/connect-mcp-clients-to-neon). - **Managed Better Auth on Vercel previews**: Both Vercel-managed and Neon-managed integrations now automatically provision Managed Better Auth on preview branches when enabled on production, so preview deployments get the right auth environment variables. [Learn more](/docs/auth/overview). diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 013769ba916..84657952b7a 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -86,10 +86,10 @@ Claimable Postgres implements [auth.md](https://auth.md/). Agents can discover t | `POST /v1/agent/identity` | Provision a project and issue a durable identity assertion | | `POST /v1/oauth2/token` | Exchange the assertion for a short-lived bearer access token | | `POST /v1/oauth2/revoke` | Revoke an access token or identity assertion | -| `GET /v1/databases/{project_id}/credentials` | Read scoped project and service credentials | -| `POST /v1/databases/{project_id}/claim` | Create a short-lived human claim code | -| `GET /v1/databases/{project_id}/claim` | Read claim and reconciliation status | -| `DELETE /v1/databases/{project_id}` | Delete an unclaimed project | +| `GET /v1/projects/{project_id}/credentials` | Read scoped project and service credentials | +| `POST /v1/projects/{project_id}/claim` | Create a short-lived human claim code | +| `GET /v1/projects/{project_id}/claim` | Read claim and reconciliation status | +| `DELETE /v1/projects/{project_id}` | Delete an unclaimed project | | `/v1/projects/{project_id}/...` | Use supported Neon Management API operations before claiming | The identity assertion is a secret. Store it like an API key. There are no refresh tokens. Exchange the assertion again when an access token expires. @@ -163,7 +163,7 @@ The response contains a bearer `access_token`, its scope, and its expiration: ### Pull credentials ```bash -curl https://claimable.neon.tech/v1/databases/quiet-fog-12345678/credentials \ +curl https://claimable.neon.tech/v1/projects/quiet-fog-12345678/credentials \ --header "Authorization: Bearer $ACCESS_TOKEN" ``` @@ -245,7 +245,7 @@ Create a claim code with the API: ```bash curl --request POST \ - https://claimable.neon.tech/v1/databases/quiet-fog-12345678/claim \ + https://claimable.neon.tech/v1/projects/quiet-fog-12345678/claim \ --header "Authorization: Bearer $ACCESS_TOKEN" ``` @@ -268,7 +268,7 @@ Open `verification_uri_complete`, sign in to Neon, and choose the destination or Poll claim status at the server-provided `interval`: ```bash -curl https://claimable.neon.tech/v1/databases/quiet-fog-12345678/claim \ +curl https://claimable.neon.tech/v1/projects/quiet-fog-12345678/claim \ --header "Authorization: Bearer $ACCESS_TOKEN" ``` @@ -305,14 +305,9 @@ Common codes include: | `project_claimed` | The project transferred and agent credentials no longer apply | | `upstream_unavailable` | A Neon API or service dependency is temporarily unavailable | -## Legacy `neon.new` clients - -The `neon-new` package and `vite-plugin-neon-new` target the earlier `https://neon.new/api/v1/database` interface. They do not implement auth.md or scoped agent tokens. New agent integrations should use `neon claim`, `auth.md`, or the `/v1` API described on this page. - ## Resources - [Create a database in the browser](/claimable-postgres) - [Claimable Neon auth.md](https://claimable.neon.tech/auth.md) -- [Claimable Neon source](https://github.com/neondatabase/claimable-neon) - [Neon CLI reference](/docs/cli) - [Claimable database integration](/docs/workflows/claimable-database-integration) diff --git a/content/faqs/databases-instantly-spin-up-postgres-instance.md b/content/faqs/databases-instantly-spin-up-postgres-instance.md index c232cbf3cb0..b703b0a8901 100644 --- a/content/faqs/databases-instantly-spin-up-postgres-instance.md +++ b/content/faqs/databases-instantly-spin-up-postgres-instance.md @@ -32,7 +32,7 @@ The last command prints a Postgres connection string you can pass to any driver. For automated provisioning, the [Neon API](/docs/reference/api) creates a project, branch, and compute in one call. This is how platforms like Vercel and Replit spin up a per-user database the moment someone signs up. -If you don't want to sign up at all, [Claimable Postgres](https://neon.new) gives you a working database in seconds without an account. Run `npx neon-new --yes` and you get a connection string that's valid for 72 hours. Claim it to a Neon account before it expires to keep it. +If you don't want to sign up at all, [Claimable Postgres](https://neon.com/claimable-postgres) gives you a working database in seconds without an account. Run `npx neon@latest claim create --env-pull` and you get a connection string. Claim it to a Neon account before it expires to keep it. See the [Claimable Postgres docs](https://neon.com/docs/reference/claimable-postgres.md). ## What you get on the Free plan diff --git a/content/guides/isolated-subagents-neon-branching.md b/content/guides/isolated-subagents-neon-branching.md index 60e8baa5e2a..6a04f678c72 100644 --- a/content/guides/isolated-subagents-neon-branching.md +++ b/content/guides/isolated-subagents-neon-branching.md @@ -4,7 +4,7 @@ subtitle: 'Automate parallel feature development by giving every Claude Code sub author: dhanush-reddy enableTableOfContents: true createdAt: '2026-03-03T00:00:00.000Z' -updatedOn: '2026-06-11T23:50:21.258Z' +updatedOn: '2026-08-13T16:00:41.084Z' --- While modern AI assistants generate code at remarkable speeds, the development process often remains sequential. You ask an agent to build a feature and wait. You request a query optimization and wait again. This workflow is bottlenecked by the linear nature of the standard AI interface, which can only run one agent at a time, completing one task before starting the next. @@ -34,7 +34,7 @@ To demonstrate this in action, a social media application built with Next.js and Before you begin, ensure you have the following: - **Claude Code:** Anthropic's official CLI tool installed. Visit [Claude code docs](https://code.claude.com/docs/en/quickstart#step-1-install-claude-code) for installation instructions. -- **Neon account and project:** A Neon account with an active project. Sign up at [neon.new](https://neon.new). +- **Neon account and project:** A Neon account with an active project. Sign up at [neon.com](https://console.neon.tech). - The [Neon CLI](/docs/cli) installed (`npm i -g neon` or `brew install neonctl`). - **Example application with Git repository**: Any application with a Git repository. This guide uses a Next.js app with Drizzle ORM (a simple social media app) as an example, but you can follow along with your own codebase. The emphasis here is on demonstrating the parallel workflow rather than the specifics of the application. diff --git a/content/pages/use-cases/ai-agents.md b/content/pages/use-cases/ai-agents.md index 2009e7015c6..7c0442a2ddb 100644 --- a/content/pages/use-cases/ai-agents.md +++ b/content/pages/use-cases/ai-agents.md @@ -126,4 +126,4 @@ For instructions on using the Neon API to provision and manage backends on behal To learn more about the Agent Plan, [see the details on this page](https://neon.com/programs/agents#agent-plan-pricing) or [fill out the application form directly, at the top of this page](#agent-form). - + diff --git a/public/.well-known/agent-skills/index.json b/public/.well-known/agent-skills/index.json index 8fe9cc8b81c..6249a9187b0 100644 --- a/public/.well-known/agent-skills/index.json +++ b/public/.well-known/agent-skills/index.json @@ -8,13 +8,6 @@ "url": "/.well-known/agent-skills/neon-postgres/SKILL.md", "digest": "sha256:eb8081a09e25ff2062e8f8de7cba00c38123aeb259771605c988ca0245389886" }, - { - "name": "claimable-postgres", - "type": "skill-md", - "description": "Provision instant temporary Postgres databases via Claimable Postgres by Neon (neon.new) with no login, signup, or credit card. Supports REST API, CLI, and SDK. Use when users ask for a quick Postgres environment, a throwaway DATABASE_URL for prototyping/tests, or \"just give me a DB now\". Triggers include: \"quick postgres\", \"temporary postgres\", \"no signup database\", \"no credit card database\", \"instant DATABASE_URL\", \"npx neon-new\", \"neon.new\", \"neon.new API\", \"claimable postgres API\".", - "url": "/.well-known/agent-skills/claimable-postgres/SKILL.md", - "digest": "sha256:54af158fccb7412ce0e00ce136c2f7e23fcf173670db84224c445f254d902b41" - }, { "name": "neon-postgres-egress-optimizer", "type": "skill-md", diff --git a/public/.well-known/skills/index.json b/public/.well-known/skills/index.json index 148b3d741fd..d09d8445f8e 100644 --- a/public/.well-known/skills/index.json +++ b/public/.well-known/skills/index.json @@ -7,13 +7,6 @@ "SKILL.md" ] }, - { - "name": "claimable-postgres", - "description": "Provision instant temporary Postgres databases via Claimable Postgres by Neon (neon.new) with no login, signup, or credit card. Supports REST API, CLI, and SDK. Use when users ask for a quick Postgres environment, a throwaway DATABASE_URL for prototyping/tests, or \"just give me a DB now\". Triggers include: \"quick postgres\", \"temporary postgres\", \"no signup database\", \"no credit card database\", \"instant DATABASE_URL\", \"npx neon-new\", \"neon.new\", \"neon.new API\", \"claimable postgres API\".", - "files": [ - "SKILL.md" - ] - }, { "name": "neon-postgres-egress-optimizer", "description": "Diagnose and fix excessive Postgres egress (network data transfer) in a codebase. Use when a user mentions high database bills, unexpected data transfer costs, network transfer charges, egress spikes, \"why is my Neon bill so high\", \"database costs jumped\", SELECT * optimization, query overfetching, reduce Neon costs, optimize database usage, or wants to reduce data sent from their database to their application. Also use when reviewing query patterns for cost efficiency, even if the user doesn't explicitly mention egress or data transfer.", diff --git a/public/docs/.well-known/agent-skills/index.json b/public/docs/.well-known/agent-skills/index.json index bb3322c8c8a..e7a313cfd6c 100644 --- a/public/docs/.well-known/agent-skills/index.json +++ b/public/docs/.well-known/agent-skills/index.json @@ -8,13 +8,6 @@ "url": "/docs/.well-known/agent-skills/neon-postgres/SKILL.md", "digest": "sha256:eb8081a09e25ff2062e8f8de7cba00c38123aeb259771605c988ca0245389886" }, - { - "name": "claimable-postgres", - "type": "skill-md", - "description": "Provision instant temporary Postgres databases via Claimable Postgres by Neon (neon.new) with no login, signup, or credit card. Supports REST API, CLI, and SDK. Use when users ask for a quick Postgres environment, a throwaway DATABASE_URL for prototyping/tests, or \"just give me a DB now\". Triggers include: \"quick postgres\", \"temporary postgres\", \"no signup database\", \"no credit card database\", \"instant DATABASE_URL\", \"npx neon-new\", \"neon.new\", \"neon.new API\", \"claimable postgres API\".", - "url": "/docs/.well-known/agent-skills/claimable-postgres/SKILL.md", - "digest": "sha256:54af158fccb7412ce0e00ce136c2f7e23fcf173670db84224c445f254d902b41" - }, { "name": "neon-postgres-egress-optimizer", "type": "skill-md", diff --git a/public/docs/.well-known/skills/index.json b/public/docs/.well-known/skills/index.json index 148b3d741fd..d09d8445f8e 100644 --- a/public/docs/.well-known/skills/index.json +++ b/public/docs/.well-known/skills/index.json @@ -7,13 +7,6 @@ "SKILL.md" ] }, - { - "name": "claimable-postgres", - "description": "Provision instant temporary Postgres databases via Claimable Postgres by Neon (neon.new) with no login, signup, or credit card. Supports REST API, CLI, and SDK. Use when users ask for a quick Postgres environment, a throwaway DATABASE_URL for prototyping/tests, or \"just give me a DB now\". Triggers include: \"quick postgres\", \"temporary postgres\", \"no signup database\", \"no credit card database\", \"instant DATABASE_URL\", \"npx neon-new\", \"neon.new\", \"neon.new API\", \"claimable postgres API\".", - "files": [ - "SKILL.md" - ] - }, { "name": "neon-postgres-egress-optimizer", "description": "Diagnose and fix excessive Postgres egress (network data transfer) in a codebase. Use when a user mentions high database bills, unexpected data transfer costs, network transfer charges, egress spikes, \"why is my Neon bill so high\", \"database costs jumped\", SELECT * optimization, query overfetching, reduce Neon costs, optimize database usage, or wants to reduce data sent from their database to their application. Also use when reviewing query patterns for cost efficiency, even if the user doesn't explicitly mention egress or data transfer.", diff --git a/public/docs/ai/skills/claimable-postgres/SKILL.md b/public/docs/ai/skills/claimable-postgres/SKILL.md deleted file mode 100644 index 3e163678050..00000000000 --- a/public/docs/ai/skills/claimable-postgres/SKILL.md +++ /dev/null @@ -1,260 +0,0 @@ ---- -name: claimable-postgres -description: >- - Provision instant temporary Postgres databases via Claimable Postgres by Neon - (neon.new) with no login, signup, or credit card. Supports REST API, CLI, and - SDK. Use when users ask for a quick Postgres environment, a throwaway - DATABASE_URL for prototyping/tests, or "just give me a DB now". Triggers - include: "quick postgres", "temporary postgres", "no signup database", - "no credit card database", "instant DATABASE_URL", "npx neon-new", "neon.new", - "neon.new API", "claimable postgres API". -metadata: - parent: neon - source: https://github.com/neondatabase/agent-skills/tree/main/skills/claimable-postgres ---- - -**FIRST**: Use the parent `neon` skill for a Neon overview, getting started with Neon, Neon development best practices, and more. - -If the `neon` skill is not installed, fetch it from https://neon.com/docs/ai/skills/neon/SKILL.md or install it with: - -```bash -npx skills add neondatabase/agent-skills --skill neon -``` - -# Claimable Postgres - -Instant Postgres databases for local development, demos, prototyping, and test environments. No account required. Databases expire after 72 hours unless claimed to a Neon account. - -## Quick Start - -```bash -curl -s -X POST "https://neon.new/api/v1/database" \ - -H "Content-Type: application/json" \ - -d '{"ref": "agent-skills"}' -``` - -Parse `connection_string` and `claim_url` from the JSON response. Write `connection_string` to the project's `.env` as `DATABASE_URL`. - -For other methods (CLI, SDK, Vite plugin), see [Which Method?](#which-method) below. - -## Which Method? - -- **REST API**: Returns structured JSON. No runtime dependency beyond `curl`. Preferred when the agent needs predictable output and error handling. -- **CLI** (`npx neon-new@latest --yes`): Provisions and writes `.env` in one command. Convenient when Node.js is available and the user wants a simple setup. -- **SDK** (`neon-new/sdk`): Scripts or programmatic provisioning in Node.js. -- **Vite plugin** (`vite-plugin-neon-new`): Auto-provisions on `vite dev` if `DATABASE_URL` is missing. Use when the user has a Vite project. -- **Browser**: User cannot run CLI or API. Direct to https://neon.new. - -## Auto-provisioning - -If the agent needs a database to fulfill a task (e.g. "build me a todo app with a real database") and the user has not provided a connection string, provision one via the API and inform the user. Include the claim URL so they can keep it. - -## Agent Workflow - -### API path - -1. **Confirm intent:** If the request is ambiguous, confirm the user wants a temporary, no-signup database. Skip this if they explicitly asked for a quick or temporary database. -2. **Provision:** POST to `https://neon.new/api/v1/database` with `{"ref": "agent-skills"}`. -3. **Parse response:** Extract `connection_string`, `claim_url`, and `expires_at` from the JSON response. -4. **Write .env:** Write `DATABASE_URL=` to the project's `.env` (or the user's preferred file and key). Do not overwrite an existing key without confirmation. -5. **Seed (if needed):** If the user has a seed SQL file, run it against the new database: - ```bash - psql "$DATABASE_URL" -f seed.sql - ``` -6. **Report:** Cover every item in the [Output Checklist](#output-checklist). -7. **Optional:** Offer a quick connection test (e.g. `SELECT 1`). - -### CLI path - -1. **Check .env:** Check the target `.env` for an existing `DATABASE_URL` (or chosen key). If present, do not run. Offer remove, `--env`, or `--key` and get confirmation (see [Pre-run Check](#pre-run-check)). -2. **Confirm intent:** If the request is ambiguous, confirm the user wants a temporary, no-signup database. Skip this if they explicitly asked for a quick or temporary database. -3. **Gather options:** Use defaults unless context suggests otherwise (e.g., user mentions a custom env file, seed SQL, or logical replication). -4. **Run:** Execute with `@latest --yes` plus the confirmed options. Always use `@latest` to avoid stale cached versions. `--yes` skips interactive prompts that would stall the agent. - ```bash - npx neon-new@latest --yes --ref agent-skills --env .env.local --seed ./schema.sql - ``` -5. **Verify:** Confirm the connection string was written to the intended file. -6. **Report:** Cover every item in the [Output Checklist](#output-checklist). -7. **Optional:** Offer a quick connection test (e.g. `SELECT 1`). - -### Output Checklist - -Always report: - -- Where the connection string was written (e.g. `.env`) -- Which variable key was used (`DATABASE_URL` or custom key) -- The claim URL (from `.env` or API response) -- That unclaimed databases are temporary (72 hours): the database works now, and claiming within 72 hours keeps it permanently - -## Safety and UX Notes - -- Do not overwrite existing env vars. Check first, then use `--env` or `--key` (CLI) or skip writing (API) to avoid conflicts. -- Ask before running destructive seed SQL (`DROP`, `TRUNCATE`, mass `DELETE`). -- For production workloads, recommend standard Neon provisioning instead of temporary claimable databases. -- If users need long-term persistence, instruct them to open the claim URL right away. -- After writing credentials to an .env file, check that it's covered by .gitignore. If not, warn the user. Do not modify `.gitignore` without confirmation. - -## REST API - -**Base URL:** `https://neon.new/api/v1` - -### Create a database - -```bash -curl -s -X POST "https://neon.new/api/v1/database" \ - -H "Content-Type: application/json" \ - -d '{"ref": "agent-skills"}' -``` - -| Parameter | Required | Description | -| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- | -| `ref` | Yes | Tracking tag that identifies who provisioned the database. Use `"agent-skills"` when provisioning through this skill. | -| `enable_logical_replication` | No | Enable logical replication (default: false, cannot be disabled once enabled) | - -The `connection_string` returned by the API is a pooled connection URL. For a direct (non-pooled) connection (e.g. Prisma migrations), remove `-pooler` from the hostname. The CLI writes both pooled and direct URLs automatically. - -**Response:** - -```json -{ - "id": "019beb39-37fb-709d-87ac-7ad6198b89f7", - "status": "UNCLAIMED", - "neon_project_id": "gentle-scene-06438508", - "connection_string": "postgresql://...", - "claim_url": "https://neon.new/claim/019beb39-...", - "expires_at": "2026-01-26T14:19:14.580Z", - "created_at": "2026-01-23T14:19:14.580Z", - "updated_at": "2026-01-23T14:19:14.580Z" -} -``` - -### Check status - -```bash -curl -s "https://neon.new/api/v1/database/{id}" -``` - -Returns the same response shape. Status transitions: `UNCLAIMED` -> `CLAIMING` -> `CLAIMED`. After the database is claimed, `connection_string` returns `null`. - -### Error responses - -| Condition | HTTP | Message | -| ---------------------- | ---- | -------------------------------- | -| Missing or empty `ref` | 400 | `Missing referrer` | -| Invalid database ID | 400 | `Database not found` | -| Invalid JSON body | 500 | `Failed to create the database.` | - -## CLI - -```bash -npx neon-new@latest --yes -``` - -Provisions a database and writes the connection string to `.env` in one step. Always use `@latest` and `--yes` (skips interactive prompts that would stall the agent). - -### Pre-run Check - -Check if `DATABASE_URL` (or the chosen key) already exists in the target `.env`. The CLI exits without provisioning if it finds the key. - -If the key exists, offer the user three options: - -1. Remove or comment out the existing line, then rerun. -2. Use `--env` to write to a different file (e.g. `--env .env.local`). -3. Use `--key` to write under a different variable name. - -Get confirmation before proceeding. - -### Options - -| Option | Alias | Description | Default | -| ----------------------- | ----- | --------------------------------------------------------------------- | -------------- | -| `--yes` | `-y` | Skip prompts, use defaults | `false` | -| `--env` | `-e` | .env file path | `./.env` | -| `--key` | `-k` | Connection string env var key | `DATABASE_URL` | -| `--prefix` | `-p` | Prefix for generated public env vars | `PUBLIC_` | -| `--seed` | `-s` | Path to seed SQL file | none | -| `--logical-replication` | `-L` | Enable logical replication | `false` | -| `--ref` | `-r` | Referrer id (use `agent-skills` when provisioning through this skill) | none | - -Alternative package managers: `yarn dlx neon-new@latest`, `pnpm dlx neon-new@latest`, `bunx neon-new@latest`, `deno run -A neon-new@latest`. - -### Output - -The CLI writes to the target `.env`: - -``` -DATABASE_URL=postgresql://... # pooled (use for application queries) -DATABASE_URL_DIRECT=postgresql://... # direct (use for migrations, e.g. Prisma) -PUBLIC_POSTGRES_CLAIM_URL=https://neon.new/claim/... -``` - -## SDK - -Use for scripts and programmatic provisioning flows. - -```typescript -import { instantPostgres } from "neon-new"; - -const { databaseUrl, databaseUrlDirect, claimUrl, claimExpiresAt } = - await instantPostgres({ - referrer: "agent-skills", - seed: { type: "sql-script", path: "./init.sql" }, - }); -``` - -Returns `databaseUrl` (pooled), `databaseUrlDirect` (direct, for migrations), `claimUrl`, and `claimExpiresAt` (Date object). The `referrer` parameter is required. - -## Vite Plugin - -For Vite projects, `vite-plugin-neon-new` auto-provisions a database on `vite dev` if `DATABASE_URL` is missing. Install with `npm install -D vite-plugin-neon-new`. See the [Claimable Postgres docs](https://neon.com/docs/reference/claimable-postgres#vite-plugin) for configuration. - -## Claiming - -Claiming is optional. The database works immediately without it. To optionally claim, the user opens the claim URL in a browser, where they sign in or create a Neon account to claim the database. - -- **API/SDK:** Give the user the `claim_url` from the create response. -- **CLI:** `npx neon-new@latest claim` reads the claim URL from `.env` and opens the browser automatically. - -Users cannot claim into Vercel-linked orgs; they must choose another Neon org. - -## Neon Infrastructure as Code (`neon.ts`) - -Claimable databases are deliberately throwaway and provisioned through `neon.new` (above), so they aren't managed by `neon.ts`. Once a user **claims** a database into a Neon account it becomes a normal Neon project — at which point `neon.ts`, Neon's infrastructure-as-code file, is how you manage it going forward (see the `neon` skill for the full reference): declare the services its branches should have, program per-branch compute, and get type-safe env vars. - -```bash -npm i @neon/config -``` - -```typescript -// neon.ts -import { defineConfig } from "@neon/config/v1"; - -export default defineConfig({ - auth: true, // Neon Auth, once you outgrow a bare connection string - dataApi: true, // Data API - branch: (branch) => (branch.exists ? {} : { ttl: "7d" }), // ephemeral non-default branches -}); -``` - -```bash -neon config apply # provision the declared services (neon deploy is an alias) -``` - -If a project needs branching, multiple services, or durable infrastructure tracked in version control, recommend claiming first and then adopting `neon.ts` — rather than re-provisioning throwaway claimable databases. - -## Defaults and Limits - -| Parameter | Value | -| --------- | --------- | -| Provider | AWS | -| Region | us-east-2 | -| Postgres | 17 | - -Region cannot be changed for claimable databases. Unclaimed databases have stricter quotas. Claiming resets limits to free plan defaults. - -| | Unclaimed | Claimed (Free plan) | -| ---------- | --------- | ------------------- | -| Storage | 100 MB | 512 MB | -| Transfer | 1 GB | ~5 GB | -| Branches | No | Yes | -| Expiration | 72 hours | None | diff --git a/public/pricing.md b/public/pricing.md index 4a221cf5314..bad38259ef2 100644 --- a/public/pricing.md +++ b/public/pricing.md @@ -73,7 +73,7 @@ See [Plans](https://neon.com/docs/introduction/plans.md) for full details. ### How can I try Neon without signing up? -Use [Claimable Postgres](https://neon.new/) for an instant database, with no signup and no card. Run `npx neon-new --yes` (or the [API](https://neon.com/docs/reference/claimable-postgres.md)) to provision Postgres that lasts 72 hours. Claim it to a Neon account before expiration to keep it. +Use [Claimable Postgres](https://neon.com/claimable-postgres) for an instant database, with no signup and no card. Run `npx neon@latest claim create --env-pull` (or the [API](https://neon.com/docs/reference/claimable-postgres.md)) to provision Postgres. Claim it to a Neon account before expiration to keep it. ### What is a project? diff --git a/src/app/.well-known/ai-catalog.json/catalog.json b/src/app/.well-known/ai-catalog.json/catalog.json index 4a071137b24..9cddb0f1664 100644 --- a/src/app/.well-known/ai-catalog.json/catalog.json +++ b/src/app/.well-known/ai-catalog.json/catalog.json @@ -25,12 +25,6 @@ "url": "https://neon.com/.well-known/agent-skills/neon-postgres/SKILL.md", "description": "Guides and best practices for working with Lakebase Postgres, the database behind Neon. Covers setup, connection methods and drivers, pooled vs direct connections, branching, schema migrations, autoscaling, scale-to-zero, instant restore, read replicas, connection pooling, IP allow lists, and logical replication. Use when users ask about \"Lakebase Postgres\", \"Neon setup\", \"connect to Neon\", \"Neon project\", \"DATABASE_URL\", \"serverless Postgres\", \"Neon CLI\", \"neon\", \"Neon MCP\", \"Neon Auth\", \"@neondatabase/serverless\", \"@neondatabase/neon-js\", \"scale to zero\", \"Neon autoscaling\", \"Neon read replica\", \"Neon connection pooling\", or \"schema migrations\"." }, - { - "identifier": "urn:air:neon.com:skill:claimable-postgres", - "type": "application/agent-skills+md", - "url": "https://neon.com/.well-known/agent-skills/claimable-postgres/SKILL.md", - "description": "Provision instant temporary Postgres databases via Claimable Postgres by Neon (neon.new) with no login, signup, or credit card. Supports REST API, CLI, and SDK. Use when users ask for a quick Postgres environment, a throwaway DATABASE_URL for prototyping/tests, or \"just give me a DB now\". Triggers include: \"quick postgres\", \"temporary postgres\", \"no signup database\", \"no credit card database\", \"instant DATABASE_URL\", \"npx neon-new\", \"neon.new\", \"neon.new API\", \"claimable postgres API\"." - }, { "identifier": "urn:air:neon.com:skill:neon-postgres-egress-optimizer", "type": "application/agent-skills+md", diff --git a/src/scripts/generate-skills-index.test.js b/src/scripts/generate-skills-index.test.js index 4c22b384aad..c076a02f89d 100644 --- a/src/scripts/generate-skills-index.test.js +++ b/src/scripts/generate-skills-index.test.js @@ -17,8 +17,8 @@ const SKILLS = [ digest: 'sha256:abc123', }, { - name: 'claimable-postgres', - description: 'Provision instant temporary Postgres databases.', + name: 'neon-functions', + description: 'Long-running serverless Node.js HTTP functions.', digest: 'sha256:def456', }, ]; @@ -44,19 +44,19 @@ describe('buildAgentSkillsIndex', () => { it('constructs root-relative urls with root prefix', () => { const index = buildAgentSkillsIndex(SKILLS, '/.well-known/agent-skills/'); expect(index.skills[0].url).toBe('/.well-known/agent-skills/neon-postgres/SKILL.md'); - expect(index.skills[1].url).toBe('/.well-known/agent-skills/claimable-postgres/SKILL.md'); + expect(index.skills[1].url).toBe('/.well-known/agent-skills/neon-functions/SKILL.md'); }); it('constructs /docs/-prefixed urls with docs prefix', () => { const index = buildAgentSkillsIndex(SKILLS, '/docs/.well-known/agent-skills/'); expect(index.skills[0].url).toBe('/docs/.well-known/agent-skills/neon-postgres/SKILL.md'); - expect(index.skills[1].url).toBe('/docs/.well-known/agent-skills/claimable-postgres/SKILL.md'); + expect(index.skills[1].url).toBe('/docs/.well-known/agent-skills/neon-functions/SKILL.md'); }); it('preserves skill ordering from input array', () => { const index = buildAgentSkillsIndex(SKILLS, '/.well-known/agent-skills/'); expect(index.skills[0].name).toBe('neon-postgres'); - expect(index.skills[1].name).toBe('claimable-postgres'); + expect(index.skills[1].name).toBe('neon-functions'); }); }); From 139514a3d85996c9320924492534c3c3c6687547 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 08:32:14 -0700 Subject: [PATCH 04/50] docs: point llms.txt at claimable.neon.tech/auth.md --- content/docs/reference/claimable-postgres.md | 2 ++ src/scripts/llms-index-config.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 84657952b7a..fda089c492f 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -75,6 +75,8 @@ The response contains an `identity_assertion`, project metadata, and one decisio ## Agent discovery +Agents start at [neon.com/docs/llms.txt](https://neon.com/docs/llms.txt), which points at [`auth.md`](https://claimable.neon.tech/auth.md) on the service origin. This page is for humans and SEO. It is not on that path. + Claimable Postgres implements [auth.md](https://auth.md/). Agents can discover the complete authentication and provisioning flow without a vendor-specific integration. | Document or endpoint | Purpose | diff --git a/src/scripts/llms-index-config.js b/src/scripts/llms-index-config.js index d2c32517116..c3bb117dd73 100644 --- a/src/scripts/llms-index-config.js +++ b/src/scripts/llms-index-config.js @@ -28,8 +28,8 @@ module.exports = { // Quick-reference links emitted as "## Common Queries" before the section list. commonQueries: [ { - label: 'Provision a temporary database for an agent (Claimable Postgres)', - url: 'https://neon.com/docs/reference/claimable-postgres.md', + label: 'Provision a temporary database for an agent (Claimable Neon)', + url: 'https://claimable.neon.tech/auth.md', }, { label: 'Pricing and plans', url: 'https://neon.com/pricing.md' }, { From 1cbab67ed92d5a6ac312ee937d1fa1d79d343e50 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 08:43:52 -0700 Subject: [PATCH 05/50] docs: keep the Claimable Postgres page off the agent path --- content/docs/reference/claimable-postgres.md | 4 ++-- .../pages/claimable-postgres/claimable-postgres.jsx | 2 +- src/scripts/llms-index-config.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index fda089c492f..1fd597453b7 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -1,6 +1,6 @@ --- title: Claimable Postgres by Neon -subtitle: Let an agent create a temporary database before a human creates an account +subtitle: Human reference. Agents fetch auth.md from neon.com/docs/llms.txt. summary: >- Claimable Postgres lets agents provision Lakebase Postgres on Neon through auth.md, the Neon CLI, or a REST API without a Neon account. The service @@ -75,7 +75,7 @@ The response contains an `identity_assertion`, project metadata, and one decisio ## Agent discovery -Agents start at [neon.com/docs/llms.txt](https://neon.com/docs/llms.txt), which points at [`auth.md`](https://claimable.neon.tech/auth.md) on the service origin. This page is for humans and SEO. It is not on that path. +Agents start at [neon.com/docs/llms.txt](https://neon.com/docs/llms.txt), which points at [`auth.md`](https://claimable.neon.tech/auth.md) on the service origin. This page is the human reference. Claimable Postgres implements [auth.md](https://auth.md/). Agents can discover the complete authentication and provisioning flow without a vendor-specific integration. diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx index 6a42de847ef..86d2ddf00a9 100644 --- a/src/components/pages/claimable-postgres/claimable-postgres.jsx +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -334,7 +334,7 @@ const ClaimablePostgres = () => ( theme="green" withArrow > - Read the agent guide + Read the docs
diff --git a/src/scripts/llms-index-config.js b/src/scripts/llms-index-config.js index c3bb117dd73..7f17a39873c 100644 --- a/src/scripts/llms-index-config.js +++ b/src/scripts/llms-index-config.js @@ -28,7 +28,7 @@ module.exports = { // Quick-reference links emitted as "## Common Queries" before the section list. commonQueries: [ { - label: 'Provision a temporary database for an agent (Claimable Neon)', + label: 'auth.md — provision a temporary Neon project for an agent', url: 'https://claimable.neon.tech/auth.md', }, { label: 'Pricing and plans', url: 'https://neon.com/pricing.md' }, From ffe38826b4594e807f26d58c1853d21f13ef2f2b Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 08:49:08 -0700 Subject: [PATCH 06/50] docs: name auth.md in the human Claimable Postgres index entry --- content/docs/reference/claimable-postgres.md | 11 ++++------- .../docs/workflows/claimable-database-integration.md | 4 ++-- src/scripts/llms-index-config.js | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 1fd597453b7..8df013669a9 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -1,12 +1,9 @@ --- title: Claimable Postgres by Neon -subtitle: Human reference. Agents fetch auth.md from neon.com/docs/llms.txt. +subtitle: CLI, claim, and HTTP reference summary: >- - Claimable Postgres lets agents provision Lakebase Postgres on Neon through - auth.md, the Neon CLI, or a REST API without a Neon account. The service - issues scoped agent credentials for one temporary project, can add Data API - and Managed Better Auth, and transfers the project into a Neon organization - when a human claims it. + Human reference for Claimable Postgres: CLI commands, claim, and HTTP. + Agents fetch https://claimable.neon.tech/auth.md from neon.com/docs/llms.txt. enableTableOfContents: true redirectFrom: - /docs/reference/neon-launchpad @@ -77,7 +74,7 @@ The response contains an `identity_assertion`, project metadata, and one decisio Agents start at [neon.com/docs/llms.txt](https://neon.com/docs/llms.txt), which points at [`auth.md`](https://claimable.neon.tech/auth.md) on the service origin. This page is the human reference. -Claimable Postgres implements [auth.md](https://auth.md/). Agents can discover the complete authentication and provisioning flow without a vendor-specific integration. +Claimable Postgres implements [auth.md](https://workos.com/auth-md/docs/auth-md). Agents can discover the complete authentication and provisioning flow without a vendor-specific integration. | Document or endpoint | Purpose | | --------------------------------------------- | ------------------------------------------------------------ | diff --git a/content/docs/workflows/claimable-database-integration.md b/content/docs/workflows/claimable-database-integration.md index fd18bb9d8dd..481a11a786d 100644 --- a/content/docs/workflows/claimable-database-integration.md +++ b/content/docs/workflows/claimable-database-integration.md @@ -10,7 +10,7 @@ summary: >- creation. The feature is in private preview and does not support transferring projects into Vercel-managed Neon organizations. enableTableOfContents: true -updatedOn: '2026-08-11T16:50:44.197Z' +updatedOn: '2026-08-16T15:49:10.359Z' --- ## Overview @@ -242,7 +242,7 @@ Without the `org_id` parameter, the project transfers to the user's personal acc - **Demo environments** - Create ready-to-use demo databases that prospects can claim - **Team environments** - Provision project databases for team members to claim into their organization -For a working implementation, try [Claimable Postgres by Neon](/claimable-postgres). Agents can provision a temporary database through auth.md, receive project-scoped credentials, and hand a claim link to a human. The registration response provides the project expiration time. See the [Claimable Postgres reference](/docs/reference/claimable-postgres) for the agent protocol, API, and CLI commands. +For a working implementation, try [Claimable Postgres by Neon](/claimable-postgres). Agents fetch [`auth.md`](https://claimable.neon.tech/auth.md), receive project-scoped credentials, and hand a claim link to a human. The registration response provides the project expiration time. See the [Claimable Postgres reference](/docs/reference/claimable-postgres) for CLI commands. ## Troubleshooting diff --git a/src/scripts/llms-index-config.js b/src/scripts/llms-index-config.js index 7f17a39873c..0657e1c589e 100644 --- a/src/scripts/llms-index-config.js +++ b/src/scripts/llms-index-config.js @@ -28,7 +28,7 @@ module.exports = { // Quick-reference links emitted as "## Common Queries" before the section list. commonQueries: [ { - label: 'auth.md — provision a temporary Neon project for an agent', + label: 'auth.md (provision a temporary Postgres database for an agent)', url: 'https://claimable.neon.tech/auth.md', }, { label: 'Pricing and plans', url: 'https://neon.com/pricing.md' }, From 659f710ccb5461f18b407b7578191f25cb154f1f Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:01:32 -0700 Subject: [PATCH 07/50] fix: call Claimable Neon /v1/projects paths --- src/app/api/claimable-postgres/route.js | 6 +++--- src/app/api/claimable-postgres/route.test.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/api/claimable-postgres/route.js b/src/app/api/claimable-postgres/route.js index 75d21676605..2676af1dbe9 100644 --- a/src/app/api/claimable-postgres/route.js +++ b/src/app/api/claimable-postgres/route.js @@ -250,13 +250,13 @@ export async function POST(request) { accessToken = requireToken(await exchangeAssertion(registration.identity_assertion)); const authorization = { Authorization: `Bearer ${accessToken}` }; const credentials = requireCredentials( - await requestClaimable(`/v1/databases/${registration.project.id}/credentials`, { + await requestClaimable(`/v1/projects/${registration.project.id}/credentials`, { headers: authorization, }), registration.project ); const claim = requireClaim( - await requestClaimable(`/v1/databases/${registration.project.id}/claim`, { + await requestClaimable(`/v1/projects/${registration.project.id}/claim`, { method: 'POST', headers: authorization, }) @@ -280,7 +280,7 @@ export async function POST(request) { if (!accessToken) { accessToken = requireToken(await exchangeAssertion(registration.identity_assertion)); } - await requestClaimable(`/v1/databases/${registration.project.id}`, { + await requestClaimable(`/v1/projects/${registration.project.id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${accessToken}` }, }); diff --git a/src/app/api/claimable-postgres/route.test.js b/src/app/api/claimable-postgres/route.test.js index 284f0ed5e21..e7d326df97e 100644 --- a/src/app/api/claimable-postgres/route.test.js +++ b/src/app/api/claimable-postgres/route.test.js @@ -122,14 +122,14 @@ describe('/api/claimable-postgres', () => { ); expect(global.fetch).toHaveBeenNthCalledWith( 3, - `${ORIGIN}/v1/databases/${registration.project.id}/credentials`, + `${ORIGIN}/v1/projects/${registration.project.id}/credentials`, expect.objectContaining({ headers: expect.objectContaining({ Authorization: `Bearer ${token.access_token}` }), }) ); expect(global.fetch).toHaveBeenNthCalledWith( 4, - `${ORIGIN}/v1/databases/${registration.project.id}/claim`, + `${ORIGIN}/v1/projects/${registration.project.id}/claim`, expect.objectContaining({ method: 'POST' }) ); }); @@ -222,7 +222,7 @@ describe('/api/claimable-postgres', () => { expect(await response.json()).toEqual(upstreamError); expect(global.fetch).toHaveBeenNthCalledWith( 4, - `${ORIGIN}/v1/databases/${registration.project.id}`, + `${ORIGIN}/v1/projects/${registration.project.id}`, expect.objectContaining({ method: 'DELETE', headers: expect.objectContaining({ Authorization: `Bearer ${token.access_token}` }), From eeae0002a5db3a226aad73035827ac60da4ad6b3 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:02:02 -0700 Subject: [PATCH 08/50] fix: drop deleted claimable-postgres skill from the static md manifest --- src/constants/static-md-manifest.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/constants/static-md-manifest.js b/src/constants/static-md-manifest.js index 770b2b3bf0a..d6b3d9808cd 100644 --- a/src/constants/static-md-manifest.js +++ b/src/constants/static-md-manifest.js @@ -4,7 +4,6 @@ export const STATIC_MD_PATHS = [ '/agentic-provisioning-llm-context.md', - '/docs/ai/skills/claimable-postgres/SKILL.md', '/docs/ai/skills/neon-ai-gateway/SKILL.md', '/docs/ai/skills/neon-functions/SKILL.md', '/docs/ai/skills/neon-functions/references/ai-sdk.md', From 3a2dbd0662ef2af3ab8b71080fd41295bea10e29 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:07:32 -0700 Subject: [PATCH 09/50] docs: remove redundant claim command comments --- content/docs/reference/claimable-postgres.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 8df013669a9..31f93b5aeef 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -204,22 +204,16 @@ Unsupported pre-claim capabilities return the `capability_requires_claim` error `neon claim` and its `neon claimable` alias manage anonymous projects: ```bash -# Create a Postgres project npx neon@latest claim create --env-pull -# Request services explicitly npx neon@latest claim create --service data-api --service auth --env-pull -# Check the current human-claim state npx neon@latest claim status -# Generate and open a human claim link npx neon@latest claim accept -# List claimable projects saved on this machine npx neon@latest claim list -# Delete the unclaimed project and local credentials npx neon@latest claim delete --yes ``` From 3286b499cfaaa590939fcaedcb2f68f63f92515a Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:15:55 -0700 Subject: [PATCH 10/50] docs: match Claimable Neon claim states, errors, and neon.ts import --- content/docs/reference/claimable-postgres.md | 30 ++++++++++--------- .../claimable-postgres/claimable-postgres.jsx | 3 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 31f93b5aeef..64a99d237ac 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -222,7 +222,7 @@ After `claim create`, regular Neon CLI commands exchange the saved identity asse If a `neon.ts` file is present, `claim create` requests its declared services automatically: ```typescript filename="neon.ts" -import { defineConfig } from '@neon/config'; +import { defineConfig } from '@neon/config/v1'; export default defineConfig({ auth: true, @@ -252,11 +252,11 @@ curl --request POST \ } ``` -Open `verification_uri_complete`, sign in to Neon, and choose the destination organization. The project transfer then moves through these states: +Open `verification_uri_complete` and sign in to Neon. Starting the browser claim prepares the transfer: it revokes the project key, access tokens, Data API, and Managed Better Auth (including Auth database data) before the console transfer URL is shown. Choose the destination organization. The project then moves through these states: 1. `pending`: the claim code exists, but the transfer has not completed. 2. `accepted`: the project has left the unclaimed-project organization. -3. `reconciled`: Claimable Neon has revoked its project key, agent tokens, and derived credentials. +3. `reconciled`: the identity assertion is revoked and the ceremony is finished. Poll claim status at the server-provided `interval`: @@ -265,7 +265,7 @@ curl https://claimable.neon.tech/v1/projects/quiet-fog-12345678/claim \ --header "Authorization: Bearer $ACCESS_TOKEN" ``` -Stop using agent credentials when the state reaches `accepted`. Use credentials from the destination Neon organization after reconciliation. +Only `reconciled` means the assertion is dead. Use credentials from the destination Neon organization after that. The recipient can enable Auth again after transfer. ## Errors @@ -287,16 +287,18 @@ Use `error.code` for control flow and show `error.message` to the user. Retry on Common codes include: -| Code | Meaning | -| --------------------------- | ------------------------------------------------------------- | -| `invalid_request` | The request body or parameter is invalid | -| `invalid_grant` | The identity assertion cannot be exchanged | -| `invalid_token` | The access token is invalid, expired, or revoked | -| `insufficient_scope` | The access token does not permit the operation | -| `capability_requires_claim` | The requested service or operation requires human ownership | -| `claim_in_progress` | Credential issuance is frozen while a claim is pending | -| `project_claimed` | The project transferred and agent credentials no longer apply | -| `upstream_unavailable` | A Neon API or service dependency is temporarily unavailable | +| Code | Meaning | +| --------------------------- | ------------------------------------------------------------ | +| `invalid_request` | The request body or parameter is invalid | +| `invalid_grant` | The identity assertion cannot be exchanged. Discard it | +| `unauthorized` | No credential was presented, or it did not verify | +| `token_expired` | The access token expired. Re-exchange the identity assertion | +| `scope_insufficient` | The access token does not permit the operation | +| `capability_requires_claim` | The requested service or operation requires human ownership | +| `claim_in_progress` | Only claim-status polling remains available | +| `project_claimed` | The project transferred. Discard the identity assertion | +| `project_expired` | The unclaimed window closed. Discard the identity assertion | +| `upstream_error` | A Neon API or service dependency failed | ## Resources diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx index 86d2ddf00a9..8b9a33013f0 100644 --- a/src/components/pages/claimable-postgres/claimable-postgres.jsx +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -37,11 +37,10 @@ Content-Type: application/json neon branches list neon claim accept`, - config: `import { defineConfig } from '@neon/config'; + config: `import { defineConfig } from '@neon/config/v1'; export default defineConfig({ dataApi: true, - auth: false, });`, }; From 386b83acfdcf934d965a58beafdc8ea27123f170 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:20:28 -0700 Subject: [PATCH 11/50] docs: re-exchange before claim poll and use origin proxy --- content/docs/reference/claimable-postgres.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 64a99d237ac..5b5c828f393 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -258,9 +258,15 @@ Open `verification_uri_complete` and sign in to Neon. Starting the browser claim 2. `accepted`: the project has left the unclaimed-project organization. 3. `reconciled`: the identity assertion is revoked and the ceremony is finished. -Poll claim status at the server-provided `interval`: +Starting the browser claim revokes existing access tokens. Re-exchange the identity assertion, then poll claim status at the server-provided `interval`. The new token is only good for that poll until `reconciled`. ```bash +curl --request POST https://claimable.neon.tech/v1/oauth2/token \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \ + --data-urlencode "assertion=$IDENTITY_ASSERTION" \ + --data-urlencode 'resource=https://claimable.neon.tech/' + curl https://claimable.neon.tech/v1/projects/quiet-fog-12345678/claim \ --header "Authorization: Bearer $ACCESS_TOKEN" ``` @@ -275,7 +281,7 @@ Errors use one JSON shape across provisioning, token, credential, and management { "error": { "code": "capability_requires_claim", - "origin": "service", + "origin": "proxy", "message": "Functions require claiming this project.", "retryable": false, "request_id": "req_..." From 842d1e410571d8329d4cd9f0e192e7e45e3862a9 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:25:04 -0700 Subject: [PATCH 12/50] docs: separate registration capability decisions from operation errors --- content/docs/reference/claimable-postgres.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 5b5c828f393..9da9175265d 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -197,7 +197,7 @@ Only requested and granted services appear under `services`. | Storage | No | Requires claiming the project | | AI Gateway | No | Requires claiming the project | -Unsupported pre-claim capabilities return the `capability_requires_claim` error code. Agents should preserve that code and give the user the claim URL instead of retrying. +Registration records those as `{ granted: false, reason: "requires_claim" }`. A later protected operation returns `capability_requires_claim`. Preserve the denied capability and give the human a claim link; do not retry or drop it. ## Use the Neon CLI From eca2bb3a66565088bfb5b0969bbd902e32d876a5 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 09:33:11 -0700 Subject: [PATCH 13/50] fix: name the 15-minute claim deadline and stop sending /instagres to neon.new --- content/docs/ai/agent-skills.md | 2 +- next.config.js | 4 ++-- .../pages/claimable-postgres/claimable-postgres.jsx | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/content/docs/ai/agent-skills.md b/content/docs/ai/agent-skills.md index 4703aa7d370..cdda5dcef5b 100644 --- a/content/docs/ai/agent-skills.md +++ b/content/docs/ai/agent-skills.md @@ -177,7 +177,7 @@ With the full skill set installed, your assistant can guide you across the Neon **Database workflows** -- **Disposable databases** via Claimable Postgres in the `neon` skill, for agents and tests +- **Disposable databases** via [Claimable Postgres](/claimable-postgres), for agents and tests - **Branch types and workflows** for migrations, schema-only branches, and reset-from-parent - **Egress optimization** for high transfer costs and query anti-patterns diff --git a/next.config.js b/next.config.js index 8db78e5c7a5..3bf1f791c16 100644 --- a/next.config.js +++ b/next.config.js @@ -2304,12 +2304,12 @@ const defaultConfig = { }, { source: '/launchpad', - destination: 'https://neon.new', + destination: '/claimable-postgres', permanent: false, }, { source: '/instagres', - destination: 'https://neon.new', + destination: '/claimable-postgres', permanent: false, }, { diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx index 8b9a33013f0..67c9768f44c 100644 --- a/src/components/pages/claimable-postgres/claimable-postgres.jsx +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -173,8 +173,9 @@ const Provisioner = () => {

- This project expires on {expiresAt} unless you claim it. The claim link expires in{' '} - {Math.round(claim.expires_in / 60)} minutes. + Copy these values now. This page will not show them again. The claim link expires in{' '} + {Math.round(claim.expires_in / 60)} minutes; after that this project cannot be claimed + from here. The project itself expires on {expiresAt}.

@@ -401,11 +405,9 @@ const ClaimablePostgres = () => (
-

- The ownership boundary -

+

Claim

- Temporary infrastructure, explicit handoff + One project, a claim link, an expiry

From 7e5bcbcb99bd26b3aea0f1361cf68b868071bfe7 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 10:08:34 -0700 Subject: [PATCH 16/50] fix: key claim teardown copy to granted services --- content/docs/reference/claimable-postgres.md | 1 + .../claimable-postgres/claimable-postgres.jsx | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/content/docs/reference/claimable-postgres.md b/content/docs/reference/claimable-postgres.md index 9da9175265d..421304df1bd 100644 --- a/content/docs/reference/claimable-postgres.md +++ b/content/docs/reference/claimable-postgres.md @@ -8,6 +8,7 @@ enableTableOfContents: true redirectFrom: - /docs/reference/neon-launchpad - /docs/reference/instagres +updatedOn: '2026-08-16T17:08:36.389Z' --- Claimable Postgres lets an agent create a temporary database on Neon before a human signs up. The agent receives credentials scoped to one project, builds with standard Postgres tools, and gives the human a claim link when the project is worth keeping. diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx index e5d82decf25..58bf65fd8ef 100644 --- a/src/components/pages/claimable-postgres/claimable-postgres.jsx +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -141,6 +141,17 @@ const Provisioner = () => { dateStyle: 'medium', timeStyle: 'short', }).format(new Date(project.expires_at)); + const granted = new Set( + capabilities.filter(({ granted }) => granted).map(({ capability }) => capability) + ); + const teardown = [ + granted.has('data_api') ? 'disables Data API' : null, + granted.has('auth') ? 'deletes Managed Better Auth and its data' : null, + ].filter(Boolean); + const teardownSentence = + teardown.length > 0 + ? ` It ${teardown.join(' and ')} so pre-claim tokens do not survive.` + : ''; return (
{

Copy these values now. This page will not show them again. The claim link expires in{' '} {Math.round(claim.expires_in / 60)} minutes; after that this project cannot be claimed - and will expire. Claiming transfers the Postgres database. It disables Data API and - deletes Managed Better Auth and its data so pre-claim tokens do not survive. The project + and will expire. Claiming transfers the Postgres database.{teardownSentence} The project itself expires on {expiresAt}.

+ {denied.length > 0 && ( +
    + {denied.map(({ capability, reason, message }) => ( +
  • + {capability.replaceAll('_', ' ')} was not granted + {message || reason ? `: ${message || reason}` : '.'} +
  • + ))} +
+ )}
@@ -188,10 +202,9 @@ const Provisioner = () => {

- Copy these values now. This page will not show them again. The claim link expires in{' '} - {Math.round(claim.expires_in / 60)} minutes; after that this project cannot be claimed - and will expire. Claiming transfers the Postgres database.{teardownSentence} The project - itself expires on {expiresAt}. + Copy these values now. This page will not show them again. The claim link expires on{' '} + {claimExpiresAt}. Claiming transfers the Postgres database.{teardownSentence} The + project itself expires on {expiresAt}.

- No account or payment details required. Claim the project to keep it. + No account or payment details required. Unclaimed projects expire in 72 hours and are capped + at 100 MB storage and 1 GB transfer.

); @@ -293,14 +307,14 @@ const Provisioner = () => { const InterfaceCard = ({ eyebrow, title, description, code }) => (
-

{eyebrow}

+
+

{eyebrow}

+ +

{title}

{description}

-
-
- -
-
+    
+
         {code}
       
@@ -432,7 +446,7 @@ const ClaimablePostgres = () => ( ], [ 'Unclaimed projects expire', - 'Every response includes an expiration time. Claim the project before then to keep the Postgres database. Claiming disables Data API and deletes Managed Better Auth and its data.', + 'Unclaimed projects expire in 72 hours and are capped at 100 MB storage and 1 GB transfer. Claim the project before then to keep the Postgres database. Claiming disables Data API and deletes Managed Better Auth and its data.', ], ].map(([title, description], index) => (
From 8bd501f18687a0e067648738ec0e52a4ef921785 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Sun, 16 Aug 2026 10:51:21 -0700 Subject: [PATCH 19/50] fix: finish claimable Postgres naming and skill URL redirects --- next.config.js | 30 +++++++++++++++++++ src/app/api/claimable-postgres/route.js | 2 +- .../claimable-postgres/claimable-postgres.jsx | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/next.config.js b/next.config.js index d85046e2918..b33a46ebc57 100644 --- a/next.config.js +++ b/next.config.js @@ -2332,6 +2332,36 @@ const defaultConfig = { destination: '/docs/reference/claimable-postgres', permanent: true, }, + { + source: '/.well-known/skills/claimable-postgres', + destination: '/docs/reference/claimable-postgres', + permanent: true, + }, + { + source: '/.well-known/skills/claimable-postgres/:path*', + destination: '/docs/reference/claimable-postgres', + permanent: true, + }, + { + source: '/docs/.well-known/agent-skills/claimable-postgres', + destination: '/docs/reference/claimable-postgres', + permanent: true, + }, + { + source: '/docs/.well-known/agent-skills/claimable-postgres/:path*', + destination: '/docs/reference/claimable-postgres', + permanent: true, + }, + { + source: '/docs/.well-known/skills/claimable-postgres', + destination: '/docs/reference/claimable-postgres', + permanent: true, + }, + { + source: '/docs/.well-known/skills/claimable-postgres/:path*', + destination: '/docs/reference/claimable-postgres', + permanent: true, + }, { source: '/docs/local/neon-local-vscode', destination: '/docs/local/neon-local-connect', diff --git a/src/app/api/claimable-postgres/route.js b/src/app/api/claimable-postgres/route.js index 438fefb477e..7e106984c56 100644 --- a/src/app/api/claimable-postgres/route.js +++ b/src/app/api/claimable-postgres/route.js @@ -62,7 +62,7 @@ const requestClaimable = async (path, init = {}) => { body = JSON.parse(text); } catch { throw new ClaimableContractError( - `Claimable Neon returned non-JSON content with HTTP ${response.status}.` + `Claimable Postgres returned non-JSON content with HTTP ${response.status}.` ); } } diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx index 41af3fe0874..6810c245b7a 100644 --- a/src/components/pages/claimable-postgres/claimable-postgres.jsx +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -398,7 +398,7 @@ const ClaimablePostgres = () => ( auth.md, the Neon CLI, and neon.ts.

- The same scoped agent credential works through the Claimable Neon API, Neon CLI, and{' '} + The same scoped agent credential works through the Claimable Postgres API, Neon CLI, and{' '} neon.ts.

From b8c2b411e0a24f493b960048146247049c1a3160 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Mon, 17 Aug 2026 01:08:05 -0700 Subject: [PATCH 20/50] fix: state that claim leaves Auth and Data API enabled --- .../claimable-postgres/claimable-postgres.jsx | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/components/pages/claimable-postgres/claimable-postgres.jsx b/src/components/pages/claimable-postgres/claimable-postgres.jsx index 6810c245b7a..be06945b62b 100644 --- a/src/components/pages/claimable-postgres/claimable-postgres.jsx +++ b/src/components/pages/claimable-postgres/claimable-postgres.jsx @@ -12,12 +12,12 @@ const SERVICES = [ { id: 'data-api', title: 'Data API', - description: 'Query the database over HTTPS. Claiming disables Data API.', + description: 'Query the database over HTTPS. Stays enabled after claim.', }, { id: 'auth', title: 'Managed Better Auth', - description: 'Add authentication. Claiming deletes Auth and its data.', + description: 'Add authentication. Stays enabled after claim.', }, ]; @@ -144,18 +144,7 @@ const Provisioner = () => { }).format(new Date(value)); const expiresAt = formatTime(project.expires_at); const claimExpiresAt = formatTime(Date.now() + claim.expires_in * 1000); - const granted = new Set( - capabilities.filter(({ granted }) => granted).map(({ capability }) => capability) - ); const denied = capabilities.filter(({ granted }) => !granted); - const teardown = [ - granted.has('data_api') ? 'disables Data API' : null, - granted.has('auth') ? 'deletes Managed Better Auth and its data' : null, - ].filter(Boolean); - const teardownSentence = - teardown.length > 0 - ? ` It ${teardown.join(' and ')} so pre-claim tokens do not survive.` - : ''; return (
{

Copy these values now. This page will not show them again. The claim link expires on{' '} - {claimExpiresAt}. Claiming transfers the Postgres database.{teardownSentence} The - project itself expires on {expiresAt}. + {claimExpiresAt}. Claiming transfers the Postgres database and rotates the database + password. Auth and the Data API stay enabled. The project itself expires on {expiresAt}.

@@ -330,7 +330,7 @@ InterfaceCard.propTypes = { code: PropTypes.string.isRequired, }; -const ClaimablePostgres = () => ( +const ClaimableNeon = () => ( <>

(
- Claimable Postgres + Claimable Neon

- A database when your agent needs one. + A project when your agent needs one.

- Agents can provision Lakebase Postgres on Neon before a human creates an account. - Start building, then claim the project into a Neon organization before it expires. + Agents can provision a Neon project before a human creates an account. Start building, + then claim the project into a Neon organization before it expires.

- @@ -400,7 +395,7 @@ const ClaimablePostgres = () => ( auth.md, the Neon CLI, and neon.ts.

- The same scoped agent credential works through the Claimable Postgres API, Neon CLI, and{' '} + The same scoped agent credential works through the Claimable Neon API, Neon CLI, and{' '} neon.ts.

@@ -466,4 +461,4 @@ const ClaimablePostgres = () => ( ); -export default ClaimablePostgres; +export default ClaimableNeon; diff --git a/src/constants/links.js b/src/constants/links.js index 4d7faa56c93..9e8256846f2 100644 --- a/src/constants/links.js +++ b/src/constants/links.js @@ -7,7 +7,7 @@ export default { careers: 'https://www.databricks.com/company/careers/open-positions?department=engineering&location=all&itm_source=www&itm_category=company&itm_page=engineering-at-databricks&itm_location=body&itm_component=hero&itm_offer=open-positions', caseStudies: '/case-studies', - claimablePostgres: '/claimable-postgres', + claimableNeon: '/claimable-neon', cli: '/cli', contactSales: '/contact-sales', enterprise: '/enterprise', @@ -119,5 +119,5 @@ export default { apiReference: '/docs/reference/api', bugBounty: 'https://hackerone.com/neon_bbp', bookMeeting: 'https://calendly.com/d/cm8j-8yw-fq8', - claimablePostgresApi: 'https://claimable.neon.tech', + claimableNeonApi: 'https://claimable.neon.tech', }; diff --git a/src/constants/seo-data.js b/src/constants/seo-data.js index 596ec4ed1ba..5d940d4240b 100644 --- a/src/constants/seo-data.js +++ b/src/constants/seo-data.js @@ -51,11 +51,11 @@ export default { pathname: LINKS.caseStudies, imagePath: '/images/social-previews/case-studies.jpg', }, - claimablePostgres: { - title: 'Claimable Postgres for agents — Neon', + claimableNeon: { + title: 'Claimable Neon for agents — Neon', description: - 'Let agents provision temporary Lakebase Postgres, Data API, and Managed Better Auth before a human creates a Neon account.', - pathname: LINKS.claimablePostgres, + 'Let agents provision a temporary Neon project — Lakebase Postgres, Data API, and Managed Better Auth — before a human creates a Neon account.', + pathname: LINKS.claimableNeon, }, cli: { title: 'Your Neon workflow lives in the terminal', From c16930d7c382601f952a8ce3d2455b10a5d429b0 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 09:50:42 -0700 Subject: [PATCH 28/50] fix(pages): validate claimable-neon requests and payloads Reject non-JSON posts so simple cross-origin requests cannot create projects. Refuse registration payloads the page cannot render, and advertise that the token endpoint uses no client authentication. --- scripts/verify-agent-endpoints.mjs | 23 +++++++++- src/app/api/claimable-neon/route.js | 24 ++++++++-- src/app/api/claimable-neon/route.test.js | 44 +++++++++++++++++++ .../pages/claimable-neon/claimable-neon.jsx | 4 +- src/constants/agent-discovery.js | 1 + 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/scripts/verify-agent-endpoints.mjs b/scripts/verify-agent-endpoints.mjs index e04dd9bf88c..e5aebdf2452 100644 --- a/scripts/verify-agent-endpoints.mjs +++ b/scripts/verify-agent-endpoints.mjs @@ -226,13 +226,21 @@ const SCHEMAS = { }, claimableAuthorizationServer: { type: 'object', - required: ['issuer', 'token_endpoint', 'jwks_uri', 'grant_types_supported', 'agent_auth'], + required: [ + 'issuer', + 'token_endpoint', + 'jwks_uri', + 'grant_types_supported', + 'token_endpoint_auth_methods_supported', + 'agent_auth', + ], properties: { issuer: { type: 'string' }, token_endpoint: { type: 'string' }, revocation_endpoint: { type: 'string' }, jwks_uri: { type: 'string' }, grant_types_supported: { type: 'array', items: { type: 'string' } }, + token_endpoint_auth_methods_supported: { type: 'array', items: { type: 'string' } }, agent_auth: { type: 'object', required: ['skill', 'identity_endpoint', 'claim_endpoint'], @@ -465,7 +473,18 @@ const VALIDATORS = { checks.push( equalsCheck('token_endpoint matches SoT', payload.token_endpoint, sot.CLAIMABLE.tokenEndpoint) ); - checks.push(equalsCheck('jwks_uri matches SoT', payload.jwks_uri, sot.CLAIMABLE.jwksUri)); + checks.push( + equalsCheck('jwks_uri matches SoT', payload.jwks_uri, sot.CLAIMABLE.jwksUri) + ); + checks.push( + Array.isArray(payload.token_endpoint_auth_methods_supported) && + payload.token_endpoint_auth_methods_supported.includes('none') + ? ok('token endpoint advertises no client authentication') + : fail( + 'token endpoint advertises no client authentication', + payload.token_endpoint_auth_methods_supported + ) + ); checks.push( equalsCheck('agent_auth.skill matches SoT', payload.agent_auth?.skill, sot.CLAIMABLE.skillUrl) ); diff --git a/src/app/api/claimable-neon/route.js b/src/app/api/claimable-neon/route.js index 5c6635ced86..553415ea0b1 100644 --- a/src/app/api/claimable-neon/route.js +++ b/src/app/api/claimable-neon/route.js @@ -92,7 +92,11 @@ const requireRegistration = (body) => { typeof body.project.id !== 'string' || typeof body.project.branch_id !== 'string' || typeof body.project.expires_at !== 'string' || - !Array.isArray(body.capabilities) + Number.isNaN(Date.parse(body.project.expires_at)) || + !Array.isArray(body.capabilities) || + body.capabilities.some( + (item) => !item || typeof item.capability !== 'string' || typeof item.granted !== 'boolean' + ) ) { throw new ClaimableContractError('Claimable Neon returned an invalid registration.'); } @@ -126,7 +130,8 @@ const requireClaim = (body) => { if ( !body || typeof body.verification_uri_complete !== 'string' || - typeof body.expires_in !== 'number' + typeof body.expires_in !== 'number' || + !Number.isFinite(body.expires_in) ) { throw new ClaimableContractError('Claimable Neon returned an invalid claim link.'); } @@ -180,6 +185,19 @@ const errorFor = (error) => { }; export async function POST(request) { + const mediaType = (request.headers.get('content-type') || '').split(';')[0].trim().toLowerCase(); + if (mediaType !== 'application/json') { + return jsonResponse( + { + error: { + code: 'invalid_request', + message: 'Content-Type must be application/json.', + }, + }, + 400 + ); + } + let input; try { input = await request.json(); @@ -281,7 +299,7 @@ export async function POST(request) { error: { code: 'claimable_cleanup_failed', message: - 'Database setup failed and the temporary project could not be deleted. It will expire automatically.', + 'Project setup failed and the temporary project could not be deleted. It will expire automatically.', }, }, 500 diff --git a/src/app/api/claimable-neon/route.test.js b/src/app/api/claimable-neon/route.test.js index a3d26ed4a62..51295ffc217 100644 --- a/src/app/api/claimable-neon/route.test.js +++ b/src/app/api/claimable-neon/route.test.js @@ -140,6 +140,50 @@ describe('/api/claimable-neon', () => { expect(global.fetch).not.toHaveBeenCalled(); }); + it('rejects non-JSON content types before provisioning', async () => { + const response = await POST( + new Request('https://neon.com/api/claimable-neon', { + method: 'POST', + headers: { 'Content-Type': 'text/plain' }, + body: '{}', + }) + ); + + expect(response.status).toBe(400); + expect(await response.json()).toEqual({ + error: { + code: 'invalid_request', + message: 'Content-Type must be application/json.', + }, + }); + expect(global.fetch).not.toHaveBeenCalled(); + }); + + it('returns 502 when registration capabilities cannot be rendered', async () => { + global.fetch.mockResolvedValueOnce( + jsonResponse({ + ...registration, + capabilities: [{}], + }) + ); + + const response = await POST( + new Request('https://neon.com/api/claimable-neon', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ services: [] }), + }) + ); + + expect(response.status).toBe(502); + expect(await response.json()).toEqual({ + error: { + code: 'invalid_claimable_response', + message: 'Claimable Neon returned an invalid registration.', + }, + }); + }); + it('preserves structured Claimable Neon errors', async () => { const upstreamError = { error: { diff --git a/src/components/pages/claimable-neon/claimable-neon.jsx b/src/components/pages/claimable-neon/claimable-neon.jsx index 626aea4c41f..1b4f2ff56ef 100644 --- a/src/components/pages/claimable-neon/claimable-neon.jsx +++ b/src/components/pages/claimable-neon/claimable-neon.jsx @@ -205,8 +205,8 @@ const Provisioner = () => {

Copy these values now. This page will not show them again. The claim link expires on{' '} - {claimExpiresAt}. Claiming transfers the Postgres database and rotates the database - password.{stayEnabledSentence} The project itself expires on {expiresAt}. + {claimExpiresAt}. Claiming transfers the project and rotates the database password. + {stayEnabledSentence} The project itself expires on {expiresAt}.

{denied.length > 0 && (
    - {denied.map(({ capability, reason, message }) => ( + {denied.map(({ capability, reason }) => (
  • {capabilityLabel(capability)} was not granted - {message || reason ? `: ${message || reason}` : '.'} + {DENIED_REASON_COPY[reason] ? `. ${DENIED_REASON_COPY[reason]}` : '.'}
  • ))}
@@ -221,12 +225,12 @@ const Provisioner = () => {
@@ -326,7 +330,7 @@ const InterfaceCard = ({ eyebrow, title, description, code }) => (

{title}

{description}

-
+      
         {code}
       
@@ -363,7 +367,7 @@ const ClaimableNeon = () => (

(
From ccb1b68c406aa7b35fcec78d216e712ba080481e Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 11:17:05 -0700 Subject: [PATCH 38/50] fix(pages): do not imply the claim link waits on the build --- src/components/pages/claimable-neon/claimable-neon.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pages/claimable-neon/claimable-neon.jsx b/src/components/pages/claimable-neon/claimable-neon.jsx index a11c6aca8ac..550650afe70 100644 --- a/src/components/pages/claimable-neon/claimable-neon.jsx +++ b/src/components/pages/claimable-neon/claimable-neon.jsx @@ -230,7 +230,7 @@ const Provisioner = () => { target="_blank" rel="noreferrer" > - Claim when you are done building + Open the claim link
From 95983fbdac2d32ba7939ef5cf99c3fe5ce19b920 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 11:21:31 -0700 Subject: [PATCH 39/50] fix(pages): say when Continuing to Neon rotates DATABASE_URL --- config/skills.json | 2 +- public/docs/ai/skills/neon/references/claimable-neon.md | 2 +- src/components/pages/claimable-neon/claimable-neon.jsx | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/skills.json b/config/skills.json index 53a32de824a..eee28a94dcc 100644 --- a/config/skills.json +++ b/config/skills.json @@ -4,7 +4,7 @@ { "name": "neon-postgres", "ref": "main" }, { "name": "neon-postgres-egress-optimizer", "ref": "main" }, { "name": "neon-postgres-branches", "ref": "main" }, - { "name": "neon", "ref": "85e398b562bb1eeb15d7631402116de0b22b2051" }, + { "name": "neon", "ref": "3671832ddffe9bcf12cd25c94c49689483b0f724" }, { "name": "neon-postgres-agent-platforms", "ref": "main", diff --git a/public/docs/ai/skills/neon/references/claimable-neon.md b/public/docs/ai/skills/neon/references/claimable-neon.md index 7863eca4c2f..19cdc1d0c1b 100644 --- a/public/docs/ai/skills/neon/references/claimable-neon.md +++ b/public/docs/ai/skills/neon/references/claimable-neon.md @@ -51,7 +51,7 @@ After create, report the `project_id`, `expires_at`, and `denied_capabilities` t ## Claim -Do not run `neon claim accept` until the human is ready. Accept mints a claim URL. Credentials keep working until the human opens that URL and accepts the transfer; then the project is `claim_in_progress` and only claim-status polling remains. +Do not run `neon claim accept` until the human is ready. Accept mints a claim URL. Opening the URL does not freeze access. Continuing from that page to Neon starts the transfer, rotates `DATABASE_URL`, and puts the project in `claim_in_progress`; then only claim-status polling remains. When the human is ready, run `neon claim accept --no-open`. Bare `neon claim accept` opens a browser. Report the `verification_url`, `user_code`, and `expires_in_seconds` the CLI printed. If the code expires, run `neon claim accept --no-open` again. Claiming transfers the project and rotates `DATABASE_URL`. Auth and the Data API stay enabled. diff --git a/src/components/pages/claimable-neon/claimable-neon.jsx b/src/components/pages/claimable-neon/claimable-neon.jsx index 550650afe70..0ca2a01801b 100644 --- a/src/components/pages/claimable-neon/claimable-neon.jsx +++ b/src/components/pages/claimable-neon/claimable-neon.jsx @@ -218,8 +218,9 @@ const Provisioner = () => {

Copy these values now. This page will not show them again. The claim link expires in{' '} - {Math.round(claim.expires_in / 60)} minutes. Open it before then. Claiming rotates{' '} - DATABASE_URL; pull a new one from the console after you claim. + {Math.round(claim.expires_in / 60)} minutes. Open it before then. Continuing to Neon on + that page rotates DATABASE_URL before you pick an organization. Pull a new + one from the console after the transfer finishes. {stayEnabledSentence} The project itself expires on {expiresAt}.

+
+
+ ); +}; + +ProvisionResult.propTypes = { + result: PropTypes.shape({ + capabilities: PropTypes.arrayOf( + PropTypes.shape({ + capability: PropTypes.string.isRequired, + granted: PropTypes.bool.isRequired, + reason: PropTypes.string, + }) + ).isRequired, + claim: PropTypes.shape({ + expires_in: PropTypes.number.isRequired, + verification_uri_complete: PropTypes.string.isRequired, + }).isRequired, + credentials: PropTypes.shape({ + database_url: PropTypes.string.isRequired, + services: PropTypes.shape({ + data_api: PropTypes.shape({ url: PropTypes.string }), + auth: PropTypes.shape({ base_url: PropTypes.string }), + }).isRequired, + }).isRequired, + project: PropTypes.shape({ + id: PropTypes.string.isRequired, + expires_at: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, +}; + const Provisioner = () => { const [selectedServices, setSelectedServices] = useState(new Set()); const [state, setState] = useState({ status: 'idle' }); @@ -145,97 +283,13 @@ const Provisioner = () => { } catch (error) { setState({ status: 'error', - message: error instanceof Error ? error.message : 'The project could not be created.', + message: provisionErrorMessage(error), }); } }; if (state.status === 'success') { - const { capabilities, claim, credentials, project } = state.result; - const formatTime = (value) => - new Intl.DateTimeFormat(undefined, { - dateStyle: 'medium', - timeStyle: 'short', - }).format(new Date(value)); - const expiresAt = formatTime(project.expires_at); - const granted = new Set( - capabilities.filter(({ granted }) => granted).map(({ capability }) => capability) - ); - const denied = capabilities.filter(({ granted }) => !granted); - const stayEnabled = [ - granted.has('auth') ? 'Managed Better Auth' : null, - granted.has('data_api') ? 'the Data API' : null, - ].filter(Boolean); - const stayEnabledSentence = - stayEnabled.length === 0 - ? '' - : stayEnabled.length === 1 - ? ` ${stayEnabled[0][0].toUpperCase()}${stayEnabled[0].slice(1)} stays enabled.` - : ` ${stayEnabled.join(' and ')} stay enabled.`; - - return ( -
-
-
-
-

Project ready

-

Connect your agent

-
- - {project.id} - -
- -
- {capabilities.map(({ capability, granted }) => ( - - ))} -
- {denied.length > 0 && ( -
    - {denied.map(({ capability, reason }) => ( -
  • - {capabilityLabel(capability)} was not granted - {DENIED_REASON_COPY[reason] ? `. ${DENIED_REASON_COPY[reason]}` : '.'} -
  • - ))} -
- )} - -
- - {credentials.services.data_api?.url && ( - - )} - {credentials.services.auth?.base_url && ( - - )} -
- -
-

- Copy these values now. This page will not show them again. The claim link expires in{' '} - {Math.round(claim.expires_in / 60)} minutes. Continuing to Neon on that page rotates{' '} - DATABASE_URL. Pull a new one from the console after the transfer finishes. - {stayEnabledSentence} The project itself expires on {expiresAt}. -

- -
-
- ); + return ; } return ( @@ -326,7 +380,7 @@ const InterfaceCard = ({ eyebrow, title, description, code }) => (

{eyebrow}

- +

{title}

{description}

@@ -410,7 +464,7 @@ const ClaimableNeon = () => ( auth.md, the Neon CLI and neon.ts

- The same scoped agent credential works through the Claimable Neon API, Neon CLI, and{' '} + The same scoped agent credential works through auth.md, the Neon CLI, and{' '} neon.ts.

diff --git a/src/constants/links.js b/src/constants/links.js index 9e8256846f2..880385b335a 100644 --- a/src/constants/links.js +++ b/src/constants/links.js @@ -119,5 +119,4 @@ export default { apiReference: '/docs/reference/api', bugBounty: 'https://hackerone.com/neon_bbp', bookMeeting: 'https://calendly.com/d/cm8j-8yw-fq8', - claimableNeonApi: 'https://claimable.neon.tech', }; From d3e9e5625e9a5adce12a65a5693cb089e93984b5 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 12:46:34 -0700 Subject: [PATCH 47/50] fix(pages): announce copied, reset the form, and pin the neon skill --- config/skills.json | 2 +- content/docs/cli/claim.md | 2 +- .../skills/neon/references/claimable-neon.md | 2 - .../pages/claimable-neon/claimable-neon.jsx | 38 ++++++++++++------- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/config/skills.json b/config/skills.json index 74b6470c133..154fc204508 100644 --- a/config/skills.json +++ b/config/skills.json @@ -4,7 +4,7 @@ { "name": "neon-postgres", "ref": "main" }, { "name": "neon-postgres-egress-optimizer", "ref": "main" }, { "name": "neon-postgres-branches", "ref": "main" }, - { "name": "neon", "ref": "0350587d7fe6291199dd4f87b67ab7037100a6c6" }, + { "name": "neon", "ref": "32e84df5831c33734ddaf03d5f12ec39d6840dfd" }, { "name": "neon-postgres-agent-platforms", "ref": "main", diff --git a/content/docs/cli/claim.md b/content/docs/cli/claim.md index cad5fdf28d6..966e4bdd25b 100644 --- a/content/docs/cli/claim.md +++ b/content/docs/cli/claim.md @@ -66,7 +66,7 @@ Creates a claim code and opens the URL where a human signs in and takes the proj neon claim accept --no-open ``` -Continuing to Neon from that URL freezes issuance and rotates `DATABASE_URL` before the console org picker. +Opening the URL does not freeze access. Continuing to Neon from that URL starts the transfer and rotates `DATABASE_URL` before the console org picker. ## neon claim list (#list) diff --git a/public/docs/ai/skills/neon/references/claimable-neon.md b/public/docs/ai/skills/neon/references/claimable-neon.md index 5200edc8a33..4fb96c2d186 100644 --- a/public/docs/ai/skills/neon/references/claimable-neon.md +++ b/public/docs/ai/skills/neon/references/claimable-neon.md @@ -32,8 +32,6 @@ npm i @neon/config neon claim --help ``` -If that help lists `create`, run `neon claim create --env-pull`. If it does not, use [If the Neon CLI cannot be used](#if-the-neon-cli-cannot-be-used). - `neon claim create` reads `neon.ts` when it is present. It writes provisioned vars to an existing `.env`, otherwise `.env.local`, and gitignores that file. If `.env` or `.env.local` already has a `DATABASE_URL` (or other Neon-managed keys), pass `--file ` or `--no-env-pull`. The identity assertion is the pre-claim credential. Before claim, Postgres is always granted; Auth and the Data API are granted when requested. Functions, Object Storage, and AI Gateway come back with `granted: false` and `reason: "requires_claim"`. The CLI prints those as `denied_capabilities`. Report what you were given. Do not retry or strip them. diff --git a/src/components/pages/claimable-neon/claimable-neon.jsx b/src/components/pages/claimable-neon/claimable-neon.jsx index fec8daa5f4f..8eb913762c6 100644 --- a/src/components/pages/claimable-neon/claimable-neon.jsx +++ b/src/components/pages/claimable-neon/claimable-neon.jsx @@ -54,7 +54,7 @@ const CopyButton = ({ value, label = 'Copy', ariaLabel }) => { +
+ + +
); }; ProvisionResult.propTypes = { + onReset: PropTypes.func.isRequired, result: PropTypes.shape({ capabilities: PropTypes.arrayOf( PropTypes.shape({ @@ -289,7 +301,7 @@ const Provisioner = () => { }; if (state.status === 'success') { - return ; + return setState({ status: 'idle' })} />; } return ( From 2fdb05ab178ff9e2641c52cca7c24b64ff3f056b Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 12:56:51 -0700 Subject: [PATCH 48/50] fix(pages): copy the claim link and say opening it does not freeze --- src/components/pages/claimable-neon/claimable-neon.jsx | 8 +++++--- src/constants/seo-data.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/pages/claimable-neon/claimable-neon.jsx b/src/components/pages/claimable-neon/claimable-neon.jsx index 8eb913762c6..cf6cdf14bb7 100644 --- a/src/components/pages/claimable-neon/claimable-neon.jsx +++ b/src/components/pages/claimable-neon/claimable-neon.jsx @@ -198,14 +198,16 @@ const ProvisionResult = ({ result, onReset }) => { {credentials.services.auth?.base_url && ( )} +

Copy these values now. This page will not show them again. The claim link expires at{' '} - {claimExpiresAt}. If it expires, create another project from this page. Continuing to Neon - on that page rotates DATABASE_URL. Pull a new one from the console after the - transfer finishes. + {claimExpiresAt}. If it expires, create another project from this page. Opening the claim + link does not freeze access. Continuing to Neon on the claim page rotates{' '} + DATABASE_URL. After the transfer finishes, pull a new one from the Neon + console. {stayEnabledSentence} The project itself expires on {expiresAt}.

diff --git a/src/constants/seo-data.js b/src/constants/seo-data.js index 5d940d4240b..63a6cc02769 100644 --- a/src/constants/seo-data.js +++ b/src/constants/seo-data.js @@ -54,7 +54,7 @@ export default { claimableNeon: { title: 'Claimable Neon for agents — Neon', description: - 'Let agents provision a temporary Neon project — Lakebase Postgres, Data API, and Managed Better Auth — before a human creates a Neon account.', + 'Let agents provision a temporary Neon project (Lakebase Postgres, Data API, and Managed Better Auth) before a human creates a Neon account.', pathname: LINKS.claimableNeon, }, cli: { From 73a0e930258cbe32b8abea5710dcdd71edd33236 Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 12:57:22 -0700 Subject: [PATCH 49/50] chore(constants): pin the neon skill to the claim-help gate --- config/skills.json | 2 +- public/.well-known/agent-skills/index.json | 2 +- .../docs/.well-known/agent-skills/index.json | 2 +- public/docs/ai/skills/neon/SKILL.md | 3 +-- .../skills/neon/references/claimable-neon.md | 21 ++++++++++--------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/config/skills.json b/config/skills.json index 154fc204508..d57e64f6902 100644 --- a/config/skills.json +++ b/config/skills.json @@ -4,7 +4,7 @@ { "name": "neon-postgres", "ref": "main" }, { "name": "neon-postgres-egress-optimizer", "ref": "main" }, { "name": "neon-postgres-branches", "ref": "main" }, - { "name": "neon", "ref": "32e84df5831c33734ddaf03d5f12ec39d6840dfd" }, + { "name": "neon", "ref": "8a4200b2fdf8c4a5b301c717064d8aeb58c8eea5" }, { "name": "neon-postgres-agent-platforms", "ref": "main", diff --git a/public/.well-known/agent-skills/index.json b/public/.well-known/agent-skills/index.json index 2f876528820..00fa86d79fc 100644 --- a/public/.well-known/agent-skills/index.json +++ b/public/.well-known/agent-skills/index.json @@ -27,7 +27,7 @@ "type": "skill-md", "description": "Overview of Neon, a complete set of cloud backend primitives for apps and agents, spanning Lakebase Postgres, Auth, the Data API, Object Storage, Compute Functions, and the AI Gateway. Start here to route to the right Neon skill, set up the CLI or MCP server, and follow the branch-first workflow. Use when \"Neon\" or \"Lakebase Postgres\" is mentioned, or when any of its individual capabilities are the trigger: \"object storage\" or \"S3\", \"buckets\", \"serverless functions\", \"AI gateway\", \"call an LLM\", \"logs\", \"branch logs\", \"query logs\", \"log export\", \"Loki\", \"Grafana\", \"observability\", \"telemetry\", \"postgres\", \"database\", or \"backend\". Also use when there is no Neon account yet, the user cannot sign in or provide an API key right now and needs a project they can claim later, or the user asks for a throwaway DATABASE_URL, Claimable Neon, Claimable Postgres, neon.new, claimable.neon.tech, instant Postgres, a no-signup database, temporary postgres, quick postgres, a no credit card database, or npx neon-new.", "url": "/.well-known/agent-skills/neon/SKILL.md", - "digest": "sha256:be11a0d5ea6e12eb628dfd16870e83145e36f6902d49a410703d5d6fef8f72e3" + "digest": "sha256:9e3aafe76e94c6dba997e164ff0a9d51431f79b04ecd36d78ae41d7ef1333bfe" }, { "name": "neon-postgres-agent-platforms", diff --git a/public/docs/.well-known/agent-skills/index.json b/public/docs/.well-known/agent-skills/index.json index f338d47cf82..fd614178f32 100644 --- a/public/docs/.well-known/agent-skills/index.json +++ b/public/docs/.well-known/agent-skills/index.json @@ -27,7 +27,7 @@ "type": "skill-md", "description": "Overview of Neon, a complete set of cloud backend primitives for apps and agents, spanning Lakebase Postgres, Auth, the Data API, Object Storage, Compute Functions, and the AI Gateway. Start here to route to the right Neon skill, set up the CLI or MCP server, and follow the branch-first workflow. Use when \"Neon\" or \"Lakebase Postgres\" is mentioned, or when any of its individual capabilities are the trigger: \"object storage\" or \"S3\", \"buckets\", \"serverless functions\", \"AI gateway\", \"call an LLM\", \"logs\", \"branch logs\", \"query logs\", \"log export\", \"Loki\", \"Grafana\", \"observability\", \"telemetry\", \"postgres\", \"database\", or \"backend\". Also use when there is no Neon account yet, the user cannot sign in or provide an API key right now and needs a project they can claim later, or the user asks for a throwaway DATABASE_URL, Claimable Neon, Claimable Postgres, neon.new, claimable.neon.tech, instant Postgres, a no-signup database, temporary postgres, quick postgres, a no credit card database, or npx neon-new.", "url": "/docs/.well-known/agent-skills/neon/SKILL.md", - "digest": "sha256:be11a0d5ea6e12eb628dfd16870e83145e36f6902d49a410703d5d6fef8f72e3" + "digest": "sha256:9e3aafe76e94c6dba997e164ff0a9d51431f79b04ecd36d78ae41d7ef1333bfe" }, { "name": "neon-postgres-agent-platforms", diff --git a/public/docs/ai/skills/neon/SKILL.md b/public/docs/ai/skills/neon/SKILL.md index f986ac3ae9e..afa8dc367bf 100644 --- a/public/docs/ai/skills/neon/SKILL.md +++ b/public/docs/ai/skills/neon/SKILL.md @@ -128,7 +128,6 @@ Use the same method that was used to install them. With the `skills` CLI, run th Before `npx neon@latest init --agent`, check whether the CLI is already authenticated: - `NEON_API_KEY` is set -- `NEON_PROFILE` is set - `npx neon@latest profile list -o json` lists a profile whose `account` is not `-` A `DEFAULT` row with `account: "-"` and `file: "missing"` is not an account. If `neon` is not installed, or `npx neon@latest profile list` cannot run, that is not an account. @@ -188,7 +187,7 @@ If the Getting Started account check found credentials, use them. If a command w If they cannot sign in or provide a key right now, ask before using Claimable Neon. Continue only after they say yes. That is a temporary workaround. -If there is no Neon account yet, follow [references/claimable-neon.md](https://neon.com/docs/ai/skills/neon/references/claimable-neon.md). Do not run `neon init --agent` or `neon auth` on this path; those need a human Neon account. If the Neon CLI cannot be used, the reference has the REST fallback. +If there is no Neon account yet, follow [references/claimable-neon.md](https://neon.com/docs/ai/skills/neon/references/claimable-neon.md). Do not run `neon init --agent` or `neon auth` on this path; those need a human Neon account. If `neon claim` is missing, the reference has the REST fallback. Requests for neon.new, Claimable Postgres, claimable.neon.tech, instant Postgres, or a no-signup database are the same path. diff --git a/public/docs/ai/skills/neon/references/claimable-neon.md b/public/docs/ai/skills/neon/references/claimable-neon.md index 4fb96c2d186..e67c63c7f1f 100644 --- a/public/docs/ai/skills/neon/references/claimable-neon.md +++ b/public/docs/ai/skills/neon/references/claimable-neon.md @@ -9,7 +9,7 @@ Use this after the neon skill account check found no account. ## Path 1. Install the CLI: `npm i -g neon@latest` -2. If `neon claim --help` does not list `create`, skip to [If the Neon CLI cannot be used](#if-the-neon-cli-cannot-be-used). +2. If `neon claim --help` does not list `create`, skip to [If neon claim is missing](#if-neon-claim-is-missing). 3. Write a `neon.ts` that declares the services you need, or skip the file and pass `--service` on create. Postgres is always requested. 4. Create the project: `neon claim create --env-pull` (add `--service data-api --service auth` if there is no `neon.ts`) 5. If create did not write env, pull it: `neon env pull` @@ -17,6 +17,13 @@ Use this after the neon skill account check found no account. Do not run `neon init --agent` or `neon auth` on this path; those need a human Neon account. `--api-key` and `--profile` are refused on `neon claim`. +```bash +npm i -g neon@latest +neon claim --help +``` + +If that help lists `create` and you need Auth or the Data API, `npm i @neon/config` and write `neon.ts`. Then `neon claim create --env-pull`. + ```typescript import { defineConfig } from "@neon/config/v1"; @@ -26,17 +33,11 @@ export default defineConfig({ }); ``` -```bash -npm i -g neon@latest -npm i @neon/config -neon claim --help -``` - `neon claim create` reads `neon.ts` when it is present. It writes provisioned vars to an existing `.env`, otherwise `.env.local`, and gitignores that file. If `.env` or `.env.local` already has a `DATABASE_URL` (or other Neon-managed keys), pass `--file ` or `--no-env-pull`. The identity assertion is the pre-claim credential. Before claim, Postgres is always granted; Auth and the Data API are granted when requested. Functions, Object Storage, and AI Gateway come back with `granted: false` and `reason: "requires_claim"`. The CLI prints those as `denied_capabilities`. Report what you were given. Do not retry or strip them. -After create, report the `project_id`, `expires_at`, and any denied capabilities. Do not invent the window. +After create, report the `project_id`, `project_expires_at`, and any denied capabilities. Do not invent the window. ## Claim @@ -61,7 +62,7 @@ neon claim delete --yes ### With REST -An agent must not complete the claim. Do not `POST /v1/projects/{id}/claim` until the human is ready. The human opens `verification_uri_complete` and accepts the transfer. If the claim code expires, `POST /v1/projects/{id}/claim` again. +An agent must not complete the claim. Do not `POST /v1/projects/{id}/claim` until the human is ready. The human opens `verification_uri_complete` and accepts the transfer. If the claim code expires, `POST /v1/projects/{id}/claim` again. The live claim response also includes `user_code` and `expires_in`. `auth.md` documents `verification_uri_complete` and the polling `interval`. After the human continues to Neon, existing access tokens are revoked: re-exchange the identity assertion, then poll `GET /v1/projects/{id}/claim` with that token at the interval `auth.md` returns. `claim_in_progress` means keep polling with the post-redemption token, not the token from create. Report `verification_uri_complete`, `user_code`, and `expires_in`. @@ -69,7 +70,7 @@ When `error.code` is `capability_requires_claim`, preserve the denied capability Only `invalid_grant`, `project_expired`, and `project_claimed` mean the stored identity assertion is dead. `token_expired` means re-exchange the assertion. -## If the Neon CLI cannot be used +## If neon claim is missing Fall back to the REST API. Fetch `https://claimable.neon.tech/auth.md` for request and response fields. The claimable resource is `/v1/projects/{id}` on `https://claimable.neon.tech`, not `/v1/databases/{id}`. Follow [Claim](#claim) for when to mint, what rotates, and what to do after `reconciled`. From 3119597ad3e50789d1aa9d18a17628d353500fee Mon Sep 17 00:00:00 2001 From: Andre Landgraf Date: Tue, 25 Aug 2026 14:05:59 -0700 Subject: [PATCH 50/50] fix(pages): drop NEON_PROFILE as an auth.md account signal --- public/auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/auth.md b/public/auth.md index e3d45af2ef5..901c4e59029 100644 --- a/public/auth.md +++ b/public/auth.md @@ -27,7 +27,7 @@ disagree on a field, use that copy. Token exchange, JWKS, and project REST stay ## Install the Neon CLI The Neon CLI is the client. Do not start from the HTTP examples unless you are implementing a -client. Install it. If `NEON_API_KEY` is set, `NEON_PROFILE` is set, or `neon profile list -o json` +client. Install it. If `NEON_API_KEY` is set, or `neon profile list -o json` lists a profile whose `account` is not `-`, use that account and stop. A `DEFAULT` row with `account: "-"` and `file: "missing"` is not an account. If a command waits on a browser or authentication fails, stop and ask the user to sign in (`neon auth`) or mint an API key. Do not