Yatabase API reference

Single-host BaaS combining a real-time graph database (Cypher / SPARQL), S3-style object storage, an MCP tool surface, and AT Protocol-native auth. Every section below is copy-pastable curl that targets https://yatabase.gftd.ai.

Machine-readable spec: /openapi.json (OpenAPI 3.1). Import into Postman / Swagger UI, or generate typed clients:
# TypeScript types (openapi-typescript)
npx openapi-typescript https://yatabase.gftd.ai/openapi.json -o yatabase.d.ts

# Generate a Python client (openapi-python-client)
openapi-python-client generate --url https://yatabase.gftd.ai/openapi.json

Quickstart

From zero to first row in 30 seconds:

# 1. Sign up — anonymous mint, returns sk_live_yata_*
SIGNUP=$(curl -sS -X POST https://yatabase.gftd.ai/auth/v1/signup)
KEY=$(echo "$SIGNUP" | python3 -c 'import sys,json;print(json.load(sys.stdin)["apiKey"])')
echo "$KEY"

# 2. Create a vertex
curl -X POST https://yatabase.gftd.ai/cypher \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{"query":"CREATE (n:Demo {name:\"hello\"}) RETURN n"}'

# 3. Query it back
curl -X POST https://yatabase.gftd.ai/cypher \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{"query":"MATCH (n:Demo) RETURN n.name LIMIT 10"}'

The signup response also contains orgDid (e.g. did:web:t-xxxxx.yata-tenant.gftd.ai), a fresh AWS access-key pair, and an emailStatus field if you passed {email, name}.

Auth

Three accepted token shapes on the Authorization header:

TokenMints viaUse
Bearer sk_live_yata_*POST /auth/v1/signupDefault. Per-tenant scope.
Bearer sk_live_*ai.gftd.auth.createApiKeyCross-product key.
AT Protocol JWTcom.atproto.server.getSessionFor atproto-native clients.

All authenticated calls also accept X-Active-DID to disambiguate when the caller controls multiple path-based DIDs.

Cypher (graph)

POST /cypher — Neo4j HTTP-shape compatible subset on RisingWave.

Supported:

Forbidden at the edge: DETACH DELETE, FOREACH, CALL { ... write ... }.

Deferred: multi-hop, variable-length path, MERGE, count()/collect(), OPTIONAL MATCH, WITH.

# Edge traversal
curl -X POST https://yatabase.gftd.ai/cypher \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{"query":"MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name LIMIT 25"}'

Response shape (Neo4j HTTP API compatible):

{
  "results": [{ "columns": ["a.name","b.name"], "data": [["alice","bob"]] }],
  "errors": []
}

SPARQL

POST /sparql — SPARQL 1.1 SELECT / CONSTRUCT / ASK on the same graph.

curl -X POST https://yatabase.gftd.ai/sparql \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/sparql-query' \
  --data 'SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10'

Storage REST (Supabase shape)

S3-compatible blob store. PUT/GET/HEAD/DELETE on /storage/v1/object/{bucket}/{key}.

# Upload
curl -X PUT --data-binary @photo.jpg \
  -H "authorization: Bearer $KEY" \
  https://yatabase.gftd.ai/storage/v1/object/my-bucket/photo.jpg

# List bucket
curl -H "authorization: Bearer $KEY" \
  https://yatabase.gftd.ai/storage/v1/object/list/my-bucket

# Public download (only if bucket public_read=true AND ACL grants it)
curl https://yatabase.gftd.ai/storage/v1/object/public/my-bucket/photo.jpg

# Presigned URL
curl -X POST -H "authorization: Bearer $KEY" \
  https://yatabase.gftd.ai/storage/v1/object/sign/my-bucket/photo.jpg

S3 SigV4 compat

Same blobs, AWS SigV4 wire format. Use the awsAccessKeyId + awsSecretAccessKey returned by /auth/v1/signup.

aws --endpoint-url https://yatabase.gftd.ai/s3 \
  s3 cp photo.jpg s3://my-bucket/photo.jpg

MCP (JSON-RPC 2.0)

Every yatabase surface is also an MCP tool. POST /mcp.

initialize / ping / tools/list / resources/list / prompts/list are public. tools/call and resources/read require auth.

# List tools (public)
curl -X POST https://yatabase.gftd.ai/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

# Call yata.graph.cypher
curl -X POST https://yatabase.gftd.ai/mcp \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"yata.graph.cypher","arguments":{"query":"MATCH (n) RETURN n LIMIT 5"}},"id":2}'

Discovery doc: /.well-known/mcp.json.

XRPC pass-through

Native AT Protocol XRPC for ai.gftd.apps.yata.* and ai.gftd.apps.billing.*.

curl -X POST https://yatabase.gftd.ai/xrpc/ai.gftd.apps.yata.runCypher \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{"query":"MATCH (n:Demo) RETURN n LIMIT 10"}'

Plans & quotas

USD-primary pricing, JPY-secondary (FX 150 JPY/USD).

PlanUSD/moJPY/moAPI req / dayStorageCypher CU-h / day
Free$0¥01,0005 GB5
Starter$13¥1,95033,33350 GB50
Developer$33¥4,950333,333500 GB500
Business$650¥97,50033M5 TB5,000
Enterprise$6,700+¥1.005M+unlimitedunlimitedunlimited

Quota check is per-day, sums today's api_request billing events. Hit 429 with Retry-After when exceeded.

Read your usage:

curl -H "authorization: Bearer $KEY" https://yatabase.gftd.ai/api/usage
curl -H "authorization: Bearer $KEY" https://yatabase.gftd.ai/api/plan

Upgrade

curl -X POST https://yatabase.gftd.ai/auth/v1/upgrade \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{"plan":"starter"}'

Returns {checkoutUrl, sessionId} when Stripe is wired (live since 2026-05-10). Open checkoutUrl to complete payment. Stripe webhook then flips the plan tier inside ~40 s of payment confirmation.

Customer portal (change card, cancel, view invoices)

curl -X POST https://yatabase.gftd.ai/auth/v1/portal \
  -H "authorization: Bearer $KEY" \
  -d '{}'

Returns {portalUrl} — a Stripe-hosted page where customers self-serve billing (update card, cancel subscription, download past invoices). Requires the org has completed at least one Checkout (400 NoStripeCustomer on free).

Key recovery (lost your API key?)

Anonymous signup is convenient but losing the key would normally orphan the tenant. Attach a recovery email before you need it. Attaching emails a 24-hour verification link — you must click it before recovery works (this prevents an attacker from attaching a victim's email and abusing yatabase to send them spam-looking "recovery" notices):

# 1a. Attach an email while you still have the key (emails a verify link)
curl -X POST https://yatabase.gftd.ai/auth/v1/attach-email \
  -H "authorization: Bearer $KEY" \
  -d '{"email":"you@example.com"}'

# 1b. Click the verification link in your inbox. The link calls:
#     GET /auth/v1/verify-email?token=...   (24-hour TTL, single-use)
#     Confirm with: curl https://yatabase.gftd.ai/auth/v1/whoami -H "authorization: Bearer $KEY"
#     attachedEmailVerified should now be true.

# 2. Later, if you lose the key, anyone can request a recovery link
curl -X POST https://yatabase.gftd.ai/auth/v1/recover \
  -d '{"email":"you@example.com"}'
# Always returns 200 (no enumeration leak). If the email matches a
# tenant, a recovery link is sent. Link contains a 48-hex token with
# a 15-minute TTL.

# 3. Click the link → it posts the token to /auth/v1/redeem and
#    returns a brand-new API key for the matching org.
curl -X POST https://yatabase.gftd.ai/auth/v1/redeem \
  -d '{"token":"...48 hex chars from the link..."}'

Existing keys remain valid after recovery — recovery is additive, not replacement. Revoke the lost key separately via /auth/v1/revoke once the new key is in hand.

Who am I?

curl -H "authorization: Bearer $KEY" https://yatabase.gftd.ai/auth/v1/whoami

Returns the tenant identity for the current bearer: {orgDid, actorDid, plan, attachedEmail, stripeCustomerId, canOpenPortal}. Useful for client bootstrap, dashboard rendering, and confirming a recovered key resolved to the original tenant.

Members & multi-tenant

# List members of your org
curl -H "authorization: Bearer $KEY" https://yatabase.gftd.ai/api/members

# Mint a new key for a teammate
curl -X POST https://yatabase.gftd.ai/auth/v1/invite \
  -H "authorization: Bearer $KEY" \
  -d '{"name":"alice"}'

# Revoke a key
curl -X POST https://yatabase.gftd.ai/auth/v1/revoke \
  -H "authorization: Bearer $KEY" \
  -d '{"vertex_id":"apikey:..."}'

Outbound webhooks

Register a URL to receive HMAC-signed POST notifications when Cypher mutations happen. Useful for replicating to Slack, Zapier, your own backend, or any HTTP endpoint.

# Register a webhook (URL must be HTTPS)
curl -X POST https://yatabase.gftd.ai/api/webhooks \
  -H "authorization: Bearer $KEY" \
  -H 'content-type: application/json' \
  -d '{
    "url": "https://your-app.example.com/yata-webhook",
    "label": "Person",
    "types": ["cypher.create", "cypher.set", "cypher.delete"]
  }'
# Response includes webhook.secret — save it. Subsequent GETs only show secretPrefix.

# List your webhooks (secret redacted)
curl -H "authorization: Bearer $KEY" https://yatabase.gftd.ai/api/webhooks

# Remove a webhook
curl -X DELETE https://yatabase.gftd.ai/api/webhooks/whk_... \
  -H "authorization: Bearer $KEY"
EventFires onPayload
cypher.createCREATE (n:Label {…}){label, properties}
cypher.setMATCH … SET n.x = "y"{label, properties, updatedCount}
cypher.deleteMATCH (n:Label) DELETE n{label, deletedCount}
cypher.create_edgeCREATE (a)-[:T]->(b){srcLabel, srcProperties, edgeType, edgeProperties, dstLabel, dstProperties}
cypher.delete_edgeMATCH (a)-[r:T]->(b) DELETE r{srcLabel, edgeType, dstLabel}

Delivery contract

Verifying a delivery (Node example)

import crypto from "node:crypto";

function verifyYatabase(req, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(req.rawBody)  // the exact bytes received
    .digest("hex");
  return req.headers["x-yatabase-signature"] === expected;
}

Data rights (CCPA / GDPR / 改正個人情報保護法)

# Right to know — full export
curl -H "authorization: Bearer $KEY" https://yatabase.gftd.ai/api/export

# Right to delete — irreversible
curl -X POST https://yatabase.gftd.ai/api/account/delete \
  -H "authorization: Bearer $KEY" \
  -d '{"confirm":"DELETE"}'

See /.well-known/agent.json for the legal disclaimer. Billing rows (vertex_billing_event) are retained 7 years per 法人税法 §126 / IRS §6001.

Errors

StatusBody shapeWhen
400{error,message}Bad JSON / forbidden Cypher / invalid params
401{error:"Unauthorized"}Missing / invalid Bearer
403{error:"Forbidden"}Admin gate (operator-only paths)
404{error:"NotFound"}Path / resource missing
409{error:"PreconditionFailed"}State machine violation
429{error:"QuotaExceeded"}Daily plan cap hit; Retry-After header set
500{error:"InternalServerError"}Worker exception
503{error:"ServiceUnavailable"}Hyperdrive / dispatcher unreachable

Observability

Compliance

RegulationArticleEndpoint
CCPA§1798.100 (right to know)GET /api/export
CCPA§1798.105 (right to delete)POST /api/account/delete
GDPRArt 17 (erasure)POST /api/account/delete
GDPRArt 20 (portability)GET /api/export
GDPRArt 30 (records of processing)vertex_audit_log (90 d)
改正個人情報保護法第33条 (開示請求)GET /api/export
改正個人情報保護法第34-36条 (削除請求)POST /api/account/delete
JP 適格請求書 (T9007028460042)etz hayimGET /api/invoice?month=YYYY-MM
法人税法§126 (7 y retention)vertex_billing_event
IRS§6001 (7 y retention)same

Operator: etz hayim (運営法人). Vendor: Gftd Japan株式会社 (T9007028460042).