# AI agents (https://docs.clouzark.com/ai-agents) ## MCP server [#mcp-server] The CLI runs as an MCP server over stdio. It uses the CLI's login and the projects linked in its working directory. ```bash npm i -g clouzark && clouzark login claude mcp add clouzark -- clouzark mcp # Claude Code claude mcp add clouzark -- clouzark mcp --read-only # reading tools only ``` For other clients (Cursor, Windsurf, VS Code), add a server with command `clouzark` and args `["mcp"]`: ```json title="mcp.json" { "mcpServers": { "clouzark": { "command": "npx", "args": ["-y", "clouzark", "mcp"] } } } ``` `--project ` and `--db ` set the default projects. With `--read-only`, only reading tools are exposed and SQL runs in a read-only transaction. | Tools | | |---|---| | Reading | `whoami`, `list_projects`, `list_deployments`, `get_deployment`, `list_env`, `list_domains`, `list_databases`, `get_api_keys`, `execute_sql`, `list_migrations`, `generate_types`, `list_backups`, `list_cron_jobs`, `list_cron_runs`, `list_app_crons`, `list_app_cron_runs` | | Changing | `create_project`, `deploy`, `redeploy`, `promote_deployment`, `rollback`, `sync_repo`, `set_env`, `link_database`, `remove_env`, `add_domain`, `remove_domain`, `create_database`, `apply_migration`, `create_cron_job`, `update_cron_job`, `run_cron_job`, `delete_cron_job`, `create_app_cron`, `update_app_cron`, `run_app_cron`, `delete_app_cron` | `apply_migration` also writes the migration file into `clouzark/migrations/`, so the change stays in the repo. ## Repo instructions [#repo-instructions] ```bash clouzark init ``` Writes a Clouzark section into `AGENTS.md`, describing the linked projects and how to deploy and migrate. If the repo has a `CLAUDE.md`, it imports `AGENTS.md`. Running `init` again refreshes only that section. ## llms.txt [#llmstxt] These docs are available as plain text for agents: - [docs.clouzark.com/llms.txt](/llms.txt) — an index of every page - [docs.clouzark.com/llms-full.txt](/llms-full.txt) — every page in one file - Any page as Markdown: add `.md` to its URL, for example `docs.clouzark.com/quickstart.md` - [clouzark.com/llms.txt](https://clouzark.com/llms.txt) — a one-page summary of the CLI, MCP tools, and APIs # Command reference (https://docs.clouzark.com/cli/commands) Run `clouzark --help` for the same information in your terminal. List commands accept `--json`. ## Account [#account] | Command | Does | |---|---| | `login [--token ] [--api-url ] [--no-browser]` | Log in through the browser, or with an access token | | `logout` | Revoke this machine's token and forget it | | `whoami` | Show the user, team, and API | | `init [--no-agents]` | Link hosting and database, and write a Clouzark section into `AGENTS.md` | | `link [-p ] [-y]` / `unlink` | Link this directory to a hosting project | | `mcp [--read-only] [-p ] [--db ]` | Run as an MCP server for coding agents ([AI agents](/ai-agents)) | ## Web hosting [#web-hosting] | Command | Does | |---|---| | `projects ls` | List the team's hosting projects | | `projects memory [size] [-p ]` | Show or set the app's [instance size](/hosting/deployments#instance-size): `512mb`, `1gb`, `2gb`, `4gb`, or `default` | | `projects database [-p ] [--json]` | Show the [linked database](/hosting/environment-variables#linked-database) whose URL and keys are injected as env vars | | `projects database link [--db ] [-p ]` / `projects database unlink` | Link a database (the one linked to this directory by default), or unlink it; applies from the next deployment | | `projects sync-repo [-p ]` | Follow a GitHub repo rename or transfer: point the project at the repo's current URL. A ref and app URL made from the old repo name move to the new name; the old URL redirects | | `projects create [repo] [-n ] [--root ] [--framework ] [--production-branch ] [--no-link] [--no-wait]` | Import a GitHub repo as a project (this repo's origin by default), link it, and follow the first production build | | `deploy [--prod] [-b ] [--force] [--no-wait]` | Build a branch's latest pushed commit and stream the log; `--force` builds without the [build cache](/hosting/deployments#build-cache) | | `deploy --upload [--prod] [--force]` | Build this directory as it is now | | `deployments ls [-n ]` | Recent deployments | | `logs [deploymentId] [-f]` | Show or follow a build log (the latest deployment by default) | | `promote ` | Make a deployment the live production deployment | | `rollback` | Roll production back to the previous live deployment | | `redeploy [deploymentId] [--force] [--no-wait]` | Rebuild with the current environment variables; `--force` skips the build cache | | `cancel ` | Cancel a queued deployment | | `env ls` | List variable names and targets | | `env add [value] [-t all\|production\|preview]` | Add or update a variable (prompts for the value if omitted) | | `env rm [-t ...]` | Remove a variable | | `env pull [file] [-e production\|preview] [-y]` | Write resolved values to a file (`.env.local` by default) | | `domains ls` | List custom domains | | `domains add ` | Add a domain and print the DNS records to create | | `domains verify ` | Check DNS and issue the certificate | | `domains rm ` | Remove a domain | | `import vercel [--team ] [--project ...] [--all] [--dry-run] [-y]` | [Migrate Vercel projects](/hosting/import-vercel): repo, build settings, env vars, domains, and crons. Reads `VERCEL_TOKEN`, or asks | | `crons ls [--json]` | List [cron jobs](/hosting/cron-jobs), from `clouzark.json` and the CLI, with their last and next runs | | `crons add [-m GET\|POST] [--tz zone] [--timeout s] [--retries n] [--disabled]` | Call a path on the production app on a schedule | | `crons edit [--path p] [-s schedule] [-m method] [--tz zone] [--timeout s] [--retries n] [--name new]` | Change only what you pass (not for jobs from `clouzark.json`) | | `crons enable ` / `crons disable ` | Resume or pause scheduled runs | | `crons run ` | Call it now and wait for the response | | `crons runs [-n 20] [--json]` | Recent runs: status, HTTP code, duration, response | | `crons rm [-y]` | Delete a job and its run history | ## Database [#database] | Command | Does | |---|---| | `db ls` | List database projects | | `db create [--no-link]` | Create a database (and link it) | | `db delete [-y]` | Permanently delete a database | | `db link [--db ]` | Link this directory to a database | | `db query [sql] [-f file] [--json]` | Run SQL from an argument, a file, or stdin | | `db url [--pooled] [--json]` | Print the direct connection string, or with `--pooled` the [connection pooler's](/database/overview#connect) (port 6543, for apps and serverless) | | `db keys [--json]` | Print the REST URL, anon key, and service key | | `db dump [-f file] [--data-only\|--full]` | Dump with your local `pg_dump` (schema only by default) | | `db backups ls [--json]` | List [backups](/database/overview#backups), newest first: nightly, manual, and pre-restore | | `db backups create` | Back up now and wait until it's done | | `db backups download [-o file]` | Download a backup as a `pg_dump` custom-format archive | | `db backups restore [-y]` | Replace the database with a backup (a safety backup is taken first) and wait | | `import supabase [-n name] [--dry-run] [-y] [--no-link]` | [Migrate a Supabase project](/database/import-supabase): database, auth users, policies, and storage into a new database. Reads `SUPABASE_DB_URL`, `SUPABASE_URL`, and `SUPABASE_SERVICE_ROLE_KEY`, or asks | | `migration new ` | Create `clouzark/migrations/_.sql` | | `migration ls` | Compare local migrations with the applied ones | | `db push [--dry-run] [-y]` | Apply pending migrations | | `gen types typescript [-o file] [-s schemas]` | Generate [TypeScript types](/database/typescript-types) for the `public` schema, or the comma-separated schemas given with `--schema` | | `db cron ls [--json]` | List [cron jobs](/database/cron) with their last and next runs | | `db cron add [sql] [-f file] [--tz zone] [--timeout s] [--disabled]` | Schedule SQL; `schedule` is 5-field cron, e.g. `"0 3 * * *"` | | `db cron edit [sql] [-s schedule] [-f file] [--tz zone] [--timeout s] [--name new]` | Change only what you pass | | `db cron enable ` / `db cron disable ` | Resume or pause scheduled runs | | `db cron run ` | Run now and wait for the result | | `db cron runs [-n 20] [--json]` | Recent runs: status, duration, message | | `db cron rm [-y]` | Delete a job and its run history | # CLI (https://docs.clouzark.com/cli) ```bash npm i -g clouzark ``` Node.js 20 or later. Without installing, prefix commands with `npx`: `npx clouzark deploy`. ## Log in [#log-in] ```bash clouzark login ``` The CLI shows a one-time code and opens [clouzark.com/cli/authorize](https://clouzark.com/cli/authorize). Approve it and pick a team; the CLI receives a token for that team that lasts 90 days. The token appears in the team's **Access Tokens** settings, where it can be revoked. Run `clouzark login` again to switch teams. ```bash clouzark whoami # user, team, and role clouzark logout # revoke this machine's token ``` The CLI has exactly your role in the team: a viewer can read but not deploy. ## Link a directory [#link-a-directory] Commands act on the project linked to the current directory, saved in `.clouzark/project.json` (git-ignored). Link once per repo: ```bash clouzark init # link hosting and database, and write AGENTS.md clouzark link # link only the hosting project clouzark db link # link only the database clouzark unlink ``` To act on another project, pass `-p, --project ` (hosting) or `--db ` (database). A ref is a project's slug or id. ## Configuration [#configuration] | | | |---|---| | `~/.config/clouzark/config.json` | API URL and token (file mode 600; honors `XDG_CONFIG_HOME`) | | `CLOUZARK_TOKEN` | Token to use instead of the stored one | | `CLOUZARK_API_URL` | API to use (default `https://api.clouzark.com`) | | `CLOUZARK_PROJECT` | Hosting project to use instead of the linked one | | `CLOUZARK_DB` | Database to use instead of the linked one | ## CI [#ci] Create a token under the team's **Settings → Access Tokens** and save it as a secret in your CI. For example, to apply migrations on every push to `main`: ```yaml title=".github/workflows/migrate.yml" - run: npx clouzark db push --yes --db my-app-db env: CLOUZARK_TOKEN: ${{ secrets.CLOUZARK_TOKEN }} ``` # Auth (https://docs.clouzark.com/database/auth) Each database has its own user accounts, stored in the `auth.users` table. These are your app's users, not Clouzark team members. Base URL: `https://api.clouzark.com/auth/`, called with the anon key in the `apikey` header. ## Sign up and sign in [#sign-up-and-sign-in] ```ts const base = 'https://api.clouzark.com/auth/my-app-db'; const headers = { apikey: ANON_KEY, 'Content-Type': 'application/json' }; const { user, token } = await fetch(`${base}/signup`, { method: 'POST', headers, body: JSON.stringify({ email, password }), }).then((r) => r.json()); ``` `POST /login` takes the same body and returns `{ user, token }`. Send the token as `Authorization: Bearer ` to the REST, GraphQL, Storage, and Realtime APIs to act as that user; `auth.uid()` then returns their id in row-level security policies. ## Endpoints [#endpoints] | Request | Does | |---|---| | `POST /signup` `{ email, password }` | Create a user and sign them in | | `POST /login` `{ email, password }` | Sign in | | `POST /logout` | Sign out | | `GET /me` | The signed-in user | | `PUT /user` `{ password }` | Change the signed-in user's password | | `POST /magiclink` | Email a sign-in link | | `POST /recover` | Email a password reset link | | `GET /factors`, `POST /factors`, `POST /factors//verify`, `DELETE /factors/` | Multi-factor authentication | Email templates, providers, and rate limits are set in the dashboard under the database's **Authentication** settings. # Cron jobs (https://docs.clouzark.com/database/cron) A cron job runs SQL against your database on a schedule, for work like purging expired rows, rolling up stats, or refreshing a materialized view. Clouzark runs the schedule itself, so the database needs no `pg_cron` extension. ## Add a job [#add-a-job] ```bash clouzark db cron add purge_sessions "0 3 * * *" "delete from sessions where expires_at < now()" clouzark db cron add nightly_rollup "30 1 * * *" -f sql/rollup.sql --tz Europe/Berlin --timeout 120 clouzark db cron ls ``` You can also add and edit jobs from the database's **Cron** page in the dashboard. ## Schedules [#schedules] A schedule is a standard 5-field cron expression: minute, hour, day of month, month, day of week. | Schedule | Runs | |---|---| | `*/15 * * * *` | Every 15 minutes | | `0 * * * *` | Every hour, on the hour | | `0 3 * * *` | Every day at 03:00 | | `0 9 * * 1-5` | Weekdays at 09:00 | | `0 0 1 * *` | The first of every month at midnight | Schedules are read in UTC unless you give the job a time zone (`--tz`, any IANA name such as `America/New_York`). With a time zone, a daily job stays at the same local time across daylight saving changes. Schedules can't run more often than once a minute. ## How jobs run [#how-jobs-run] - **As the database owner.** The SQL can read and change any table, and row level security policies don't apply to it (unless a table uses `FORCE ROW LEVEL SECURITY`). Several `;`-separated statements run in order. - **With a timeout.** A run that takes longer than the job's timeout (60 seconds by default, at most 300) is stopped and marked failed. - **Never overlapping.** If a job's previous run is still going when it's due again, the new run is skipped and recorded as `skipped`. - **Without retries.** A failed run waits for the next scheduled time. Write SQL that's safe to run again, so a run you trigger by hand after a failure does no harm. - **Only while the database is active.** A paused database's jobs don't run. A database can have up to 20 cron jobs. ## Check and change jobs [#check-and-change-jobs] ```bash clouzark db cron runs purge_sessions # recent runs: status, duration, message clouzark db cron run purge_sessions # run it now and wait for the result clouzark db cron edit purge_sessions -s "0 4 * * *" clouzark db cron disable purge_sessions # stop scheduled runs; it can still be run by hand clouzark db cron enable purge_sessions clouzark db cron rm purge_sessions ``` Each job keeps its last 100 runs. Every run also shows up in the database's **Logs**, under `cron`. Jobs are addressed by name or id. The same operations are available in the [Management API](/management-api#database-cron-jobs) and to [AI agents](/ai-agents). # Migrate from Supabase (https://docs.clouzark.com/database/import-supabase) `clouzark import supabase` copies a Supabase project into a new Clouzark database. Your app then switches by changing two values, its URL and anon key, because [supabase-js works against Clouzark](/database/rest-api#use-supabase-js). ```bash export SUPABASE_DB_URL='postgresql://postgres.:@aws-0-.pooler.supabase.com:5432/postgres' export SUPABASE_URL='https://.supabase.co' # optional, to copy storage export SUPABASE_SERVICE_ROLE_KEY='' # optional, to copy storage clouzark import supabase --dry-run # inspect and report, change nothing clouzark import supabase --name my-app ``` In the dashboard: on the Databases page, **Import from Supabase**. Paste the connection string (and the project URL and service key to copy files), review the report, name the new database, and import. Get the connection string in Supabase under **Connect** (Session pooler or Direct). It includes your database password. Without the env vars, the command asks for each value without echoing it. The credentials are sent to Clouzark for this import only and are never stored. ## What's copied [#whats-copied] | From Supabase | To Clouzark | |---|---| | Your schemas (`public` and any you created), with tables, data, views, functions, triggers, and indexes | The new database. Other schemas than `public` are copied but not exposed to the APIs until you add them under **Settings → Exposed schemas** | | Extensions in the `extensions` schema (for example `uuid-ossp`, `pgcrypto`) | Created again where this server has them | | Auth users, with the same ids and password hashes | [Auth](/database/auth): users sign in with their existing passwords | | OAuth identities (GitHub, Google) | Kept; set up the same provider under Auth settings | | Row-level security policies, including storage ones | Recreated. `TO authenticated` becomes `auth.uid() IS NOT NULL`, `TO anon` becomes `auth.uid() IS NULL`, and policies only for `service_role` are dropped, since the service key bypasses RLS here too | | Triggers on `auth.users` (such as `handle_new_user`) | Recreated on Clouzark's `auth.users` | | Storage buckets and files | [Storage](/database/storage); bucket names become lowercase with hyphens | `auth.uid()`, `auth.jwt()`, `auth.role()`, and `auth.email()` all exist, so policies and functions that use them keep working. ## What isn't [#what-isnt] - Edge functions: run server code in a [Clouzark-hosted app](/hosting/deployments) and [link the database](/hosting/environment-variables#linked-database) to it. - `pg_cron` jobs: recreate them as [cron jobs](/database/cron). - `pg_net` calls and realtime `postgres_changes` subscriptions. - Files over 100 MB. Users without an email (anonymous or phone sign-in) are copied with a placeholder email so their rows stay linked, but can't sign in until they have a real one. The report at the end lists everything that needs attention. ## After the import [#after-the-import] 1. Replace your Supabase URL and anon key with the values the command prints (also in `clouzark db keys`). 2. If the app runs on Clouzark, link the database to it with `clouzark projects database link` so it gets them as env vars. 3. Set up OAuth providers and email settings under Auth if you use them. # Migrations (https://docs.clouzark.com/database/migrations) Migrations are SQL files in `clouzark/migrations/`, named `_.sql`. They're applied in order, and Clouzark records which ones ran inside the database itself (`clouzark_migrations.schema_migrations`). ## Create and apply [#create-and-apply] ```bash clouzark migration new add_orders # write the SQL in clouzark/migrations/20261028093000_add_orders.sql clouzark db push --dry-run # list what would run clouzark db push # apply pending migrations clouzark migration ls # compare local files with what's applied ``` ```sql title="clouzark/migrations/20261028093000_add_orders.sql" create table orders ( id bigint generated always as identity primary key, user_id uuid not null, total_cents integer not null, created_at timestamptz not null default now() ); alter table orders enable row level security; create policy "own orders" on orders for select using (user_id = auth.uid()); ``` ## Rules [#rules] - **Each migration runs in one transaction**, together with its record. Don't add `BEGIN` or `COMMIT`, and don't use statements that can't run in a transaction, such as `CREATE INDEX CONCURRENTLY`. - **Never edit an applied migration.** Clouzark stores a checksum and refuses a changed file. Add a new migration instead. - Commit the migration files, so every environment and teammate applies the same history. After changing the schema, [regenerate your types](/database/typescript-types). # Overview (https://docs.clouzark.com/database/overview) Each database project is its own Postgres database. Use it with any Postgres client, or call its APIs from your app without writing a backend. ## Create one [#create-one] ```bash clouzark db create my-app-db # creates the database and links this directory to it clouzark db ls clouzark db link # link an existing database instead ``` ## Connect [#connect] ```bash clouzark db url # direct postgres:// connection string (port 5432) clouzark db url --pooled # through the connection pooler (port 6543) clouzark db keys # REST URL, anon key, and service key ``` Use a connection string from server code, ORMs, and migration tools: | Connection | Port | Use it for | |---|---|---| | Pooled | 6543 | Your app, especially serverless and edge functions, which open a connection per request | | Direct | 5432 | Migrations, `pg_dump`, `LISTEN/NOTIFY`, and long-lived sessions | The pooler shares connections per transaction, so thousands of app connections use a handful of real ones. Session state doesn't carry between transactions: `LISTEN`, session-level `SET`, and advisory locks held across transactions need the direct connection. Both are in **Settings → Connection**. Use the keys with the [REST API](/database/rest-api), [Auth](/database/auth), [Storage](/database/storage), and [Realtime](/database/realtime). | Key | Where it's safe | Row-level security | |---|---|---| | Anon key | Browsers and mobile apps | Applies | | Service key | Server-side only | Bypassed | Never ship the service key or the connection string to a browser. ## Run SQL [#run-sql] ```bash clouzark db query "select count(*) from orders" clouzark db query -f report.sql --json cat seed.sql | clouzark db query ``` `db query` runs as the database owner, so it bypasses row-level security. The dashboard also has an SQL editor, a table editor, and CSV import and export. ## Backups [#backups] Every database is backed up nightly, and backups are kept for 7 days. A backup is the whole database: every schema, its data, and its grants and policies. ```bash clouzark db backups ls # newest first clouzark db backups create # back up now clouzark db backups download # a pg_dump archive; restore locally with pg_restore clouzark db backups restore # replace the database with a backup ``` The same is in the dashboard under **Database → Backups**. A restore replaces every table and row, including tables created after the backup was taken. A safety backup is taken first, so restoring that one undoes the restore. If anything goes wrong mid-restore, nothing is changed. To take your own copy with your local `pg_dump`: ```bash clouzark db dump -f schema.sql # schema only clouzark db dump --full -f backup.sql # schema and data clouzark db dump --data-only -f data.sql ``` ## Pausing [#pausing] On the Free plan, a database with no API requests, dashboard use, or connections for 7 days is paused. The team's owners get an email 3 days before, and again when it happens. A paused database keeps all its data. It resumes on its own with its next API request (REST, GraphQL, Auth, Storage, or Realtime), or from **Settings → Resume database**. Direct Postgres connections are refused while it's paused, so resume it first if you only connect directly. Scheduled [cron jobs](/database/cron) don't count as activity. Pro databases are never paused. ## Size [#size] On the Free plan each database includes 500 MB. The dashboard shows the current size under **Usage**, and the team's owners get an email when a database reaches 80% of that and again if it goes over. Nothing is blocked for being over; delete data you don't need (`VACUUM FULL` returns the space) or upgrade to Pro. # Realtime (https://docs.clouzark.com/database/realtime) Channels let clients send each other messages (Broadcast) and share who's online (Presence). Subscriptions use Server-Sent Events. Base URL: `https://api.clouzark.com/realtime//channels/` Channel names use letters, digits, and `_ - : .`. Channels starting with `private:` refuse the anon key, so only signed-in users (or the service key) can join them. ## Subscribe [#subscribe] `EventSource` can't send headers, so pass credentials as query parameters (`apikey`, and `access_token` for a user token): ```ts const url = `https://api.clouzark.com/realtime/my-app-db/channels/room-1?apikey=${ANON_KEY}`; const events = new EventSource(url); let connectionToken = ''; events.addEventListener('system', (e) => { connectionToken = JSON.parse(e.data).connectionToken; }); events.addEventListener('broadcast', (e) => console.log(JSON.parse(e.data))); events.addEventListener('presence', (e) => console.log(JSON.parse(e.data))); ``` ## Broadcast [#broadcast] ```ts await fetch(`https://api.clouzark.com/realtime/my-app-db/channels/room-1/broadcast`, { method: 'POST', headers: { apikey: ANON_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ event: 'cursor', payload: { x: 10, y: 20 }, connectionToken }), }); ``` Pass the `connectionToken` from your subscription to skip echoing the message back to yourself, or `self: true` to receive it too. ## Presence [#presence] | Request | Does | |---|---| | `GET .../presence` | Who's on the channel | | `POST .../presence` `{ connectionToken, key?, state }` | Share this connection's state | | `DELETE .../presence` `{ connectionToken }` | Stop sharing | Subscribers get `presence` events whenever someone joins, leaves, or updates their state. # REST and GraphQL (https://docs.clouzark.com/database/rest-api) Every table in the database's exposed schemas (`public` unless you add more) gets REST endpoints at: ``` https://api.clouzark.com/rest/ ``` Find the URL and keys with `clouzark db keys`. ## Use supabase-js [#use-supabase-js] Each database also speaks [PostgREST](https://postgrest.org)'s API, so `supabase-js` (or the smaller `@supabase/postgrest-js`) works against it. Use the project URL from `clouzark db keys` as the Supabase URL: ```ts import { createClient } from '@supabase/supabase-js'; const db = createClient('https://api.clouzark.com/projects/', process.env.NEXT_PUBLIC_CLOUZARK_ANON_KEY!); const { data, error } = await db .from('posts') .select('id, title, author:authors(name), comments(body)') .eq('published', true) .gt('views', 100) .order('created_at', { ascending: false }) .range(0, 19); await db.from('posts').insert({ title: 'Hello' }).select().single(); await db.from('posts').upsert({ id: 1, title: 'Updated' }); await db.from('posts').update({ published: true }).eq('id', 1); await db.from('posts').delete().eq('id', 1); const { data: total } = await db.rpc('add', { a: 2, b: 3 }); ``` Supported: every filter (`eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `match`, `in`, `is`, `contains`, `containedBy`, `overlaps`, `textSearch`, `or`, `not`, `filter`), JSON paths (`meta->>country`), casts, embedding over foreign keys (nested, aliased, `!inner`, with their own filters, order, and limit), `count` (`exact`, `planned`, `estimated`), `single()` and `maybeSingle()`, `insert`, `upsert`, `update`, `delete` with `select()`, and `rpc()` for functions (`get: true` for read-only ones). Errors use PostgREST's codes, such as `PGRST116` and `23505`. Only the database part of `supabase-js` (`from()`, `rpc()`, `schema()`) works against Clouzark. For sign-in, files, and realtime use [Auth](/database/auth), [Storage](/database/storage), and [Realtime](/database/realtime). To send a signed-in user's token, pass it in `global.headers.Authorization`. Many-to-many embedding through a join table and spread embeds (`...`) aren't supported. The endpoints below (`/rest/`) keep working as they are. ## Authentication [#authentication] Send one of: - `apikey: ` — browsers and mobile apps. [Row-level security](#row-level-security) applies. - `apikey: ` — server-side only. Bypasses row-level security. - `Authorization: Bearer ` — acts as a signed-in user from [Auth](/database/auth). ## Endpoints [#endpoints] | Request | Returns | |---|---| | `GET /rest/` | `{ tables }` | | `GET /rest//?page=1` | `{ columns, rows, total, pageSize }`, 50 rows per page | | `GET /rest//
/` | `{ row }` | | `POST /rest//
` with a JSON row | `{ row }` (201) | | `PATCH /rest//
/` with the changed columns | `{ row }` | | `DELETE /rest//
/` | 204 | Errors come back as `{ "error": "..." }`. ```ts const base = 'https://api.clouzark.com/rest/my-app-db'; const headers = { apikey: process.env.NEXT_PUBLIC_CLOUZARK_ANON_KEY!, 'Content-Type': 'application/json' }; const { rows } = await fetch(`${base}/orders?page=1`, { headers }).then((r) => r.json()); const { row } = await fetch(`${base}/orders`, { method: 'POST', headers, body: JSON.stringify({ total_cents: 4200 }), }).then((r) => r.json()); ``` ## Schemas [#schemas] By default the APIs serve the `public` schema. To serve others, check them under **Settings → Exposed schemas**; the list also shows which tables in each schema have row-level security off, because the anon key can read and write every row in those once the schema is exposed. Pick the schema per request, as with PostgREST: `Accept-Profile` on `GET`, `Content-Profile` on `POST`, `PATCH`, and `DELETE`. Without the header, the first exposed schema is used. A schema that isn't exposed returns 406. ```ts await fetch(`${base}/jobs`, { headers: { ...headers, 'Accept-Profile': 'career' } }); await fetch(`${base}/jobs`, { method: 'POST', headers: { ...headers, 'Content-Profile': 'career' }, body: JSON.stringify({ title: 'Engineer' }), }); ``` ## Row-level security [#row-level-security] With the anon key or a user token, Postgres row-level security decides what each request can see and change. Enable it on every table your app exposes, and write policies with `auth.uid()`, which returns the signed-in user's id (or null): ```sql alter table orders enable row level security; create policy "read own orders" on orders for select using (user_id = auth.uid()); create policy "create own orders" on orders for insert with check (user_id = auth.uid()); ``` ## GraphQL [#graphql] The same data is available over GraphQL, with the same keys and policies: ``` POST https://api.clouzark.com/graphql/ { "query": "...", "variables": { } } ``` GraphQL covers every exposed schema. Tables outside `public` are prefixed with their schema, so `career.jobs` is queried as `career_jobs`. # Storage (https://docs.clouzark.com/database/storage) Create buckets in the dashboard under the database's **Storage** page. Public buckets serve files to anyone; private buckets need a key, a user token, or a signed URL. Storage policies control which users can read and write. Base URL: `https://api.clouzark.com/storage/` | Request | Does | |---|---| | `POST /object//` | Upload (raw body). Add `x-upsert: true` to overwrite | | `PUT /object//` | Upload or replace | | `GET /object//` | Download | | `DELETE /object//` | Delete | | `GET /list/?prefix=&limit=100&offset=0` | `{ objects }` | | `GET /public//` | Download from a public bucket, no key needed | | `POST /sign//` `{ expiresIn }` | `{ signedUrl, expiresAt }`, a temporary link to a private file | ```ts await fetch(`https://api.clouzark.com/storage/my-app-db/object/avatars/${userId}.png`, { method: 'PUT', headers: { apikey: ANON_KEY, Authorization: `Bearer ${token}`, 'Content-Type': 'image/png' }, body: file, }); ``` # TypeScript types (https://docs.clouzark.com/database/typescript-types) ```bash clouzark gen types typescript -o src/database.types.ts ``` The output describes the `public` schema. For other schemas, list them with `--schema`; each gets its own key under `Database`: ```bash clouzark gen types typescript --schema public,career -o src/database.types.ts ``` ```ts type Job = Database['career']['Tables']['jobs']['Row']; ``` The output includes helpers for the `public` schema (or the first one listed, without `public`): ```ts import type { Tables, TablesInsert, TablesUpdate, Enums } from './database.types'; type Order = Tables<'orders'>; // a row type NewOrder = TablesInsert<'orders'>; // generated and defaulted columns are optional type OrderPatch = TablesUpdate<'orders'>; // every column optional ``` The full schema is also exported as the `Database` type. Run the command again after every migration. # Cron jobs (https://docs.clouzark.com/hosting/cron-jobs) A cron job sends a request to a path on your app's production deployment on a schedule, for work like sending digest emails, syncing with another service, or cleaning up old data. Your app does the work in an ordinary route handler; Clouzark only calls it. ## Declare jobs in clouzark.json [#declare-jobs-in-clouzarkjson] Add a `crons` array to `clouzark.json` in your project's root directory (the same directory as `package.json`): ```json title="clouzark.json" { "crons": [ { "path": "/api/cron/cleanup", "schedule": "0 3 * * *" }, { "path": "/api/cron/digest", "schedule": "0 8 * * 1", "timezone": "Europe/Berlin", "name": "weekly-digest" } ] } ``` The jobs take effect when the deployment becomes production, and a [rollback](/hosting/deployments) restores the jobs of the deployment you roll back to. Preview deployments never run cron jobs. If `clouzark.json` is invalid, the build fails and says why. | Field | | | |---|---|---| | `path` | required | Path on your app, starting with `/`. A query string is fine: `/api/cron?task=cleanup` | | `schedule` | required | 5-field cron expression (see [Schedules](#schedules)) | | `name` | | Unique within the project. Defaults to one made from the path, e.g. `api-cron-cleanup` | | `timezone` | | IANA time zone the schedule is read in. Default `UTC` | | `method` | | `GET` (default) or `POST` | | `timeout` | | Seconds to wait for the response, 1 to 300. Default 60 | | `retries` | | Times to retry a failed request, 0 to 3. Default 0 | ## Or add them with the CLI or dashboard [#or-add-them-with-the-cli-or-dashboard] Jobs can also be added without a deploy, from the project's **Cron Jobs** page or the CLI: ```bash clouzark crons add cleanup /api/cron/cleanup "0 3 * * *" clouzark crons add digest /api/cron/digest "0 8 * * 1" --tz Europe/Berlin --method POST --retries 2 clouzark crons ls ``` Jobs from `clouzark.json` are kept in step with the file: you can't edit or delete them from the CLI or dashboard, only switch them off and on. A paused job stays paused across deploys. A project can have up to 20 cron jobs in total. ## Secure the endpoint [#secure-the-endpoint] Your cron path is a public URL, so check that a request really comes from Clouzark. Add a `CRON_SECRET` [environment variable](/hosting/environment-variables) to production with a long random value; every cron request then carries it as a bearer token: ```ts title="app/api/cron/cleanup/route.ts" export async function GET(request: Request) { if (request.headers.get("authorization") !== `Bearer ${process.env.CRON_SECRET}`) { return new Response("Unauthorized", { status: 401 }); } // ... do the work return Response.json({ ok: true }); } ``` Requests also send `User-Agent: clouzark-cron/1.0`. A new `CRON_SECRET` value is used from the next run; your app sees it after a redeploy. ## Schedules [#schedules] | Schedule | Runs | |---|---| | `*/15 * * * *` | Every 15 minutes | | `0 * * * *` | Every hour, on the hour | | `0 3 * * *` | Every day at 03:00 | | `0 9 * * 1-5` | Weekdays at 09:00 | | `0 0 1 * *` | The first of every month at midnight | Fields are minute, hour, day of month, month, and day of week. Schedules are read in UTC unless the job has a time zone; with one, a daily job stays at the same local time across daylight saving changes. The most often a job can run is once a minute. ## How requests are made [#how-requests-are-made] - **To your production URL**, `https://.`. If the app is asleep, it's woken first. - **A 2xx response is a success.** Anything else is a failure, and the run records the status code and the start of the response body. - **Redirects aren't followed.** A 3xx response fails the run, so point the job at the final path. - **Retries** are for 5xx responses, timeouts, and network errors, a few seconds apart. A 4xx response isn't retried. - **Never overlapping.** If a job's previous run is still going when it's due again, the new run is skipped and recorded as `skipped`. Still, write handlers that are safe to run twice. ## Check and change jobs [#check-and-change-jobs] ```bash clouzark crons runs cleanup # recent runs: status, HTTP code, duration, response clouzark crons run cleanup # call it now and wait for the response clouzark crons disable cleanup # stop scheduled runs; it can still be run by hand clouzark crons enable cleanup clouzark crons edit digest -s "0 9 * * 1" clouzark crons rm digest ``` Each job keeps its last 100 runs. Jobs are addressed by name or id. The same operations are in the [Management API](/management-api#app-cron-jobs) and available to [AI agents](/ai-agents). To run SQL against a database on a schedule instead, see [database cron jobs](/database/cron). # Deployments (https://docs.clouzark.com/hosting/deployments) ## From GitHub [#from-github] Every push to a connected repository creates a deployment: - A push to the **production branch** (set per project, `main` by default) deploys to production. - A push to any other branch, or a pull request, deploys a **preview** with its own URL. Each deployment also keeps its own immutable URL, so you can always open exactly what a given build served. To deploy without pushing, run: ```bash clouzark deploy # the current branch's latest pushed commit clouzark deploy --branch staging clouzark deploy --prod # the production branch, to production ``` `clouzark deploy` builds what's on GitHub, not your local files. It warns when you have uncommitted changes or unpushed commits that won't be included. ## From your machine [#from-your-machine] `--upload` builds the directory as it is now, uncommitted changes included: ```bash clouzark deploy --upload # preview, named after the current branch clouzark deploy --upload --prod # production ``` - In a git repo, the CLI sends the files git sees (tracked and untracked, minus `.gitignore`). Outside a git repo it skips `node_modules`, build output, and similar folders, and `--upload` is the default. - `.env` files are never sent. Set variables with [`clouzark env add`](/hosting/environment-variables). - The upload limit is 100 MB. ## Frameworks [#frameworks] Clouzark detects the framework from the repository: | Framework | How it runs | |---|---| | Next.js | `next build`, then `next start` | | React (Vite and similar) | Built, then served as a single-page app from the output directory (`dist` by default) | | Node | `npm run start`; the app should listen on the `PORT` and `HOST` environment variables | | Static HTML | Served as-is | The build command, output directory, install command, and root directory (for monorepos) can be changed in the project's settings. ## Monorepos [#monorepos] One repository can hold several projects. Import the repo once per app and give each project its own **root directory**, such as `apps/web` and `docs`: ```bash clouzark projects create acme/monorepo --name acme-web --root apps/web clouzark projects create acme/monorepo --name acme-docs --root docs ``` A push then builds every project that uses the repo, except projects whose root directory had no changes. To always build on every push, turn off **Skip deployments when nothing in the root directory changed** in the project's settings. Deploys started from the dashboard or with `clouzark deploy` always build. ## Build cache [#build-cache] Builds reuse a cache from the project's earlier builds, so they don't download every package and recompile every page each time: - **npm downloads** are kept per project and reused by every build. npm still checks each package against your lockfile. - **Next.js's `.next/cache`** is restored into every build, but only production builds save it. A preview from another branch never changes what production builds with. `node_modules` itself isn't cached, so a dependency change always installs cleanly. If a build behaves oddly and you suspect the cache, rebuild without it: ```bash clouzark redeploy --force # rebuild the latest deployment without the cache clouzark deploy --force # deploy without the cache ``` In the dashboard, untick **Use existing build cache** when you redeploy. A production build without the cache saves a fresh one for the builds after it. ## Instance size [#instance-size] The instance size sets how much memory and CPU your app gets while it runs: | Size | Memory | CPU | |---|---|---| | `512mb` | 512 MB | 0.5 vCPU | | `1gb` | 1 GB | 1 vCPU | | `2gb` | 2 GB | 2 vCPU | | `4gb` | 4 GB | 2 vCPU | Set it in the project's settings (**General → Instance size**), or with the CLI: ```bash clouzark projects memory # show the current size clouzark projects memory 2gb # set it clouzark projects memory default # back to the platform default ``` - A new size applies to running deployments straight away. - A running deployment that's already using close to or more than a smaller size keeps its current limit until it's next deployed. - An app that goes over its memory is restarted. ## Build logs [#build-logs] ```bash clouzark deployments ls # recent deployments clouzark logs # the latest deployment's log clouzark logs -f # follow a build until it finishes ``` A deployment moves through `queued`, `building`, `deploying`, and then `live`, `failed`, or `canceled`. ## Promote and roll back [#promote-and-roll-back] ```bash clouzark promote # make a deployment the live production deployment clouzark rollback # go back to the previous production deployment clouzark redeploy # rebuild the latest deployment with current env vars clouzark redeploy --force # the same, without the build cache clouzark cancel # cancel a deployment that's still queued ``` Promotion and rollback switch traffic without rebuilding, so they only work with deployments that are still `live`. # Custom domains (https://docs.clouzark.com/hosting/domains) Every project gets a default URL. To add your own domain: ```bash clouzark domains add www.example.com ``` The CLI prints the DNS records to create at your DNS provider: a `CNAME` for subdomains, or an `A` record for an apex domain. Once the records are in place: ```bash clouzark domains verify www.example.com ``` Clouzark checks the DNS and issues a TLS certificate automatically. ```bash clouzark domains ls clouzark domains rm www.example.com ``` In the dashboard, under the project's **Domains** tab, you can also point a domain at a specific branch or make it redirect to another domain. # Environment variables (https://docs.clouzark.com/hosting/environment-variables) Environment variables are encrypted at rest and injected into both the build and the running app. Each variable targets **production**, **preview**, or **all** environments. ## In the dashboard [#in-the-dashboard] Open the project's **Settings → Environment Variables**. To add many at once, paste the contents of a `.env` file into a key field: each line becomes its own row. You can also import a `.env` file. ## With the CLI [#with-the-cli] ```bash clouzark env ls clouzark env add DATABASE_URL "postgres://..." -t production clouzark env add API_KEY # prompts for the value clouzark env rm API_KEY -t preview clouzark env pull # writes .env.local with the preview values clouzark env pull .env.production -e production ``` `-t` (`--target`) takes `all`, `production`, or `preview`. Changes apply to the next deployment. Run `clouzark redeploy` to rebuild the latest deployment with the new values. Never commit `.env` files. `clouzark deploy --upload` skips them on purpose. ## Linked database [#linked-database] Link one of the team's [databases](/database/overview) to the app and its connection details are added to every build and deployment automatically, kept up to date with the database's password and keys: | Variable | Value | |---|---| | `DATABASE_URL` | Connection string through the [connection pooler](/database/overview#connect) (direct when the pooler isn't available) | | `DIRECT_DATABASE_URL` | Direct connection string, for migrations and session features | | `CLOUZARK_PROJECT_URL`, `NEXT_PUBLIC_CLOUZARK_PROJECT_URL` | The database's base URL for [supabase-js](/database/rest-api#use-supabase-js) | | `CLOUZARK_ANON_KEY`, `NEXT_PUBLIC_CLOUZARK_ANON_KEY` | Anon key, safe in the browser | | `CLOUZARK_SERVICE_KEY` | Service key, server-side only (no `NEXT_PUBLIC_` variant) | Link it under **Settings → Environment Variables → Database**, or: ```bash clouzark projects database link --db # or the database linked to this directory clouzark projects database # show the link clouzark projects database unlink ``` `clouzark init` offers to link them when the directory has both. A variable you set yourself with the same name is used instead. Linking or unlinking applies from the next deployment. Resetting the database password or rotating its service key redeploys linked apps automatically, since their running deployments stop working with the old values. `clouzark env pull` includes the linked values, so local development gets them too. # Migrate from Vercel (https://docs.clouzark.com/hosting/import-vercel) `clouzark import vercel` reads your Vercel projects and recreates them on Clouzark: the GitHub repo, build settings, environment variables, custom domains, and cron jobs. It runs on your machine: your Vercel token is only sent to Vercel's API. ```bash export VERCEL_TOKEN='' clouzark import vercel --dry-run # pick projects, show the plan, change nothing clouzark import vercel # pick and import clouzark import vercel --team arius --project web --project admin -y ``` In the dashboard: **New Project → Import from Vercel**. Paste the token, pick the account or team and projects, review the plan (variable names only; values never reach the browser), and import. The token is used for that import only and isn't stored. The Clouzark GitHub app needs access to each project's repo. If it doesn't have it yet, the import says so; install it from **New Project**, then run the command again. ## What's copied [#whats-copied] | From Vercel | To Clouzark | |---|---| | GitHub repo, root directory, production branch | A new hosting project building the same repo | | Framework preset | Next.js, React (Vite, Create React App), static, or Node (Express, Hono, Fastify, NestJS) | | Install, build, and output commands | The same commands | | Environment variables for production and preview | [Environment variables](/hosting/environment-variables), saved before the first build | | Custom domains | Added to the project, with the DNS records to point at Clouzark when you're ready to switch | | `crons` in `vercel.json` | [Cron jobs](/hosting/cron-jobs) | The first production build starts as soon as each project is created. Your Vercel deployment keeps running until you move the DNS, so you can check the Clouzark build first. ## What isn't [#what-isnt] - Projects not connected to a GitHub repo, and frameworks Clouzark doesn't build yet (for example SvelteKit, Nuxt, Remix, Astro with SSR). The plan lists them. - Sensitive variables, whose values Vercel never reveals: add them with `clouzark env add`. - Development-only variables and ones scoped to a single preview branch. - Domains that redirect or are assigned to a branch. - Vercel-only services such as Edge Config, KV, Blob, and Vercel Postgres. To move a Supabase database too, see [Migrate from Supabase](/database/import-supabase). Cron endpoints should check `Authorization: Bearer $CRON_SECRET`; set `CRON_SECRET` with `clouzark env add` after the import. # Introduction (https://docs.clouzark.com) Clouzark has two services, managed from one dashboard, CLI, and API: - **Web hosting.** Connect a GitHub repo and every push builds and deploys: the production branch goes to production, other branches and pull requests get preview URLs. Next.js, React (Vite), Node servers, and static sites are detected automatically. - **Databases.** Managed Postgres with an SQL editor, migrations, generated TypeScript types, and APIs your app can call directly: REST, GraphQL, end-user auth, file storage, and realtime. A team (organization) owns both kinds of project. Members get one of three roles: | Role | Can | |---|---| | Viewer | Read projects, deployments, logs, and data | | Member | Everything a viewer can, plus deploy, run SQL, and apply migrations | | Owner | Everything, including environment variables, domains, members, and deleting databases | # Management API (https://docs.clouzark.com/management-api) The CLI and MCP server are built on this API; anything they do, you can do with plain HTTP. ``` https://api.clouzark.com/v1 ``` ## Authentication [#authentication] Send a team access token, created under the team's **Settings → Access Tokens** (or by `clouzark login`): ```bash curl https://api.clouzark.com/v1/me -H "Authorization: Bearer cz_pat_..." ``` A token acts with its creator's current role in the team. Requests and responses are JSON, errors are `{ "error": "..." }`, and each token can make 120 requests a minute. Wherever a path has `:ref`, pass a project's slug or id. ## Account [#account] | Request | | |---|---| | `GET /me` | User, team, role, and token expiry | | `DELETE /tokens/current` | Revoke the calling token | ## Hosting projects [#hosting-projects] | Request | | |---|---| | `GET /projects` | List projects | | `GET /projects/:ref` | One project, including `runtime_memory_mb` (its instance size, `null` for the platform default) and the resulting `runtime_limits` | | `PATCH /projects/:ref` `{ runtime_memory_mb }` | Set the [instance size](/hosting/deployments#instance-size): `512`, `1024`, `2048`, `4096`, or `null`. Owner role. Returns `{ project, runtime_limits_applied: { updated, skipped } }` | | `POST /projects` `{ repo, name?, root_directory?, framework?, production_branch?, install_command?, build_command?, output_directory?, env_vars? }` | Import a GitHub repo (`owner/name`) and queue its first production build; returns `{ project, deployment_id }`. `env_vars` (`[{ key, value, target }]`) are saved before that build | | `GET /projects/:ref/deployments?limit=20` | Recent deployments | | `POST /projects/:ref/deployments` `{ branch?, skip_build_cache? }` | Build a branch's latest pushed commit (the production branch by default) | | `POST /projects/:ref/deployments/upload?environment=preview&skip_build_cache=false` | Build an uploaded `.tar.gz` of the project (`Content-Type: application/gzip`) | | `POST /projects/:ref/rollback` | Roll production back | | `POST /projects/:ref/repository/sync` | Re-read the repo from GitHub and follow a rename or transfer to its current URL. A project ref (and app URL) made from the old repo name moves to one made from the new name, and the old app URL redirects to it. Deployer role. Returns `{ project, changed, previous_repo_url, previous_ref, previous_url }` | | `GET /deployments/:id?log_offset=0` | Deployment status, plus build log from `log_offset` | | `POST /deployments/:id/promote` | Promote to production | | `POST /deployments/:id/redeploy` `{ skip_build_cache? }` | Rebuild; `skip_build_cache: true` builds without the [build cache](/hosting/deployments#build-cache) | | `POST /deployments/:id/cancel` | Cancel a queued deployment | A deployment's `status` is one of `queued`, `building`, `deploying`, `live`, `failed`, `canceled`, or `stopped`; `done` is true once it stops changing. To follow a build, poll `GET /deployments/:id` and pass the previous `log_length` as `log_offset`. ### Environment variables [#environment-variables] | Request | | |---|---| | `GET /projects/:ref/env` | Names and targets | | `GET /projects/:ref/env/values?environment=preview` | Resolved values for one environment | | `POST /projects/:ref/env` `{ key, value, target }` | Add or update | | `DELETE /projects/:ref/env/:key?target=all` | Remove | | `GET /projects/:ref/database` | The [linked database](/hosting/environment-variables#linked-database) (`{ database: { id, name, ref } \| null, env_keys }`) | | `PATCH /projects/:ref/database` `{ database }` | Link a database by ref or id, or unlink with `null`; needs the deployer role. Applies from the next deployment | ### Domains [#domains] | Request | | |---|---| | `GET /projects/:ref/domains` | Domains, with the DNS records each needs | | `POST /projects/:ref/domains` `{ hostname }` | Add | | `POST /projects/:ref/domains/:hostname/verify` | Check DNS | | `DELETE /projects/:ref/domains/:hostname` | Remove | ### App cron jobs [#app-cron-jobs] [Cron jobs](/hosting/cron-jobs) are addressed by `:job`, the job's name or id. Creating, changing, deleting, and running jobs needs the deployer role. A job with `source: "config"` comes from `clouzark.json`: it only accepts `enabled`, and can't be deleted. | Request | | |---|---| | `GET /projects/:ref/cron-jobs` | Jobs, each with `source` (`config` or `api`), `next_run_at`, and `last_run` | | `GET /projects/:ref/cron-jobs/:job` | One job | | `POST /projects/:ref/cron-jobs` `{ name, path, schedule, method?, timezone?, timeout_seconds?, retries?, enabled? }` | Add a job. `method` defaults to `GET`, `timezone` to `UTC`, `timeout_seconds` to 60 (at most 300), `retries` to 0 (at most 3). Returns `{ cron_job }` | | `PATCH /projects/:ref/cron-jobs/:job` `{ name?, path?, schedule?, method?, timezone?, timeout_seconds?, retries?, enabled? }` | Change only the fields sent; `enabled: false` pauses the job | | `DELETE /projects/:ref/cron-jobs/:job` | Delete the job and its runs | | `POST /projects/:ref/cron-jobs/:job/run` | Call the path now and wait; returns `{ run: { id, status, status_code, attempts, duration_ms, message } }` | | `GET /projects/:ref/cron-jobs/:job/runs?limit=20` | Recent runs, newest first | ## Database projects [#database-projects] | Request | | |---|---| | `GET /database-projects` | List databases | | `GET /database-projects/:ref` | One database | | `POST /database-projects` `{ name }` | Create | | `DELETE /database-projects/:ref` | Delete permanently | | `POST /database-projects/:ref/query` `{ sql, read_only? }` | Run SQL; returns `{ command, row_count, fields, rows, duration_ms }` | | `GET /database-projects/:ref/migrations` | Applied migrations | | `POST /database-projects/:ref/migrations` `{ version, name, sql }` | Apply a migration. `version` is `YYYYMMDDHHMMSS`; re-sending the same SQL is a no-op, different SQL for an applied version is a 409 | | `GET /database-projects/:ref/types/typescript?schemas=public,career` | `{ types }`; `schemas` defaults to `public`, and an unknown schema returns 404 with `available_schemas` | | `GET /database-projects/:ref/keys` | `{ rest_url, anon_key, service_key }` | | `GET /database-projects/:ref/credentials` | Host, port, database, username, password, `connection_string` (direct), and `pooled_connection_string` (the connection pooler; null if unavailable) | ### Database cron jobs [#database-cron-jobs] [Cron jobs](/database/cron) are addressed by `:job`, the job's name or id. Creating, changing, deleting, and running jobs needs the deployer role. | Request | | |---|---| | `GET /database-projects/:ref/cron-jobs` | Jobs, each with `next_run_at` and `last_run` | | `GET /database-projects/:ref/cron-jobs/:job` | One job | | `POST /database-projects/:ref/cron-jobs` `{ name, schedule, command, timezone?, timeout_seconds?, enabled? }` | Add a job. `schedule` is 5-field cron; `timezone` defaults to `UTC`, `timeout_seconds` to 60 (at most 300). Returns `{ cron_job }` | | `PATCH /database-projects/:ref/cron-jobs/:job` `{ name?, schedule?, command?, timezone?, timeout_seconds?, enabled? }` | Change only the fields sent; `enabled: false` pauses the job | | `DELETE /database-projects/:ref/cron-jobs/:job` | Delete the job and its runs | | `POST /database-projects/:ref/cron-jobs/:job/run` | Run now and wait; returns `{ run: { id, status, duration_ms, message } }` | | `GET /database-projects/:ref/cron-jobs/:job/runs?limit=20` | Recent runs, newest first | ### Database backups [#database-backups] [Backups](/database/overview#backups) are addressed by `:id`. Taking or downloading one needs the deployer role; restoring needs the owner role. | Request | Does | |---|---| | `GET /database-projects/:ref/backups` | `{ enabled, retention_days, backups, restores }`, newest first. Each backup has `kind` (`scheduled`, `manual`, `pre_restore`), `status`, and `size_bytes` | | `POST /database-projects/:ref/backups` | Back up now and wait; returns `{ backup }` (201, or 500 with the error if it failed) | | `GET /database-projects/:ref/backups/:id/download` | `{ url }`: a download link valid for 15 minutes | | `POST /database-projects/:ref/backups/:id/restore` | Start a restore (202, `{ restore }`). Poll `GET .../backups` until that restore's `status` is `succeeded` or `failed` | ### Import from Supabase [#import-from-supabase] The Supabase credentials are used for that request or import only and never stored. Needs the deployer role. | Request | Does | |---|---| | `POST /import/supabase/inspect` `{ connection_string, supabase_url?, service_key? }` | Dry run: `{ report }` with schemas, counts, warnings, and what won't carry over | | `POST /import/supabase` `{ name, connection_string, supabase_url?, service_key? }` | Create a database and start importing into it (202, `{ import_id }`) | | `GET /imports/:id` | `{ import: { status, step, report, error, database_ref } }`; poll until `status` is `succeeded` or `failed` | # Quickstart (https://docs.clouzark.com/quickstart) ### Create a project [#create-a-project] Sign in at [clouzark.com](https://clouzark.com) with GitHub, then create a project from one of your repositories. The first build starts right away. You can also create it from the terminal once the CLI is installed and logged in (the next two steps): run `clouzark projects create` inside a clone of the repo. ### Install the CLI [#install-the-cli] Clouzark's CLI needs Node.js 20 or later. ```bash npm i -g clouzark ``` Or run any command without installing, with `npx clouzark `. ### Log in [#log-in] ```bash clouzark login ``` The CLI shows a code and opens the browser. Approve it, pick the team, and the CLI is logged in for 90 days. In CI, use a token instead; see [Configuration](/cli#configuration). ### Link your repo [#link-your-repo] From the repo's directory: ```bash clouzark init ``` `init` links the directory to its hosting project and database (saved in `.clouzark/project.json`, which is git-ignored) and writes instructions for coding agents into `AGENTS.md`. To link only the hosting project, run `clouzark link`. ### Deploy [#deploy] ```bash clouzark deploy # preview of the current branch clouzark deploy --prod # production branch to production ``` The CLI streams the build log and prints the URL when the deployment is live. Pushing to GitHub deploys the same way, without the CLI.