## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## Problem
The Docs E2E link checker found broken links throughout docs, starting
with `phone-login.mdx` pointing to `/docs/guides/cli/config` (404).
Old links like `/docs/guides/cli/config` still work on the live site
because `supabase.com` has redirects set up for them, but these links
break on the docs preview site, which is what the E2E check tests
against. These issues look clean on the live site, and I didn't catch
them in my first pass because I was testing production instead of the
preview.
The E2E check only tests the ~20 pages a given PR happens to touch, so
fixing the pages it flagged kept exposing more of the same problem one
page at a time as each fix pulled in a new file. To stop chasing this
incrementally, I cross-referenced every `/docs/guides/*` and
`/docs/reference/*` redirect source in `apps/www/lib/redirects.js`
against actual usage across all of `apps/docs`, and verified each
candidate against the live preview.
## Solution
Rather than updating the Docs E2E link checker, this PR resolves the
links. **Why:** we own these docs, so keeping the links clean without
redirects is keeping the house maintained. See [Broken Window
Theory](https://blog.codinghorror.com/the-broken-window-theory/).
Updated every link still using an old path to point straight at the
current page instead of relying on a redirect. This covers old links
like:
- `/docs/guides/cli/config` →
`/docs/guides/local-development/cli/config`
- `/docs/guides/cli/getting-started` →
`/docs/guides/local-development/cli/getting-started`
- `/docs/guides/cli/local-development` →
`/docs/guides/local-development/database-migrations`
- `/docs/guides/cli/managing-environments` →
`/docs/guides/deployment/managing-environments`
- `/docs/guides/cli/seeding-your-database` →
`/docs/guides/local-development/seeding-your-database`
- bare `/docs/guides/cli` → `/docs/guides/local-development`
- `/docs/guides/platform/compute-add-ons` →
`/docs/guides/platform/compute-and-disk`
- `/docs/guides/platform/shared-responsibility-model` →
`/docs/guides/deployment/shared-responsibility-model`
- `/docs/guides/database` → `/docs/guides/database/overview`
- `/docs/reference/javascript`, `/docs/reference/dart`,
`/docs/reference/kotlin`, `/docs/reference/python`,
`/docs/reference/csharp` → their `/introduction` pages (the redirect's
own destination, `/start`, turned out to be dead even on production — a
separate bug in `redirects.js` I didn't touch here)
- and about 35 more of the same pattern, listed in the commit messages
Also fixed a handful of dead heading anchors found along the way (links
that resolve to the right page but point at a `#section` that got
renamed or moved), including the original `#bigquery` anchor and a few
in `connecting-to-postgres.mdx` where content moved to its own dedicated
page.
Left alone on purpose:
- `content/guides/cli.mdx` — this page has no route in the docs app at
all (no `app/guides/cli/` directory), so it 404s even in production
before the `www` redirect ever fires. Fixing its internal link wouldn't
change that; it needs an actual routing/content decision, not a link
fix.
- A few candidates that already resolve fine as-is (`pg_partman`, bare
`/docs/reference/api`, bare `/docs/reference/cli`) — confirmed via curl,
left untouched.
## Manual testing
1. Confirmed every new link target actually exists by checking the
destination file/page and matching heading anchors.
2. Cross-referenced every `/docs/guides/*` and `/docs/reference/*`
redirect source in `apps/www/lib/redirects.js` against real usage in
`apps/docs`, and curl-verified each old path (404) and new path (200)
against the live PR preview before fixing it.
3. Ran the Docs E2E link checker locally against changed pages.
4. Spot-checked the original broken link from CI
(`/docs/guides/cli/config`) to confirm it now points to a working page.
## Description
Replaces legacy `anon` / `service_role` key references with the new
`publishable` / `secret` naming across SDK spec files under
`apps/docs/spec/`. Mirrors the rename table established by
[supabase-js#2280](https://github.com/supabase/supabase-js/pull/2280),
so the auto-generated reference docs at
supabase.com/docs/reference/{js,dart,kotlin,python,swift} stay
consistent with the SDK source.
## Files changed
| File | Highlights |
| --- | --- |
| `supabase_js_v2.yml` | Rename example vars `anon_key` →
`publishable_key`, `service_role_key` → `secret_key`; admin notes use
`secret`. |
| `supabase_dart_v2.yml` | Rename `anonKey` param → `publishableKey`
(matches the live Flutter SDK — see
[supabase-flutter/supabase.dart#L81](https://github.com/supabase/supabase-flutter/blob/main/packages/supabase_flutter/lib/src/supabase.dart#L81));
update call-site; example var `serviceRoleKey` → `secretKey`; admin
notes use `secret`. |
| `supabase_kt_v2.yml`, `supabase_kt_v3.yml` | Replace
`'publishable-or-anon-key'` placeholders with `'your-publishable-key'`;
admin notes use `secret`; fix `importAuthToken(\"service_role\")` →
`importAuthToken(\"your-secret-key\")` (the Kotlin SDK's
`importAuthToken(accessToken: String, …)` takes a JWT, not a role name).
|
| `supabase_py_v2.yml` | All `service_role` references → `secret`;
example var `service_role_key` → `secret_key`. |
| `supabase_swift_v2.yml` | Replace placeholders with
`'your-publishable-key'`; admin notes use `secret`; example var
`serviceRoleKey` → `secretKey`. |
| `storage_v0_config.yaml` | Rewrite `ANON_KEY` / `SERVICE_KEY` env var
**descriptions** in publishable/secret terms. **Env var names kept** —
see below. |
## Why the storage env var names are unchanged
`storage_v0_config.yaml` documents the env vars used to configure the
storage server (multi-tenant deployments). The `id` and `title` fields
must match the actual env var names the binary reads. The storage server
still reads its config from env vars literally named `ANON_KEY` and
`SERVICE_KEY`:
-
[`storage/src/config.ts#L614`](https://github.com/supabase/storage/blob/master/src/config.ts#L614)
— `getOptionalConfigFromEnv('SERVICE_KEY')`
-
[`storage/src/config.ts#L625`](https://github.com/supabase/storage/blob/master/src/config.ts#L625)
— `getOptionalConfigFromEnv('ANON_KEY')`
There is no `PUBLISHABLE_KEY` / `SECRET_KEY` reader on master. Renaming
the doc IDs would break self-hosted deployments — users following the
docs would set the wrong env var and the storage server would silently
fall back to generating its own JWT. Until the storage server itself
adds publishable/secret env support, only descriptions can be updated.
(Same reasoning for not bumping `storage_v0_config.yaml` →
`storage_v1_config.yaml`: the `_v0_` tracks the storage server's own API
version, paired with `storage_v0_openapi.json` downloaded from
`supabase.github.io/storage/api.json`. The server hasn't shipped a v1
API.)
## What we deliberately did NOT rename
Per the same rules established in supabase-js#2280:
- **JWT role claims** like `role: 'anon'` / `role: 'service_role'` —
these are functional Postgres role names in JWT payloads, not key
labels.
- **Real SDK identifiers** that haven't been renamed in the source (we
only rename in the doc when the underlying SDK rename has shipped). The
Dart `anonKey` rename was safe to apply because the Flutter SDK already
ships `publishableKey` as the preferred named parameter (with `anonKey`
`@Deprecated`).
## Out of scope
- **All `*_v1.yml` SDK spec files** (`supabase_js_v1`,
`supabase_dart_v1`, `supabase_kt_v1`, `supabase_swift_v1`). Older SDK
versions, not worth churning.
- **`cli_v1_commands.yaml`**. This file is auto-generated by the CLI
repo's release workflow
([`cli/tools/bumpdoc/main.go`](https://github.com/supabase/cli/blob/develop/tools/bumpdoc/main.go))
and the example outputs come from
[`cli/docs/templates/examples.yaml`](https://github.com/supabase/cli/blob/develop/docs/templates/examples.yaml)
— embedded in the CLI binary at build time. Any edits we make here would
be clobbered by the next CLI release. The fix needs to land upstream in
the CLI repo (note: the CLI itself already ships publishable/secret
naming in \`supabase status\` output — see
[`cli/internal/status/status.go#L40-L44`](https://github.com/supabase/cli/blob/develop/internal/status/status.go#L40-L44)
— but the doc-generation template is stale).
- **`supabase_csharp_v0.yml` / `supabase_csharp_v1.yml`** — checked,
already neutral. Both files use a generic `SUPABASE_KEY` env var with no
`anon` / `service_role` references.
- **Renaming the legacy Dart `anonKey` parameter itself** — that's an
SDK-side change. The Flutter SDK already exposes `publishableKey` as the
preferred parameter; full removal of `anonKey` will happen in a future
major version per the SDK's own deprecation comment.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Standardized API key naming across SDK docs: use publishable (client)
and secret (server/admin) key terminology
* Updated server-side admin examples and warnings to require secret keys
and emphasize never exposing them in client code
* Unified initialization examples across JavaScript, Dart, Kotlin,
Python, and Swift
* Corrected Storage spec metadata to point to the proper configuration
file
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
This PR updates documentation based on recent changes across multiple
SDK repositories since the last run on 2026-01-22.
## Changes Analyzed
| SDK | Repository | Commits | Latest Tag |
|-----|-----------|---------|------------|
| **js** | supabase/supabase-js | 40 | v2.95.4-canary.2 |
| **dart** | supabase/supabase-flutter | 4 | - |
| **py** | supabase/supabase-py | 12 | v2.28.0 |
| **swift** | supabase/supabase-swift | 14 | v2.41.1 |
| **kt** | supabase-community/supabase-kt | 36 | 3.3.0 |
| **csharp** | supabase-community/supabase-csharp | 1 | v1.1.2 |
## Documentation Updates
### JavaScript SDK (`supabase_js_v2.yml`)
- Added optional `jwt` parameter documentation to
`mfa.getAuthenticatorAssuranceLevel()`
- Added `timeout` and `urlLengthLimit` options to `PostgrestClient`
constructor with example
### Kotlin SDK (`supabase_kt_v3.yml`)
- Added new `getClaims()` API section with description, parameters, and
3 examples
- Added `channel` parameter to OTP `signInWith` config for WhatsApp
support
- Added WhatsApp OTP sign-in example
### Python SDK (`supabase_py_v2.yml`)
- Added `from_.list_v2()` method documentation with cursor-based
pagination support
- Includes `SearchV2Options` parameter documentation and pagination
example
- Added note about new User model fields (`is_sso_user`, `deleted_at`,
`banned_until`) on `get_user`
### Swift SDK (`supabase_swift_v2.yml`)
- Added breaking change note for `mfa.unenroll()`: response now uses
`id` instead of `factorId` (since v2.41.1)
### Phone Login Guide (`phone-login.mdx`)
- Added Kotlin WhatsApp OTP example to the sign-in section
### SDKs with no documentation updates needed
- **Dart**: Only CI and realtime type-cast fix (no user-facing API
changes)
- **C#**: Only README badge fix
## Test plan
- [ ] Verify YAML spec files parse correctly
- [ ] Review rendered documentation for new sections
- [ ] Confirm code examples match actual SDK APIs
---
Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Updated the email update note in the docs to explain that confirmation emails are still sent due to the Secure Email Change setting. Added a direct link to the relevant dashboard setting to help users better understand and manage the behavior.
Adds a note explaining that the `data` field maps to `raw_user_meta_data` in the database,
and is returned as `user_metadata` when calling `getUser()`.
* Move the spec/parser package into packages folder.
* Fix the spec generation with the new paths.
* Remove the spec folder from prettierignore.
* Run prettier on the spec folder (without regenerating, that's should be done by the product teams).
Migrates client SDK References to App Router. (Management and CLI API references aren't migrated yet, nor are self-hosting config references.)
Some notes:
Big changes to the way crawler pages are built and individual section URLs (e.g., javascript/select) are served. All of these used to be SSG-generated pages, but the number of heavy pages was just too much to handle -- slow as molasses and my laptop sounded like it was taking off, and CI sometimes refuses to build it all at all.
Tried various tricks with caching and pre-generating data but no dice.
So I changed to only building one copy of each SDK+version page, then serving the sub-URLs through a response rewrite. That's for the actual user-visible pages.
For the bot pages, each sub-URL needs to be its own page, but prebuilding it doesn't work, and rendering on demand from React components is too slow (looking for super-fast response here for SEO). Instead I changed to using an API route that serves very minimal, hand-crafted HTML. It looks ugly, but it's purely for the search bots.
You can test what bots see by running curl --user-agent "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" <URL_OF_PAGE>
Also added some smoke tests to run against prod for the crawler routes, since we don't keep an eye on those regularly, and Vercel config changes could surprise-break them. Tested the meta images on Open Graph and all seems to work fine.
With this approach, full production builds are really fast: ~5 minutes
Starts using the new type spec handling, which is better at finding params automatically, so I could remove some of the manually written ones from the spec files.
Our syntax highlighter doesn't recognize HTML (tried to install it but it's not
on the lsit of supported languages) and this somehow causes the server and
client to make different highlighting decisions, with the server attempting to
highlight the code block and the client not.
Removing the html language specifier removes the hydration error (and since the
client never properly highlighted it anyway, we don't lose much in terms of
syntax highlighting.)