
Neon Postgres Agent Platforms
- 29 installs
- 3 repo stars
- Updated August 1, 2026
- neondatabase/neon-for-agent-platforms
Helps with databases tasks during AI-assisted development.
About
neon-postgres-agent-platforms is a Claude Code skill for databases. It helps solo builders move faster with AI-assisted coding.
- neon-postgres-agent-platforms
- Databases
- AI-coding skill
Neon Postgres Agent Platforms by the numbers
- 29 all-time installs (skills.sh)
- +3 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #513 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/neondatabase/neon-for-agent-platforms --skill neon-postgres-agent-platformsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 29 |
|---|---|
| repo stars | ★ 3 |
| Last updated | August 1, 2026 |
| Repository | neondatabase/neon-for-agent-platforms ↗ |
What it does
Helps with databases tasks during AI-assisted development.
Files
Neon AI Agent Program
Companion to **neon-postgres** (install that first). This skill covers fleet-scale control plane for the Neon Agent Program: dual-org layout, project transfer, per-tenant provisioning, compound checkpoints, consumption, and commercial terms.
For connection strings, drivers, ORMs, branching tutorials, Auth in apps, Data API, and MCP, use **neon-postgres** and Neon docs.
Scope
Use **neon-postgres** for general Neon usage. Use this skill when the question involves:
- Dual-org economics and API keys (personal, organization, project-scoped)
- Project-per-tenant provisioning and transfer
- Fleet-wide snapshot/restore orchestration and housekeeping
- Compound checkpoints
- Consumption polling for metered fleets
- Agent Plan commercial terms (with links, not invented numbers)
- Partner support paths
Install Skills
npx skills add neondatabase/agent-skills -s neon-postgresAssistants: what you can do after install
With **neon-postgres** and this skill loaded, you have enough context for platform-level Neon work: dual-org layout, provisioning a project for a new tenant, compound checkpoints, org-wide consumption polling, and transfer/upgrade flows—without the user pasting long documentation.
Where runnable examples live: TypeScript samples run from this skill’s `scripts/` directory (package.json, npm run …). The **references/** directory keeps markdown guides beside the runnable examples; MANAGEMENT_API_SAMPLES.md is the script catalog and env map. The human Quick start is the root README.
Gotchas
Non-obvious facts agents often get wrong:
- Checkpoints are compound records. A tenant checkpoint includes source
revision + Neon snapshot/branch + secrets/env version + deploy URL + agent metadata. Do not equate "checkpoint" with "Neon branch" alone. See the compound checkpoints doc.
- Cross-org transfer needs a personal API key (org keys only work
inside one org). Projects with GitHub or Vercel integrations in Neon cannot be transferred; the API returns 422 (Transfer projects).
- After a finalized snapshot restore, the active branch ID changes. Poll
operations to completion before reconnecting. Delete orphaned (old) branches to avoid storage cost.
- Billing-aligned usage: prefer
GET /api/v2/consumption_history/v2/projects over legacy consumption endpoints. `GET /api/v2/consumption_history/account` is deprecated with a planned sunset of 2026-06-01; migrate to the v2 per-project endpoint (legacy consumption guide).
- V2 `metrics` parameter values (for implementers):
compute_unit_seconds,
root_branch_bytes_month, child_branch_bytes_month, instant_restore_bytes_month, snapshot_storage_bytes_month, public_network_transfer_bytes, private_network_transfer_bytes, extra_branches_month (consumption metrics).
- Snapshot schedules are not provided on Agent Plan. Partners implement via
snapshot API + their own scheduler.
- Rates and caps: never invent dollar amounts or limits. Confirm on live
neon.com docs.
Agent Plan and two organizations
Partners run two Neon organizations:
| Org | Role |
|---|---|
| Sponsored free org | Free-tier end users (within program rules) |
| Paid org | Paying customers (metered per Agent Plan) |
Key points:
- Dollar rates, credits, and project caps come only from the live
Agent Plan and neon.com/agents. Do not invent numbers.
- Organization API key: automation inside one org (create project, set
quotas).
- Personal API key: required to transfer a project between orgs when a
customer changes tier, then PATCH quotas to match the new tier.
- Project-scoped API key: member-level access to one project only—narrower than an org key and useful for per-tenant runtime or automation that must not touch the rest of the org. Cannot create new projects org-wide; invalid if the project is transferred out of the org.
Links: Agent Plan · AI Agents · Project transfer · AI Agent integration
HIPAA
- Agent Plan includes HIPAA with no extra fee. Partners must still follow
Neon's published HIPAA program (workflows, agreements, configuration).
- To get access or start the process, reach out to your **primary Neon
contact**.
- This skill is not legal advice.
Link: HIPAA on Neon
Fleet shape: project-per-tenant
- Project-per-tenant is Neon's documented fleet pattern: each tenant you
provision for (an end user, a customer app, or an agent workload) gets its own dedicated Neon project. That project is the isolation boundary for branches, databases, roles, and computes—not a shared Postgres cluster where you only partition by schema.
- Isolation and billing: Separate projects give **complete data and resource
isolation between tenants, keep consumption limits and billing straightforward at project scale (aligned with Agent Plan metering elsewhere in this skill), and match how the Neon Management API and Console are structured** (project-scoped create, quota, and lifecycle calls).
Staging and production
- For each tenant project, treat staging versus production (and ephemeral
previews) as branch- and snapshot-driven lifecycle inside that project—use Snapshots and database versioning and Sandbox and preview databases below for fleet orchestration, not a second project by default.
- Agent and app builders: separate your platform's environments (for
example how you host the builder or control plane) from each tenant's staging and production branches—avoid conflating "our production service" with "the tenant's production branch" in ledgers and automation.
- Some embedded products also split an end customer's **production and
development Neon assets across separate orgs for trust, keys, and billing boundaries; when that applies, read Isolation beyond branches (project and org edge cases)** next.
Link: AI Agent integration guide
Isolation beyond branches (project and org edge cases)
Use project- or org-level splits when tenant scope or trust needs go beyond branch- and snapshot-first staging and production in Fleet shape. Embedded products may isolate an end customer's production versus development databases across separate Neon orgs, not only branches—tighter billing, org API keys, and console boundaries while you still manage branches within each org.
Project-level isolation (multiple projects per tenant or workload):
- Stronger blast radius if a connection string or role is compromised—one
leak should not span unrelated workloads.
- Separate operational lifecycles (for example a disposable analytics or
migration sandbox versus production data) when automation or ownership would otherwise collide in one Postgres.
- Different teams or automation with conflicting migration or admin rights.
- Harder compliance or data-mixing rules where a single database must not
host combined workloads.
Each extra project adds fleet surface area: more API keys, more consumption rows, more housekeeping, and higher operational cost—keep project-per-tenant as the default unless a boundary above clearly applies.
Org-level isolation (beyond sponsored free versus paid):
- The two-organization layout in Agent Plan and two organizations is
the commercial split (free-tier users versus paying customers). That pattern can stack with an embedded product split: for example prod org versus dev org per end customer so playground databases never share org scope with shipped production. Keep a clear internal map of which org owns which environment and tier.
- Separately, partners sometimes need additional Neon orgs or accounts for
contracting (enterprise “their org only”), reseller or MSP models, or geographic or legal separation—product defaults and limits belong on live docs; do not invent caps.
- Organization API keys are scoped to one org. Cross-org moves use a
personal API key and project transfer, as in Gotchas—do not assume an org key can operate across orgs. Project-scoped keys are further limited to a single project (API keys).
Embedding hygiene:
- Map each platform service (control plane, tenant runtime, billing or
consumption jobs) to least-privilege keys; do not reuse production keys in sandboxes at the wrong layer.
- When prod and dev for an end customer live in different Neon orgs, scope
automation per org (typically one organization API key per org) and persist org_id with project_id / branch_id so jobs and restores target the correct side.
- Keep your ledger (
project_id,branch_id, org, checkpoint metadata)
aligned with the isolation layer you chose so restores, transfers, and audits stay consistent.
Snapshots and database versioning
For snapshot semantics, active-branch patterns, and restore tutorials, defer to **neon-postgres** and AI database versioning. Here, emphasize tenant fleets:
- Persist snapshot and branch IDs per tenant in your ledger. Tie each to
non-Neon state via compound checkpoints.
- After finalized restores, branch IDs change and orphaned
(old)branches
accumulate. Automate cleanup and update stored IDs.
- Poll operations to completion before reconnecting tenant apps.
- Product semantics (snapshot counts per tier, Beta pricing dates) change.
Confirm on Agent Plan docs.
Typical platform-level checkpoint triggers:
- Before promoting generated schema changes for a tenant
- Start or end of an agent run that mutates a tenant's database
- Before destructive migrations or customer-visible restore actions
Links: AI database versioning · Backup and restore · Snapshots-as-checkpoints demo
Sandbox and preview databases
Use this when a partner needs per-tenant preview or sandbox databases for generated apps. ("How do I create a branch?" for a single app goes to **neon-postgres**.)
- Track
project_id/branch_idper customer / agent run when spinning
previews via the Management API.
- Branch and storage counts scale with fleet size. Monitor caps and
garbage-collect idle previews.
- Short
suspend_timeout_secondson preview computes reduces cost. - Pair branch/snapshot lifecycle with secrets rotation and deploy URLs via
compound checkpoints.
Link: AI Agent integration guide
Cost, consumption, and entitlements
- Never invent pricing, quotas, or limits. Confirm on
Agent Plan and consumption metrics.
- Use
GET /api/v2/consumption_history/v2/projectsfor billing-aligned fields.
Legacy endpoints differ. `GET /api/v2/consumption_history/account` is deprecated (sunset 2026-06-01); use v2 per-project metrics instead (legacy guide).
- V2
metricsquery strings are exactly:compute_unit_seconds,
root_branch_bytes_month, child_branch_bytes_month, instant_restore_bytes_month, snapshot_storage_bytes_month, public_network_transfer_bytes, private_network_transfer_bytes, extra_branches_month.
- Poll consumption roughly every 15 minutes. Polling does not wake suspended
computes.
- Run
auth-users.ts metafrom
scripts/ for a routing map (Neon Auth REST vs Postgres roles vs consumption APIs).
Links: Agent Plan · Consumption metrics · Consumption limits · Cost optimization · Plans
Organization and project limit increases
- Current defaults and ceilings are on
Agent Plan and AI Agent integration. Do not invent limits.
- For project increase requests, email
agents@neon.tech with org ID(s), growth context, and timeline. Also flag in shared Slack if available.
Co-marketing
- Co-marketing is an included Agent Plan benefit.
- Available: joint blog posts, social promotion, hackathon sponsorship, case
studies, landing page features.
- Reach out via shared Slack or your Neon representative with context on what
you're building.
Link: Agent Plan
Support
- Shared Slack channel: fastest path for technical questions and urgent
issues.
- Neon representative: account-level requests, custom configuration,
escalations.
- Limit increases: email
agents@neon.tech with org ID(s), growth context, and timeline.
- Billing: raise via Slack or your Neon representative. Credit balances and
invoices are in the Neon Console under Billing.
- Community: Neon Discord ·
Repository samples
Runnable Management API automation from neondatabase/neon-for-agent-platforms.
- Quick start:
- Script catalog:
- Compound checkpoints:
COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md
- Checkpoint orchestration:
CHECKPOINT_ORCHESTRATION_PATTERN.md
- Doc index:
All scripts use @neondatabase/api-client only. Shared utils.ts polls async operations. For SQL access from app code (drivers, pooling, ORMs), use **neon-postgres**.
Application REST API: curl reference
These **curl** examples are for an application’s own HTTP API (your product server), not Neon’s Management API. Route shapes mirror a common Next.js App Router layout (src/app/api/.../route.ts) so you can map URLs to handlers in your codebase. See [COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md](../COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md) for what a platform-owned checkpoint should bind together beyond a Neon branch id.
Use `APP_BASE_URL=http://localhost:3000` (or your dev URL). Do not point these at Neon `console.neon.tech`; control-plane scripts are documented in [README.md](../../../../README.md) and [MANAGEMENT_API_SAMPLES.md](../MANAGEMENT_API_SAMPLES.md).
Auth
Authenticated routes typically expect a browser session cookie (or bearer token), not NEON_API_KEY. For curl, paste a Cookie or Authorization header from DevTools after signing in (keep it out of git), or expect **401 Unauthorized** when omitting it, still useful to verify the route exists.
Example handler paths (adjust for your app)
| Method | Path | Example source file |
|---|---|---|
GET | /api/v1/models | src/app/api/v1/models/route.ts |
POST | /api/v1/projects | src/app/api/v1/projects/route.ts |
GET | /api/v1/projects/{projectId}/versions | src/app/api/v1/projects/[projectId]/versions/route.ts |
POST | /api/v1/projects/{projectId}/checkpoint | src/app/api/v1/projects/[projectId]/checkpoint/route.ts |
POST | /api/v1/projects/{projectId}/versions | Same versions/route.ts (POST restores a version) |
There is no dedicated /health route in this illustration; add one in your app if you need probes, or treat **GET /api/v1/models** as an authenticated liveness check against your stack.
Run
From the repository root:
cd skills/neon-postgres-agent-platforms/references/application-rest-api
chmod +x curl-examples.sh # once
./curl-examples.shOptional env (see [.env.example](.env.example) in this folder or [../../../scripts/.env.example](../../../scripts/.env.example) for Neon keys): APP_BASE_URL, APP_PROJECT_ID, APP_VERSION_ID, STACK_SESSION_COOKIE.
#!/usr/bin/env bash
# Reference-only curls for sample product REST routes (see CURL_REFERENCE.md: not Neon Management API).
# Safe defaults: localhost, no destructive verbs. Expect 401 without STACK_SESSION_COOKIE.
APP_BASE_URL="${APP_BASE_URL:-http://localhost:3000}"
APP_PROJECT_ID="${APP_PROJECT_ID:-YOUR_PROJECT_ID}"
APP_VERSION_ID="${APP_VERSION_ID:-YOUR_VERSION_ID}"
COOKIE_HEADER=()
if [[ -n "${STACK_SESSION_COOKIE:-}" ]]; then
COOKIE_HEADER=(-H "Cookie: ${STACK_SESSION_COOKIE}")
fi
echo "# GET /api/v1/models (requires auth)"
curl -sS "${COOKIE_HEADER[@]}" "${APP_BASE_URL}/api/v1/models" | head -c 800
echo -e "\n"
echo "# POST /api/v1/projects, body: {\"name\": \"dev-placeholder\"}"
curl -sS -X POST "${COOKIE_HEADER[@]}" \
-H "Content-Type: application/json" \
-d '{"name":"dev-placeholder"}' \
"${APP_BASE_URL}/api/v1/projects" | head -c 800
echo -e "\n"
echo "# GET /api/v1/projects/{projectId}/versions"
curl -sS "${COOKIE_HEADER[@]}" \
"${APP_BASE_URL}/api/v1/projects/${APP_PROJECT_ID}/versions" | head -c 1200
echo -e "\n"
echo "# POST /api/v1/projects/{projectId}/checkpoint, optional assistantMessageId"
curl -sS -X POST "${COOKIE_HEADER[@]}" \
-H "Content-Type: application/json" \
-d '{}' \
"${APP_BASE_URL}/api/v1/projects/${APP_PROJECT_ID}/checkpoint" | head -c 800
echo -e "\n"
echo "# POST /api/v1/projects/{projectId}/versions, body: {\"versionId\": \"...\"}"
curl -sS -X POST "${COOKIE_HEADER[@]}" \
-H "Content-Type: application/json" \
-d "{\"versionId\":\"${APP_VERSION_ID}\"}" \
"${APP_BASE_URL}/api/v1/projects/${APP_PROJECT_ID}/versions" | head -c 1200
echo -e "\n"
Full-stack checkpoint orchestration (pattern)
Read [COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md](COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md) first for the “what to record” dimension table. This doc is about how meta DB, workflows, Neon API usage, and restore steps usually layer.
Agent platforms usually implement checkpoints as orchestration, not a single Management API script. A useful mental model: pair source revision (Git commit, template hash, artifact digest, or codegen bundle id) with Neon database state (snapshot id, branch id, project id) and store both in your meta-database or ledger, alongside secrets, deployment URLs, and agent run metadata. The compound record itself is defined in COMPOUND_CHECKPOINTS (link above).
This skill’s *`scripts/.ts` files stay API-sized**: they cover the Neon control-plane slice only. Your product adds workflows, HTTP routes, durable jobs, and schema.
---
Typical layers (abstract)
| Layer | Responsibility |
|---|---|
| Neon integration | Wrap @neondatabase/api-client: projects, branches, logical snapshots, restore, connection URIs, operation polling (patterns align with `utils.ts` and the sample scripts). |
| Meta database | Version rows that bind neon_snapshot_id (and related Neon ids) to git_commit_hash / artifact id / timestamps / optional assistant or run ids. Often separate tables for tenant secrets or env snapshots. |
| Checkpoint workflow | When the user or agent creates a checkpoint: resolve latest source revision and create a Neon snapshot (often in parallel), then insert one version row so code and DB state stay aligned. |
| HTTP or queue entry | User-facing POST …/checkpoint or internal job that starts the workflow (workflow engine is product-specific). |
| Restore | Load version row → restore matching secrets/env → align source control or deployment to the recorded revision → apply Neon snapshot to the correct branch in a defined order. |
---
Mapping Neon operations to samples here
| Neon-shaped concern | Sample in this skill (scripts/) |
|---|---|
| Create logical snapshot | `snapshot.ts` |
| Restore snapshot onto a branch | `restore-snapshot.ts` |
| Snapshot → branch → restore demo | `versioning-flow.ts` |
| Provision project + enable branch Auth (admin) | `create-project-with-auth.ts` |
---
Auth providers
Products differ on Neon Auth setup (better_auth, other providers, or separate IdPs). The Management API samples use `create-project-with-auth.ts` for the `better_auth` path; Neon Auth APIs and Console flows are documented on [neon.com/docs](https://neon.com/docs).
---
Further reading
- [MANAGEMENT_API_SAMPLES.md](MANAGEMENT_API_SAMPLES.md) — env vars and npm commands for the scripts above.
- [COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md](COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md) — dimensions to persist beyond Neon ids.
- [AI database versioning](https://neon.com/docs/ai/ai-database-versioning.md) — Neon semantics for snapshots and restore.
Compound checkpoints for agent platforms
Read this for: what dimensions belong in a compound checkpoint record (beyond Neon branch/snapshot ids alone). For system layering and restore flow, continue to [CHECKPOINT_ORCHESTRATION_PATTERN.md](CHECKPOINT_ORCHESTRATION_PATTERN.md) after you understand the table below.
---
For multi-tenant AI agent platforms, products that provision, mutate, version, and retire many Neon-backed databases on behalf of users, a checkpoint is not merely a Neon branch or snapshot.
Treat a checkpoint as a compound version record your control plane owns. It should bind together, at minimum:
| Dimension | What to record | Why it matters for agent platforms |
|---|---|---|
| Source revision | Git commit, template hash, artifact id, or codegen bundle id | Code and schema migrations move together with database state. |
| Database state | Neon snapshot id and/or branch id (plus project id) | Neon gives point-in-time and branch semantics; your ledger tells you which API objects belong to which tenant generation. |
| Secrets / environment | Version of env injected into the generated app (API keys, feature flags, DATABASE_URL rotation) | Restoring DB without matching secrets produces “works in Console, broken in app” failures. |
| Deployment or sandbox URL | Preview host, workflow run URL, or worker endpoint | Users and agents reconnect to the same runnable surface you rolled back. |
| Agent run metadata | Run id, session id, tool trace pointer | Audit, support, and coordinated retries across orchestration steps. |
| Rollback / promotion status | e.g. preview vs promoted, failed restore, orphaned branch cleanup | Fleet hygiene (avoid `main (old)` storage leaks; know which snapshot is “live” for billing). |
The scripts in `scripts/` intentionally stay Management API-sized: they implement the Neon slice (projects, branches, snapshots, restore, transfer, consumption, Auth admin endpoints). They do not replace your meta-database schema, workflow engine, or deployment pipeline.
Generic Neon usage (connection strings, serverless driver, Drizzle, basic branching tutorials, Neon Auth app integration) lives in the `neon-postgres` skill and [Neon documentation](https://neon.com/docs), use those first; use this repository for fleet and control-plane orchestration on top.
---
Mapping Neon calls to your compound ledger
The Management API shapes below match the `@neondatabase/api-client` scripts under `scripts/`. They are one slice of a checkpoint, you still persist the non-Neon dimensions in your platform store.
| Concern | Minimal API surface (Neon) | Sample script (scripts/) |
|---|---|---|
| Create logical snapshot | POST .../branches/{id}/snapshot | `snapshot.ts` |
| Restore snapshot onto a branch | POST .../snapshots/{id}/restore | `restore-snapshot.ts` |
| End-to-end demo (root snapshot → branch → restore) | Same operations composed | `versioning-flow.ts` |
| Provision tenant project + enable branch Auth (admin) | createProject, branch Auth enable | `create-project-with-auth.ts` |
For full-stack orchestration (pairing Neon ids with Git/deploy/secrets in one workflow), see [CHECKPOINT_ORCHESTRATION_PATTERN.md](CHECKPOINT_ORCHESTRATION_PATTERN.md) for an abstract layering model and restore ordering. Your stack’s filenames and frameworks will differ; the compound record idea stays the same.
---
Example: storing compound rows in your meta-database
A common pattern is a `project_versions` (or similarly named) table where each row combines `neon_snapshot_id` with a source revision (git_commit_hash, artifact digest, or bundle id) and related metadata. Workflows often resolve the latest revision and create a Neon snapshot, then insert one row so code and database state stay coupled. On restore, systems typically align source control or deployment to that revision, replay secrets/env as needed, then apply the Neon snapshot to the target branch in a defined order. Substitute CI artifacts, container digests, or preview URLs depending on your platform; the ledger shape is what matters.
---
Further reading
- `neon-postgres` (agent-skills), drivers, ORMs, Auth for applications, generic branching and connection patterns.
- AI database versioning, Neon product semantics for snapshots and restore.
- Checkpoints for agents with Neon Snapshots
- Product HTTP examples (your app API, not
console.neon.tech): `application-rest-api/CURL_REFERENCE.md`
Management API samples (scripts/)
Small Node.js + TypeScript scripts that call Neon’s official [Management API TypeScript SDK](https://neon.com/docs/reference/typescript-sdk.md) (`@neondatabase/api-client`) via `createApiClient`, no other Neon npm packages. Sources live in [`scripts/`](../scripts/); `npm run build` runs `tsc` and emits *`dist/scripts/.js** per **[tsconfig.json](../scripts/tsconfig.json)**. **npm run typecheck** runs **tsc --noEmit** (no emit). Scripts **import "dotenv/config"** so variables from **.env** load automatically; run with **node dist/scripts/<name>.js** or **npm run …** (each npm script runs **build** then **node dist/scripts/...`**).
When we say “Neon TypeScript SDK” here, we mean [`@neondatabase/api-client`](https://registry.npmjs.org/@neondatabase/api-client) and nothing else, not @neondatabase/serverless, @neondatabase/neon-js, @neondatabase/toolkit, or any other @neondatabase/* package.
Use these to prototype per-tenant provisioning, fleet branching/snapshot orchestration, database versioning (snapshots + restore), org transfer (free ↔ paid org), consumption polling, and Neon Auth management endpoints, not introductory app connectivity (that is `neon-postgres` + app docs).
Fleet provisioning and org layout
Agent Program teams usually maintain two Neon orgs (sponsored free vs paid) and route `NEON_ORG_ID` per customer tier when calling `create-project.ts`. Upgrades use `transfer-project.ts` with a personal API key; fleet-wide usage uses `consumption-query.ts` with `NEON_ORG_ID`.
For the full mapping (keys, patterns, which script covers which fleet operation), see [Fleet and org model (summary) in README](../../../README.md#fleet-and-org-model-summary).
Application REST API vs Neon Management API
Scripts in `scripts/` call Neon’s Management API (console.neon.tech, @neondatabase/api-client), provisioning, branches, snapshots, org transfer, consumption, Neon Auth management endpoints.
For `curl` examples aimed at your product’s own REST API (checkpoints, versions, etc.), see [application-rest-api/CURL_REFERENCE.md](application-rest-api/CURL_REFERENCE.md). Those routes are not Neon control-plane calls. Compound checkpoints are described in [COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md](COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md).
---
Prerequisites
| Requirement | Notes |
|---|---|
| Node.js 20+ | Enables node --env-file=.env (or export vars manually). |
NEON_API_KEY | API key. Organization keys cover one org; personal keys can transfer projects across orgs. Project-scoped keys are limited to one project (good for per-tenant runtime automation; cannot create org-wide resources or new projects). |
NEON_ORG_ID | Often required when creating projects with a personal key. |
| `.env` | Create a local `.env` file, `NEON_API_KEY` required; scripts load it via `dotenv`. You can still use `node --env-file=.env …` if you prefer explicit loading. |
Install dependencies:
cd neon-for-agent-platforms/skills/neon-postgres-agent-platforms/scripts
npm install
touch .env
# Set NEON_API_KEY=... and any IDs your scripts need (see tables below)
npm run buildRun a compiled script (from `scripts/`, same directory as `package.json`):
node dist/scripts/list-projects.js
# Or: npm run neon:list-projectsAfter editing `scripts//.ts`, run `npm run build` again (or rely on npm scripts that invoke `build`* first).
---
Script catalog
| Script | npm shortcut | What it does |
|---|---|---|
| `scripts/list-projects.ts` | npm run neon:list-projects | Lists project id and name (Management API). |
| `scripts/create-project.ts` | npm run create-project | Creates a Neon project; prints projectId and DATABASE_URL. Waits for initial operations to finish. |
| `scripts/create-project-with-auth.ts` | npm run create-project-with-auth | Same as create-project, then enables Neon Auth on the default branch (Better Auth). Saves Auth keys once, see stderr note. |
| `scripts/delete-project.ts` | npm run delete-project | Deletes a project by id (destructive). |
| `scripts/branch.ts` | npm run branch | `list`, JSON list of branches. `create <name>`, new branch from main / production (or NEON_PARENT_BRANCH_ID). |
| `scripts/snapshot.ts` | npm run snapshot | Creates a logical snapshot on the default branch. Optional `NEON_SNAPSHOT_EXPIRES_AT` (RFC 3339) for auto-deletion per cleanup strategy. |
| `scripts/list-snapshots.ts` | npm run list-snapshots | Lists all snapshots for `NEON_PROJECT_ID`. |
| `scripts/delete-snapshot.ts` | npm run delete-snapshot | Deletes one snapshot by `NEON_SNAPSHOT_ID` (polls operations). |
| `scripts/rename-snapshot.ts` | npm run rename-snapshot | PATCH rename, `NEON_SNAPSHOT_NEW_NAME`. |
| `scripts/delete-branch.ts` | npm run delete-branch | Deletes a branch by `NEON_BRANCH_ID` (e.g. orphaned `main (old)` after restore); destructive. |
| `scripts/versioning-flow.ts` | npm run versioning-flow | Versioning demo: snapshot production → child branch → restore baseline onto child (Management API only; no bundled SQL driver). See AI database versioning. |
| `scripts/restore-snapshot.ts` | npm run restore-snapshot | One-shot restore: applies an existing snapshot id to a target branch id. |
| `scripts/promote-safe-production.ts` | npm run promote-safe -- <subcommand> | [Promoting Postgres safely](https://neon.com/blog/promoting-postgres-changes-safely-production), bootstrap-dev, promote, refresh-dev, rollback-prod. |
| `scripts/transfer-project.ts` | npm run transfer | Moves project(s) between orgs (e.g. sponsored → paid). Needs personal API key + permissions. 422 if any listed project has a GitHub or Vercel integration (transfer limits). |
| `scripts/consumption-query.ts` | npm run consumption | `GET /consumption_history/v2/projects`, usage-based metrics aligned with billing. |
| `scripts/auth-users.ts` | npm run auth-users | Neon Auth REST: `meta` (no API call, prints routing + SQL hint), `create`, `delete`. Requires Auth enabled on the branch first. |
Restore / bootstrap: If the API returns `ROOT_BRANCHES_LIMIT_EXCEEDED`, the project has hit Neon’s root-branch limit for your plan. Delete old branches (preview restores, bootstrap-*, before_restore_*, orphaned `main (old)`) using `delete-branch.ts` or the Console, or point `NEON_PROJECT_ID` at an emptier project.
Compound checkpoints (agent platforms)
These scripts cover Neon snapshot + restore + branch ops only, the database slice of a version. For agent platforms, a checkpoint usually binds source revision, Neon snapshot/branch ids, secrets/env, deployment or preview URL, agent run metadata, and rollback/promotion state. See [COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md](COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md). Generic Neon branching guidance stays in `neon-postgres`.
---
Environment variables (by script)
Create a local `.env` file and set only what you need.
Always
| Variable | Used by |
|---|---|
NEON_API_KEY | All scripts |
Projects
| Variable | Used by |
|---|---|
NEON_ORG_ID | create-project.ts (often required with personal keys) |
NEON_PROJECT_NAME | create-project.ts (optional; default tenant-<timestamp>) |
NEON_PROJECT_ID | delete-project, branch, snapshot, list-snapshots, delete-snapshot, rename-snapshot, delete-branch, promote-safe-production, versioning-flow, auth-users, restore-snapshot (with snapshot vars). Not used by consumption-query.ts (see `CONSUMPTION_PROJECT_IDS` below). |
Branches
| Variable | Used by |
|---|---|
NEON_PARENT_BRANCH_ID | branch.ts create, optional; defaults to production branch id |
NEON_BRANCH_ID | auth-users.ts, `delete-branch.ts` |
NEON_PROD_BRANCH_ID | `promote-safe-production.ts`, optional; defaults to main / production branch |
NEON_DEV_BRANCH_ID | `promote-safe-production.ts`, required for `promote` and `refresh-dev` |
NEON_BOOTSTRAP_DEV_BRANCH_NAME | `promote-safe-production.ts bootstrap-dev`, default dev |
NEON_SNAPSHOT_BOOTSTRAP_NAME | `bootstrap-dev`, optional snapshot label |
NEON_SNAPSHOT_PRE_PROMOTION_NAME, NEON_SNAPSHOT_DEV_CANDIDATE_NAME | `promote`, optional snapshot labels |
NEON_SNAPSHOT_REFRESH_NAME | `refresh-dev`, optional |
NEON_RESTORE_BACKUP_BRANCH_NAME | `promote-safe-production` restore steps, optional backup branch name prefix |
Snapshots & versioning
| Variable | Used by |
|---|---|
NEON_SNAPSHOT_NAME | snapshot.ts, optional label |
NEON_SNAPSHOT_EXPIRES_AT | snapshot.ts, optional RFC 3339 auto-deletion time |
NEON_SNAPSHOT_LSN | snapshot.ts, optional; mutually exclusive with default timestamp (advanced) |
NEON_SNAPSHOT_ID | restore-snapshot.ts, `delete-snapshot.ts`, `rename-snapshot.ts`, `promote-safe-production.ts rollback-prod` |
NEON_SNAPSHOT_NEW_NAME | `rename-snapshot.ts` |
NEON_TARGET_BRANCH_ID | restore-snapshot.ts |
VERSION_BASELINE_NAME, VERSION_DEMO_BRANCH_NAME | versioning-flow.ts, optional branch/snapshot name overrides |
Org transfer
| Variable | Used by |
|---|---|
NEON_SOURCE_ORG_ID, NEON_DESTINATION_ORG_ID | transfer-project.ts |
NEON_PROJECT_IDS or NEON_PROJECT_ID | Comma-separated or single project id |
Projects linked to GitHub or Vercel in Neon cannot be transferred; the transfer API responds with 422 for those project IDs. Remove or re-home the integration first, or provision a fresh project in the destination org. See Transfer projects.
Consumption API (v2)
| Variable | Used by |
|---|---|
NEON_ORG_ID | consumption-query.ts |
CONSUMPTION_FROM, CONSUMPTION_TO | RFC 3339 range |
CONSUMPTION_GRANULARITY | hourly \ |
CONSUMPTION_METRICS | Optional comma list (defaults include compute + storage + transfer) |
CONSUMPTION_PROJECT_IDS | Optional comma-separated Neon project ids to include (this script does not read NEON_PROJECT_ID) |
CONSUMPTION_LIMIT, CONSUMPTION_CURSOR | Pagination |
Legacy account endpoint: GET /api/v2/consumption_history/account is deprecated with a planned sunset of 2026-06-01. Prefer this repo’s `consumption-query.ts` (GET /consumption_history/v2/projects) for invoice-aligned, per-project metrics. If you still need legacy metric shapes, see Query consumption metrics (legacy).
`metrics` values (v2): pass a subset or all of: compute_unit_seconds, root_branch_bytes_month, child_branch_bytes_month, instant_restore_bytes_month, snapshot_storage_bytes_month, public_network_transfer_bytes, private_network_transfer_bytes, extra_branches_month. These are the strings accepted in CONSUMPTION_METRICS and by the API metrics parameter (consumption metrics guide).
Neon Auth users
| Variable | Used by |
|---|---|
USER_EMAIL, USER_NAME | auth-users.ts create |
AUTH_USER_ID | auth-users.ts delete |
Enable Auth on the branch once: POST .../projects/{id}/branches/{id}/auth with better_auth, see Manage Neon Auth via the API.
---
Typical flows
Commands below assume current working directory is `scripts/` (where `dist/scripts/` is written after `npm run build`).
Provision fleet tenants (free vs paid org)
1. Store `NEON_ORG_ID` for each Neon org (free pool vs paid pool) and choose an API key that can create projects there (details). 2. For each new customer, set `NEON_ORG_ID` (and optionally `NEON_PROJECT_NAME`) and run:
node --env-file=.env dist/scripts/create-project.js3. Persist `projectId` and `DATABASE_URL` from the JSON output in your control-plane database.
Spin up a single tenant project
node --env-file=.env dist/scripts/create-project.jsList branches, then create a branch (tenant sandbox / preview)
node --env-file=.env dist/scripts/branch.js list
node --env-file=.env dist/scripts/branch.js create my-featureUse this pattern to script per-tenant sandboxes from your control plane. Tutorials on branching concepts for a single app belong in `neon-postgres` and Neon’s branching guides, not duplicated here.
Database versioning (snapshots + restore)
End-to-end demo (creates a child branch named versioning-demo-<timestamp>):
node --env-file=.env dist/scripts/versioning-flow.jsRestore an arbitrary snapshot onto a branch:
node --env-file=.env dist/scripts/restore-snapshot.js
# Needs NEON_SNAPSHOT_ID and NEON_TARGET_BRANCH_ID in .envMove a customer from free org to paid org
node --env-file=.env dist/scripts/transfer-project.jsPoll usage (invoice-aligned metrics)
node --env-file=.env dist/scripts/consumption-query.jsNeon Auth app users (REST)
node --env-file=.env dist/scripts/auth-users.js meta---
Shared helpers
`scripts/utils.ts` holds shared helpers on top of the same `@neondatabase/api-client` surface: operation polling (waitForOperationsToSettle), error formatting, and small compose helpers around `createApiClient` calls so scripts stay readable. There is no second Neon client package.
---
Related docs
- README — fleet and org model · Neon Auth API · Postgres roles · Consumption metrics.
- README, Agent Program model (two orgs, keys, skills).
Pricing, consumption, and Agent Plan feature matrix
Read this file when the user needs line-item rates, quota numbers, the full Agent Plan entitlement list, or consumption API field guidance, not for a high-level “what is the Agent Program” answer (use SKILL.md for that).
Disclaimer (read first)
- Everything below with dollar amounts, per-unit rates, included gigabytes, project or branch caps, credit ceilings, Beta or GA dates (for example snapshot storage), and feature limits is a convenience snapshot transcribed from Neon’s public docs.
- Neon updates [Agent Plan](https://neon.com/docs/introduction/agent-plan.md), [Plans](https://neon.com/docs/introduction/plans.md), [consumption metrics](https://neon.com/docs/guides/consumption-metrics.md), and related pages without this repository changing. Treat neon.com as the only source of truth for anything you put in a contract, invoice, support reply, or customer-facing UI.
- Last intentional alignment with those pages: April 2026. Before you quote externally (blog, deck, ticket, social), open the live links in this file and copy numbers from there, not from this markdown alone.
---
Cost implications and tracking
Use when a partner asks about pricing, cost optimization, or consumption monitoring for their fleet.
Pricing (paid org only; free org is $0)
Illustrative line items from Neon’s public pricing as of the snapshot above; confirm on [Agent Plan](https://neon.com/docs/introduction/agent-plan.md) and [Plans](https://neon.com/docs/introduction/plans.md) before relying on them.
- Compute: $0.106/CU-hour (roughly half of standard Scale pricing at $0.222)
- Storage: $0.35/GB-month
- Instant restore (PITR): $0.20/GB-month for change history
- Snapshots: $0.09/GB-month (after Beta, starting May 1, 2026)
- Extra branches: $1.50/branch-month (beyond plan allowance)
- Public network transfer: 100 GB included, then $0.10/GB
- Initial credits: up to $25,000 for the paid org
Cost optimization levers
- Scale-to-zero: Most tenant databases are idle most of the time. Set
suspend_timeout_secondsaggressively (300s for free, 60s-300s for paid). - Autoscaling caps: Set
autoscaling_limit_max_cuper project to prevent runaway compute. Free: max 2 CU. Paid: up to 16 CU. - Per-project quotas: Configure
active_time_seconds,logical_size_bytes,compute_time_seconds,data_transfer_byteson project creation or via PATCH. - Branch cleanup: Delete old dev branches and orphaned
(old)branches after restores. - Snapshot rotation: Delete snapshots no longer needed; use
expires_atfor automatic cleanup.
Consumption tracking
- On usage-based plans (Launch, Scale, Agent, Enterprise), use
GET /api/v2/consumption_history/v2/projectsfor metrics that match billing. `GET /api/v2/consumption_history/account` is deprecated with a planned sunset of 2026-06-01; migrate to v2 per-project metrics (consumption metrics, legacy notice). Legacy endpoints return different fields; see Neon’s consumption docs. - V2 `metrics` values (exact strings for the API and for
CONSUMPTION_METRICSin samples):compute_unit_seconds,root_branch_bytes_month,child_branch_bytes_month,instant_restore_bytes_month,snapshot_storage_bytes_month,public_network_transfer_bytes,private_network_transfer_bytes,extra_branches_month. - Poll about every 15 minutes (Neon’s update cadence). Polling does not wake suspended computes.
- Alert users at 80% and 95% of their quota to prevent unexpected suspension.
- Routing index (Auth
/usersvs Postgres roles vs consumption): run**auth-users.ts meta; see MANAGEMENT_API_SAMPLES.md**.
Links: Agent Plan · Consumption metrics · Consumption limits · Cost optimization
---
Agent Plan features summary
Use when a partner asks what's included in the Agent Plan. Do not invent numbers. The bullets below mirror Neon’s Agent Plan / Plans documentation as of April 2026; caps, windows, and included quantities change—always confirm on [Agent Plan](https://neon.com/docs/introduction/agent-plan.md) (and linked pages) before quoting outside this repo.
Database and compute
- Up to 30,000 projects per org (can request increases via agents@neon.tech)
- 25 branches per project (paid), 10 (free)
- Autoscaling up to 16 CU (paid) or 2 CU (free); fixed computes up to 56 CU on paid
- Scale to zero: configurable 1 min to always-on (paid); 5 min fixed (free)
- Up to 16 TB logical data per branch
APIs and services
- Neon Auth: managed auth built on Better Auth (up to 1M MAU on paid)
- Data API: PostgREST-compatible REST API for direct DB access
- Management API: full CRUD for projects, branches, snapshots, quotas
- Higher rate limits on both Management and Data APIs for Agent Plan
Versioning and recovery
- Instant restore with up to 30-day restore window (paid), 1-day (free)
- 10 manual snapshots per project (paid), 1 (free)
- Copy-on-write branching for dev environments
Security and compliance
- SOC 2, SOC 3, ISO 27001, ISO 27701, GDPR, CCPA
- HIPAA included (contact Neon to enable)
- Protected branches, IP Allow, Private Networking (AWS PrivateLink)
Monitoring
- 14-day monitoring retention (paid)
- Metrics/logs export to Datadog or OTel-compatible platforms
Not included
- Automated backup schedules are not available on the Agent Plan (build your own via API)
Links: Plans · Agent Plan
References
Doc index for skills/neon-postgres-agent-platforms/references/. For clone, install, and quick start, see the root README.
Reading order
1. [MANAGEMENT_API_SAMPLES.md](MANAGEMENT_API_SAMPLES.md) — env vars and npm run … commands for each script. 2. [COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md](COMPOUND_CHECKPOINTS_FOR_AGENT_PLATFORMS.md) — what belongs in a compound checkpoint record (dimensions: revision, Neon ids, secrets, URLs, metadata). 3. [CHECKPOINT_ORCHESTRATION_PATTERN.md](CHECKPOINT_ORCHESTRATION_PATTERN.md) — how meta DB, workflows, Neon API calls, and restore ordering usually fit together. Read after (2) when you need architecture, not before.
Optional: [application-rest-api/](application-rest-api/) (curl against your app API, not Neon console), [pricing-and-plan-features.md](pricing-and-plan-features.md), [LICENSE](../../../LICENSE).
TypeScript samples (../scripts/)
The canonical sample files live under `scripts/` in this skill.
| Script | Purpose (short) |
|---|---|
| auth-users.ts | Neon Auth REST admin (meta, create, delete). |
| branch.ts | List or create branches. |
| consumption-query.ts | Consumption v2 per project/org. |
| create-project-with-auth.ts | Create project + enable Neon Auth on the branch. |
| create-project.ts | Create project; wait on operations. |
| delete-branch.ts | Delete a branch by id (e.g. orphaned main (old)). |
| delete-project.ts | Delete a project (destructive). |
| delete-snapshot.ts | Delete one snapshot; polls operations. |
| list-projects.ts | List projects (ids + names). |
| list-snapshots.ts | List snapshots for NEON_PROJECT_ID. |
| promote-safe-production.ts | Safe promote / bootstrap / rollback flows. |
| rename-snapshot.ts | PATCH rename snapshot. |
| restore-snapshot.ts | Apply snapshot to a branch. |
| snapshot.ts | Create logical snapshot on default branch. |
| transfer-project.ts | Move project between orgs (personal key). 422 if GitHub/Vercel integration is installed on a project. |
| versioning-flow.ts | Snapshot → branch → restore demo. |
| utils.ts | Shared polling helpers for async Management API ops. |
Run: from `scripts/`, `npm run build` (or any `npm run <sample>`, which runs `build` first) emits *`dist/scripts/.js**. Then **node dist/scripts/<name>.js** or **npm run …`**.
/**
* Neon Auth: application users (REST) and how they show up in Postgres.
*
* Subcommands:
* meta print [meta] map: REST vs Postgres, doc links
* create POST .../auth/users (needs USER_EMAIL, optional USER_NAME)
* delete DELETE .../auth/users/{id} (needs AUTH_USER_ID)
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const branchId = process.env.NEON_BRANCH_ID;
const [, , cmd] = process.argv;
function printMeta(): void {
console.log(
JSON.stringify(
{
title:
"[meta] Application users (Neon Auth) vs Postgres database roles",
rest: {
createUser:
"POST /api/v2/projects/{project_id}/branches/{branch_id}/auth/users",
deleteUser:
"DELETE /api/v2/projects/{project_id}/branches/{branch_id}/auth/users/{auth_user_id}",
updateRole:
"PUT /api/v2/projects/{project_id}/branches/{branch_id}/auth/users/{auth_user_id}/role",
enableAuth:
"POST /api/v2/projects/{project_id}/branches/{branch_id}/auth",
},
postgres: {
note: "Neon Auth syncs user data into the branch database (default schema `neon_auth`, e.g. table `users_sync`).",
listUsersExampleSql: "SELECT * FROM neon_auth.users_sync LIMIT 50;",
rolesNote:
"Database roles (connection users, privileges) are separate; use SQL or Console (Manage roles), not this REST API.",
},
docs: [
"https://neon.com/docs/neon-auth/api",
"https://neon.com/docs/auth/guides/user-management",
"https://neon.com/docs/manage/users",
"https://api-docs.neon.tech/reference/createbranchneonauthnewuser",
],
envForThisScript: {
create: [
"NEON_API_KEY",
"NEON_PROJECT_ID",
"NEON_BRANCH_ID",
"USER_EMAIL",
"USER_NAME (optional)",
],
delete: [
"NEON_API_KEY",
"NEON_PROJECT_ID",
"NEON_BRANCH_ID",
"AUTH_USER_ID",
],
},
},
null,
2,
),
);
}
if (cmd === "meta") {
printMeta();
process.exit(0);
}
if (!["create", "delete"].includes(cmd ?? "")) {
console.error(
"Usage: npm run auth-users -- meta | create | delete\n" +
" meta [meta] REST vs Postgres, links\n" +
" create requires USER_EMAIL (and project/branch)\n" +
" delete requires AUTH_USER_ID",
);
process.exit(1);
}
if (!apiKey) {
console.error("NEON_API_KEY is required.");
process.exit(1);
}
if (!projectId || !branchId) {
console.error("Set NEON_PROJECT_ID and NEON_BRANCH_ID.");
process.exit(1);
}
const api = createApiClient({ apiKey });
if (cmd === "create") {
const email = process.env.USER_EMAIL;
const name = process.env.USER_NAME;
if (!email) {
console.error("Set USER_EMAIL for create.");
process.exit(1);
}
const { data } = await api.createBranchNeonAuthNewUser(projectId, branchId, {
email,
...(name ? { name } : {}),
});
console.log(JSON.stringify(data, null, 2));
process.exit(0);
}
if (cmd === "delete") {
const authUserId = process.env.AUTH_USER_ID;
if (!authUserId) {
console.error("Set AUTH_USER_ID for delete.");
process.exit(1);
}
await api.deleteBranchNeonAuthUser(projectId, branchId, authUserId);
console.log(JSON.stringify({ ok: true, deleted: authUserId }, null, 2));
process.exit(0);
}
/**
* List branches or create a dev branch from production (main / production).
*
* Usage (from `scripts/` after `npm run build`):
* NEON_API_KEY=... NEON_PROJECT_ID=... node dist/scripts/branch.js list
* NEON_API_KEY=... NEON_PROJECT_ID=... node dist/scripts/branch.js create <branch-name>
* Or: npm run branch -- list | create <branch-name>
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import { createBranchWithOperations, getProductionBranchId } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const [, , cmd, branchName] = process.argv;
if (!apiKey) {
console.error("NEON_API_KEY is required.");
process.exit(1);
}
if (!projectId) {
console.error("Set NEON_PROJECT_ID.");
process.exit(1);
}
const api = createApiClient({ apiKey });
if (cmd === "list") {
const { data } = await api.listProjectBranches({ projectId });
const branches = (data.branches ?? []).map((b) => ({
id: b.id,
name: b.name,
created_at: b.created_at,
parent_id: b.parent_id,
}));
console.log(JSON.stringify(branches, null, 2));
process.exit(0);
}
if (cmd === "create") {
if (!branchName) {
console.error("Usage: npm run branch -- create <branch-name>");
process.exit(1);
}
const prodId =
process.env.NEON_PARENT_BRANCH_ID?.trim() ||
(await getProductionBranchId(api, projectId));
if (!prodId) {
console.error("Could not resolve production branch (main or production).");
process.exit(1);
}
const { id } = await createBranchWithOperations(api, projectId, {
name: branchName,
parentId: prodId,
});
console.log(
JSON.stringify({ branchId: id, parentBranchId: prodId }, null, 2),
);
process.exit(0);
}
console.error("Usage: npm run branch -- list | create <branch-name>");
process.exit(1);
/**
* GET /consumption_history/v2/projects: usage-based metrics aligned with billing.
*
* `metrics` query parameter: comma-separated or repeated; each value must be one of:
* `compute_unit_seconds`, `root_branch_bytes_month`, `child_branch_bytes_month`,
* `instant_restore_bytes_month`, `snapshot_storage_bytes_month`,
* `public_network_transfer_bytes`, `private_network_transfer_bytes`, `extra_branches_month`.
*
* Do not use legacy `GET /consumption_history/account` for new work; it is deprecated with a planned
* sunset of **2026-06-01** — use this v2 project endpoint (or legacy per-project metrics only if you
* still need legacy fields). See Neon’s consumption docs.
* @see https://neon.com/docs/guides/consumption-metrics
* @see https://neon.com/docs/guides/consumption-metrics-legacy
*/
import "dotenv/config";
import {
ConsumptionHistoryGranularity,
createApiClient,
} from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
const orgId = process.env.NEON_ORG_ID;
const from = process.env.CONSUMPTION_FROM;
const to = process.env.CONSUMPTION_TO;
const granularityRaw = process.env.CONSUMPTION_GRANULARITY || "daily";
const DEFAULT_METRICS = [
"compute_unit_seconds",
"root_branch_bytes_month",
"child_branch_bytes_month",
"instant_restore_bytes_month",
"snapshot_storage_bytes_month",
"public_network_transfer_bytes",
"private_network_transfer_bytes",
"extra_branches_month",
];
const metricsRaw = process.env.CONSUMPTION_METRICS;
const metrics = metricsRaw
? metricsRaw
.split(",")
.map((s: string) => s.trim())
.filter(Boolean)
: DEFAULT_METRICS;
const projectIdsRaw = process.env.CONSUMPTION_PROJECT_IDS;
const projectIds = projectIdsRaw
? projectIdsRaw
.split(",")
.map((s: string) => s.trim())
.filter(Boolean)
: undefined;
const GRANULARITY_BY_ENV: Record<string, ConsumptionHistoryGranularity> = {
hourly: ConsumptionHistoryGranularity.Hourly,
daily: ConsumptionHistoryGranularity.Daily,
monthly: ConsumptionHistoryGranularity.Monthly,
};
if (!apiKey || !orgId || !from || !to) {
console.error(
"Set NEON_API_KEY, NEON_ORG_ID, CONSUMPTION_FROM, CONSUMPTION_TO (RFC 3339). Optional: CONSUMPTION_GRANULARITY, CONSUMPTION_METRICS (comma list), CONSUMPTION_PROJECT_IDS.",
);
process.exit(1);
}
const granularity = GRANULARITY_BY_ENV[granularityRaw];
if (granularity === undefined) {
console.error("CONSUMPTION_GRANULARITY must be hourly, daily, or monthly.");
process.exit(1);
}
const api = createApiClient({ apiKey });
const { data } = await api.getConsumptionHistoryPerProjectV2({
org_id: orgId,
from,
to,
granularity,
metrics,
project_ids: projectIds,
limit: process.env.CONSUMPTION_LIMIT
? Number(process.env.CONSUMPTION_LIMIT)
: undefined,
cursor: process.env.CONSUMPTION_CURSOR || undefined,
});
console.log(JSON.stringify(data, null, 2));
/**
* Create a Neon project, then enable Neon Auth on the default branch (Better Auth).
*
* Prints Neon Auth keys once; store pub_client_key / secret_server_key securely.
*/
import "dotenv/config";
import {
createApiClient,
NeonAuthSupportedAuthProvider,
} from "@neondatabase/api-client";
import { createProjectWithOperations, getProductionBranchId } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const orgId = process.env.NEON_ORG_ID;
const name =
process.env.NEON_PROJECT_NAME?.trim() || `tenant-auth-${Date.now()}`;
const authDb = process.env.NEON_AUTH_DATABASE_NAME?.trim();
if (!apiKey) {
console.error("NEON_API_KEY is required.");
process.exit(1);
}
const api = createApiClient({ apiKey });
const { projectId, databaseUrl } = await createProjectWithOperations(api, {
name,
orgId: orgId || undefined,
endpointSettings: {
autoscaling_limit_min_cu: 0.25,
autoscaling_limit_max_cu: 2,
suspend_timeout_seconds: 300,
},
});
const prodBranchId = await getProductionBranchId(api, projectId);
if (!prodBranchId) {
console.error(
JSON.stringify(
{ error: "No production branch after create", projectId },
null,
2,
),
);
process.exit(1);
}
const { data: neonAuth } = await api.createNeonAuth(projectId, prodBranchId, {
auth_provider: NeonAuthSupportedAuthProvider.BetterAuth,
...(authDb ? { database_name: authDb } : {}),
});
console.error(
"[neon-auth] Keys below are shown once by Neon; save pub_client_key and secret_server_key.",
);
console.log(
JSON.stringify(
{
projectId,
branchId: prodBranchId,
databaseUrl,
name,
neonAuth,
},
null,
2,
),
);
/**
* Create a Neon project (REST API). Optional org + autoscaling match multi-tenant / Agent Program flows.
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import { createProjectWithOperations } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const orgId = process.env.NEON_ORG_ID;
const name = process.env.NEON_PROJECT_NAME?.trim() || `tenant-${Date.now()}`;
if (!apiKey) {
console.error("NEON_API_KEY is required.");
process.exit(1);
}
const api = createApiClient({ apiKey });
const { projectId, databaseUrl } = await createProjectWithOperations(api, {
name,
orgId: orgId || undefined,
endpointSettings: {
autoscaling_limit_min_cu: 0.25,
autoscaling_limit_max_cu: 2,
suspend_timeout_seconds: 300,
},
});
console.log(JSON.stringify({ projectId, databaseUrl, name }, null, 2));
/**
* Delete a branch by id (e.g. orphaned `main (old)` after a finalized restore).
* Destructive; Neon may reject protected/root branches.
*
* @see https://neon.com/docs/ai/ai-database-versioning#cleanup-strategy
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import { deleteBranchWithWait } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const branchId = process.env.NEON_BRANCH_ID;
if (!apiKey || !projectId || !branchId) {
console.error("Set NEON_API_KEY, NEON_PROJECT_ID, and NEON_BRANCH_ID.");
process.exit(1);
}
console.error(
"[delete-branch] Deleting branch; irreversible for that branch environment.",
);
const api = createApiClient({ apiKey });
await deleteBranchWithWait(api, projectId, branchId);
console.log(JSON.stringify({ ok: true, projectId, branchId }, null, 2));
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
if (!apiKey) {
console.error("NEON_API_KEY is required.");
process.exit(1);
}
if (!projectId) {
console.error("Set NEON_PROJECT_ID to the project to delete.");
process.exit(1);
}
const api = createApiClient({ apiKey });
await api.deleteProject(projectId);
console.log(`Deleted project ${projectId}`);
/**
* Delete one snapshot by id (async operations are polled to completion).
* @see https://neon.com/docs/ai/ai-database-versioning#delete-snapshot
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import { deleteSnapshotWithWait } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const snapshotId = process.env.NEON_SNAPSHOT_ID;
if (!apiKey || !projectId || !snapshotId) {
console.error("Set NEON_API_KEY, NEON_PROJECT_ID, and NEON_SNAPSHOT_ID.");
process.exit(1);
}
const api = createApiClient({ apiKey });
await deleteSnapshotWithWait(api, projectId, snapshotId);
console.log(JSON.stringify({ ok: true, projectId, snapshotId }, null, 2));
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
if (!apiKey) {
throw new Error(
"NEON_API_KEY is required. Add it to .env (see .env.example) or export it in your shell.",
);
}
const api = createApiClient({ apiKey });
const { data } = await api.listProjects({ limit: 400 });
const projects = data.projects ?? [];
for (const p of projects) {
const id = p.id;
const name = p.name ?? "";
if (!id) continue;
console.log(`${id}\t${name}`);
}
/**
* List all logical snapshots for a project (IDs, names, timestamps; see Neon API).
* @see https://neon.com/docs/ai/ai-database-versioning#list-available-snapshots
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
if (!apiKey || !projectId) {
console.error("Set NEON_API_KEY and NEON_PROJECT_ID.");
process.exit(1);
}
const api = createApiClient({ apiKey });
const { data } = await api.listSnapshots(projectId);
const snapshots = data.snapshots ?? [];
console.log(JSON.stringify({ projectId, snapshots }, null, 2));
{
"name": "neon-for-agent-platforms",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "neon-for-agent-platforms",
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"@neondatabase/api-client": "^2.7.1",
"dotenv": "^16.5.0"
},
"devDependencies": {
"@types/node": "^25.6.0",
"prettier": "^3.4.2",
"typescript": "^6.0.3"
}
},
"node_modules/@neondatabase/api-client": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/@neondatabase/api-client/-/api-client-2.7.1.tgz",
"integrity": "sha512-hEYOJ89xIa2eEXBu9HRKYTJc9lrmszhNc0SIxzJvNE/3Av4xK7vkXWQ3LWy0DTTFY4Kn6wfM2wAjRIjf/jOu6w==",
"license": "MIT",
"dependencies": {
"axios": "^1.13.5"
}
},
"node_modules/@types/node": {
"version": "25.6.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.19.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz",
"integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://npm-proxy.dev.databricks.com/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/follow-redirects": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/prettier": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.19.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"dev": true,
"license": "MIT"
}
}
}
{
"name": "neon-for-agent-platforms",
"version": "1.0.0",
"private": true,
"description": "Neon Agent Program examples and companion Agent Skill",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/neondatabase/neon-for-agent-platforms"
},
"keywords": ["neon", "postgres", "agent-skills", "ai-agents"],
"type": "module",
"scripts": {
"fmt": "prettier --write \"**/*.ts\"",
"fmt:check": "prettier --check \"**/*.ts\"",
"typecheck": "tsc --noEmit",
"build": "tsc",
"neon:list-projects": "npm run build && node dist/scripts/list-projects.js",
"create-project": "npm run build && node dist/scripts/create-project.js",
"create-project-with-auth": "npm run build && node dist/scripts/create-project-with-auth.js",
"delete-project": "npm run build && node dist/scripts/delete-project.js",
"branch": "npm run build && node dist/scripts/branch.js",
"snapshot": "npm run build && node dist/scripts/snapshot.js",
"list-snapshots": "npm run build && node dist/scripts/list-snapshots.js",
"delete-snapshot": "npm run build && node dist/scripts/delete-snapshot.js",
"rename-snapshot": "npm run build && node dist/scripts/rename-snapshot.js",
"delete-branch": "npm run build && node dist/scripts/delete-branch.js",
"promote-safe": "npm run build && node dist/scripts/promote-safe-production.js",
"versioning-flow": "npm run build && node dist/scripts/versioning-flow.js",
"restore-snapshot": "npm run build && node dist/scripts/restore-snapshot.js",
"transfer": "npm run build && node dist/scripts/transfer-project.js",
"consumption": "npm run build && node dist/scripts/consumption-query.js",
"auth-users": "npm run build && node dist/scripts/auth-users.js"
},
"dependencies": {
"@neondatabase/api-client": "^2.7.1",
"dotenv": "^16.5.0"
},
"devDependencies": {
"@types/node": "^25.6.0",
"prettier": "^3.4.2",
"typescript": "^6.0.3"
}
}
/**
* Snapshot-based promotion workflow from:
* https://neon.com/blog/promoting-postgres-changes-safely-production
*
* Usage (from `scripts/` after `npm run build`):
* npm run promote-safe -- <subcommand>
* node dist/scripts/promote-safe-production.js <subcommand>
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import {
applySnapshotToBranch,
createLogicalSnapshot,
getProductionBranchId,
restoreSnapshotAsNewBranch,
} from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectIdEnv = process.env.NEON_PROJECT_ID;
const [, , sub] = process.argv;
function usage(): void {
console.error(`Usage: npm run promote-safe -- <bootstrap-dev|promote|refresh-dev|rollback-prod>
See https://neon.com/blog/promoting-postgres-changes-safely-production
`);
}
if (!apiKey || !projectIdEnv) {
console.error("Set NEON_API_KEY and NEON_PROJECT_ID.");
usage();
process.exit(1);
}
const projectId: string = projectIdEnv;
if (!sub) {
usage();
process.exit(1);
}
const api = createApiClient({ apiKey });
async function prodBranchId(): Promise<string> {
const id = process.env.NEON_PROD_BRANCH_ID?.trim();
if (id) return id;
const prod = await getProductionBranchId(api, projectId);
if (!prod) {
throw new Error(
"No production branch (main/production) and NEON_PROD_BRANCH_ID unset",
);
}
return prod;
}
const runId = Date.now();
if (sub === "bootstrap-dev") {
const newName = process.env.NEON_BOOTSTRAP_DEV_BRANCH_NAME?.trim() || "dev";
const pre = await prodBranchId();
console.error(
`[bootstrap-dev] Snapshot production branch, restore as new branch "${newName}"...`,
);
const snapId = await createLogicalSnapshot(api, projectId, {
branchId: pre,
name:
process.env.NEON_SNAPSHOT_BOOTSTRAP_NAME?.trim() ??
`bootstrap-from-prod-${runId}`,
});
const branchId = await restoreSnapshotAsNewBranch(
api,
projectId,
snapId,
newName,
false,
);
console.log(
JSON.stringify(
{
phase: "bootstrap-dev",
prodBranchId: pre,
snapshotFromProdId: snapId,
newDevBranchId: branchId,
newDevBranchName: newName,
note: "Point dev workloads at newDevBranchId; cleanup orphaned branches in Console if needed.",
},
null,
2,
),
);
process.exit(0);
}
if (sub === "promote") {
const devBranchId = process.env.NEON_DEV_BRANCH_ID?.trim();
if (!devBranchId) {
console.error(
"Set NEON_DEV_BRANCH_ID (branch whose state you promote to production).",
);
process.exit(1);
}
const prodId = await prodBranchId();
const preName =
process.env.NEON_SNAPSHOT_PRE_PROMOTION_NAME?.trim() ??
`prod_snap_${runId}_pre_promotion`;
const candName =
process.env.NEON_SNAPSHOT_DEV_CANDIDATE_NAME?.trim() ??
`dev_snap_${runId}_candidate`;
console.error("[promote] 1/3 Snapshot prod (rollback point)...");
const rollbackSnapId = await createLogicalSnapshot(api, projectId, {
branchId: prodId,
name: preName,
});
console.error("[promote] 2/3 Snapshot dev (candidate to publish)...");
const candidateSnapId = await createLogicalSnapshot(api, projectId, {
branchId: devBranchId,
name: candName,
});
console.error(
"[promote] 3/3 Restore dev snapshot onto prod (finalize); brief connection drop on prod...",
);
await applySnapshotToBranch(api, projectId, candidateSnapId, prodId, {
finalizeRestore: true,
restoreBranchName:
process.env.NEON_RESTORE_BACKUP_BRANCH_NAME ?? `before_promote_${runId}`,
});
console.error(
"[promote] Done. Re-fetch prod branch id after finalize if you store it (Neon may rotate branch id).",
);
console.log(
JSON.stringify(
{
phase: "promote",
prodBranchIdBeforeNote: prodId,
rollbackSnapshotId: rollbackSnapId,
promotedSnapshotId: candidateSnapId,
warning:
"Production writes after rollbackSnapId was taken are not included. Delete orphaned backup branches when ready.",
},
null,
2,
),
);
process.exit(0);
}
if (sub === "refresh-dev") {
const devBranchId = process.env.NEON_DEV_BRANCH_ID?.trim();
if (!devBranchId) {
console.error(
"Set NEON_DEV_BRANCH_ID (branch to refresh from current prod).",
);
process.exit(1);
}
const prodId = await prodBranchId();
const snapName =
process.env.NEON_SNAPSHOT_REFRESH_NAME?.trim() ??
`prod_snap_${runId}_refresh_dev`;
console.error("[refresh-dev] 1/2 Snapshot prod...");
const prodSnapId = await createLogicalSnapshot(api, projectId, {
branchId: prodId,
name: snapName,
});
console.error("[refresh-dev] 2/2 Restore prod snapshot onto dev...");
await applySnapshotToBranch(api, projectId, prodSnapId, devBranchId, {
finalizeRestore: true,
restoreBranchName:
process.env.NEON_RESTORE_BACKUP_BRANCH_NAME ??
`before_refresh_dev_${runId}`,
});
console.log(
JSON.stringify(
{
phase: "refresh-dev",
prodBranchId: prodId,
devBranchId,
prodSnapshotId: prodSnapId,
},
null,
2,
),
);
process.exit(0);
}
if (sub === "rollback-prod") {
const snap = process.env.NEON_SNAPSHOT_ID?.trim();
if (!snap) {
console.error(
"Set NEON_SNAPSHOT_ID (e.g. pre-promotion rollback snapshot id).",
);
process.exit(1);
}
const prodId = await prodBranchId();
console.error("[rollback-prod] Restore snapshot onto prod...");
await applySnapshotToBranch(api, projectId, snap, prodId, {
finalizeRestore: true,
restoreBranchName:
process.env.NEON_RESTORE_BACKUP_BRANCH_NAME ?? `before_rollback_${runId}`,
});
console.log(
JSON.stringify(
{
phase: "rollback-prod",
prodBranchId: prodId,
restoredFromSnapshotId: snap,
},
null,
2,
),
);
process.exit(0);
}
console.error(`Unknown subcommand: ${sub}`);
usage();
process.exit(1);
/**
* Rename a snapshot (PATCH metadata only).
* @see https://neon.com/docs/ai/ai-database-versioning#update-snapshot-name
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const snapshotId = process.env.NEON_SNAPSHOT_ID;
const name = process.env.NEON_SNAPSHOT_NEW_NAME?.trim();
if (!apiKey || !projectId || !snapshotId || !name) {
console.error(
"Set NEON_API_KEY, NEON_PROJECT_ID, NEON_SNAPSHOT_ID, and NEON_SNAPSHOT_NEW_NAME.",
);
process.exit(1);
}
const api = createApiClient({ apiKey });
await api.updateSnapshot(projectId, snapshotId, {
snapshot: { name },
});
console.log(JSON.stringify({ ok: true, projectId, snapshotId, name }, null, 2));
/**
* Restore an existing snapshot onto a branch (single REST step after versioning-flow or Console).
*
* @see https://neon.com/docs/ai/ai-database-versioning
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import { applySnapshotToBranch } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const snapshotId = process.env.NEON_SNAPSHOT_ID;
const targetBranchId = process.env.NEON_TARGET_BRANCH_ID;
if (!apiKey || !projectId || !snapshotId || !targetBranchId) {
console.error(
"Set NEON_API_KEY, NEON_PROJECT_ID, NEON_SNAPSHOT_ID, NEON_TARGET_BRANCH_ID.",
);
process.exit(1);
}
const api = createApiClient({ apiKey });
await applySnapshotToBranch(api, projectId, snapshotId, targetBranchId);
console.log(
JSON.stringify(
{
ok: true,
projectId,
snapshotId,
targetBranchId,
},
null,
2,
),
);
/**
* Create a logical snapshot on the default branch (same pattern as many agent hosts).
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import { createLogicalSnapshot } from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
const snapshotName = process.env.NEON_SNAPSHOT_NAME;
const expiresAt = process.env.NEON_SNAPSHOT_EXPIRES_AT?.trim();
const lsn = process.env.NEON_SNAPSHOT_LSN?.trim();
if (!apiKey) {
console.error("NEON_API_KEY is required.");
process.exit(1);
}
if (!projectId) {
console.error("Set NEON_PROJECT_ID.");
process.exit(1);
}
const api = createApiClient({ apiKey });
const snapshotId = await createLogicalSnapshot(api, projectId, {
name: snapshotName || undefined,
...(expiresAt ? { expiresAt } : {}),
...(lsn ? { lsn } : {}),
});
console.log(JSON.stringify({ snapshotId, projectId }, null, 2));
/**
* Transfer one or more projects from a source org to a destination org (e.g. free / sponsored → paid).
* Personal API key with access to both orgs; see Neon docs on org project transfer.
*
* Projects with GitHub or Vercel integrations cannot be transferred; the API returns **422** in that case.
* @see https://neon.com/docs/manage/orgs-project-transfer
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
const apiKey = process.env.NEON_API_KEY?.trim();
const sourceOrgId = process.env.NEON_SOURCE_ORG_ID;
const destinationOrgId = process.env.NEON_DESTINATION_ORG_ID;
const rawIds =
process.env.NEON_PROJECT_IDS || process.env.NEON_PROJECT_ID || "";
if (!apiKey || !sourceOrgId || !destinationOrgId || !rawIds.trim()) {
console.error(
"Set NEON_API_KEY, NEON_SOURCE_ORG_ID, NEON_DESTINATION_ORG_ID, and NEON_PROJECT_IDS (comma-separated) or NEON_PROJECT_ID.",
);
process.exit(1);
}
const projectIds = rawIds
.split(",")
.map((s: string) => s.trim())
.filter(Boolean);
const api = createApiClient({ apiKey });
await api.transferProjectsFromOrgToOrg(sourceOrgId, {
destination_org_id: destinationOrgId,
project_ids: projectIds,
});
console.log(
JSON.stringify(
{
ok: true,
transferred: projectIds,
sourceOrgId,
destinationOrgId,
},
null,
2,
),
);
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"skipLibCheck": true,
"noEmit": false,
"outDir": "dist/scripts",
"rootDir": ".",
"sourceMap": false,
"types": ["node"]
},
"include": ["**/*.ts"],
"exclude": ["node_modules", "dist"]
}
/**
* Shared Neon Management API utilities (operation polling + common SDK call patterns).
* Uses {@link createApiClient} types only; no alternate Neon packages.
*/
import type { Api } from "@neondatabase/api-client";
import {
OperationStatus,
type GeneralError,
type Operation,
} from "@neondatabase/api-client";
export function formatNeonManagementError(err: unknown): Error {
if (err && typeof err === "object" && "response" in err) {
const data = (err as { response?: { data?: GeneralError } }).response?.data;
if (data?.message) {
const text = data.code ? `${data.code}: ${data.message}` : data.message;
return new Error(text);
}
}
if (err instanceof Error) return err;
return new Error(String(err));
}
export function operationIdsFrom(ops: Operation[] | undefined): string[] {
return (ops ?? [])
.map((o) => o.id)
.filter((id): id is string => typeof id === "string" && id.length > 0);
}
function isTerminalSuccess(status: string): boolean {
return (
status === OperationStatus.Finished ||
status === OperationStatus.Skipped ||
status === OperationStatus.Cancelled
);
}
function isTerminalFailure(status: string): boolean {
return status === OperationStatus.Failed || status === OperationStatus.Error;
}
export async function waitForOperationsToSettle(
api: Api<unknown>,
projectId: string,
operationIds: string[],
options: { pollIntervalMs?: number; timeoutMs?: number } = {},
): Promise<void> {
const pollIntervalMs = options.pollIntervalMs ?? 2000;
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1000;
for (const opId of operationIds) {
const startedAt = Date.now();
for (;;) {
const { data } = await api.getProjectOperation(projectId, opId);
const status = data.operation?.status;
if (!status) throw new Error(`Operation status missing for ${opId}`);
if (isTerminalFailure(status)) {
throw new Error(`Operation ${opId} ended with status ${status}`);
}
if (isTerminalSuccess(status)) break;
if (Date.now() - startedAt > timeoutMs) {
throw new Error(
`Timed out waiting for operation ${opId} (last status: ${status})`,
);
}
await new Promise((r) => setTimeout(r, pollIntervalMs));
}
}
}
export async function getProductionBranchId(
api: Api<unknown>,
projectId: string,
): Promise<string | undefined> {
const { data } = await api.listProjectBranches({ projectId });
const branches = data.branches ?? [];
const main = branches.find((b) => b.name === "main");
const prod = branches.find((b) => b.name === "production");
const chosen = main ?? prod;
return typeof chosen?.id === "string" ? chosen.id : undefined;
}
export async function createProjectWithOperations(
api: Api<unknown>,
params: {
name: string;
orgId?: string;
endpointSettings?: {
autoscaling_limit_min_cu?: number;
autoscaling_limit_max_cu?: number;
suspend_timeout_seconds?: number;
};
},
): Promise<{ projectId: string; databaseUrl: string }> {
const { data } = await api.createProject({
project: {
name: params.name,
...(params.orgId ? { org_id: params.orgId } : {}),
...(params.endpointSettings
? { default_endpoint_settings: params.endpointSettings }
: {}),
},
});
const projectId = data.project?.id;
if (!projectId)
throw new Error("Create project: missing project id in response");
const databaseUrl = data.connection_uris?.[0]?.connection_uri;
if (!databaseUrl) {
throw new Error("Create project: missing connection URI in response");
}
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
return { projectId, databaseUrl };
}
export async function createLogicalSnapshot(
api: Api<unknown>,
projectId: string,
options: {
branchId?: string;
name?: string;
timestamp?: string;
lsn?: string;
expiresAt?: string;
},
): Promise<string> {
let branchId = options.branchId;
if (!branchId) {
const prodId = await getProductionBranchId(api, projectId);
if (!prodId) {
throw new Error(
"No production branch (expected name main or production)",
);
}
branchId = prodId;
}
const base = {
projectId,
branchId,
...(options.name ? { name: options.name } : {}),
...(options.expiresAt ? { expires_at: options.expiresAt } : {}),
};
const { data } = await api.createSnapshot(
options.lsn
? { ...base, lsn: options.lsn }
: {
...base,
timestamp: options.timestamp ?? new Date().toISOString(),
},
);
const snapshotId = data.snapshot?.id;
if (!snapshotId)
throw new Error("Create snapshot: missing snapshot id in response");
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
return snapshotId;
}
export async function applySnapshotToBranch(
api: Api<unknown>,
projectId: string,
snapshotId: string,
targetBranchId: string,
options: {
restoreBranchName?: string;
finalizeRestore?: boolean;
} = {},
): Promise<void> {
try {
const { data } = await api.restoreSnapshot(
{ projectId, snapshotId },
{
name: options.restoreBranchName ?? `before_restore_${Date.now()}`,
finalize_restore: options.finalizeRestore !== false,
target_branch_id: targetBranchId,
},
);
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
} catch (e) {
throw formatNeonManagementError(e);
}
}
export async function restoreSnapshotAsNewBranch(
api: Api<unknown>,
projectId: string,
snapshotId: string,
newBranchName: string,
finalizeRestore = false,
): Promise<string> {
try {
const { data } = await api.restoreSnapshot(
{ projectId, snapshotId },
{
name: newBranchName,
finalize_restore: finalizeRestore,
},
);
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
const branchId = data.branch?.id;
if (!branchId) {
throw new Error(
"restoreSnapshotAsNewBranch: missing branch id in response",
);
}
return branchId;
} catch (e) {
throw formatNeonManagementError(e);
}
}
export async function createBranchWithOperations(
api: Api<unknown>,
projectId: string,
params: { name: string; parentId?: string },
): Promise<{ id: string }> {
const { data } = await api.createProjectBranch(projectId, {
branch: {
name: params.name,
...(params.parentId ? { parent_id: params.parentId } : {}),
},
});
const id = data.branch?.id;
if (!id) throw new Error("Create branch: missing id in response");
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
return { id };
}
export async function deleteSnapshotWithWait(
api: Api<unknown>,
projectId: string,
snapshotId: string,
): Promise<void> {
const { data } = await api.deleteSnapshot(projectId, snapshotId);
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
}
export async function deleteBranchWithWait(
api: Api<unknown>,
projectId: string,
branchId: string,
): Promise<void> {
const { data } = await api.deleteProjectBranch(projectId, branchId);
const opIds = operationIdsFrom(data.operations);
if (opIds.length > 0) {
await waitForOperationsToSettle(api, projectId, opIds);
}
}
/**
* Full snapshot → branch → restore flow for AI database versioning (Management API only; no SQL driver).
*
* 1. Snapshot the production branch (baseline).
* 2. Create a child branch from production (sandbox).
* 3. SQL mutation step removed; this package uses only @neondatabase/api-client (no DB query client).
* 4. Logical snapshots are **root-branch only** in the Neon API for non-root branches.
* 5. Restore the baseline snapshot onto the child branch (undo / rewind).
*
* @see https://neon.com/docs/ai/ai-database-versioning
*/
import "dotenv/config";
import { createApiClient } from "@neondatabase/api-client";
import {
applySnapshotToBranch,
createBranchWithOperations,
createLogicalSnapshot,
getProductionBranchId,
} from "./utils.js";
const apiKey = process.env.NEON_API_KEY?.trim();
const projectId = process.env.NEON_PROJECT_ID;
if (!apiKey || !projectId) {
console.error("Set NEON_API_KEY and NEON_PROJECT_ID.");
process.exit(1);
}
const api = createApiClient({ apiKey });
const prodBranchId = await getProductionBranchId(api, projectId);
if (!prodBranchId) {
console.error("No production branch (main or production).");
process.exit(1);
}
const runId = Date.now();
const baselineName =
process.env.VERSION_BASELINE_NAME ?? `flow-baseline-${runId}`;
const demoBranchName =
process.env.VERSION_DEMO_BRANCH_NAME ?? `versioning-demo-${runId}`;
console.error("[versioning-flow] 1/5 Snapshot production branch (baseline)...");
const baselineSnapshotId = await createLogicalSnapshot(api, projectId, {
branchId: prodBranchId,
name: baselineName,
});
console.error("[versioning-flow] 2/5 Create child branch from production...");
const { id: demoBranchId } = await createBranchWithOperations(api, projectId, {
name: demoBranchName,
parentId: prodBranchId,
});
const sqlNote =
"skipped: no SQL/query client in this package (Neon API SDK only). Previous DEMO_MUTATE + pg demo removed.";
console.error("[versioning-flow] 3/5", sqlNote);
console.error(
"[versioning-flow] 4/5 Skip snapshot of demo branch: Neon allows logical snapshots on the root branch only.",
);
console.error(
"[versioning-flow] 5/5 Restore baseline snapshot onto demo branch (rewind)...",
);
await applySnapshotToBranch(api, projectId, baselineSnapshotId, demoBranchId);
console.log(
JSON.stringify(
{
projectId,
productionBranchId: prodBranchId,
baselineSnapshotId,
demoBranchId,
demoBranchName,
afterSnapshotId: null,
afterSnapshotNote:
"Logical snapshots are root-branch only; see versioning-flow.ts header.",
demoMutation: sqlNote,
restoredBaselineToDemoBranch: true,
},
null,
2,
),
);