Choicely × Supabase integration

Connect a Supabase project to a Choicely app and the app talks to your Supabase project directly — sign-in, database reads and writes, realtime. This page documents exactly what that connection asks for, what it stores, and how to revoke it.

Last updated: 16 August 2026 · Technical documentation for the Choicely × Supabase integration

Choicely builds and publishes native iOS and Android apps. If your data already lives in Supabase, you can connect that project to a Choicely app: the app then talks to your Supabase project directly for sign-in, database reads and writes, file uploads, and realtime updates. It is one of several services a Choicely app can connect to — the full list is on the integrations index.

This page documents what that connection actually does — what Choicely asks Supabase for, what it stores and where, what it refuses to accept, and how to revoke it. It describes the integration as it ships today, not a roadmap.

What connecting does

Connecting is an OAuth 2.0 authorization-code flow against the Supabase Management API, with PKCE (S256). In order:

  1. In Choicely Studio you choose Connect Supabase. You are redirected to Supabase, where you sign in and approve the connection. Choicely never sees your Supabase password.
  2. Choicely lists the projects on your Supabase account. With exactly one project it is selected automatically; otherwise you pick one. The project you pick is verified against your own project list before anything is stored, and the connect fails closed if it does not match.
  3. Choicely reads that project's URL and its publishable (anon) API key and stores them as your app's client configuration. That pair is what your app needs in order to reach Supabase.
  4. Choicely runs one read-only query listing the tables and columns of your public schema, and saves the result as an ordinary, editable documentation page on your app titled “Supabase schema (auto-imported)”. The AI builder reads that page so the code it writes matches your real tables. You can edit or delete it at any time.

One Supabase project can be connected per Choicely app.

There is also a manual path, for teams who would rather not grant Management API access at all: paste the project URL and the publishable key yourself. It produces the same client configuration without steps 1, 2 and 4 — the schema import then runs in your own browser against your project, or from a SQL snippet you run yourself in the Supabase SQL editor.

What Choicely asks Supabase for, and why

Supabase fixes an integration's permissions when the OAuth application is registered, not per request — the scope parameter on the authorize URL is deprecated, and the consent screen renders the application's registered permissions. Choicely's Supabase application holds three, and those three are what the consent screen shows you:

  • Projects — Read (projects:read). Lists your projects so you can pick one: GET /v1/projects.
  • Secrets — Read (secrets:read). Reads the project's publishable (anon) key: GET /v1/projects/{ref}/api-keys.
  • Database — Read and Write (database:write). Runs the one schema-import query: POST /v1/projects/{ref}/database/query.

Every other permission Supabase offers — Auth, Storage, Edge Functions, Domains, Organizations, REST, Environment, Analytics — is registered as no access. There is no fourth call.

The third one deserves the plain version, because it is the broadest permission on the list: Choicely holds Supabase's Database: Write permission purely in order to run one read-only SELECT against information_schema. Supabase's Management API annotates its SQL endpoint database:write whatever the statement happens to be, and there is no read tier for that endpoint — so this is the smallest permission that can run the query at all.

The mitigation sits in the request rather than in the grant: that call sends read_only: true, so Postgres runs the session read-only and the write half of the permission is unusable by the only call that holds it. The query reads table names, column names, data types, nullability and key relationships. It reads no rows of your data, and it is the only statement Choicely's servers ever send to your database.

The permission set can only be changed in Supabase's own dashboard, and changing it forces every existing user to re-authorize. If schema import is ever made optional, the Database permission goes away entirely; that is the only route to a narrower consent screen, and it is the one we would take.

What Choicely stores, and where

  • Your project URL and publishable (anon) key. Stored as your app's client configuration and served to your app at runtime from a public, token-less endpoint (/apps/{app_key}/client-config/). This is deliberate: the publishable key is designed to be public, and your app fetches it in order to talk to Supabase directly. Read the Row Level Security section below before you ship.
  • The Management API refresh token. Server-side only. It is sealed with a per-connection data key that is itself wrapped by Google Cloud KMS; only the ciphertext is written to storage, and only Choicely's auth service can decrypt it. It is never returned by any Choicely API, never displayed in Studio, and never included in an app bundle.
  • The imported schema. An editable documentation page on your app. Structure only — no row data.

Your Supabase password, your secret keys and the contents of your tables are never stored by Choicely.

What Choicely refuses to accept

