Start here

building

From a new account to your first successful API call, in about two minutes.

Quickstart

Two minutes from nothing to a working call. You need an account and a project — if you have one open in the studio, you already have everything below.

1. Your base URL and key

Every request goes to the same base and carries the same kind of key:

bash
export BASE="https://<your-deployment>"
export KEY="sk_live_…"          # the secret key from your project page

Your project page shows both. The key is displayed once, when it is issued — if you have lost it, issue another; keys are free and revoking the old one takes a click.

Two kinds exist and the difference matters:

Where it goesWhat it can do
sk_… secretOn your server, onlyEverything
pk_… publishableIn your app's frontendSign users in, and nothing else

A publishable key is safe in a browser because it can't reach the management APIs. A secret key in a browser is a breach, so we make it impossible rather than discouraged — the endpoints that matter answer no preflight at all.

2. Your first call

bash
curl "$BASE/v1/files" -H "authorization: Bearer $KEY"
json
{ "files": [], "total": 0, "page": 1, "totalPages": 0, "nextCursor": null }

That's a working integration. An empty list is the correct answer for a new project, and the response shape is the one you'll get with a thousand files in it.

If you got 401, the key is wrong or revoked. If you got nothing at all, $BASE is unset — the most common first mistake, and the reason it is spelled out above.

3. Upload something

Uploading is three calls, and the middle one doesn't touch us at all:

bash
# a) Ask where to put it
curl -X POST "$BASE/v1/files/upload-intent" \
  -H "authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"filename":"hello.txt","contentType":"text/plain","size":11}'
bash
# b) Send the bytes straight to storage, using the url that came back
curl -X PUT "<uploadUrl>" -H "content-type: text/plain" --data-binary "hello world"
bash
# c) Tell us it landed
curl -X POST "$BASE/v1/files/confirm" \
  -H "authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"id":"<id from step a>"}'

The bytes never pass through us, which is why a 500 MB upload costs you one request rather than a timeout. Upload files has the full shape.

4. Sign someone in

Auth is enabled per project, because it gives you a Postgres database of your own — open Auth on your project page and turn it on. Then:

bash
# a. email a six-digit code to the address
curl -X POST "$BASE/v1/auth/email-otp/send-verification-otp" \
  -H "authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"email":"ada@example.com","type":"sign-in"}'

# b. send the code back — this creates the account and signs them in
curl -X POST "$BASE/v1/auth/sign-in/email-otp" \
  -H "authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"email":"ada@example.com","otp":"481920"}'

A session cookie comes back, and that user now exists in a database no other project can reach. No password was involved and none is required — the account exists because somebody read mail at that address, which is the only proof worth having. They can add a password later if your product wants one. Auth covers social login, passkeys, two-factor, organizations and SSO — all of it on the same key.

5. Give it a database

Open Database on your project page and press Create database. About two seconds later you have Postgres 18 of your own, and a connection string:

bash
curl "$BASE/v1/data/_connection" -H "authorization: Bearer $KEY" | jq -r .connectionString

Paste it into your app and use whatever you already use — Prisma, Drizzle, psql, anything that speaks Postgres. There is no driver of ours in the way: your queries go straight to the database, and it sleeps when nothing is asking. Database covers pooling, migrations and what it costs.

  • Files API — every endpoint, one page each
  • Auth — sessions, tokens, and the rest of it
  • Database — Postgres of your own, and its connection string
  • Usage and billing — what any of this costs

Nothing here assumes a client library. There is no SDK yet, so this HTTP API is the whole interface, and every example above is a request you can paste into a terminal.