What this covers
CM Cloud provisions PocketBase v0.23 or newer. In v0.23 the old admin API was merged into a built-in _superusers collection, so the admin login endpoint moved. Apps written against older PocketBase (v0.22 and earlier) that call /api/admins/auth-with-password receive a 404 Not Found and cannot authenticate, so any setup or migration code that creates collections never runs. Regular end-user and custom-collection auth endpoints are unchanged. The same v0.23 update also changed the collection-creation API: collection fields moved from the old "schema" array to a "fields" array, and field properties are now flattened (no nested "options" object). Code that still posts a "schema" payload creates a collection with no fields, so any index referencing those columns fails with a 400 "no such column" error. You can seed collections two ways: import a full snapshot in one call with PUT /api/collections/import (the same JSON the admin dashboard exports under Settings → Import collections), or create them individually with POST /api/collections. Run whichever you choose from your app's startup code so it re-executes automatically on every redeploy.
Before you start
Know your PocketBase URL (for example https://pocketbase.yourdomain.com), your superuser email and password (created at /_/ the first time you open the dashboard), and which PocketBase version your client library targets. Set the PocketBase URL and credentials as environment variables in the deploy form instead of hard-coding them.
How to do it
- Recommended shape: build your API for one of the code runtimes (node, python_fastapi, laravel/PHP, java) and keep PocketBase as a separate backend service. Do not try to run your API inside PocketBase — it builds and runs no code of yours, and the two-service shape is what support assumes when you ask for help.
- Email for your users comes from PocketBase once SMTP is on: verification, one-time-password login and 'forgot password' are all sent by the backend, so your API never needs a mail provider of its own. Turn it on from the Password-reset email card on your PocketBase service page.
- ⚠️ The reset link points at the backend by default. PocketBase builds it from its own appURL, which is your Pocketbase Backend service's domain — so your users land on PocketBase's reset page, not inside your app. It works, but if you want the reset to finish in your own UI, edit the template under Settings → Mail in /_/ to point at your app's route and have your frontend submit the token with confirmPasswordReset.
- Confirm the version: open https://your-pocketbase-domain/api/health — CM Cloud runs PocketBase v0.23 or newer.
- For admin/superuser login call POST /api/collections/_superusers/auth-with-password — NOT the old /api/admins/auth-with-password, which returns 404 on v0.23+.
- Send the body {"identity": "<superuser-email>", "password": "<superuser-password>"} and read the returned token from the response.
- For regular end-user login keep using /api/collections/users/auth-with-password — that endpoint is unchanged.
- For any custom auth collection (for example staff) use /api/collections/<collection>/auth-with-password.
- Store your PocketBase URL and superuser credentials as environment variables in the deploy form (commonly POCKETBASE_URL, POCKETBASE_ADMIN_EMAIL, POCKETBASE_ADMIN_PASSWORD). Environment variables persist across redeploys and are never committed to your repository.
- If you use an official PocketBase SDK, upgrade it to a version that targets v0.23+ — recent SDKs call the superuser endpoint when you use pb.collection('_superusers').authWithPassword(...).
- Redeploy your app and check the runtime logs: a successful superuser login means your setup/migration code that creates collections will run.
- When creating collections via POST /api/collections, use the v0.23 format: send fields under "fields" (not "schema"), and put properties like maxSelect, values, min, max, pattern, collectionId, and cascadeDelete directly on each field — not nested under an "options" object.
- When reading or patching an existing collection (for example adding fields to the built-in users collection), read its field list from "fields", not "schema".
- If a collection create returns 400 with "no such column" on an index, your fields were sent in the old "schema" format and were ignored — switch the payload to "fields" so the columns exist before the index is built.
- v0.23 no longer adds created/updated system fields automatically. If your app sorts or filters by created (or updated), declare them explicitly as autodate fields in the collection's fields array — for example {"name": "created", "type": "autodate", "onCreate": true, "onUpdate": false} and {"name": "updated", "type": "autodate", "onCreate": true, "onUpdate": true}. Otherwise list queries using created return 400 Bad Request even though the collection exists.
- Adding a field to a collection definition does not change collections that already exist — typical setup code skips collections that are already present ("if it exists, skip"). So if you add fields (like the created/updated autodate fields) after a collection was already created, the new fields are never applied and queries still fail. Fix it one of three ways: delete the existing collection in the admin dashboard at /_/ and let your setup recreate it (only safe if it has no data you need), add the fields manually at /_/, or make your setup PATCH existing collections to add missing fields (the same approach used for the built-in users collection).
- The most reliable place to run collection setup is your app's startup hook — FastAPI lifespan, before app.listen() in Express, AppConfig.ready() in Django, or boot() in Laravel. CM Cloud restarts your app on every deploy, so startup code runs automatically on each deploy. Wrap it in try/except (or try/catch) so a transient PocketBase hiccup logs an error instead of crashing the boot — the next deploy retries.
- If you already have a collections snapshot (admin dashboard → Settings → Import collections shows the JSON), you can import the whole snapshot in one request instead of coding each collection by hand. Commit that exported JSON to your repository, for example as pb_schema.json.
- After the superuser login (step 2), send PUT /api/collections/import with the body {"collections": <your exported array>, "deleteMissing": false}. This is additive and idempotent — it creates or updates the collections in the file and runs safely on every deploy without touching existing records.
- Keep deleteMissing set to false unless you deliberately want the live database to become an exact mirror of the file. deleteMissing: true DELETES any collection (and all its records) that is not present in the file — only use it when that is exactly what you intend.
- Redeploy and check the runtime logs: you should see the import (or per-collection setup) succeed once per boot. Because the operation is idempotent, your existing data is preserved on every subsequent deploy.
What our team checks
Support can confirm the PocketBase version, that the admin dashboard at /_/ is reachable, and that a superuser account exists. Support does not edit your application code or change your SDK version.
Next step
Update your app's admin login call to /api/collections/_superusers/auth-with-password, set POCKETBASE_URL and the superuser credentials as environment variables, then redeploy.