A Supabase secret key — an sb_secret_… key, or a legacy JWT whose role claim is service_role — bypasses Row Level Security completely. Choicely's backend hard-rejects both: a client configuration carrying one is refused at write time with an explicit error, so it can never be stored and never be served to an app. The check runs on the OAuth path and the manual-paste path alike, and the project URL must be a bare https origin with no path, query or credentials in it.

If you paste a secret key by mistake, the integration will not save. That is intended behaviour, not a bug.

What Choicely never writes

Choicely does not hold Supabase's Auth permission, and the integration never writes to your project's configuration. Nothing in it edits your auth settings, your policies, your schema or your data. The connection reads; that is the whole of it.

One consequence is worth stating plainly, because it is a step you own rather than one we perform. Redirect-based auth flows require the redirect URL to be on your project's allow-list, which lives in the Supabase dashboard under Authentication → URL Configuration → Redirect URLs. Choicely will not add an entry there on your behalf, now or later. We considered doing it automatically and decided against it: Supabase's allow-list is a single replace-the-whole-thing field, so an automatic edit would silently overwrite a change you had just made in the dashboard, and requesting the permission needed to write it would widen the consent screen for everyone. Today there is nothing for you to add — redirect-based sign-in is not shipped in the app template (see the limits above) — and this is documented in advance so the boundary is clear before it matters.

Row Level Security is your responsibility

This is the most important paragraph on this page.

The publishable key is delivered to your app and therefore lives on your users' phones. Anyone who installs your app can extract it. That is true of every Supabase mobile client, not just apps built with Choicely, and it is why the key is called publishable: it is not a secret and must not be treated as one. The only thing deciding what a holder of that key can read or write is your Row Level Security policy.

Choicely cannot write those policies for you and does not try to. Before you publish an app, enable RLS on every table the app touches and write policies for it; Supabase documents this in Row Level Security. A table with RLS switched off, behind a published app, is a public table.

What your app can do today

Apps built in Choicely reach Supabase through a maintained wrapper in the app template rather than raw SDK calls, so client construction, session storage and token refresh are handled for you. It supports:

  • Authentication — sign-up and sign-in with email and password, or with a one-time code sent by email. Sessions persist across app restarts and refresh automatically.
  • Database — reads and writes through the Supabase query builder, with the signed-in user's token attached, so every request is evaluated by your RLS policies.
  • Realtime — subscriptions to Postgres changes on tables you have added to a realtime publication in your Supabase project.
  • Storage — picking a photo or file on the device and uploading it to a Supabase Storage bucket in one call, then rendering it back from the returned URL, public or signed. Uploads are subject to your bucket's own policies, exactly as queries are subject to RLS.

The limits, stated plainly, as of the date at the top of this page:

  • No social sign-in and no magic links yet. Sign in with Apple, Sign in with Google and email magic links all need a deep link back into the native app. That is not shipped, so email and password or an emailed one-time code are the sign-in methods available today.
  • Schema import covers the public schema. Other schemas are not imported.
  • One Supabase project per app.

These limits are on this page because a documentation page that overstates a capability costs you a day of debugging. When they change, this page changes.

Where your users' data goes

Your app talks to your Supabase project directly. Queries, auth calls and realtime sockets go from the device to your project's own URL; they are not proxied through Choicely, and Choicely's servers are not in the path of your users' data. That is also why realtime subscriptions and per-user tokens behave exactly as they do in any other Supabase client.

Refreshing a connection

Refreshing rotates the Management API token and re-imports the schema for the same project. The project is pinned to the one you originally chose, so a refresh can never quietly move your app onto a different project.

Disconnecting and revoking access

In Studio, Disconnect Supabase removes the connection. Before anything is deleted locally, Choicely revokes the stored Management API refresh token at Supabase using OAuth 2.0 token revocation (RFC 7009) — so the grant stops working at Supabase's end, not merely at ours. You can also revoke the authorization yourself from your Supabase account's authorized applications, at any time, without telling us.

After a disconnect, the app's client configuration no longer carries the Supabase pair and the app's Supabase features report themselves as unconfigured rather than failing. Your Supabase project, its data and its keys are untouched.

Questions and security reports

For questions about this integration, contact us. To report a security issue, follow our Vulnerability Disclosure Policy; our security overview, sub-processors and data-processing terms are collected on the Trust page.

Trademarks

Supabase and the Supabase logo are trademarks of Supabase, Inc. Choicely is an independent product built by Choicely Oy, and is not affiliated with, endorsed by or sponsored by Supabase, Inc. The Supabase name is used on this page solely to describe interoperability with the Supabase service.