--- name: documenting-warehouse-sources description: Write or update the user-facing posthog.com documentation for a PostHog Data warehouse import source. Use when adding a new source doc, fixing an inconsistent or stub source doc, or standardizing the docs at contents/docs/cdp/sources. Covers the canonical template, shared snippets, the auto-rendered and components, frontmatter, and the docsUrl/slug rule that prevents 404s. --- # Documenting Data warehouse sources User-facing source docs live in the **posthog.com** repo (not this one) at `contents/docs/cdp/sources/.md`, served at both `/docs/cdp/sources/` and `/docs/data-warehouse/sources/`. This skill defines the one consistent shape every source doc must follow. Pair it with `/implementing-warehouse-sources` when shipping a new source. Assume a sibling posthog.com checkout (e.g. `../posthog.com`). ## The two things the website renders for you You do **not** hand-write connection fields or the table list — both come from the `public_source_configs` API the site fetches at build time, mirrored into the doc via MDX components: - `` renders the connection/config form fields from `get_source_config.fields`. - `` renders the **Supported tables** reference (table name, description, sync method, incremental field, primary key) from the source's `get_documented_tables()`. `` only has data when the source opts in by setting `lists_tables_without_credentials = True` on its source class (only valid when `get_schemas` iterates a **static** endpoint catalog with no I/O — see `/implementing-warehouse-sources`). Otherwise it renders a generic "discovered from your account" note. **If a table is missing or its description is thin, fix the source code** (`settings.py` endpoints + `canonical_descriptions.py`), not the doc — the doc just renders what the API returns, so the code stays the single source of truth. ## Frontmatter ```yaml --- title: Linking as a source sidebar: Docs showTitle: true availability: { free: full, selfServe: full, enterprise: full } sourceId: # MUST equal the ExternalDataSourceType value, e.g. ActiveCampaign, Stripe beta: true # optional — only for beta sources --- ``` `sourceId` is what links the doc to its API config (icon, fields, tables). Get it wrong and the doc renders with no `` / `` data. It must be a real `ExternalDataSourceType` value (PascalCase, e.g. `ActiveCampaign`, not `Active Campaign`). ## Canonical template ```text --- title: Linking as a source sidebar: Docs showTitle: true availability: { free: full, selfServe: full, enterprise: full } sourceId: --- import SourceSetupIntro from "../_snippets/source-setup-intro.mdx" import SyncModes from "../_snippets/sync-modes.mdx" import TroubleshootingLink from "../_snippets/dw-troubleshooting-link.mdx" One or two sentences: what this connector syncs and the typical use case. ## Prerequisites Account tier / admin rights / API access the user needs before they can connect. ## Adding a data source List the specific credentials this source needs and exactly where to get them (link to the provider's dashboard). For sources with more than one auth method, use `###` subsections (mirror Stripe's "Option 1 / Option 2"). ## Sync modes Add any source-specific recommendation here (e.g. "use webhooks for Stripe"). ## Configuration ## Supported tables ## Troubleshooting Source-specific errors and fixes (optional but encouraged), then: ``` ### Essential sections (every source) Status callout (alpha/beta only) → intro → Prerequisites → Adding a data source → Sync modes → Configuration → Supported tables → Troubleshooting. ### Optional sections (when applicable) Webhooks (real-time sync), CDC (databases), Column selection, Row filters, Inbound IP addresses (``), data-type handling, known limitations, ERD/relationships. Reference implementations already in the repo: **Stripe** (SaaS + webhooks), **Postgres** (database + CDC), **ClickHouse** (database). Don't invent sections the source doesn't need. ## Shared snippets Reuse these instead of re-writing the same prose (they live in `contents/docs/cdp/_snippets/`): - `source-setup-intro.mdx` — the standard "Adding a data source" steps. - `sync-modes.mdx` — sync-mode summary linking to the canonical explanation. - `alpha-release.mdx` / `beta-release.mdx` — status callouts (also set `beta: true` in frontmatter). - `dw-troubleshooting-link.mdx` — the troubleshooting/support footer. - `inbound-ip-addresses.mdx` — IP allowlist table for DB sources. - `feedback-questions.mdx` — feedback/FAQ footer. `.md` source docs support MDX `import` (e.g. `convex.md`, `mongodb.md`), so you don't need to rename to `.mdx` to use snippets — but `.mdx` is fine too. `CalloutBox`, `ProductScreenshot`, `SourceParameters`, and `SourceTables` are global components — no import needed. ## docsUrl / slug rule (prevents 404s) The website derives the doc slug from the source's `docsUrl` (its last `/docs/cdp/sources/` segment), so these three must agree: 1. The doc **filename**: `.md`. 2. The source's `docsUrl` in `get_source_config`: `https://posthog.com/docs/cdp/sources/`. 3. (implicitly) the listing link — now derived from `docsUrl`, so it follows automatically. Use kebab-case for multi-word slugs (`active-campaign`, not `activecampaign`). After writing or renaming a doc, run the audit from this (posthog) repo: ```sh python manage.py audit_source_docs --docs-dir ../posthog.com/contents/docs/cdp/sources ``` It fails if any source `docsUrl` points at a missing file or any doc's `sourceId` isn't a real source. Renaming a published doc also needs a 301 in `posthog.com/vercel.json` for both `/docs/cdp/sources/*` and `/docs/data-warehouse/sources/*`. ## Checklist - [ ] Frontmatter `sourceId` matches the `ExternalDataSourceType` value exactly - [ ] Intro, Prerequisites, Adding a data source, Sync modes, Configuration, Supported tables, Troubleshooting - [ ] Status snippet + `beta: true` if alpha/beta - [ ] Shared snippets used instead of bespoke prose - [ ] `` and `` present (don't hand-write fields or the table list) - [ ] If the rendered table list is empty/thin and the source is fixed-schema, enrich its code (`lists_tables_without_credentials`, `settings.py`, `canonical_descriptions.py`) — see `/implementing-warehouse-sources` - [ ] Filename, `docsUrl`, and slug all agree (kebab-case) - [ ] `audit_source_docs` passes