Console API for agents and CI
Most of what the console does to set a project up is an HTTP call, so a script, a CI job or an AI agent can create an organization, a project, its row rules and its auth settings without a browser. Every route on this page except sign-in takes a console access token in Authorization: Bearer …, not a project API key; see Access tokens. Request bodies are JSON (Content-Type: application/json).
1. Create the account in the console
Section titled “1. Create the account in the console”VAIF Studio accounts are created only in the console, where sign-up runs a bot check that a script cannot pass. Create the account there once, confirm its email address, and use it from then on. See Sign up.
2. Get a token that lasts: a CLI token
Section titled “2. Get a token that lasts: a CLI token”POST /auth/cli/login with { "email", "password" } answers { "ok": true, "accessToken", "refreshToken", "expiresIn", "user" }, where accessToken is a CLI token: valid for 30 days, with the cli scope. Every route on this page accepts it; billing and admin routes refuse it.
VAIF_AUTH_TOKEN=$(curl -s -X POST https://api.vaif.studio/auth/cli/login \ -H "Content-Type: application/json" \ -d '{"email":"ci@example.com","password":"a-long-unique-passphrase"}' | jq -er .accessToken)export VAIF_AUTH_TOKENjq -e makes the step fail when the answer has no accessToken, for example after a wrong password or for an account with two-factor sign-in on. Plain jq -r would set the variable to the text null and let the job carry on. The examples below use -e for the same reason.
When the account has two-factor sign-in on, the answer is { "ok": true, "mfaRequired": true, "challengeToken" } instead. Finish with POST /auth/mfa/verify and { "challengeToken", "code" }, using a 6-digit code or a backup code; it answers with the CLI token.
The same token works for the CLI: put it in VAIF_AUTH_TOKEN and every vaif command uses it (see the CLI). Keep it in your CI’s secret store, never in a repository.
3. Create an organization
Section titled “3. Create an organization”POST /orgs with { "name" } (2 to 50 characters; companyName is optional). The caller becomes its owner, on the Free plan. Names do not have to be unique; each organization gets a unique slug.
ORG_ID=$(curl -s -X POST https://api.vaif.studio/orgs \ -H "Authorization: Bearer $VAIF_AUTH_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Acme CI"}' | jq -er .id)The answer is the organization (id, name, slug and the rest). Creating one needs an account whose email address is confirmed, and one user can create 5 organizations an hour.
4. Create a project
Section titled “4. Create a project”POST /projects with { "name", "orgId" } (name up to 50 characters; slug, description and regionKey are optional). The caller must be an admin or owner of the organization, and the plan must have room for another project; otherwise the answer is 403 and names the limit.
curl -s -X POST https://api.vaif.studio/projects \ -H "Authorization: Bearer $VAIF_AUTH_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"name\":\"my-app\",\"orgId\":\"$ORG_ID\"}" > project.json
PROJECT_ID=$(jq -er .project.id project.json)PUBLISHABLE_KEY=$(jq -er .keys.anon project.json)The answer carries the project’s keys:
| Field | What it is |
|---|---|
project.id |
The project ID |
keys.anon |
The publishable key (vk_pub_…), for browser and app code. Row rules apply |
keys.service_role |
The secret key (vk_srv_…). It bypasses row rules, belongs on servers only, and the API refuses it from browser origins |
apiKey |
The publishable key again (an older field) |
Store the secret key in your secret store as soon as you have it, and delete project.json.
5. Create the tables
Section titled “5. Create the tables”Send SQL migration files through the schema engine with the CLI: run vaif db push --project-id $PROJECT_ID with VAIF_AUTH_TOKEN set. See Migrations. Or send a schema definition to POST /schema-engine/apply.
6. Add row rules
Section titled “6. Add row rules”POST /rls/policies/{projectId} (project developer or higher) with a claim rule, { "tableName", "name", "operation", "field", "matchClaim" }, or a public rule, { "tableName", "name", "operation", "access": "public" }. operation is SELECT, INSERT, UPDATE, DELETE or ALL, and enabled is optional (on by default). It answers 201 with { "policy" }.
curl -X POST https://api.vaif.studio/rls/policies/$PROJECT_ID \ -H "Authorization: Bearer $VAIF_AUTH_TOKEN" \ -H "Content-Type: application/json" \ -d '{"tableName":"posts","name":"Authors own their posts","operation":"ALL","field":"author_id","matchClaim":"sub"}'Give every table that holds user data a claim rule: a table with no rules at all is open to every signed-in user, for reading and writing. With a claim rule that covers inserts, the server fills the owner column (author_id here) from the signed-in user. See Row rules.
7. Set the auth settings
Section titled “7. Set the auth settings”PATCH /projects/{projectId}/auth/settings (project admin or higher) with the fields to change. It answers { "settings" }. The two an app usually needs:
| Field | Value |
|---|---|
allowedRedirectUrls |
The URLs users may be sent back to after social sign-in or a magic link. Matched by exact origin, so list each preview deploy too. The list you send replaces the old one |
userTableName |
The table that signed-up users are copied into (users by default), or null to turn that off |
curl -X PATCH https://api.vaif.studio/projects/$PROJECT_ID/auth/settings \ -H "Authorization: Bearer $VAIF_AUTH_TOKEN" \ -H "Content-Type: application/json" \ -d '{"allowedRedirectUrls":["https://app.example.com","http://localhost:5173"],"userTableName":"users"}'8. Turn on realtime for tables
Section titled “8. Turn on realtime for tables”POST /realtime/install (project developer or higher) with { "projectId", "tables" }, and envId for an environment other than the default. Every table must already exist.
curl -X POST https://api.vaif.studio/realtime/install \ -H "Authorization: Bearer $VAIF_AUTH_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"projectId\":\"$PROJECT_ID\",\"tables\":[\"posts\"]}"Then give the app its two values
Section titled “Then give the app its two values”The app needs only the project ID and the publishable key, for example as VITE_VAIF_PROJECT_ID and VITE_VAIF_PUBLISHABLE_KEY, or NEXT_PUBLIC_VAIF_PROJECT_ID and NEXT_PUBLIC_VAIF_PUBLISHABLE_KEY. Never give it the secret key. See AI app builders.