Provisions and manages Neo4j Aura instances via CLI (aura-cli v1.7+) or REST API. Use when creating, pausing, resuming, resizing, or deleting AuraDB Free/Professional/Business Critical/VDC instances; downloading credentials; scripting CI/CD pipelines; polling async status; or using the Terraform neo4j/neo4j-aura provider. Covers auth setup (client credentials OAuth2), credential lifecycle (download once — never recoverable), instance type selection, region codes, and Python provisioning scripts. Does NOT handle Cypher queries — use neo4j-cypher-skill. Does NOT cover Graph Data Science algorithms — use neo4j-gds-skill or neo4j-aura-graph-analytics-skill. Does NOT cover neo4j-admin/cypher-shell — use neo4j-cli-tools-skill.
npx skills add https://github.com/neo4j-contrib/neo4j-skills --skill neo4j-aura-provisioning-skill
creating → runningneo4j-cypher-skillneo4j-gds-skill (Pro with plugin) or neo4j-aura-graph-analytics-skill (serverless)neo4j-cli-tools-skill| Tier | API type code | Memory | GDS | Replicas | Use when |
|---|---|---|---|---|---|
| AuraDB Free | free-db | 1 GB | ❌ | ❌ | Dev/demo; ≤200k nodes/400k rels |
| AuraDB Professional | professional-db | 2–64 GB | plugin available | ❌ | Production workloads |
| AuraDB Business Critical | business-critical | 4–384 GB | plugin available | ✅ | HA, multi-AZ, SLA |
| AuraDB VDC | enterprise-db | custom | ✅ | ✅ | Dedicated infra, compliance |
| AuraDS Professional | professional-ds | 2–64 GB | ✅ built-in | ❌ | Data science / GDS |
| AuraDS Enterprise | enterprise-ds | custom | ✅ | ✅ | Enterprise GDS |
AuraDB Free limits: 200k nodes, 400k rels; auto-pauses after 72 h inactivity; deleted if paused >30 days; no resize.
Install (binary, not pip):
# macOS
curl -L https://github.com/neo4j/aura-cli/releases/latest/download/aura-cli-darwin-amd64.tar.gz | tar xz
sudo mv aura-cli /usr/local/bin/
aura-cli -v # verify
Add credentials (from console.neo4j.io → Account Settings → API Credentials):
aura-cli credential add \
--name "my-creds" \
--client-id "$AURA_CLIENT_ID" \
--client-secret "$AURA_CLIENT_SECRET"
aura-cli credential use --name "my-creds"
Verify:
aura-cli instance list --output table
Token endpoint: POST https://api.neo4j.io/oauth/token
Token expires: 3600 s (1 h). On 403 → refresh token.
TOKEN=$(curl -s --request POST 'https://api.neo4j.io/oauth/token' \
--user "${AURA_CLIENT_ID}:${AURA_CLIENT_SECRET}" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
| jq -r '.access_token')
echo "Token: ${TOKEN:0:20}..."
Use in all subsequent calls: --header "Authorization: Bearer $TOKEN"
CLI:
aura-cli tenants list --output table
# Copy TENANT_ID for create operations
REST:
curl -s https://api.neo4j.io/v1/tenants \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | {id, name}'
CRITICAL: Capture output immediately. Initial password shown ONCE — never retrievable again.
If lost: delete and recreate. Store aura-creds.json before doing anything else.
aura-cli instance create \
--name "my-instance" \
--cloud-provider gcp \
--region europe-west1 \
--type professional-db \
--tenant-id "$TENANT_ID" \
--output json | tee aura-creds.json
# Extract for .env
INSTANCE_ID=$(jq -r '.id' aura-creds.json)
PASSWORD=$(jq -r '.password' aura-creds.json)
RESPONSE=$(curl -s -X POST https://api.neo4j.io/v1/instances \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-instance",
"cloud_provider": "gcp",
"region": "europe-west1",
"type": "professional-db",
"tenant_id": "'"$TENANT_ID"'",
"memory": "4GB",
"version": "5"
}')
echo "$RESPONSE" | tee aura-creds.json
INSTANCE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
PASSWORD=$(echo "$RESPONSE" | jq -r '.data.password')
Instance create request body fields:
| Field | Required | Values |
|---|---|---|
| name | ✅ | any string |
| cloud_provider | ✅ | gcp aws azure |
| region | ✅ | see region table |
| type | ✅ | see tier table |
| tenant_id | ✅ | from tenant list |
| memory | ✗ | 1GB 2GB 4GB 8GB … 384GB |
| version | ✗ | 5 (default) |
ALL lifecycle operations (create, pause, resume, resize) are async. Do NOT attempt connection or next operation until status = running (or paused for pause op).
poll_status() {
local INSTANCE_ID=$1 TARGET=$2 MAX_WAIT=${3:-600}
local ELAPSED=0 STATUS
echo "Polling for status=$TARGET (max ${MAX_WAIT}s)..."
while [ $ELAPSED -lt $MAX_WAIT ]; do
STATUS=$(aura-cli instance get --instance-id "$INSTANCE_ID" --output json \
| jq -r '.status' 2>/dev/null)
echo " [${ELAPSED}s] status=$STATUS"
[ "$STATUS" = "$TARGET" ] && echo "Ready." && return 0
[ "$STATUS" = "destroying" ] && echo "ERROR: instance is being destroyed" && return 1
sleep 10; ELAPSED=$((ELAPSED + 10))
done
echo "TIMEOUT after ${MAX_WAIT}s — last status: $STATUS" && return 1
}
poll_status "$INSTANCE_ID" "running" 600
REST equivalent:
while true; do
STATUS=$(curl -s "https://api.neo4j.io/v1/instances/$INSTANCE_ID" \
-H "Authorization: Bearer $TOKEN" | jq -r '.data.status')
[ "$STATUS" = "running" ] && break
sleep 10
done
Status lifecycle:
creating → running → pausing → paused → resuming → running
↘ destroying → (gone)
CONNECTION_URI="neo4j+s://${INSTANCE_ID}.databases.neo4j.io"
cat > .env <<EOF
NEO4J_URI=${CONNECTION_URI}
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=${PASSWORD}
NEO4J_DATABASE=neo4j
AURA_INSTANCE_ID=${INSTANCE_ID}
EOF
# Ensure .env never committed
grep -q '^\.env$' .gitignore 2>/dev/null || echo '.env' >> .gitignore
# Verify connectivity
cypher-shell -a "$CONNECTION_URI" -u neo4j -p "$PASSWORD" "RETURN 'connected' AS status"
All operations require instance in the correct state. Wrong-state ops return 4xx error.
Required state: running
aura-cli instance pause --instance-id "$INSTANCE_ID"
poll_status "$INSTANCE_ID" "paused" 600
Required state: paused
aura-cli instance resume --instance-id "$INSTANCE_ID"
poll_status "$INSTANCE_ID" "running" 900 # resume can take longer
Required state: running; instance remains available during resize.
# REST only — CLI resize not available in v1.7
curl -s -X PATCH "https://api.neo4j.io/v1/instances/$INSTANCE_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"memory": "8GB"}'
poll_status "$INSTANCE_ID" "running" 600
# Cannot reduce below current usage level
IRREVERSIBLE. Export snapshots first if data needed.
aura-cli instance delete --instance-id "$INSTANCE_ID"
# No poll needed — immediate
REST:
curl -s -X DELETE "https://api.neo4j.io/v1/instances/$INSTANCE_ID" \
-H "Authorization: Bearer $TOKEN"
import os, time, requests
CLIENT_ID = os.environ["AURA_CLIENT_ID"]
CLIENT_SECRET = os.environ["AURA_CLIENT_SECRET"]
BASE = "https://api.neo4j.io/v1"
def get_token() -> str:
r = requests.post(
"https://api.neo4j.io/oauth/token",
auth=(CLIENT_ID, CLIENT_SECRET),
data={"grant_type": "client_credentials"},
)
r.raise_for_status()
return r.json()["access_token"]
def auth_headers(token: str) -> dict:
return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
def create_instance(token: str, tenant_id: str, **kwargs) -> dict:
payload = {"tenant_id": tenant_id, "version": "5", **kwargs}
r = requests.post(f"{BASE}/instances", headers=auth_headers(token), json=payload)
r.raise_for_status()
return r.json()["data"] # contains id, password, connection_url
def poll_status(token: str, instance_id: str, target: str, timeout: int = 600) -> None:
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(f"{BASE}/instances/{instance_id}", headers=auth_headers(token))
r.raise_for_status()
status = r.json()["data"]["status"]
print(f" status={status}")
if status == target:
return
if status == "destroying":
raise RuntimeError("Instance destroyed unexpectedly")
time.sleep(10)
raise TimeoutError(f"Instance {instance_id} did not reach '{target}' in {timeout}s")
# --- usage ---
token = get_token()
instance = create_instance(
token,
tenant_id = os.environ["AURA_TENANT_ID"],
name = "ci-test-instance",
cloud_provider = "aws",
region = "us-east-1",
type = "professional-db",
memory = "2GB",
)
# SAVE CREDENTIALS IMMEDIATELY — password never retrievable again
print(f"ID: {instance['id']}")
print(f"URI: neo4j+s://{instance['id']}.databases.neo4j.io")
print(f"Password: {instance['password']}") # log to secure vault NOW
poll_status(token, instance["id"], "running", timeout=600)
print("Instance ready.")
| Region code | Location |
|---|---|
| us-east-1 | N. Virginia |
| us-east-2 | Ohio |
| us-west-2 | Oregon |
| eu-west-1 | Ireland |
| eu-west-3 | Paris |
| eu-central-1 | Frankfurt |
| ap-southeast-1 | Singapore |
| ap-southeast-2 | Sydney |
| ap-south-1 | Mumbai |
| sa-east-1 | São Paulo |
| Region code | Location |
|---|---|
| europe-west1 | Belgium |
| europe-west3 | Frankfurt |
| europe-west4 | Netherlands |
| us-central1 | Iowa |
| us-east1 | S. Carolina |
| us-east4 | N. Virginia |
| asia-east1 | Taiwan |
| asia-northeast1 | Tokyo |
| asia-southeast1 | Singapore |
| australia-southeast1 | Sydney |
| Region code | Location |
|---|---|
| eastus | E. US |
| eastus2 | E. US 2 |
| westeurope | Netherlands |
| northeurope | Ireland |
| uksouth | London |
| southeastasia | Singapore |
| brazilsouth | Brazil |
| koreacentral | Korea |
Enterprise tiers (Business Critical, VDC) add 20+ additional regions per provider. Check console for full list.
Free tier: GCP only; limited subset of regions.
terraform {
required_providers {
aura = {
source = "neo4j/neo4j-aura"
}
}
}
provider "aura" {
client_id = var.aura_client_id # or AURA_CLIENT_ID env var
client_secret = var.aura_client_secret # or AURA_CLIENT_SECRET env var
}
resource "aura_instance" "db" {
name = "prod-db"
type = "professional-db"
cloud_provider = "gcp"
region = "europe-west1"
memory = "4GB"
tenant_id = var.aura_tenant_id
}
output "neo4j_uri" {
value = "neo4j+s://${aura_instance.db.id}.databases.neo4j.io"
sensitive = false
}
output "neo4j_password" {
value = aura_instance.db.password
sensitive = true
}
After terraform apply: poll status before marking infra ready — Terraform resource creation returns when API call completes, not when DB is running.
| Error | Cause | Fix |
|---|---|---|
| 403 Forbidden after working | Token expired (1 h TTL) | Re-run get_token() |
| 409 Conflict on create | Name already exists in tenant | Change name or delete existing |
| 422 on pause | Instance not running | Check status; wait for ongoing op to finish |
| 422 on resume | Instance not paused | Check status |
| 422 on resize | Below current usage | Reduce data first; can't shrink below usage |
| Region not found | Tier doesn't support that region | Use Free tier on GCP only; Pro/BC on all 3 clouds |
| Credentials lost after create | Password only returned at create time | Delete + recreate — no reset exists |
| 429 Too Many Requests | Rate limit hit (25 req/min Free, 125 req/min Pro+) | Add time.sleep(2) between polling calls |
| instance list returns empty | Wrong credential active | aura-cli credential use --name <name> |
| Tier | Requests/minute |
|---|---|
| Free / Pro Trial (no billing) | 25 |
| Pro with billing, BC, VDC | 125 |
Poll interval: ≥10 s to stay within limits on Free; 5 s safe on Pro+.
On Retry-After header in 5xx response: wait that many seconds before retry.
.env; verify .env in .gitignore before proceedingPASSWORD in CI logs — write to secrets vault (AWS Secrets Manager, GitHub secret, Vault)from_env() / os.environ — never hardcode credentials.env absent: python-dotenv load_dotenv() auto-loads; do NOT prompt user unless loading fails| Need | URL |
|---|---|
| REST API spec (OpenAPI) | https://neo4j.com/docs/aura/platform/api/specification/ |
| CLI reference | https://neo4j.com/docs/aura/aura-cli/ |
| Region list | https://neo4j.com/docs/aura/managing-instances/regions/ |
| Auth details | https://neo4j.com/docs/aura/api/authentication/ |
| Instance actions | https://neo4j.com/docs/aura/managing-instances/instance-actions/ |
.env created with URI/user/password; .env in .gitignorepoll_status called after create — do NOT connect before status = runningpoll_status called after pause/resumeRun Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Advanced GitHub Actions workflow automation with AI swarm coordination, intelligent CI/CD pipelines, and comprehensive repository management
Google Cloud Platform CLI - manage GCP resources including Compute Engine, Cloud Run, GKE, Cloud Functions, Storage, BigQuery, and more.
Expert backend architect specializing in scalable API design, microservices architecture, and distributed systems. Masters REST/GraphQL/gRPC APIs, event-driven architectures, service mesh patterns, and modern backend frameworks. Handles service boundary definition, inter-service communication, resilience patterns, and observability. Use PROACTIVELY when creating new backend services or APIs.
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Aspire skill covering the Aspire CLI, AppHost orchestration, service discovery, integrations, MCP server, VS Code extension, Dev Containers, GitHub Codespaces, templates, dashboard, and deployment. Use when the user asks to create, run, debug, configure, deploy, or troubleshoot an Aspire distributed application.
Audits Python + BigQuery pipelines for cost safety, idempotency, and production readiness. Returns a structured report with exact patch locations.
Microsoft Store Developer CLI (msstore) for publishing Windows applications to the Microsoft Store. Use when asked to configure Store credentials, list Store apps, check submission status, publish submissions, manage package flights, set up CI/CD for Store publishing, or integrate with Partner Center. Supports Windows App SDK/WinUI, UWP, .NET MAUI, Flutter, Electron, React Native, and PWA applications.
Take neo4j-contrib/neo4j-aura-provisioning-skill from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.