[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-posthog-setting-up-a-data-warehouse-source":3,"mdc-eobmp0-key":44,"related-repo-posthog-setting-up-a-data-warehouse-source":3071,"related-org-posthog-setting-up-a-data-warehouse-source":3193},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":29,"repoUrl":30,"updatedAt":31,"license":32,"forks":33,"topics":34,"repo":39,"sourceUrl":42,"mdContent":43},"setting-up-a-data-warehouse-source","connect new data warehouse sources","Guide the user through connecting a new data warehouse source — Postgres, MySQL, Stripe, Hubspot, MongoDB, Salesforce, BigQuery, Snowflake, and so on. Use when the user wants to \"connect Stripe\", \"import data from Postgres\", \"add a new data source\", \"sync my warehouse tables\", or wants to pick sync methods for each table. Walks through source-type discovery, credential validation, table discovery, per-table sync_type selection, and the final create call. Also covers picking a good prefix and what to do right after creation.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"posthog","PostHog","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fposthog.png",[12,14,17,20,23,26],{"name":9,"slug":8,"type":13},"tag",{"name":15,"slug":16,"type":13},"Data Engineering","data-engineering",{"name":18,"slug":19,"type":13},"Integrations","integrations",{"name":21,"slug":22,"type":13},"Data Warehouse","data-warehouse",{"name":24,"slug":25,"type":13},"ETL","etl",{"name":27,"slug":28,"type":13},"Database","database",59,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fai-plugin","2026-06-18T08:19:34.799226",null,11,[35,36,37,38],"claude-code-plugin","codex-plugin","cursor-plugin","gemini-cli-extension",{"repoUrl":30,"stars":29,"forks":33,"topics":40,"description":41},[35,36,37,38],"Official PostHog plugin for Claude Code, Cursor, Gemini, Codex and other AI coding tools","https:\u002F\u002Fgithub.com\u002FPostHog\u002Fai-plugin\u002Ftree\u002FHEAD\u002Fskills\u002Fsetting-up-a-data-warehouse-source","---\nname: setting-up-a-data-warehouse-source\ndescription: >\n  Guide the user through connecting a new data warehouse source — Postgres, MySQL, Stripe, Hubspot, MongoDB,\n  Salesforce, BigQuery, Snowflake, and so on. Use when the user wants to \"connect Stripe\", \"import data from\n  Postgres\", \"add a new data source\", \"sync my warehouse tables\", or wants to pick sync methods for each table. Walks\n  through source-type discovery, credential validation, table discovery, per-table sync_type selection, and the\n  final create call. Also covers picking a good prefix and what to do right after creation.\n---\n\n# Setting up a data warehouse source\n\nUse this skill when the user wants to connect an external data source to PostHog's data warehouse for the first time.\n\n**Default to the one-step flow:** `data-warehouse-source-setup` validates credentials, discovers every table, enables\nthem with sensible sync defaults (incremental where possible), and creates the source in a single call — no\n`schemas` array to assemble. For credentials, hand the user a secure browser link with `data-warehouse-source-connect-link`\ninstead of collecting secrets in chat. Only drop to the manual `wizard → db-schema → create` flow when the user wants to\nhand-pick which tables sync or set non-default sync types per table.\n\n## When to use this skill\n\n- The user wants to connect a new source: \"connect Stripe\", \"import my Postgres orders table\", \"sync Hubspot contacts\"\n- The user isn't sure what source types PostHog supports\n- The user has credentials but doesn't know how to structure the `schemas` payload\n- The user wants guidance on which sync method to pick per table\n\n## Available tools\n\n| Tool                                                   | Purpose                                                                                                                   |\n| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |\n| `data-warehouse-source-connect-link`                   | **Preferred for credentials** — get a secure browser\u002FOAuth link so the user authenticates without pasting secrets in chat |\n| `data-warehouse-source-setup`                          | **Preferred to create** — one call: validate creds, discover tables, apply sync defaults, create the source               |\n| `external-data-sources-wizard`                         | Discover which source types exist and what fields each needs (advanced flow)                                              |\n| `external-data-sources-db-schema`                      | Validate credentials and list tables with available sync methods per table (advanced flow)                                |\n| `external-data-sources-create`                         | Advanced create — requires a `schemas` array built from the db-schema response                                            |\n| `external-data-sources-check-cdc-prerequisites-create` | Postgres CDC pre-flight check (optional, only for Postgres CDC)                                                           |\n| `external-data-sources-webhook-info-retrieve`          | Check if a source supports webhooks and whether one has been registered                                                   |\n| `external-data-sources-create-webhook-create`          | Register a webhook with the external service after source creation                                                        |\n| `external-data-sources-update-webhook-inputs-create`   | Supply the signing secret manually when auto-registration failed                                                          |\n| `external-data-sources-list`                           | After creation, confirm the source is listed and see its initial status                                                   |\n| `external-data-schemas-list`                           | See per-table sync status once the source is created                                                                      |\n\n## Pre-flight: credential gotchas that cause most failures\n\nSurface these **before** collecting credentials — they're the top reasons setup fails on the first try. Validating\nagainst them up front avoids burning credential prompts on retries.\n\n- **The host must be reachable from PostHog's network.** `localhost`, `127.0.0.1`, and private\u002FRFC-1918 hosts\n  (`10.x`, `192.168.x`, `172.16–31.x`) are rejected — PostHog runs the connection from its own infrastructure, not the\n  user's machine. Serverless\u002Fmanaged Postgres (Neon, Supabase, RDS behind strict rules) often also needs PostHog's\n  egress IPs allowlisted first. If the DB isn't publicly reachable, route to the browser deep-link\n  (`data-warehouse-source-connect-link`) or an SSH tunnel rather than collecting credentials that can't validate.\n- **Supabase is Postgres — don't collect it twice.** Use the **Session pooler** connection, not the direct host (the\n  direct host is IPv6-only). The pooler host looks like `aws-0-\u003Cregion>.pooler.supabase.com`, the **username** must be\n  `postgres.\u003Cproject-ref>`, and the **port is 6543** (not 5432). The password is the **database** password (Settings →\n  Database), which is distinct from the `anon`\u002F`service_role` JWT keys and from the Supabase account password. If\n  `SUPABASE_URL` is in the project env, derive the project ref from `db.\u003Cref>.supabase.co` to pre-fill these instead of\n  asking the user to guess.\n- **Many SaaS sources need a specific key type or plan** — get the right one before the create call fails:\n  - **Stripe** — a _restricted_ key (`rk_live_…`), not the standard secret key (`sk_live_…`).\n  - **RevenueCat** — a v2 secret key (`sk_…`) with the read scopes enabled.\n  - **Sentry** — an internal-integration token, not a DSN and not a personal auth token.\n  - **Convex** — requires the Professional plan.\n  - **Twilio** — API Key SID + Secret, not the account auth token.\n  - **Mailchimp** — the key carries its datacenter suffix (`key-usX`).\n  - For send-only services (Resend, Mailgun), the key already in the project env is often restricted; the warehouse\n    import needs a full\u002Fread-access key.\n- **Never pass an unresolved secret reference.** If a credential field is still a `{\"secretRef\": ...}` object, PostHog\n  can't resolve it — the create\u002Fdb-schema\u002Fsetup calls reject it with a clear error. Resolve it to the real value\n  first, or collect credentials via `data-warehouse-source-connect-link` and pass the resulting `credential_id`.\n\n## Recommended: one-step setup\n\nMost setups should use this path — it avoids the most common failures (skipping db-schema, malformed `schemas`,\nmissing required fields).\n\n1. **Discover the source type and its fields** (optional): `external-data-sources-wizard` lists every source type and\n   the credential fields each needs. Use it to know what to ask the user for; skip it if the source type is obvious.\n2. **Collect credentials securely**: call `data-warehouse-source-connect-link({source_type})`. It returns a\n   `connect_url` to a minimal connect page rendering the source's full connection form — the user authorizes via\n   OAuth or enters credentials there, whichever the source offers (the response's `auth_method` tells you which to\n   expect). The page validates the details against a live connection and stashes them encrypted in a temporary\n   store — it does NOT create the source. After the user confirms they're done, find the stored credential id via\n   `data-warehouse-stored-credentials-list` (filter by `source_type`, newest first; the page also shows the id to\n   the user) and pass `{\"credential_id\": \u003Cid>}` to setup. Stored credentials are single-use — deleted as soon as\n   setup consumes them — and expire after 24 hours.\n\n   Never ask the user to paste raw database passwords, API keys, or OAuth tokens into the chat.\n\n3. **Create in one call**: `data-warehouse-source-setup({source_type, payload, prefix})`. The server validates\n   credentials, discovers all tables, enables them with sync defaults (incremental where a tracking column exists,\n   else append, else full_refresh — never CDC), sets `created_via=mcp`, and creates the source. The `payload` carries\n   a credential reference (`{\"credential_id\": ...}` or the OAuth integration id key) — or inline credentials for\n   headless automation; no `schemas` array is needed. On success you get the new source `id`; call\n   `external-data-schemas-list` to show the user what was enabled and how each table will sync.\n\nNotes specific to this path:\n\n- **All discovered tables are enabled.** That's intended (incremental defaults keep ongoing cost low), but flag row\n  counts for very large tables and offer the advanced flow if the user wants to sync only a subset.\n- **Webhooks are auto-registered** for sources that support them (currently Stripe). Check the `webhook` key in the\n  setup response: on success, webhook-capable tables sync in real time and webhook-only tables (e.g. Stripe Discount)\n  are enabled too; on failure (e.g. the API key can't create webhooks), tables keep the polling defaults and\n  webhook-only tables stay disabled — relay the `webhook.error` to the user and offer Step 6 to register manually.\n  If `webhook.pending_inputs` is non-empty, collect those values and submit via\n  `external-data-sources-update-webhook-inputs-create`. CDC is never chosen automatically; use the advanced flow +\n  CDC steps for near-real-time Postgres.\n- Inline credentials in `payload` still work for headless\u002Fautomation, but prefer the connect-link handoff above.\n\n## Advanced: hand-pick tables (three-step flow)\n\nUse this when the user wants to choose exactly which tables sync or set non-default sync types. Don't try to shortcut\nto `external-data-sources-create` — you need the db-schema response to build a valid `schemas` payload.\n\n```text\n         ┌────────────────────┐\n         │ 1. wizard          │  What source types exist? What fields does each need?\n         └────────┬───────────┘\n                  ▼\n         ┌────────────────────┐\n         │ 2. db-schema       │  Validate creds. List tables + available sync methods per table.\n         └────────┬───────────┘\n                  ▼\n         ┌────────────────────┐\n         │ 3. create          │  Send source_type + credentials + schemas[] to actually create.\n         └────────────────────┘\n```\n\n## Workflow\n\n### Step 1 — Discover the source type\n\nCall `external-data-sources-wizard` **with `source_type` set to the kind(s) you need** (comma-separated, e.g.\n`Postgres,Stripe`). The unfiltered response describes every supported source and is hundreds of KB — large enough to\nblow your context budget. Only omit `source_type` when you genuinely need to enumerate every available type, and\nexpect a big payload if you do. The response is a dict keyed by source type. Each entry describes:\n\n- `name` — the canonical source_type string you'll pass to later calls (e.g. `\"Postgres\"`, `\"Stripe\"`, `\"Hubspot\"`).\n- `label` \u002F `caption` — human-readable.\n- `fields` — the config fields needed (host, port, database, api_key, client_id\u002Fsecret, ...). Each has `name`,\n  `type` (input, password, switch, select, file-upload), and `required`.\n- `featured`, `unreleasedSource` — use to gauge readiness. Skip sources marked `unreleasedSource: true` unless the\n  user explicitly asked for a preview.\n\nMatch the user's request to a source. If they said \"Postgres\", look up `Postgres`. If they said something ambiguous\nlike \"database\", present the top relevant matches (Postgres, MySQL, MongoDB, BigQuery, Snowflake, Redshift) and let\nthem pick.\n\nFor OAuth-based sources (Hubspot, Salesforce, Google Ads), the wizard entry hints at an OAuth flow. These typically\nneed the user to authorize in the PostHog UI rather than pasting credentials — explain this and direct them to the\nsource setup page rather than trying to collect tokens in chat. OAuth is about _authentication_, not about how data\nflows; OAuth sources still use polling bulk sync, not webhooks.\n\nGather the required credentials from the user. Never ask for more fields than the wizard entry says are required —\nasking for an unnecessary `port` when the source doesn't need one confuses users.\n\n### Step 2 — Validate credentials and discover tables\n\nCall `external-data-sources-db-schema` with `source_type` plus all credential fields. This does two things at once:\n\n1. Validates the credentials against the live source. Returns 400 with a `message` if anything is wrong (bad host,\n   wrong password, permission denied). Show the error verbatim — it's often actionable (\"password authentication\n   failed for user 'x'\").\n2. If valid, returns an array of table entries. Each entry:\n\n```text\n{\n  \"table\": \"orders\",\n  \"should_sync\": false,\n  \"rows\": 1_250_000,\n  \"incremental_available\": true,   # can do sync_type=incremental\n  \"append_available\": true,        # can do sync_type=append\n  \"cdc_available\": true,           # can do sync_type=cdc  (null = not enabled for team)\n  \"supports_webhooks\": false,      # can do sync_type=webhook for real-time push\n  \"incremental_fields\": [          # candidates: usually updated_at, created_at, id\n    {\"field\": \"updated_at\", \"type\": \"datetime\", \"label\": \"updated_at\", ...},\n    {\"field\": \"created_at\", \"type\": \"datetime\", ...},\n    {\"field\": \"id\", \"type\": \"integer\", ...}\n  ],\n  \"detected_primary_keys\": [\"id\"],\n  \"available_columns\": [{\"field\": \"id\", \"type\": \"integer\", \"nullable\": false}, ...],\n  \"description\": \"...\"\n}\n```\n\nPresent this to the user. Don't dump the raw JSON — summarize: which tables were found, row counts, and the default\nsync method recommendation per table (see [sync-type decision guide](.\u002Freferences\u002Fsync-types.md)).\n\n### Step 3 — Confirm per-table sync configuration\n\nFor each table the user wants to sync, pick a sync_type. See the\n[sync-type decision guide](.\u002Freferences\u002Fsync-types.md) for detailed rules, but the short version is:\n\n- **Small \u002F dimension tables (\u003C50k rows, no natural ordering column):** `full_refresh` — simple and always correct.\n- **Large tables with an `updated_at` \u002F `modified_at`:** `incremental` — much cheaper per sync.\n- **Append-only immutable tables (logs, events):** `append` if available — preserves history.\n- **Postgres with CDC enabled and you need near-real-time:** `cdc` — requires primary keys and Postgres prerequisites.\n- **Sources that support webhooks (currently Stripe):** for near-real-time ingestion set `sync_type: \"webhook\"` on\n  the tables where `supports_webhooks: true`, then register the webhook as a post-create step (see step 6 below).\n  Tables that don't support webhooks on the same source still need a bulk sync_type.\n\nFor each schema that will use `incremental`\u002F`append`\u002F`cdc`, you also need:\n\n- `incremental_field` — which column to track for high-water-mark ordering. Pick from the `incremental_fields` list\n  returned by db-schema. Prefer `updated_at` over `created_at` (updated_at catches late-arriving updates;\n  created_at misses them). For integer-only tables, use the monotonically increasing primary key.\n- `incremental_field_type` — must match the chosen field's type (`datetime`, `timestamp`, `date`, `integer`,\n  `numeric`, `objectid`).\n- `primary_key_columns` — required for CDC. Use `detected_primary_keys` from db-schema.\n\n### Step 4 — Pick a good prefix\n\nThe source's `prefix` is prepended to table names in HogQL. Tables end up as `{prefix}_{table_name}`.\n\n- Default to the source type lowercased if there's only one source of that type: `stripe`, `postgres`.\n- If the user already has a Postgres source, pick something distinguishing: `postgres_prod`, `postgres_analytics`.\n- Use lowercase, underscore-separated. The prefix becomes part of every HogQL query the user writes.\n\nConfirm the prefix with the user before creating — changing it later is possible but renames every table.\n\n### Step 5 — Create the source\n\nCall `external-data-sources-create` with:\n\n```json\n{\n  \"source_type\": \"Postgres\",\n  \"prefix\": \"postgres_prod\",\n  \"payload\": {\n    \"host\": \"...\",\n    \"port\": \"5432\",\n    \"dbname\": \"...\",\n    \"user\": \"...\",\n    \"password\": \"...\",\n    \"schema\": \"public\",\n    \"schemas\": [\n      {\n        \"name\": \"orders\",\n        \"should_sync\": true,\n        \"sync_type\": \"incremental\",\n        \"incremental_field\": \"updated_at\",\n        \"incremental_field_type\": \"datetime\",\n        \"primary_key_columns\": [\"id\"]\n      },\n      {\n        \"name\": \"users\",\n        \"should_sync\": true,\n        \"sync_type\": \"full_refresh\"\n      },\n      {\n        \"name\": \"audit_log\",\n        \"should_sync\": false\n      }\n    ]\n  }\n}\n```\n\nRules for the `schemas` array:\n\n- Every table returned by db-schema should be included, even ones the user doesn't want (set `should_sync: false`).\n  Tables the user didn't mention default to `should_sync: false`.\n- `sync_type` is required only when `should_sync: true`.\n- `incremental_field` \u002F `incremental_field_type` must be present when `sync_type` is `incremental` or `append`.\n- `primary_key_columns` must be present when `sync_type` is `cdc`.\n\nOn success you'll get back a source with a new `id`. The first sync is triggered automatically.\n\n### Step 6 — Register a webhook (only when any schema is `sync_type: \"webhook\"`)\n\nWebhook-type schemas don't start receiving data just by existing — the external service needs to know where to POST\nevents, and PostHog needs to know how to verify them. This is a second call after source creation, not part of the\n`external-data-sources-create` payload. Do this **before** telling the user the setup is complete, otherwise they\nhear \"syncs are running\" while the push channel is still unregistered.\n\nOnly needed when at least one schema on the source has `sync_type: \"webhook\"` and `should_sync: true`. Currently only\nStripe implements this flow; for everything else skip this step.\n\nBefore calling create-webhook, check `external-data-sources-webhook-info-retrieve({id})`. If it already returns\n`exists: true`, do NOT call create-webhook again — each successful call registers a new external endpoint and would\nresult in duplicate deliveries.\n\n1. Call `external-data-sources-create-webhook-create({id})`. PostHog:\n   - creates the HogFunction that will receive webhook POSTs,\n   - builds a schema_mapping from external event types to PostHog schema ids,\n   - calls the source's API (e.g. Stripe) to register the webhook URL and subscribe to the relevant events,\n   - on Stripe, auto-captures the `signing_secret` and stores it securely.\n\n   Returns `{success, webhook_url, error}`. On success report the `webhook_url` to the user for their records — but\n   they don't need to paste it anywhere; registration is already done.\n\n2. If `success: false` with a permissions error like \"API key doesn't have permission to create webhooks\":\n   - The HogFunction is still created, just disabled.\n   - Ask the user to create the webhook manually in the source's dashboard using the returned `webhook_url`.\n   - Have them copy the signing secret from the source's webhook settings.\n   - Call `external-data-sources-update-webhook-inputs-create({id}, {inputs: {signing_secret: \"whsec_...\"}})` to\n     store it. The HogFunction picks it up and verifies incoming payloads.\n\n3. Verify with `external-data-sources-webhook-info-retrieve({id})`. A healthy webhook has `exists: true`,\n   `external_status.status: \"enabled\"`, and no `error`.\n\nWebhooks are supplementary to bulk sync. The first load of a webhook-enabled schema is still done via polling\n(`initial_sync_complete` flips to true when done); after that, the webhook becomes the primary ingestion path. A\nwebhook schema will still have a `sync_frequency` that schedules a periodic bulk refresh as a safety net. This is\nexpected — not something to \"fix\".\n\n### Step 7 — Confirm and explain what happens next\n\nAfter creation (and, for webhook schemas, after Step 6):\n\n- Call `external-data-schemas-list` to show the user the initial state.\n- Explain: every enabled schema enters `Running`, then moves to `Completed` when the first sync finishes. First\n  syncs can take anywhere from seconds to hours depending on row count — a multi-million-row table is fine, just\n  slow.\n- Tell them how to query: `SELECT * FROM {prefix}_{table_name} LIMIT 10` in HogQL.\n- Offer to check back in a few minutes to confirm the initial syncs succeeded.\n\n## CDC setup for Postgres (optional, when requested)\n\nIf the user wants near-real-time replication from Postgres:\n\n1. Before calling db-schema, run `external-data-sources-check-cdc-prerequisites-create` with their Postgres creds.\n   It returns `{valid, errors[]}` listing anything missing (wal_level, replication slot, publication, permissions).\n2. If `valid: false`, present the errors and ask the user to fix on the Postgres side. Don't try to create a CDC\n   source that will immediately fail.\n3. Once prerequisites pass, proceed to db-schema and create. Set `sync_type: \"cdc\"` on the tables that need it, and\n   include `primary_key_columns` for each (CDC requires them).\n\n## Important notes\n\n- **Always validate creds with db-schema before create.** The create endpoint will accept invalid creds and then fail\n  asynchronously — the source appears in the list with status `Error` and no tables. Skipping the validation step\n  just pushes the failure into the background.\n- **Present the table list before creating.** Large databases may have hundreds of tables. Don't auto-select them all\n  — row counts and relevance matter for billing. Let the user opt in explicitly.\n- **Don't invent schemas.** Every entry in the `schemas` array must correspond to a real table from the db-schema\n  response. You can't \"also add an orders table\" unless db-schema found one.\n- **Prefix is load-bearing.** It's part of every HogQL query the user will ever write against these tables. Pick\n  something short, descriptive, and not already taken.\n- **Prefer the secure connect-link for any credentials.** Use `data-warehouse-source-connect-link` so the user\n  authenticates in their browser — the connect page renders the source's full connection form (OAuth and credential\n  options alike) and stores the result without creating the source. Don't collect OAuth tokens or database passwords\n  in chat; pass the `credential_id` reference to setup — source creation always happens through setup, not the UI.\n  (An already-connected OAuth integration can also be passed directly via its id key, e.g.\n  `{\"hubspot_integration_id\": 123}`.)\n- **Webhooks are a separate step after create.** Setting `sync_type: \"webhook\"` on a schema doesn't register the\n  webhook — the `create-webhook` call does. Always follow create → create-webhook → webhook-info for webhook-type\n  schemas, and never leave a webhook schema dangling without registration (it just won't receive events).\n- **Webhook support is source-specific and sparse.** Currently only Stripe implements `WebhookSource`. Don't promise\n  webhooks for Hubspot, Salesforce, or Postgres — they'll use polling sync.\n- **Row counts drive billing.** Warehouse syncing is metered by rows synced. A chatty 500M-row events table synced\n  hourly is very different from a 10k-row dimension table synced daily. Flag large tables and offer longer sync\n  frequencies (`sync_frequency: \"24hour\"`) as the default.\n",{"data":45,"body":46},{"name":4,"description":6},{"type":47,"children":48},"root",[49,57,63,107,114,146,152,382,388,400,692,698,710,859,864,930,936,955,967,973,980,1020,1133,1146,1158,1171,1177,1195,1216,1225,1238,1244,1255,1367,1391,1500,1506,1526,1572,1577,1583,1594,2417,2429,2525,2537,2550,2568,2587,2608,2757,2778,2784,2789,2842,2848,2853,2908,2914,3065],{"type":50,"tag":51,"props":52,"children":53},"element","h1",{"id":4},[54],{"type":55,"value":56},"text","Setting up a data warehouse source",{"type":50,"tag":58,"props":59,"children":60},"p",{},[61],{"type":55,"value":62},"Use this skill when the user wants to connect an external data source to PostHog's data warehouse for the first time.",{"type":50,"tag":58,"props":64,"children":65},{},[66,72,74,81,83,89,91,97,99,105],{"type":50,"tag":67,"props":68,"children":69},"strong",{},[70],{"type":55,"value":71},"Default to the one-step flow:",{"type":55,"value":73}," ",{"type":50,"tag":75,"props":76,"children":78},"code",{"className":77},[],[79],{"type":55,"value":80},"data-warehouse-source-setup",{"type":55,"value":82}," validates credentials, discovers every table, enables\nthem with sensible sync defaults (incremental where possible), and creates the source in a single call — no\n",{"type":50,"tag":75,"props":84,"children":86},{"className":85},[],[87],{"type":55,"value":88},"schemas",{"type":55,"value":90}," array to assemble. For credentials, hand the user a secure browser link with ",{"type":50,"tag":75,"props":92,"children":94},{"className":93},[],[95],{"type":55,"value":96},"data-warehouse-source-connect-link",{"type":55,"value":98},"\ninstead of collecting secrets in chat. Only drop to the manual ",{"type":50,"tag":75,"props":100,"children":102},{"className":101},[],[103],{"type":55,"value":104},"wizard → db-schema → create",{"type":55,"value":106}," flow when the user wants to\nhand-pick which tables sync or set non-default sync types per table.",{"type":50,"tag":108,"props":109,"children":111},"h2",{"id":110},"when-to-use-this-skill",[112],{"type":55,"value":113},"When to use this skill",{"type":50,"tag":115,"props":116,"children":117},"ul",{},[118,124,129,141],{"type":50,"tag":119,"props":120,"children":121},"li",{},[122],{"type":55,"value":123},"The user wants to connect a new source: \"connect Stripe\", \"import my Postgres orders table\", \"sync Hubspot contacts\"",{"type":50,"tag":119,"props":125,"children":126},{},[127],{"type":55,"value":128},"The user isn't sure what source types PostHog supports",{"type":50,"tag":119,"props":130,"children":131},{},[132,134,139],{"type":55,"value":133},"The user has credentials but doesn't know how to structure the ",{"type":50,"tag":75,"props":135,"children":137},{"className":136},[],[138],{"type":55,"value":88},{"type":55,"value":140}," payload",{"type":50,"tag":119,"props":142,"children":143},{},[144],{"type":55,"value":145},"The user wants guidance on which sync method to pick per table",{"type":50,"tag":108,"props":147,"children":149},{"id":148},"available-tools",[150],{"type":55,"value":151},"Available tools",{"type":50,"tag":153,"props":154,"children":155},"table",{},[156,175],{"type":50,"tag":157,"props":158,"children":159},"thead",{},[160],{"type":50,"tag":161,"props":162,"children":163},"tr",{},[164,170],{"type":50,"tag":165,"props":166,"children":167},"th",{},[168],{"type":55,"value":169},"Tool",{"type":50,"tag":165,"props":171,"children":172},{},[173],{"type":55,"value":174},"Purpose",{"type":50,"tag":176,"props":177,"children":178},"tbody",{},[179,201,222,239,256,280,297,314,331,348,365],{"type":50,"tag":161,"props":180,"children":181},{},[182,191],{"type":50,"tag":183,"props":184,"children":185},"td",{},[186],{"type":50,"tag":75,"props":187,"children":189},{"className":188},[],[190],{"type":55,"value":96},{"type":50,"tag":183,"props":192,"children":193},{},[194,199],{"type":50,"tag":67,"props":195,"children":196},{},[197],{"type":55,"value":198},"Preferred for credentials",{"type":55,"value":200}," — get a secure browser\u002FOAuth link so the user authenticates without pasting secrets in chat",{"type":50,"tag":161,"props":202,"children":203},{},[204,212],{"type":50,"tag":183,"props":205,"children":206},{},[207],{"type":50,"tag":75,"props":208,"children":210},{"className":209},[],[211],{"type":55,"value":80},{"type":50,"tag":183,"props":213,"children":214},{},[215,220],{"type":50,"tag":67,"props":216,"children":217},{},[218],{"type":55,"value":219},"Preferred to create",{"type":55,"value":221}," — one call: validate creds, discover tables, apply sync defaults, create the source",{"type":50,"tag":161,"props":223,"children":224},{},[225,234],{"type":50,"tag":183,"props":226,"children":227},{},[228],{"type":50,"tag":75,"props":229,"children":231},{"className":230},[],[232],{"type":55,"value":233},"external-data-sources-wizard",{"type":50,"tag":183,"props":235,"children":236},{},[237],{"type":55,"value":238},"Discover which source types exist and what fields each needs (advanced flow)",{"type":50,"tag":161,"props":240,"children":241},{},[242,251],{"type":50,"tag":183,"props":243,"children":244},{},[245],{"type":50,"tag":75,"props":246,"children":248},{"className":247},[],[249],{"type":55,"value":250},"external-data-sources-db-schema",{"type":50,"tag":183,"props":252,"children":253},{},[254],{"type":55,"value":255},"Validate credentials and list tables with available sync methods per table (advanced flow)",{"type":50,"tag":161,"props":257,"children":258},{},[259,268],{"type":50,"tag":183,"props":260,"children":261},{},[262],{"type":50,"tag":75,"props":263,"children":265},{"className":264},[],[266],{"type":55,"value":267},"external-data-sources-create",{"type":50,"tag":183,"props":269,"children":270},{},[271,273,278],{"type":55,"value":272},"Advanced create — requires a ",{"type":50,"tag":75,"props":274,"children":276},{"className":275},[],[277],{"type":55,"value":88},{"type":55,"value":279}," array built from the db-schema response",{"type":50,"tag":161,"props":281,"children":282},{},[283,292],{"type":50,"tag":183,"props":284,"children":285},{},[286],{"type":50,"tag":75,"props":287,"children":289},{"className":288},[],[290],{"type":55,"value":291},"external-data-sources-check-cdc-prerequisites-create",{"type":50,"tag":183,"props":293,"children":294},{},[295],{"type":55,"value":296},"Postgres CDC pre-flight check (optional, only for Postgres CDC)",{"type":50,"tag":161,"props":298,"children":299},{},[300,309],{"type":50,"tag":183,"props":301,"children":302},{},[303],{"type":50,"tag":75,"props":304,"children":306},{"className":305},[],[307],{"type":55,"value":308},"external-data-sources-webhook-info-retrieve",{"type":50,"tag":183,"props":310,"children":311},{},[312],{"type":55,"value":313},"Check if a source supports webhooks and whether one has been registered",{"type":50,"tag":161,"props":315,"children":316},{},[317,326],{"type":50,"tag":183,"props":318,"children":319},{},[320],{"type":50,"tag":75,"props":321,"children":323},{"className":322},[],[324],{"type":55,"value":325},"external-data-sources-create-webhook-create",{"type":50,"tag":183,"props":327,"children":328},{},[329],{"type":55,"value":330},"Register a webhook with the external service after source creation",{"type":50,"tag":161,"props":332,"children":333},{},[334,343],{"type":50,"tag":183,"props":335,"children":336},{},[337],{"type":50,"tag":75,"props":338,"children":340},{"className":339},[],[341],{"type":55,"value":342},"external-data-sources-update-webhook-inputs-create",{"type":50,"tag":183,"props":344,"children":345},{},[346],{"type":55,"value":347},"Supply the signing secret manually when auto-registration failed",{"type":50,"tag":161,"props":349,"children":350},{},[351,360],{"type":50,"tag":183,"props":352,"children":353},{},[354],{"type":50,"tag":75,"props":355,"children":357},{"className":356},[],[358],{"type":55,"value":359},"external-data-sources-list",{"type":50,"tag":183,"props":361,"children":362},{},[363],{"type":55,"value":364},"After creation, confirm the source is listed and see its initial status",{"type":50,"tag":161,"props":366,"children":367},{},[368,377],{"type":50,"tag":183,"props":369,"children":370},{},[371],{"type":50,"tag":75,"props":372,"children":374},{"className":373},[],[375],{"type":55,"value":376},"external-data-schemas-list",{"type":50,"tag":183,"props":378,"children":379},{},[380],{"type":55,"value":381},"See per-table sync status once the source is created",{"type":50,"tag":108,"props":383,"children":385},{"id":384},"pre-flight-credential-gotchas-that-cause-most-failures",[386],{"type":55,"value":387},"Pre-flight: credential gotchas that cause most failures",{"type":50,"tag":58,"props":389,"children":390},{},[391,393,398],{"type":55,"value":392},"Surface these ",{"type":50,"tag":67,"props":394,"children":395},{},[396],{"type":55,"value":397},"before",{"type":55,"value":399}," collecting credentials — they're the top reasons setup fails on the first try. Validating\nagainst them up front avoids burning credential prompts on retries.",{"type":50,"tag":115,"props":401,"children":402},{},[403,457,542,659],{"type":50,"tag":119,"props":404,"children":405},{},[406,411,412,418,420,426,428,434,435,441,442,448,450,455],{"type":50,"tag":67,"props":407,"children":408},{},[409],{"type":55,"value":410},"The host must be reachable from PostHog's network.",{"type":55,"value":73},{"type":50,"tag":75,"props":413,"children":415},{"className":414},[],[416],{"type":55,"value":417},"localhost",{"type":55,"value":419},", ",{"type":50,"tag":75,"props":421,"children":423},{"className":422},[],[424],{"type":55,"value":425},"127.0.0.1",{"type":55,"value":427},", and private\u002FRFC-1918 hosts\n(",{"type":50,"tag":75,"props":429,"children":431},{"className":430},[],[432],{"type":55,"value":433},"10.x",{"type":55,"value":419},{"type":50,"tag":75,"props":436,"children":438},{"className":437},[],[439],{"type":55,"value":440},"192.168.x",{"type":55,"value":419},{"type":50,"tag":75,"props":443,"children":445},{"className":444},[],[446],{"type":55,"value":447},"172.16–31.x",{"type":55,"value":449},") are rejected — PostHog runs the connection from its own infrastructure, not the\nuser's machine. Serverless\u002Fmanaged Postgres (Neon, Supabase, RDS behind strict rules) often also needs PostHog's\negress IPs allowlisted first. If the DB isn't publicly reachable, route to the browser deep-link\n(",{"type":50,"tag":75,"props":451,"children":453},{"className":452},[],[454],{"type":55,"value":96},{"type":55,"value":456},") or an SSH tunnel rather than collecting credentials that can't validate.",{"type":50,"tag":119,"props":458,"children":459},{},[460,465,467,472,474,480,482,487,489,495,497,502,504,508,510,516,518,524,526,532,534,540],{"type":50,"tag":67,"props":461,"children":462},{},[463],{"type":55,"value":464},"Supabase is Postgres — don't collect it twice.",{"type":55,"value":466}," Use the ",{"type":50,"tag":67,"props":468,"children":469},{},[470],{"type":55,"value":471},"Session pooler",{"type":55,"value":473}," connection, not the direct host (the\ndirect host is IPv6-only). The pooler host looks like ",{"type":50,"tag":75,"props":475,"children":477},{"className":476},[],[478],{"type":55,"value":479},"aws-0-\u003Cregion>.pooler.supabase.com",{"type":55,"value":481},", the ",{"type":50,"tag":67,"props":483,"children":484},{},[485],{"type":55,"value":486},"username",{"type":55,"value":488}," must be\n",{"type":50,"tag":75,"props":490,"children":492},{"className":491},[],[493],{"type":55,"value":494},"postgres.\u003Cproject-ref>",{"type":55,"value":496},", and the ",{"type":50,"tag":67,"props":498,"children":499},{},[500],{"type":55,"value":501},"port is 6543",{"type":55,"value":503}," (not 5432). The password is the ",{"type":50,"tag":67,"props":505,"children":506},{},[507],{"type":55,"value":28},{"type":55,"value":509}," password (Settings →\nDatabase), which is distinct from the ",{"type":50,"tag":75,"props":511,"children":513},{"className":512},[],[514],{"type":55,"value":515},"anon",{"type":55,"value":517},"\u002F",{"type":50,"tag":75,"props":519,"children":521},{"className":520},[],[522],{"type":55,"value":523},"service_role",{"type":55,"value":525}," JWT keys and from the Supabase account password. If\n",{"type":50,"tag":75,"props":527,"children":529},{"className":528},[],[530],{"type":55,"value":531},"SUPABASE_URL",{"type":55,"value":533}," is in the project env, derive the project ref from ",{"type":50,"tag":75,"props":535,"children":537},{"className":536},[],[538],{"type":55,"value":539},"db.\u003Cref>.supabase.co",{"type":55,"value":541}," to pre-fill these instead of\nasking the user to guess.",{"type":50,"tag":119,"props":543,"children":544},{},[545,550,552],{"type":50,"tag":67,"props":546,"children":547},{},[548],{"type":55,"value":549},"Many SaaS sources need a specific key type or plan",{"type":55,"value":551}," — get the right one before the create call fails:\n",{"type":50,"tag":115,"props":553,"children":554},{},[555,589,607,617,627,637,654],{"type":50,"tag":119,"props":556,"children":557},{},[558,563,565,571,573,579,581,587],{"type":50,"tag":67,"props":559,"children":560},{},[561],{"type":55,"value":562},"Stripe",{"type":55,"value":564}," — a ",{"type":50,"tag":566,"props":567,"children":568},"em",{},[569],{"type":55,"value":570},"restricted",{"type":55,"value":572}," key (",{"type":50,"tag":75,"props":574,"children":576},{"className":575},[],[577],{"type":55,"value":578},"rk_live_…",{"type":55,"value":580},"), not the standard secret key (",{"type":50,"tag":75,"props":582,"children":584},{"className":583},[],[585],{"type":55,"value":586},"sk_live_…",{"type":55,"value":588},").",{"type":50,"tag":119,"props":590,"children":591},{},[592,597,599,605],{"type":50,"tag":67,"props":593,"children":594},{},[595],{"type":55,"value":596},"RevenueCat",{"type":55,"value":598}," — a v2 secret key (",{"type":50,"tag":75,"props":600,"children":602},{"className":601},[],[603],{"type":55,"value":604},"sk_…",{"type":55,"value":606},") with the read scopes enabled.",{"type":50,"tag":119,"props":608,"children":609},{},[610,615],{"type":50,"tag":67,"props":611,"children":612},{},[613],{"type":55,"value":614},"Sentry",{"type":55,"value":616}," — an internal-integration token, not a DSN and not a personal auth token.",{"type":50,"tag":119,"props":618,"children":619},{},[620,625],{"type":50,"tag":67,"props":621,"children":622},{},[623],{"type":55,"value":624},"Convex",{"type":55,"value":626}," — requires the Professional plan.",{"type":50,"tag":119,"props":628,"children":629},{},[630,635],{"type":50,"tag":67,"props":631,"children":632},{},[633],{"type":55,"value":634},"Twilio",{"type":55,"value":636}," — API Key SID + Secret, not the account auth token.",{"type":50,"tag":119,"props":638,"children":639},{},[640,645,647,653],{"type":50,"tag":67,"props":641,"children":642},{},[643],{"type":55,"value":644},"Mailchimp",{"type":55,"value":646}," — the key carries its datacenter suffix (",{"type":50,"tag":75,"props":648,"children":650},{"className":649},[],[651],{"type":55,"value":652},"key-usX",{"type":55,"value":588},{"type":50,"tag":119,"props":655,"children":656},{},[657],{"type":55,"value":658},"For send-only services (Resend, Mailgun), the key already in the project env is often restricted; the warehouse\nimport needs a full\u002Fread-access key.",{"type":50,"tag":119,"props":660,"children":661},{},[662,667,669,675,677,682,684,690],{"type":50,"tag":67,"props":663,"children":664},{},[665],{"type":55,"value":666},"Never pass an unresolved secret reference.",{"type":55,"value":668}," If a credential field is still a ",{"type":50,"tag":75,"props":670,"children":672},{"className":671},[],[673],{"type":55,"value":674},"{\"secretRef\": ...}",{"type":55,"value":676}," object, PostHog\ncan't resolve it — the create\u002Fdb-schema\u002Fsetup calls reject it with a clear error. Resolve it to the real value\nfirst, or collect credentials via ",{"type":50,"tag":75,"props":678,"children":680},{"className":679},[],[681],{"type":55,"value":96},{"type":55,"value":683}," and pass the resulting ",{"type":50,"tag":75,"props":685,"children":687},{"className":686},[],[688],{"type":55,"value":689},"credential_id",{"type":55,"value":691},".",{"type":50,"tag":108,"props":693,"children":695},{"id":694},"recommended-one-step-setup",[696],{"type":55,"value":697},"Recommended: one-step setup",{"type":50,"tag":58,"props":699,"children":700},{},[701,703,708],{"type":55,"value":702},"Most setups should use this path — it avoids the most common failures (skipping db-schema, malformed ",{"type":50,"tag":75,"props":704,"children":706},{"className":705},[],[707],{"type":55,"value":88},{"type":55,"value":709},",\nmissing required fields).",{"type":50,"tag":711,"props":712,"children":713},"ol",{},[714,731,795],{"type":50,"tag":119,"props":715,"children":716},{},[717,722,724,729],{"type":50,"tag":67,"props":718,"children":719},{},[720],{"type":55,"value":721},"Discover the source type and its fields",{"type":55,"value":723}," (optional): ",{"type":50,"tag":75,"props":725,"children":727},{"className":726},[],[728],{"type":55,"value":233},{"type":55,"value":730}," lists every source type and\nthe credential fields each needs. Use it to know what to ask the user for; skip it if the source type is obvious.",{"type":50,"tag":119,"props":732,"children":733},{},[734,739,741,747,749,755,757,763,765,771,773,779,781,787,789,793],{"type":50,"tag":67,"props":735,"children":736},{},[737],{"type":55,"value":738},"Collect credentials securely",{"type":55,"value":740},": call ",{"type":50,"tag":75,"props":742,"children":744},{"className":743},[],[745],{"type":55,"value":746},"data-warehouse-source-connect-link({source_type})",{"type":55,"value":748},". It returns a\n",{"type":50,"tag":75,"props":750,"children":752},{"className":751},[],[753],{"type":55,"value":754},"connect_url",{"type":55,"value":756}," to a minimal connect page rendering the source's full connection form — the user authorizes via\nOAuth or enters credentials there, whichever the source offers (the response's ",{"type":50,"tag":75,"props":758,"children":760},{"className":759},[],[761],{"type":55,"value":762},"auth_method",{"type":55,"value":764}," tells you which to\nexpect). The page validates the details against a live connection and stashes them encrypted in a temporary\nstore — it does NOT create the source. After the user confirms they're done, find the stored credential id via\n",{"type":50,"tag":75,"props":766,"children":768},{"className":767},[],[769],{"type":55,"value":770},"data-warehouse-stored-credentials-list",{"type":55,"value":772}," (filter by ",{"type":50,"tag":75,"props":774,"children":776},{"className":775},[],[777],{"type":55,"value":778},"source_type",{"type":55,"value":780},", newest first; the page also shows the id to\nthe user) and pass ",{"type":50,"tag":75,"props":782,"children":784},{"className":783},[],[785],{"type":55,"value":786},"{\"credential_id\": \u003Cid>}",{"type":55,"value":788}," to setup. Stored credentials are single-use — deleted as soon as\nsetup consumes them — and expire after 24 hours.",{"type":50,"tag":790,"props":791,"children":792},"br",{},[],{"type":55,"value":794},"Never ask the user to paste raw database passwords, API keys, or OAuth tokens into the chat.",{"type":50,"tag":119,"props":796,"children":797},{},[798,803,805,811,813,819,821,827,829,835,837,842,844,850,852,857],{"type":50,"tag":67,"props":799,"children":800},{},[801],{"type":55,"value":802},"Create in one call",{"type":55,"value":804},": ",{"type":50,"tag":75,"props":806,"children":808},{"className":807},[],[809],{"type":55,"value":810},"data-warehouse-source-setup({source_type, payload, prefix})",{"type":55,"value":812},". The server validates\ncredentials, discovers all tables, enables them with sync defaults (incremental where a tracking column exists,\nelse append, else full_refresh — never CDC), sets ",{"type":50,"tag":75,"props":814,"children":816},{"className":815},[],[817],{"type":55,"value":818},"created_via=mcp",{"type":55,"value":820},", and creates the source. The ",{"type":50,"tag":75,"props":822,"children":824},{"className":823},[],[825],{"type":55,"value":826},"payload",{"type":55,"value":828}," carries\na credential reference (",{"type":50,"tag":75,"props":830,"children":832},{"className":831},[],[833],{"type":55,"value":834},"{\"credential_id\": ...}",{"type":55,"value":836}," or the OAuth integration id key) — or inline credentials for\nheadless automation; no ",{"type":50,"tag":75,"props":838,"children":840},{"className":839},[],[841],{"type":55,"value":88},{"type":55,"value":843}," array is needed. On success you get the new source ",{"type":50,"tag":75,"props":845,"children":847},{"className":846},[],[848],{"type":55,"value":849},"id",{"type":55,"value":851},"; call\n",{"type":50,"tag":75,"props":853,"children":855},{"className":854},[],[856],{"type":55,"value":376},{"type":55,"value":858}," to show the user what was enabled and how each table will sync.",{"type":50,"tag":58,"props":860,"children":861},{},[862],{"type":55,"value":863},"Notes specific to this path:",{"type":50,"tag":115,"props":865,"children":866},{},[867,877,918],{"type":50,"tag":119,"props":868,"children":869},{},[870,875],{"type":50,"tag":67,"props":871,"children":872},{},[873],{"type":55,"value":874},"All discovered tables are enabled.",{"type":55,"value":876}," That's intended (incremental defaults keep ongoing cost low), but flag row\ncounts for very large tables and offer the advanced flow if the user wants to sync only a subset.",{"type":50,"tag":119,"props":878,"children":879},{},[880,885,887,893,895,901,903,909,911,916],{"type":50,"tag":67,"props":881,"children":882},{},[883],{"type":55,"value":884},"Webhooks are auto-registered",{"type":55,"value":886}," for sources that support them (currently Stripe). Check the ",{"type":50,"tag":75,"props":888,"children":890},{"className":889},[],[891],{"type":55,"value":892},"webhook",{"type":55,"value":894}," key in the\nsetup response: on success, webhook-capable tables sync in real time and webhook-only tables (e.g. Stripe Discount)\nare enabled too; on failure (e.g. the API key can't create webhooks), tables keep the polling defaults and\nwebhook-only tables stay disabled — relay the ",{"type":50,"tag":75,"props":896,"children":898},{"className":897},[],[899],{"type":55,"value":900},"webhook.error",{"type":55,"value":902}," to the user and offer Step 6 to register manually.\nIf ",{"type":50,"tag":75,"props":904,"children":906},{"className":905},[],[907],{"type":55,"value":908},"webhook.pending_inputs",{"type":55,"value":910}," is non-empty, collect those values and submit via\n",{"type":50,"tag":75,"props":912,"children":914},{"className":913},[],[915],{"type":55,"value":342},{"type":55,"value":917},". CDC is never chosen automatically; use the advanced flow +\nCDC steps for near-real-time Postgres.",{"type":50,"tag":119,"props":919,"children":920},{},[921,923,928],{"type":55,"value":922},"Inline credentials in ",{"type":50,"tag":75,"props":924,"children":926},{"className":925},[],[927],{"type":55,"value":826},{"type":55,"value":929}," still work for headless\u002Fautomation, but prefer the connect-link handoff above.",{"type":50,"tag":108,"props":931,"children":933},{"id":932},"advanced-hand-pick-tables-three-step-flow",[934],{"type":55,"value":935},"Advanced: hand-pick tables (three-step flow)",{"type":50,"tag":58,"props":937,"children":938},{},[939,941,946,948,953],{"type":55,"value":940},"Use this when the user wants to choose exactly which tables sync or set non-default sync types. Don't try to shortcut\nto ",{"type":50,"tag":75,"props":942,"children":944},{"className":943},[],[945],{"type":55,"value":267},{"type":55,"value":947}," — you need the db-schema response to build a valid ",{"type":50,"tag":75,"props":949,"children":951},{"className":950},[],[952],{"type":55,"value":88},{"type":55,"value":954}," payload.",{"type":50,"tag":956,"props":957,"children":962},"pre",{"className":958,"code":960,"language":55,"meta":961},[959],"language-text","         ┌────────────────────┐\n         │ 1. wizard          │  What source types exist? What fields does each need?\n         └────────┬───────────┘\n                  ▼\n         ┌────────────────────┐\n         │ 2. db-schema       │  Validate creds. List tables + available sync methods per table.\n         └────────┬───────────┘\n                  ▼\n         ┌────────────────────┐\n         │ 3. create          │  Send source_type + credentials + schemas[] to actually create.\n         └────────────────────┘\n","",[963],{"type":50,"tag":75,"props":964,"children":965},{"__ignoreMap":961},[966],{"type":55,"value":960},{"type":50,"tag":108,"props":968,"children":970},{"id":969},"workflow",[971],{"type":55,"value":972},"Workflow",{"type":50,"tag":974,"props":975,"children":977},"h3",{"id":976},"step-1-discover-the-source-type",[978],{"type":55,"value":979},"Step 1 — Discover the source type",{"type":50,"tag":58,"props":981,"children":982},{},[983,985,990,991,1003,1005,1011,1013,1018],{"type":55,"value":984},"Call ",{"type":50,"tag":75,"props":986,"children":988},{"className":987},[],[989],{"type":55,"value":233},{"type":55,"value":73},{"type":50,"tag":67,"props":992,"children":993},{},[994,996,1001],{"type":55,"value":995},"with ",{"type":50,"tag":75,"props":997,"children":999},{"className":998},[],[1000],{"type":55,"value":778},{"type":55,"value":1002}," set to the kind(s) you need",{"type":55,"value":1004}," (comma-separated, e.g.\n",{"type":50,"tag":75,"props":1006,"children":1008},{"className":1007},[],[1009],{"type":55,"value":1010},"Postgres,Stripe",{"type":55,"value":1012},"). The unfiltered response describes every supported source and is hundreds of KB — large enough to\nblow your context budget. Only omit ",{"type":50,"tag":75,"props":1014,"children":1016},{"className":1015},[],[1017],{"type":55,"value":778},{"type":55,"value":1019}," when you genuinely need to enumerate every available type, and\nexpect a big payload if you do. The response is a dict keyed by source type. Each entry describes:",{"type":50,"tag":115,"props":1021,"children":1022},{},[1023,1055,1074,1107],{"type":50,"tag":119,"props":1024,"children":1025},{},[1026,1032,1034,1040,1041,1047,1048,1054],{"type":50,"tag":75,"props":1027,"children":1029},{"className":1028},[],[1030],{"type":55,"value":1031},"name",{"type":55,"value":1033}," — the canonical source_type string you'll pass to later calls (e.g. ",{"type":50,"tag":75,"props":1035,"children":1037},{"className":1036},[],[1038],{"type":55,"value":1039},"\"Postgres\"",{"type":55,"value":419},{"type":50,"tag":75,"props":1042,"children":1044},{"className":1043},[],[1045],{"type":55,"value":1046},"\"Stripe\"",{"type":55,"value":419},{"type":50,"tag":75,"props":1049,"children":1051},{"className":1050},[],[1052],{"type":55,"value":1053},"\"Hubspot\"",{"type":55,"value":588},{"type":50,"tag":119,"props":1056,"children":1057},{},[1058,1064,1066,1072],{"type":50,"tag":75,"props":1059,"children":1061},{"className":1060},[],[1062],{"type":55,"value":1063},"label",{"type":55,"value":1065}," \u002F ",{"type":50,"tag":75,"props":1067,"children":1069},{"className":1068},[],[1070],{"type":55,"value":1071},"caption",{"type":55,"value":1073}," — human-readable.",{"type":50,"tag":119,"props":1075,"children":1076},{},[1077,1083,1085,1090,1092,1098,1100,1106],{"type":50,"tag":75,"props":1078,"children":1080},{"className":1079},[],[1081],{"type":55,"value":1082},"fields",{"type":55,"value":1084}," — the config fields needed (host, port, database, api_key, client_id\u002Fsecret, ...). Each has ",{"type":50,"tag":75,"props":1086,"children":1088},{"className":1087},[],[1089],{"type":55,"value":1031},{"type":55,"value":1091},",\n",{"type":50,"tag":75,"props":1093,"children":1095},{"className":1094},[],[1096],{"type":55,"value":1097},"type",{"type":55,"value":1099}," (input, password, switch, select, file-upload), and ",{"type":50,"tag":75,"props":1101,"children":1103},{"className":1102},[],[1104],{"type":55,"value":1105},"required",{"type":55,"value":691},{"type":50,"tag":119,"props":1108,"children":1109},{},[1110,1116,1117,1123,1125,1131],{"type":50,"tag":75,"props":1111,"children":1113},{"className":1112},[],[1114],{"type":55,"value":1115},"featured",{"type":55,"value":419},{"type":50,"tag":75,"props":1118,"children":1120},{"className":1119},[],[1121],{"type":55,"value":1122},"unreleasedSource",{"type":55,"value":1124}," — use to gauge readiness. Skip sources marked ",{"type":50,"tag":75,"props":1126,"children":1128},{"className":1127},[],[1129],{"type":55,"value":1130},"unreleasedSource: true",{"type":55,"value":1132}," unless the\nuser explicitly asked for a preview.",{"type":50,"tag":58,"props":1134,"children":1135},{},[1136,1138,1144],{"type":55,"value":1137},"Match the user's request to a source. If they said \"Postgres\", look up ",{"type":50,"tag":75,"props":1139,"children":1141},{"className":1140},[],[1142],{"type":55,"value":1143},"Postgres",{"type":55,"value":1145},". If they said something ambiguous\nlike \"database\", present the top relevant matches (Postgres, MySQL, MongoDB, BigQuery, Snowflake, Redshift) and let\nthem pick.",{"type":50,"tag":58,"props":1147,"children":1148},{},[1149,1151,1156],{"type":55,"value":1150},"For OAuth-based sources (Hubspot, Salesforce, Google Ads), the wizard entry hints at an OAuth flow. These typically\nneed the user to authorize in the PostHog UI rather than pasting credentials — explain this and direct them to the\nsource setup page rather than trying to collect tokens in chat. OAuth is about ",{"type":50,"tag":566,"props":1152,"children":1153},{},[1154],{"type":55,"value":1155},"authentication",{"type":55,"value":1157},", not about how data\nflows; OAuth sources still use polling bulk sync, not webhooks.",{"type":50,"tag":58,"props":1159,"children":1160},{},[1161,1163,1169],{"type":55,"value":1162},"Gather the required credentials from the user. Never ask for more fields than the wizard entry says are required —\nasking for an unnecessary ",{"type":50,"tag":75,"props":1164,"children":1166},{"className":1165},[],[1167],{"type":55,"value":1168},"port",{"type":55,"value":1170}," when the source doesn't need one confuses users.",{"type":50,"tag":974,"props":1172,"children":1174},{"id":1173},"step-2-validate-credentials-and-discover-tables",[1175],{"type":55,"value":1176},"Step 2 — Validate credentials and discover tables",{"type":50,"tag":58,"props":1178,"children":1179},{},[1180,1181,1186,1188,1193],{"type":55,"value":984},{"type":50,"tag":75,"props":1182,"children":1184},{"className":1183},[],[1185],{"type":55,"value":250},{"type":55,"value":1187}," with ",{"type":50,"tag":75,"props":1189,"children":1191},{"className":1190},[],[1192],{"type":55,"value":778},{"type":55,"value":1194}," plus all credential fields. This does two things at once:",{"type":50,"tag":711,"props":1196,"children":1197},{},[1198,1211],{"type":50,"tag":119,"props":1199,"children":1200},{},[1201,1203,1209],{"type":55,"value":1202},"Validates the credentials against the live source. Returns 400 with a ",{"type":50,"tag":75,"props":1204,"children":1206},{"className":1205},[],[1207],{"type":55,"value":1208},"message",{"type":55,"value":1210}," if anything is wrong (bad host,\nwrong password, permission denied). Show the error verbatim — it's often actionable (\"password authentication\nfailed for user 'x'\").",{"type":50,"tag":119,"props":1212,"children":1213},{},[1214],{"type":55,"value":1215},"If valid, returns an array of table entries. Each entry:",{"type":50,"tag":956,"props":1217,"children":1220},{"className":1218,"code":1219,"language":55,"meta":961},[959],"{\n  \"table\": \"orders\",\n  \"should_sync\": false,\n  \"rows\": 1_250_000,\n  \"incremental_available\": true,   # can do sync_type=incremental\n  \"append_available\": true,        # can do sync_type=append\n  \"cdc_available\": true,           # can do sync_type=cdc  (null = not enabled for team)\n  \"supports_webhooks\": false,      # can do sync_type=webhook for real-time push\n  \"incremental_fields\": [          # candidates: usually updated_at, created_at, id\n    {\"field\": \"updated_at\", \"type\": \"datetime\", \"label\": \"updated_at\", ...},\n    {\"field\": \"created_at\", \"type\": \"datetime\", ...},\n    {\"field\": \"id\", \"type\": \"integer\", ...}\n  ],\n  \"detected_primary_keys\": [\"id\"],\n  \"available_columns\": [{\"field\": \"id\", \"type\": \"integer\", \"nullable\": false}, ...],\n  \"description\": \"...\"\n}\n",[1221],{"type":50,"tag":75,"props":1222,"children":1223},{"__ignoreMap":961},[1224],{"type":55,"value":1219},{"type":50,"tag":58,"props":1226,"children":1227},{},[1228,1230,1237],{"type":55,"value":1229},"Present this to the user. Don't dump the raw JSON — summarize: which tables were found, row counts, and the default\nsync method recommendation per table (see ",{"type":50,"tag":1231,"props":1232,"children":1234},"a",{"href":1233},".\u002Freferences\u002Fsync-types.md",[1235],{"type":55,"value":1236},"sync-type decision guide",{"type":55,"value":588},{"type":50,"tag":974,"props":1239,"children":1241},{"id":1240},"step-3-confirm-per-table-sync-configuration",[1242],{"type":55,"value":1243},"Step 3 — Confirm per-table sync configuration",{"type":50,"tag":58,"props":1245,"children":1246},{},[1247,1249,1253],{"type":55,"value":1248},"For each table the user wants to sync, pick a sync_type. See the\n",{"type":50,"tag":1231,"props":1250,"children":1251},{"href":1233},[1252],{"type":55,"value":1236},{"type":55,"value":1254}," for detailed rules, but the short version is:",{"type":50,"tag":115,"props":1256,"children":1257},{},[1258,1275,1307,1324,1341],{"type":50,"tag":119,"props":1259,"children":1260},{},[1261,1266,1267,1273],{"type":50,"tag":67,"props":1262,"children":1263},{},[1264],{"type":55,"value":1265},"Small \u002F dimension tables (\u003C50k rows, no natural ordering column):",{"type":55,"value":73},{"type":50,"tag":75,"props":1268,"children":1270},{"className":1269},[],[1271],{"type":55,"value":1272},"full_refresh",{"type":55,"value":1274}," — simple and always correct.",{"type":50,"tag":119,"props":1276,"children":1277},{},[1278,1298,1299,1305],{"type":50,"tag":67,"props":1279,"children":1280},{},[1281,1283,1289,1290,1296],{"type":55,"value":1282},"Large tables with an ",{"type":50,"tag":75,"props":1284,"children":1286},{"className":1285},[],[1287],{"type":55,"value":1288},"updated_at",{"type":55,"value":1065},{"type":50,"tag":75,"props":1291,"children":1293},{"className":1292},[],[1294],{"type":55,"value":1295},"modified_at",{"type":55,"value":1297},":",{"type":55,"value":73},{"type":50,"tag":75,"props":1300,"children":1302},{"className":1301},[],[1303],{"type":55,"value":1304},"incremental",{"type":55,"value":1306}," — much cheaper per sync.",{"type":50,"tag":119,"props":1308,"children":1309},{},[1310,1315,1316,1322],{"type":50,"tag":67,"props":1311,"children":1312},{},[1313],{"type":55,"value":1314},"Append-only immutable tables (logs, events):",{"type":55,"value":73},{"type":50,"tag":75,"props":1317,"children":1319},{"className":1318},[],[1320],{"type":55,"value":1321},"append",{"type":55,"value":1323}," if available — preserves history.",{"type":50,"tag":119,"props":1325,"children":1326},{},[1327,1332,1333,1339],{"type":50,"tag":67,"props":1328,"children":1329},{},[1330],{"type":55,"value":1331},"Postgres with CDC enabled and you need near-real-time:",{"type":55,"value":73},{"type":50,"tag":75,"props":1334,"children":1336},{"className":1335},[],[1337],{"type":55,"value":1338},"cdc",{"type":55,"value":1340}," — requires primary keys and Postgres prerequisites.",{"type":50,"tag":119,"props":1342,"children":1343},{},[1344,1349,1351,1357,1359,1365],{"type":50,"tag":67,"props":1345,"children":1346},{},[1347],{"type":55,"value":1348},"Sources that support webhooks (currently Stripe):",{"type":55,"value":1350}," for near-real-time ingestion set ",{"type":50,"tag":75,"props":1352,"children":1354},{"className":1353},[],[1355],{"type":55,"value":1356},"sync_type: \"webhook\"",{"type":55,"value":1358}," on\nthe tables where ",{"type":50,"tag":75,"props":1360,"children":1362},{"className":1361},[],[1363],{"type":55,"value":1364},"supports_webhooks: true",{"type":55,"value":1366},", then register the webhook as a post-create step (see step 6 below).\nTables that don't support webhooks on the same source still need a bulk sync_type.",{"type":50,"tag":58,"props":1368,"children":1369},{},[1370,1372,1377,1378,1383,1384,1389],{"type":55,"value":1371},"For each schema that will use ",{"type":50,"tag":75,"props":1373,"children":1375},{"className":1374},[],[1376],{"type":55,"value":1304},{"type":55,"value":517},{"type":50,"tag":75,"props":1379,"children":1381},{"className":1380},[],[1382],{"type":55,"value":1321},{"type":55,"value":517},{"type":50,"tag":75,"props":1385,"children":1387},{"className":1386},[],[1388],{"type":55,"value":1338},{"type":55,"value":1390},", you also need:",{"type":50,"tag":115,"props":1392,"children":1393},{},[1394,1428,1481],{"type":50,"tag":119,"props":1395,"children":1396},{},[1397,1403,1405,1411,1413,1418,1420,1426],{"type":50,"tag":75,"props":1398,"children":1400},{"className":1399},[],[1401],{"type":55,"value":1402},"incremental_field",{"type":55,"value":1404}," — which column to track for high-water-mark ordering. Pick from the ",{"type":50,"tag":75,"props":1406,"children":1408},{"className":1407},[],[1409],{"type":55,"value":1410},"incremental_fields",{"type":55,"value":1412}," list\nreturned by db-schema. Prefer ",{"type":50,"tag":75,"props":1414,"children":1416},{"className":1415},[],[1417],{"type":55,"value":1288},{"type":55,"value":1419}," over ",{"type":50,"tag":75,"props":1421,"children":1423},{"className":1422},[],[1424],{"type":55,"value":1425},"created_at",{"type":55,"value":1427}," (updated_at catches late-arriving updates;\ncreated_at misses them). For integer-only tables, use the monotonically increasing primary key.",{"type":50,"tag":119,"props":1429,"children":1430},{},[1431,1437,1439,1445,1446,1452,1453,1459,1460,1466,1467,1473,1474,1480],{"type":50,"tag":75,"props":1432,"children":1434},{"className":1433},[],[1435],{"type":55,"value":1436},"incremental_field_type",{"type":55,"value":1438}," — must match the chosen field's type (",{"type":50,"tag":75,"props":1440,"children":1442},{"className":1441},[],[1443],{"type":55,"value":1444},"datetime",{"type":55,"value":419},{"type":50,"tag":75,"props":1447,"children":1449},{"className":1448},[],[1450],{"type":55,"value":1451},"timestamp",{"type":55,"value":419},{"type":50,"tag":75,"props":1454,"children":1456},{"className":1455},[],[1457],{"type":55,"value":1458},"date",{"type":55,"value":419},{"type":50,"tag":75,"props":1461,"children":1463},{"className":1462},[],[1464],{"type":55,"value":1465},"integer",{"type":55,"value":1091},{"type":50,"tag":75,"props":1468,"children":1470},{"className":1469},[],[1471],{"type":55,"value":1472},"numeric",{"type":55,"value":419},{"type":50,"tag":75,"props":1475,"children":1477},{"className":1476},[],[1478],{"type":55,"value":1479},"objectid",{"type":55,"value":588},{"type":50,"tag":119,"props":1482,"children":1483},{},[1484,1490,1492,1498],{"type":50,"tag":75,"props":1485,"children":1487},{"className":1486},[],[1488],{"type":55,"value":1489},"primary_key_columns",{"type":55,"value":1491}," — required for CDC. Use ",{"type":50,"tag":75,"props":1493,"children":1495},{"className":1494},[],[1496],{"type":55,"value":1497},"detected_primary_keys",{"type":55,"value":1499}," from db-schema.",{"type":50,"tag":974,"props":1501,"children":1503},{"id":1502},"step-4-pick-a-good-prefix",[1504],{"type":55,"value":1505},"Step 4 — Pick a good prefix",{"type":50,"tag":58,"props":1507,"children":1508},{},[1509,1511,1517,1519,1525],{"type":55,"value":1510},"The source's ",{"type":50,"tag":75,"props":1512,"children":1514},{"className":1513},[],[1515],{"type":55,"value":1516},"prefix",{"type":55,"value":1518}," is prepended to table names in HogQL. Tables end up as ",{"type":50,"tag":75,"props":1520,"children":1522},{"className":1521},[],[1523],{"type":55,"value":1524},"{prefix}_{table_name}",{"type":55,"value":691},{"type":50,"tag":115,"props":1527,"children":1528},{},[1529,1548,1567],{"type":50,"tag":119,"props":1530,"children":1531},{},[1532,1534,1540,1541,1547],{"type":55,"value":1533},"Default to the source type lowercased if there's only one source of that type: ",{"type":50,"tag":75,"props":1535,"children":1537},{"className":1536},[],[1538],{"type":55,"value":1539},"stripe",{"type":55,"value":419},{"type":50,"tag":75,"props":1542,"children":1544},{"className":1543},[],[1545],{"type":55,"value":1546},"postgres",{"type":55,"value":691},{"type":50,"tag":119,"props":1549,"children":1550},{},[1551,1553,1559,1560,1566],{"type":55,"value":1552},"If the user already has a Postgres source, pick something distinguishing: ",{"type":50,"tag":75,"props":1554,"children":1556},{"className":1555},[],[1557],{"type":55,"value":1558},"postgres_prod",{"type":55,"value":419},{"type":50,"tag":75,"props":1561,"children":1563},{"className":1562},[],[1564],{"type":55,"value":1565},"postgres_analytics",{"type":55,"value":691},{"type":50,"tag":119,"props":1568,"children":1569},{},[1570],{"type":55,"value":1571},"Use lowercase, underscore-separated. The prefix becomes part of every HogQL query the user writes.",{"type":50,"tag":58,"props":1573,"children":1574},{},[1575],{"type":55,"value":1576},"Confirm the prefix with the user before creating — changing it later is possible but renames every table.",{"type":50,"tag":974,"props":1578,"children":1580},{"id":1579},"step-5-create-the-source",[1581],{"type":55,"value":1582},"Step 5 — Create the source",{"type":50,"tag":58,"props":1584,"children":1585},{},[1586,1587,1592],{"type":55,"value":984},{"type":50,"tag":75,"props":1588,"children":1590},{"className":1589},[],[1591],{"type":55,"value":267},{"type":55,"value":1593}," with:",{"type":50,"tag":956,"props":1595,"children":1599},{"className":1596,"code":1597,"language":1598,"meta":961,"style":961},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"source_type\": \"Postgres\",\n  \"prefix\": \"postgres_prod\",\n  \"payload\": {\n    \"host\": \"...\",\n    \"port\": \"5432\",\n    \"dbname\": \"...\",\n    \"user\": \"...\",\n    \"password\": \"...\",\n    \"schema\": \"public\",\n    \"schemas\": [\n      {\n        \"name\": \"orders\",\n        \"should_sync\": true,\n        \"sync_type\": \"incremental\",\n        \"incremental_field\": \"updated_at\",\n        \"incremental_field_type\": \"datetime\",\n        \"primary_key_columns\": [\"id\"]\n      },\n      {\n        \"name\": \"users\",\n        \"should_sync\": true,\n        \"sync_type\": \"full_refresh\"\n      },\n      {\n        \"name\": \"audit_log\",\n        \"should_sync\": false\n      }\n    ]\n  }\n}\n","json",[1600],{"type":50,"tag":75,"props":1601,"children":1602},{"__ignoreMap":961},[1603,1615,1656,1692,1717,1757,1794,1831,1868,1905,1943,1967,1976,2015,2041,2078,2114,2150,2192,2201,2209,2246,2270,2303,2311,2319,2356,2381,2390,2399,2408],{"type":50,"tag":1604,"props":1605,"children":1608},"span",{"class":1606,"line":1607},"line",1,[1609],{"type":50,"tag":1604,"props":1610,"children":1612},{"style":1611},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1613],{"type":55,"value":1614},"{\n",{"type":50,"tag":1604,"props":1616,"children":1618},{"class":1606,"line":1617},2,[1619,1624,1629,1634,1638,1643,1648,1652],{"type":50,"tag":1604,"props":1620,"children":1621},{"style":1611},[1622],{"type":55,"value":1623},"  \"",{"type":50,"tag":1604,"props":1625,"children":1627},{"style":1626},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1628],{"type":55,"value":778},{"type":50,"tag":1604,"props":1630,"children":1631},{"style":1611},[1632],{"type":55,"value":1633},"\"",{"type":50,"tag":1604,"props":1635,"children":1636},{"style":1611},[1637],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1639,"children":1640},{"style":1611},[1641],{"type":55,"value":1642}," \"",{"type":50,"tag":1604,"props":1644,"children":1646},{"style":1645},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1647],{"type":55,"value":1143},{"type":50,"tag":1604,"props":1649,"children":1650},{"style":1611},[1651],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1653,"children":1654},{"style":1611},[1655],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1657,"children":1659},{"class":1606,"line":1658},3,[1660,1664,1668,1672,1676,1680,1684,1688],{"type":50,"tag":1604,"props":1661,"children":1662},{"style":1611},[1663],{"type":55,"value":1623},{"type":50,"tag":1604,"props":1665,"children":1666},{"style":1626},[1667],{"type":55,"value":1516},{"type":50,"tag":1604,"props":1669,"children":1670},{"style":1611},[1671],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1673,"children":1674},{"style":1611},[1675],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1677,"children":1678},{"style":1611},[1679],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1681,"children":1682},{"style":1645},[1683],{"type":55,"value":1558},{"type":50,"tag":1604,"props":1685,"children":1686},{"style":1611},[1687],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1689,"children":1690},{"style":1611},[1691],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1693,"children":1695},{"class":1606,"line":1694},4,[1696,1700,1704,1708,1712],{"type":50,"tag":1604,"props":1697,"children":1698},{"style":1611},[1699],{"type":55,"value":1623},{"type":50,"tag":1604,"props":1701,"children":1702},{"style":1626},[1703],{"type":55,"value":826},{"type":50,"tag":1604,"props":1705,"children":1706},{"style":1611},[1707],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1709,"children":1710},{"style":1611},[1711],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1713,"children":1714},{"style":1611},[1715],{"type":55,"value":1716}," {\n",{"type":50,"tag":1604,"props":1718,"children":1720},{"class":1606,"line":1719},5,[1721,1726,1732,1736,1740,1744,1749,1753],{"type":50,"tag":1604,"props":1722,"children":1723},{"style":1611},[1724],{"type":55,"value":1725},"    \"",{"type":50,"tag":1604,"props":1727,"children":1729},{"style":1728},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1730],{"type":55,"value":1731},"host",{"type":50,"tag":1604,"props":1733,"children":1734},{"style":1611},[1735],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1737,"children":1738},{"style":1611},[1739],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1741,"children":1742},{"style":1611},[1743],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1745,"children":1746},{"style":1645},[1747],{"type":55,"value":1748},"...",{"type":50,"tag":1604,"props":1750,"children":1751},{"style":1611},[1752],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1754,"children":1755},{"style":1611},[1756],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1758,"children":1760},{"class":1606,"line":1759},6,[1761,1765,1769,1773,1777,1781,1786,1790],{"type":50,"tag":1604,"props":1762,"children":1763},{"style":1611},[1764],{"type":55,"value":1725},{"type":50,"tag":1604,"props":1766,"children":1767},{"style":1728},[1768],{"type":55,"value":1168},{"type":50,"tag":1604,"props":1770,"children":1771},{"style":1611},[1772],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1774,"children":1775},{"style":1611},[1776],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1778,"children":1779},{"style":1611},[1780],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1782,"children":1783},{"style":1645},[1784],{"type":55,"value":1785},"5432",{"type":50,"tag":1604,"props":1787,"children":1788},{"style":1611},[1789],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1791,"children":1792},{"style":1611},[1793],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1795,"children":1797},{"class":1606,"line":1796},7,[1798,1802,1807,1811,1815,1819,1823,1827],{"type":50,"tag":1604,"props":1799,"children":1800},{"style":1611},[1801],{"type":55,"value":1725},{"type":50,"tag":1604,"props":1803,"children":1804},{"style":1728},[1805],{"type":55,"value":1806},"dbname",{"type":50,"tag":1604,"props":1808,"children":1809},{"style":1611},[1810],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1812,"children":1813},{"style":1611},[1814],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1816,"children":1817},{"style":1611},[1818],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1820,"children":1821},{"style":1645},[1822],{"type":55,"value":1748},{"type":50,"tag":1604,"props":1824,"children":1825},{"style":1611},[1826],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1828,"children":1829},{"style":1611},[1830],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1832,"children":1834},{"class":1606,"line":1833},8,[1835,1839,1844,1848,1852,1856,1860,1864],{"type":50,"tag":1604,"props":1836,"children":1837},{"style":1611},[1838],{"type":55,"value":1725},{"type":50,"tag":1604,"props":1840,"children":1841},{"style":1728},[1842],{"type":55,"value":1843},"user",{"type":50,"tag":1604,"props":1845,"children":1846},{"style":1611},[1847],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1849,"children":1850},{"style":1611},[1851],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1853,"children":1854},{"style":1611},[1855],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1857,"children":1858},{"style":1645},[1859],{"type":55,"value":1748},{"type":50,"tag":1604,"props":1861,"children":1862},{"style":1611},[1863],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1865,"children":1866},{"style":1611},[1867],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1869,"children":1871},{"class":1606,"line":1870},9,[1872,1876,1881,1885,1889,1893,1897,1901],{"type":50,"tag":1604,"props":1873,"children":1874},{"style":1611},[1875],{"type":55,"value":1725},{"type":50,"tag":1604,"props":1877,"children":1878},{"style":1728},[1879],{"type":55,"value":1880},"password",{"type":50,"tag":1604,"props":1882,"children":1883},{"style":1611},[1884],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1886,"children":1887},{"style":1611},[1888],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1890,"children":1891},{"style":1611},[1892],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1894,"children":1895},{"style":1645},[1896],{"type":55,"value":1748},{"type":50,"tag":1604,"props":1898,"children":1899},{"style":1611},[1900],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1902,"children":1903},{"style":1611},[1904],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1906,"children":1908},{"class":1606,"line":1907},10,[1909,1913,1918,1922,1926,1930,1935,1939],{"type":50,"tag":1604,"props":1910,"children":1911},{"style":1611},[1912],{"type":55,"value":1725},{"type":50,"tag":1604,"props":1914,"children":1915},{"style":1728},[1916],{"type":55,"value":1917},"schema",{"type":50,"tag":1604,"props":1919,"children":1920},{"style":1611},[1921],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1923,"children":1924},{"style":1611},[1925],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1927,"children":1928},{"style":1611},[1929],{"type":55,"value":1642},{"type":50,"tag":1604,"props":1931,"children":1932},{"style":1645},[1933],{"type":55,"value":1934},"public",{"type":50,"tag":1604,"props":1936,"children":1937},{"style":1611},[1938],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1940,"children":1941},{"style":1611},[1942],{"type":55,"value":1091},{"type":50,"tag":1604,"props":1944,"children":1945},{"class":1606,"line":33},[1946,1950,1954,1958,1962],{"type":50,"tag":1604,"props":1947,"children":1948},{"style":1611},[1949],{"type":55,"value":1725},{"type":50,"tag":1604,"props":1951,"children":1952},{"style":1728},[1953],{"type":55,"value":88},{"type":50,"tag":1604,"props":1955,"children":1956},{"style":1611},[1957],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1959,"children":1960},{"style":1611},[1961],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1963,"children":1964},{"style":1611},[1965],{"type":55,"value":1966}," [\n",{"type":50,"tag":1604,"props":1968,"children":1970},{"class":1606,"line":1969},12,[1971],{"type":50,"tag":1604,"props":1972,"children":1973},{"style":1611},[1974],{"type":55,"value":1975},"      {\n",{"type":50,"tag":1604,"props":1977,"children":1979},{"class":1606,"line":1978},13,[1980,1985,1990,1994,1998,2002,2007,2011],{"type":50,"tag":1604,"props":1981,"children":1982},{"style":1611},[1983],{"type":55,"value":1984},"        \"",{"type":50,"tag":1604,"props":1986,"children":1988},{"style":1987},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[1989],{"type":55,"value":1031},{"type":50,"tag":1604,"props":1991,"children":1992},{"style":1611},[1993],{"type":55,"value":1633},{"type":50,"tag":1604,"props":1995,"children":1996},{"style":1611},[1997],{"type":55,"value":1297},{"type":50,"tag":1604,"props":1999,"children":2000},{"style":1611},[2001],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2003,"children":2004},{"style":1645},[2005],{"type":55,"value":2006},"orders",{"type":50,"tag":1604,"props":2008,"children":2009},{"style":1611},[2010],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2012,"children":2013},{"style":1611},[2014],{"type":55,"value":1091},{"type":50,"tag":1604,"props":2016,"children":2018},{"class":1606,"line":2017},14,[2019,2023,2028,2032,2036],{"type":50,"tag":1604,"props":2020,"children":2021},{"style":1611},[2022],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2024,"children":2025},{"style":1987},[2026],{"type":55,"value":2027},"should_sync",{"type":50,"tag":1604,"props":2029,"children":2030},{"style":1611},[2031],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2033,"children":2034},{"style":1611},[2035],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2037,"children":2038},{"style":1611},[2039],{"type":55,"value":2040}," true,\n",{"type":50,"tag":1604,"props":2042,"children":2044},{"class":1606,"line":2043},15,[2045,2049,2054,2058,2062,2066,2070,2074],{"type":50,"tag":1604,"props":2046,"children":2047},{"style":1611},[2048],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2050,"children":2051},{"style":1987},[2052],{"type":55,"value":2053},"sync_type",{"type":50,"tag":1604,"props":2055,"children":2056},{"style":1611},[2057],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2059,"children":2060},{"style":1611},[2061],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2063,"children":2064},{"style":1611},[2065],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2067,"children":2068},{"style":1645},[2069],{"type":55,"value":1304},{"type":50,"tag":1604,"props":2071,"children":2072},{"style":1611},[2073],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2075,"children":2076},{"style":1611},[2077],{"type":55,"value":1091},{"type":50,"tag":1604,"props":2079,"children":2081},{"class":1606,"line":2080},16,[2082,2086,2090,2094,2098,2102,2106,2110],{"type":50,"tag":1604,"props":2083,"children":2084},{"style":1611},[2085],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2087,"children":2088},{"style":1987},[2089],{"type":55,"value":1402},{"type":50,"tag":1604,"props":2091,"children":2092},{"style":1611},[2093],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2095,"children":2096},{"style":1611},[2097],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2099,"children":2100},{"style":1611},[2101],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2103,"children":2104},{"style":1645},[2105],{"type":55,"value":1288},{"type":50,"tag":1604,"props":2107,"children":2108},{"style":1611},[2109],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2111,"children":2112},{"style":1611},[2113],{"type":55,"value":1091},{"type":50,"tag":1604,"props":2115,"children":2117},{"class":1606,"line":2116},17,[2118,2122,2126,2130,2134,2138,2142,2146],{"type":50,"tag":1604,"props":2119,"children":2120},{"style":1611},[2121],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2123,"children":2124},{"style":1987},[2125],{"type":55,"value":1436},{"type":50,"tag":1604,"props":2127,"children":2128},{"style":1611},[2129],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2131,"children":2132},{"style":1611},[2133],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2135,"children":2136},{"style":1611},[2137],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2139,"children":2140},{"style":1645},[2141],{"type":55,"value":1444},{"type":50,"tag":1604,"props":2143,"children":2144},{"style":1611},[2145],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2147,"children":2148},{"style":1611},[2149],{"type":55,"value":1091},{"type":50,"tag":1604,"props":2151,"children":2153},{"class":1606,"line":2152},18,[2154,2158,2162,2166,2170,2175,2179,2183,2187],{"type":50,"tag":1604,"props":2155,"children":2156},{"style":1611},[2157],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2159,"children":2160},{"style":1987},[2161],{"type":55,"value":1489},{"type":50,"tag":1604,"props":2163,"children":2164},{"style":1611},[2165],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2167,"children":2168},{"style":1611},[2169],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2171,"children":2172},{"style":1611},[2173],{"type":55,"value":2174}," [",{"type":50,"tag":1604,"props":2176,"children":2177},{"style":1611},[2178],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2180,"children":2181},{"style":1645},[2182],{"type":55,"value":849},{"type":50,"tag":1604,"props":2184,"children":2185},{"style":1611},[2186],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2188,"children":2189},{"style":1611},[2190],{"type":55,"value":2191},"]\n",{"type":50,"tag":1604,"props":2193,"children":2195},{"class":1606,"line":2194},19,[2196],{"type":50,"tag":1604,"props":2197,"children":2198},{"style":1611},[2199],{"type":55,"value":2200},"      },\n",{"type":50,"tag":1604,"props":2202,"children":2204},{"class":1606,"line":2203},20,[2205],{"type":50,"tag":1604,"props":2206,"children":2207},{"style":1611},[2208],{"type":55,"value":1975},{"type":50,"tag":1604,"props":2210,"children":2212},{"class":1606,"line":2211},21,[2213,2217,2221,2225,2229,2233,2238,2242],{"type":50,"tag":1604,"props":2214,"children":2215},{"style":1611},[2216],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2218,"children":2219},{"style":1987},[2220],{"type":55,"value":1031},{"type":50,"tag":1604,"props":2222,"children":2223},{"style":1611},[2224],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2226,"children":2227},{"style":1611},[2228],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2230,"children":2231},{"style":1611},[2232],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2234,"children":2235},{"style":1645},[2236],{"type":55,"value":2237},"users",{"type":50,"tag":1604,"props":2239,"children":2240},{"style":1611},[2241],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2243,"children":2244},{"style":1611},[2245],{"type":55,"value":1091},{"type":50,"tag":1604,"props":2247,"children":2249},{"class":1606,"line":2248},22,[2250,2254,2258,2262,2266],{"type":50,"tag":1604,"props":2251,"children":2252},{"style":1611},[2253],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2255,"children":2256},{"style":1987},[2257],{"type":55,"value":2027},{"type":50,"tag":1604,"props":2259,"children":2260},{"style":1611},[2261],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2263,"children":2264},{"style":1611},[2265],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2267,"children":2268},{"style":1611},[2269],{"type":55,"value":2040},{"type":50,"tag":1604,"props":2271,"children":2273},{"class":1606,"line":2272},23,[2274,2278,2282,2286,2290,2294,2298],{"type":50,"tag":1604,"props":2275,"children":2276},{"style":1611},[2277],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2279,"children":2280},{"style":1987},[2281],{"type":55,"value":2053},{"type":50,"tag":1604,"props":2283,"children":2284},{"style":1611},[2285],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2287,"children":2288},{"style":1611},[2289],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2291,"children":2292},{"style":1611},[2293],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2295,"children":2296},{"style":1645},[2297],{"type":55,"value":1272},{"type":50,"tag":1604,"props":2299,"children":2300},{"style":1611},[2301],{"type":55,"value":2302},"\"\n",{"type":50,"tag":1604,"props":2304,"children":2306},{"class":1606,"line":2305},24,[2307],{"type":50,"tag":1604,"props":2308,"children":2309},{"style":1611},[2310],{"type":55,"value":2200},{"type":50,"tag":1604,"props":2312,"children":2314},{"class":1606,"line":2313},25,[2315],{"type":50,"tag":1604,"props":2316,"children":2317},{"style":1611},[2318],{"type":55,"value":1975},{"type":50,"tag":1604,"props":2320,"children":2322},{"class":1606,"line":2321},26,[2323,2327,2331,2335,2339,2343,2348,2352],{"type":50,"tag":1604,"props":2324,"children":2325},{"style":1611},[2326],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2328,"children":2329},{"style":1987},[2330],{"type":55,"value":1031},{"type":50,"tag":1604,"props":2332,"children":2333},{"style":1611},[2334],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2336,"children":2337},{"style":1611},[2338],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2340,"children":2341},{"style":1611},[2342],{"type":55,"value":1642},{"type":50,"tag":1604,"props":2344,"children":2345},{"style":1645},[2346],{"type":55,"value":2347},"audit_log",{"type":50,"tag":1604,"props":2349,"children":2350},{"style":1611},[2351],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2353,"children":2354},{"style":1611},[2355],{"type":55,"value":1091},{"type":50,"tag":1604,"props":2357,"children":2359},{"class":1606,"line":2358},27,[2360,2364,2368,2372,2376],{"type":50,"tag":1604,"props":2361,"children":2362},{"style":1611},[2363],{"type":55,"value":1984},{"type":50,"tag":1604,"props":2365,"children":2366},{"style":1987},[2367],{"type":55,"value":2027},{"type":50,"tag":1604,"props":2369,"children":2370},{"style":1611},[2371],{"type":55,"value":1633},{"type":50,"tag":1604,"props":2373,"children":2374},{"style":1611},[2375],{"type":55,"value":1297},{"type":50,"tag":1604,"props":2377,"children":2378},{"style":1611},[2379],{"type":55,"value":2380}," false\n",{"type":50,"tag":1604,"props":2382,"children":2384},{"class":1606,"line":2383},28,[2385],{"type":50,"tag":1604,"props":2386,"children":2387},{"style":1611},[2388],{"type":55,"value":2389},"      }\n",{"type":50,"tag":1604,"props":2391,"children":2393},{"class":1606,"line":2392},29,[2394],{"type":50,"tag":1604,"props":2395,"children":2396},{"style":1611},[2397],{"type":55,"value":2398},"    ]\n",{"type":50,"tag":1604,"props":2400,"children":2402},{"class":1606,"line":2401},30,[2403],{"type":50,"tag":1604,"props":2404,"children":2405},{"style":1611},[2406],{"type":55,"value":2407},"  }\n",{"type":50,"tag":1604,"props":2409,"children":2411},{"class":1606,"line":2410},31,[2412],{"type":50,"tag":1604,"props":2413,"children":2414},{"style":1611},[2415],{"type":55,"value":2416},"}\n",{"type":50,"tag":58,"props":2418,"children":2419},{},[2420,2422,2427],{"type":55,"value":2421},"Rules for the ",{"type":50,"tag":75,"props":2423,"children":2425},{"className":2424},[],[2426],{"type":55,"value":88},{"type":55,"value":2428}," array:",{"type":50,"tag":115,"props":2430,"children":2431},{},[2432,2451,2468,2504],{"type":50,"tag":119,"props":2433,"children":2434},{},[2435,2437,2443,2445,2450],{"type":55,"value":2436},"Every table returned by db-schema should be included, even ones the user doesn't want (set ",{"type":50,"tag":75,"props":2438,"children":2440},{"className":2439},[],[2441],{"type":55,"value":2442},"should_sync: false",{"type":55,"value":2444},").\nTables the user didn't mention default to ",{"type":50,"tag":75,"props":2446,"children":2448},{"className":2447},[],[2449],{"type":55,"value":2442},{"type":55,"value":691},{"type":50,"tag":119,"props":2452,"children":2453},{},[2454,2459,2461,2467],{"type":50,"tag":75,"props":2455,"children":2457},{"className":2456},[],[2458],{"type":55,"value":2053},{"type":55,"value":2460}," is required only when ",{"type":50,"tag":75,"props":2462,"children":2464},{"className":2463},[],[2465],{"type":55,"value":2466},"should_sync: true",{"type":55,"value":691},{"type":50,"tag":119,"props":2469,"children":2470},{},[2471,2476,2477,2482,2484,2489,2491,2496,2498,2503],{"type":50,"tag":75,"props":2472,"children":2474},{"className":2473},[],[2475],{"type":55,"value":1402},{"type":55,"value":1065},{"type":50,"tag":75,"props":2478,"children":2480},{"className":2479},[],[2481],{"type":55,"value":1436},{"type":55,"value":2483}," must be present when ",{"type":50,"tag":75,"props":2485,"children":2487},{"className":2486},[],[2488],{"type":55,"value":2053},{"type":55,"value":2490}," is ",{"type":50,"tag":75,"props":2492,"children":2494},{"className":2493},[],[2495],{"type":55,"value":1304},{"type":55,"value":2497}," or ",{"type":50,"tag":75,"props":2499,"children":2501},{"className":2500},[],[2502],{"type":55,"value":1321},{"type":55,"value":691},{"type":50,"tag":119,"props":2505,"children":2506},{},[2507,2512,2513,2518,2519,2524],{"type":50,"tag":75,"props":2508,"children":2510},{"className":2509},[],[2511],{"type":55,"value":1489},{"type":55,"value":2483},{"type":50,"tag":75,"props":2514,"children":2516},{"className":2515},[],[2517],{"type":55,"value":2053},{"type":55,"value":2490},{"type":50,"tag":75,"props":2520,"children":2522},{"className":2521},[],[2523],{"type":55,"value":1338},{"type":55,"value":691},{"type":50,"tag":58,"props":2526,"children":2527},{},[2528,2530,2535],{"type":55,"value":2529},"On success you'll get back a source with a new ",{"type":50,"tag":75,"props":2531,"children":2533},{"className":2532},[],[2534],{"type":55,"value":849},{"type":55,"value":2536},". The first sync is triggered automatically.",{"type":50,"tag":974,"props":2538,"children":2540},{"id":2539},"step-6-register-a-webhook-only-when-any-schema-is-sync_type-webhook",[2541,2543,2548],{"type":55,"value":2542},"Step 6 — Register a webhook (only when any schema is ",{"type":50,"tag":75,"props":2544,"children":2546},{"className":2545},[],[2547],{"type":55,"value":1356},{"type":55,"value":2549},")",{"type":50,"tag":58,"props":2551,"children":2552},{},[2553,2555,2560,2562,2566],{"type":55,"value":2554},"Webhook-type schemas don't start receiving data just by existing — the external service needs to know where to POST\nevents, and PostHog needs to know how to verify them. This is a second call after source creation, not part of the\n",{"type":50,"tag":75,"props":2556,"children":2558},{"className":2557},[],[2559],{"type":55,"value":267},{"type":55,"value":2561}," payload. Do this ",{"type":50,"tag":67,"props":2563,"children":2564},{},[2565],{"type":55,"value":397},{"type":55,"value":2567}," telling the user the setup is complete, otherwise they\nhear \"syncs are running\" while the push channel is still unregistered.",{"type":50,"tag":58,"props":2569,"children":2570},{},[2571,2573,2578,2580,2585],{"type":55,"value":2572},"Only needed when at least one schema on the source has ",{"type":50,"tag":75,"props":2574,"children":2576},{"className":2575},[],[2577],{"type":55,"value":1356},{"type":55,"value":2579}," and ",{"type":50,"tag":75,"props":2581,"children":2583},{"className":2582},[],[2584],{"type":55,"value":2466},{"type":55,"value":2586},". Currently only\nStripe implements this flow; for everything else skip this step.",{"type":50,"tag":58,"props":2588,"children":2589},{},[2590,2592,2598,2600,2606],{"type":55,"value":2591},"Before calling create-webhook, check ",{"type":50,"tag":75,"props":2593,"children":2595},{"className":2594},[],[2596],{"type":55,"value":2597},"external-data-sources-webhook-info-retrieve({id})",{"type":55,"value":2599},". If it already returns\n",{"type":50,"tag":75,"props":2601,"children":2603},{"className":2602},[],[2604],{"type":55,"value":2605},"exists: true",{"type":55,"value":2607},", do NOT call create-webhook again — each successful call registers a new external endpoint and would\nresult in duplicate deliveries.",{"type":50,"tag":711,"props":2609,"children":2610},{},[2611,2675,2724],{"type":50,"tag":119,"props":2612,"children":2613},{},[2614,2615,2621,2623,2654,2657,2659,2665,2667,2673],{"type":55,"value":984},{"type":50,"tag":75,"props":2616,"children":2618},{"className":2617},[],[2619],{"type":55,"value":2620},"external-data-sources-create-webhook-create({id})",{"type":55,"value":2622},". PostHog:",{"type":50,"tag":115,"props":2624,"children":2625},{},[2626,2631,2636,2641],{"type":50,"tag":119,"props":2627,"children":2628},{},[2629],{"type":55,"value":2630},"creates the HogFunction that will receive webhook POSTs,",{"type":50,"tag":119,"props":2632,"children":2633},{},[2634],{"type":55,"value":2635},"builds a schema_mapping from external event types to PostHog schema ids,",{"type":50,"tag":119,"props":2637,"children":2638},{},[2639],{"type":55,"value":2640},"calls the source's API (e.g. Stripe) to register the webhook URL and subscribe to the relevant events,",{"type":50,"tag":119,"props":2642,"children":2643},{},[2644,2646,2652],{"type":55,"value":2645},"on Stripe, auto-captures the ",{"type":50,"tag":75,"props":2647,"children":2649},{"className":2648},[],[2650],{"type":55,"value":2651},"signing_secret",{"type":55,"value":2653}," and stores it securely.",{"type":50,"tag":790,"props":2655,"children":2656},{},[],{"type":55,"value":2658},"Returns ",{"type":50,"tag":75,"props":2660,"children":2662},{"className":2661},[],[2663],{"type":55,"value":2664},"{success, webhook_url, error}",{"type":55,"value":2666},". On success report the ",{"type":50,"tag":75,"props":2668,"children":2670},{"className":2669},[],[2671],{"type":55,"value":2672},"webhook_url",{"type":55,"value":2674}," to the user for their records — but\nthey don't need to paste it anywhere; registration is already done.",{"type":50,"tag":119,"props":2676,"children":2677},{},[2678,2680,2686,2688],{"type":55,"value":2679},"If ",{"type":50,"tag":75,"props":2681,"children":2683},{"className":2682},[],[2684],{"type":55,"value":2685},"success: false",{"type":55,"value":2687}," with a permissions error like \"API key doesn't have permission to create webhooks\":",{"type":50,"tag":115,"props":2689,"children":2690},{},[2691,2696,2707,2712],{"type":50,"tag":119,"props":2692,"children":2693},{},[2694],{"type":55,"value":2695},"The HogFunction is still created, just disabled.",{"type":50,"tag":119,"props":2697,"children":2698},{},[2699,2701,2706],{"type":55,"value":2700},"Ask the user to create the webhook manually in the source's dashboard using the returned ",{"type":50,"tag":75,"props":2702,"children":2704},{"className":2703},[],[2705],{"type":55,"value":2672},{"type":55,"value":691},{"type":50,"tag":119,"props":2708,"children":2709},{},[2710],{"type":55,"value":2711},"Have them copy the signing secret from the source's webhook settings.",{"type":50,"tag":119,"props":2713,"children":2714},{},[2715,2716,2722],{"type":55,"value":984},{"type":50,"tag":75,"props":2717,"children":2719},{"className":2718},[],[2720],{"type":55,"value":2721},"external-data-sources-update-webhook-inputs-create({id}, {inputs: {signing_secret: \"whsec_...\"}})",{"type":55,"value":2723}," to\nstore it. The HogFunction picks it up and verifies incoming payloads.",{"type":50,"tag":119,"props":2725,"children":2726},{},[2727,2729,2734,2736,2741,2742,2748,2750,2756],{"type":55,"value":2728},"Verify with ",{"type":50,"tag":75,"props":2730,"children":2732},{"className":2731},[],[2733],{"type":55,"value":2597},{"type":55,"value":2735},". A healthy webhook has ",{"type":50,"tag":75,"props":2737,"children":2739},{"className":2738},[],[2740],{"type":55,"value":2605},{"type":55,"value":1091},{"type":50,"tag":75,"props":2743,"children":2745},{"className":2744},[],[2746],{"type":55,"value":2747},"external_status.status: \"enabled\"",{"type":55,"value":2749},", and no ",{"type":50,"tag":75,"props":2751,"children":2753},{"className":2752},[],[2754],{"type":55,"value":2755},"error",{"type":55,"value":691},{"type":50,"tag":58,"props":2758,"children":2759},{},[2760,2762,2768,2770,2776],{"type":55,"value":2761},"Webhooks are supplementary to bulk sync. The first load of a webhook-enabled schema is still done via polling\n(",{"type":50,"tag":75,"props":2763,"children":2765},{"className":2764},[],[2766],{"type":55,"value":2767},"initial_sync_complete",{"type":55,"value":2769}," flips to true when done); after that, the webhook becomes the primary ingestion path. A\nwebhook schema will still have a ",{"type":50,"tag":75,"props":2771,"children":2773},{"className":2772},[],[2774],{"type":55,"value":2775},"sync_frequency",{"type":55,"value":2777}," that schedules a periodic bulk refresh as a safety net. This is\nexpected — not something to \"fix\".",{"type":50,"tag":974,"props":2779,"children":2781},{"id":2780},"step-7-confirm-and-explain-what-happens-next",[2782],{"type":55,"value":2783},"Step 7 — Confirm and explain what happens next",{"type":50,"tag":58,"props":2785,"children":2786},{},[2787],{"type":55,"value":2788},"After creation (and, for webhook schemas, after Step 6):",{"type":50,"tag":115,"props":2790,"children":2791},{},[2792,2803,2824,2837],{"type":50,"tag":119,"props":2793,"children":2794},{},[2795,2796,2801],{"type":55,"value":984},{"type":50,"tag":75,"props":2797,"children":2799},{"className":2798},[],[2800],{"type":55,"value":376},{"type":55,"value":2802}," to show the user the initial state.",{"type":50,"tag":119,"props":2804,"children":2805},{},[2806,2808,2814,2816,2822],{"type":55,"value":2807},"Explain: every enabled schema enters ",{"type":50,"tag":75,"props":2809,"children":2811},{"className":2810},[],[2812],{"type":55,"value":2813},"Running",{"type":55,"value":2815},", then moves to ",{"type":50,"tag":75,"props":2817,"children":2819},{"className":2818},[],[2820],{"type":55,"value":2821},"Completed",{"type":55,"value":2823}," when the first sync finishes. First\nsyncs can take anywhere from seconds to hours depending on row count — a multi-million-row table is fine, just\nslow.",{"type":50,"tag":119,"props":2825,"children":2826},{},[2827,2829,2835],{"type":55,"value":2828},"Tell them how to query: ",{"type":50,"tag":75,"props":2830,"children":2832},{"className":2831},[],[2833],{"type":55,"value":2834},"SELECT * FROM {prefix}_{table_name} LIMIT 10",{"type":55,"value":2836}," in HogQL.",{"type":50,"tag":119,"props":2838,"children":2839},{},[2840],{"type":55,"value":2841},"Offer to check back in a few minutes to confirm the initial syncs succeeded.",{"type":50,"tag":108,"props":2843,"children":2845},{"id":2844},"cdc-setup-for-postgres-optional-when-requested",[2846],{"type":55,"value":2847},"CDC setup for Postgres (optional, when requested)",{"type":50,"tag":58,"props":2849,"children":2850},{},[2851],{"type":55,"value":2852},"If the user wants near-real-time replication from Postgres:",{"type":50,"tag":711,"props":2854,"children":2855},{},[2856,2876,2888],{"type":50,"tag":119,"props":2857,"children":2858},{},[2859,2861,2866,2868,2874],{"type":55,"value":2860},"Before calling db-schema, run ",{"type":50,"tag":75,"props":2862,"children":2864},{"className":2863},[],[2865],{"type":55,"value":291},{"type":55,"value":2867}," with their Postgres creds.\nIt returns ",{"type":50,"tag":75,"props":2869,"children":2871},{"className":2870},[],[2872],{"type":55,"value":2873},"{valid, errors[]}",{"type":55,"value":2875}," listing anything missing (wal_level, replication slot, publication, permissions).",{"type":50,"tag":119,"props":2877,"children":2878},{},[2879,2880,2886],{"type":55,"value":2679},{"type":50,"tag":75,"props":2881,"children":2883},{"className":2882},[],[2884],{"type":55,"value":2885},"valid: false",{"type":55,"value":2887},", present the errors and ask the user to fix on the Postgres side. Don't try to create a CDC\nsource that will immediately fail.",{"type":50,"tag":119,"props":2889,"children":2890},{},[2891,2893,2899,2901,2906],{"type":55,"value":2892},"Once prerequisites pass, proceed to db-schema and create. Set ",{"type":50,"tag":75,"props":2894,"children":2896},{"className":2895},[],[2897],{"type":55,"value":2898},"sync_type: \"cdc\"",{"type":55,"value":2900}," on the tables that need it, and\ninclude ",{"type":50,"tag":75,"props":2902,"children":2904},{"className":2903},[],[2905],{"type":55,"value":1489},{"type":55,"value":2907}," for each (CDC requires them).",{"type":50,"tag":108,"props":2909,"children":2911},{"id":2910},"important-notes",[2912],{"type":55,"value":2913},"Important notes",{"type":50,"tag":115,"props":2915,"children":2916},{},[2917,2935,2945,2962,2972,3004,3029,3047],{"type":50,"tag":119,"props":2918,"children":2919},{},[2920,2925,2927,2933],{"type":50,"tag":67,"props":2921,"children":2922},{},[2923],{"type":55,"value":2924},"Always validate creds with db-schema before create.",{"type":55,"value":2926}," The create endpoint will accept invalid creds and then fail\nasynchronously — the source appears in the list with status ",{"type":50,"tag":75,"props":2928,"children":2930},{"className":2929},[],[2931],{"type":55,"value":2932},"Error",{"type":55,"value":2934}," and no tables. Skipping the validation step\njust pushes the failure into the background.",{"type":50,"tag":119,"props":2936,"children":2937},{},[2938,2943],{"type":50,"tag":67,"props":2939,"children":2940},{},[2941],{"type":55,"value":2942},"Present the table list before creating.",{"type":55,"value":2944}," Large databases may have hundreds of tables. Don't auto-select them all\n— row counts and relevance matter for billing. Let the user opt in explicitly.",{"type":50,"tag":119,"props":2946,"children":2947},{},[2948,2953,2955,2960],{"type":50,"tag":67,"props":2949,"children":2950},{},[2951],{"type":55,"value":2952},"Don't invent schemas.",{"type":55,"value":2954}," Every entry in the ",{"type":50,"tag":75,"props":2956,"children":2958},{"className":2957},[],[2959],{"type":55,"value":88},{"type":55,"value":2961}," array must correspond to a real table from the db-schema\nresponse. You can't \"also add an orders table\" unless db-schema found one.",{"type":50,"tag":119,"props":2963,"children":2964},{},[2965,2970],{"type":50,"tag":67,"props":2966,"children":2967},{},[2968],{"type":55,"value":2969},"Prefix is load-bearing.",{"type":55,"value":2971}," It's part of every HogQL query the user will ever write against these tables. Pick\nsomething short, descriptive, and not already taken.",{"type":50,"tag":119,"props":2973,"children":2974},{},[2975,2980,2982,2987,2989,2994,2996,3002],{"type":50,"tag":67,"props":2976,"children":2977},{},[2978],{"type":55,"value":2979},"Prefer the secure connect-link for any credentials.",{"type":55,"value":2981}," Use ",{"type":50,"tag":75,"props":2983,"children":2985},{"className":2984},[],[2986],{"type":55,"value":96},{"type":55,"value":2988}," so the user\nauthenticates in their browser — the connect page renders the source's full connection form (OAuth and credential\noptions alike) and stores the result without creating the source. Don't collect OAuth tokens or database passwords\nin chat; pass the ",{"type":50,"tag":75,"props":2990,"children":2992},{"className":2991},[],[2993],{"type":55,"value":689},{"type":55,"value":2995}," reference to setup — source creation always happens through setup, not the UI.\n(An already-connected OAuth integration can also be passed directly via its id key, e.g.\n",{"type":50,"tag":75,"props":2997,"children":2999},{"className":2998},[],[3000],{"type":55,"value":3001},"{\"hubspot_integration_id\": 123}",{"type":55,"value":3003},".)",{"type":50,"tag":119,"props":3005,"children":3006},{},[3007,3012,3014,3019,3021,3027],{"type":50,"tag":67,"props":3008,"children":3009},{},[3010],{"type":55,"value":3011},"Webhooks are a separate step after create.",{"type":55,"value":3013}," Setting ",{"type":50,"tag":75,"props":3015,"children":3017},{"className":3016},[],[3018],{"type":55,"value":1356},{"type":55,"value":3020}," on a schema doesn't register the\nwebhook — the ",{"type":50,"tag":75,"props":3022,"children":3024},{"className":3023},[],[3025],{"type":55,"value":3026},"create-webhook",{"type":55,"value":3028}," call does. Always follow create → create-webhook → webhook-info for webhook-type\nschemas, and never leave a webhook schema dangling without registration (it just won't receive events).",{"type":50,"tag":119,"props":3030,"children":3031},{},[3032,3037,3039,3045],{"type":50,"tag":67,"props":3033,"children":3034},{},[3035],{"type":55,"value":3036},"Webhook support is source-specific and sparse.",{"type":55,"value":3038}," Currently only Stripe implements ",{"type":50,"tag":75,"props":3040,"children":3042},{"className":3041},[],[3043],{"type":55,"value":3044},"WebhookSource",{"type":55,"value":3046},". Don't promise\nwebhooks for Hubspot, Salesforce, or Postgres — they'll use polling sync.",{"type":50,"tag":119,"props":3048,"children":3049},{},[3050,3055,3057,3063],{"type":50,"tag":67,"props":3051,"children":3052},{},[3053],{"type":55,"value":3054},"Row counts drive billing.",{"type":55,"value":3056}," Warehouse syncing is metered by rows synced. A chatty 500M-row events table synced\nhourly is very different from a 10k-row dimension table synced daily. Flag large tables and offer longer sync\nfrequencies (",{"type":50,"tag":75,"props":3058,"children":3060},{"className":3059},[],[3061],{"type":55,"value":3062},"sync_frequency: \"24hour\"",{"type":55,"value":3064},") as the default.",{"type":50,"tag":3066,"props":3067,"children":3068},"style",{},[3069],{"type":55,"value":3070},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":3072,"total":3192},[3073,3090,3108,3125,3142,3156,3174],{"slug":3074,"name":3074,"fn":3075,"description":3076,"org":3077,"tags":3078,"stars":29,"repoUrl":30,"updatedAt":3089},"analyzing-experiment-session-replays","analyze session replays for PostHog experiments","Analyze session replay patterns across experiment variants to understand user behavior differences. Use when the user wants to see how users interact with different experiment variants, identify usability issues, compare behavior patterns between control and test groups, or get qualitative insights to complement quantitative experiment results.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3079,3082,3085,3086],{"name":3080,"slug":3081,"type":13},"Analytics","analytics",{"name":3083,"slug":3084,"type":13},"Design","design",{"name":9,"slug":8,"type":13},{"name":3087,"slug":3088,"type":13},"User Research","user-research","2026-04-06T18:44:38.291781",{"slug":3091,"name":3091,"fn":3092,"description":3093,"org":3094,"tags":3095,"stars":29,"repoUrl":30,"updatedAt":3107},"assessing-heatmaps","analyze page heatmaps and suggest improvements","Assesses what a page's heatmap is telling you and recommends concrete changes. Pulls click \u002F rageclick \u002F scroll-depth data for a URL, names the hot elements by cross-referencing autocapture events on the same page, and can create a saved heatmap the user opens in PostHog, then summarizes the behavior and proposes improvements.\nTRIGGER when: user asks what a heatmap shows, why people aren't clicking something, where users rage-click, how far they scroll, what to change on a page based on heatmap\u002Fclick data, or to 'analyze\u002Fassess\u002Freview the heatmap' for a URL.\nDO NOT TRIGGER when: the user only wants to create a saved heatmap screenshot with no analysis (use heatmaps-saved-create directly), or is asking about session replay in general (use investigating-replay).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3096,3097,3100,3101,3104],{"name":3080,"slug":3081,"type":13},{"name":3098,"slug":3099,"type":13},"Frontend","frontend",{"name":9,"slug":8,"type":13},{"name":3102,"slug":3103,"type":13},"Product Management","product-management",{"name":3105,"slug":3106,"type":13},"UX Design","ux-design","2026-06-05T07:40:43.37798",{"slug":3109,"name":3109,"fn":3110,"description":3111,"org":3112,"tags":3113,"stars":29,"repoUrl":30,"updatedAt":3124},"auditing-experiments-flags","audit PostHog experiments and feature flags","Audit PostHog experiments and feature flags for configuration issues, staleness, and best-practice violations. Read when the user asks to audit, health-check, or review experiments or feature flags, check flag hygiene, or verify experiment setup.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3114,3117,3120,3121],{"name":3115,"slug":3116,"type":13},"Audit","audit",{"name":3118,"slug":3119,"type":13},"Feature Flags","feature-flags",{"name":9,"slug":8,"type":13},{"name":3122,"slug":3123,"type":13},"QA","qa","2026-04-06T18:44:30.657553",{"slug":3126,"name":3126,"fn":3127,"description":3128,"org":3129,"tags":3130,"stars":29,"repoUrl":30,"updatedAt":3141},"authoring-scouts","author and edit PostHog Signals scouts","How to author, edit, and adapt PostHog Signals scouts — the scheduled agents that scan a project and write reports into the Signals inbox. Use when a user wants to customize a canonical scout for their own setup (narrow its scope, retune its thresholds, add disqualifiers), tweak a scout's schedule or dry-run posture, or write a brand-new scout from scratch for a specific use case (a custom event, a product surface no canonical scout covers), or steer a scout without editing it at all by leaving it a note. Covers the scout SKILL.md anatomy, the report contract, the dedupe + scratchpad-memory conventions, the scout-notes steering channel, the per-team skills-store path vs the canonical in-repo path, and the write-and-inspect test loop (with dry-run as an optional safety net). Trigger on \"write\u002Fedit\u002Fcustomize a signals scout\", \"new scout for X\", \"tune my scout schedule\", \"make a scout that watches \u003Cevent>\", \"leave a note for \u002F give feedback to a scout\", \"tell the scouts about X\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3131,3134,3137,3140],{"name":3132,"slug":3133,"type":13},"Agents","agents",{"name":3135,"slug":3136,"type":13},"Automation","automation",{"name":3138,"slug":3139,"type":13},"Observability","observability",{"name":9,"slug":8,"type":13},"2026-07-28T05:33:45.509154",{"slug":3143,"name":3143,"fn":3144,"description":3145,"org":3146,"tags":3147,"stars":29,"repoUrl":30,"updatedAt":3155},"building-a-dashboard","build and update PostHog dashboards","Build a new dashboard, or update an existing one, from a set of insights — the same job the in-app assistant does with its upsert-dashboard tool, but over MCP. Use when a user asks to create a dashboard, put several metrics\u002Fcharts together on one page, assemble a dashboard for a topic (product analytics, retention, revenue, activation, etc.), or add\u002Fremove\u002Freplace insights on a dashboard they already have. Covers deciding create vs update, reusing existing insights vs creating new ones, and using PostHog's vetted dashboard templates as reference for what a strong dashboard on a topic looks like.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3148,3149,3152],{"name":3080,"slug":3081,"type":13},{"name":3150,"slug":3151,"type":13},"Dashboards","dashboards",{"name":3153,"slug":3154,"type":13},"MCP","mcp","2026-07-21T06:07:38.060598",{"slug":3157,"name":3157,"fn":3158,"description":3159,"org":3160,"tags":3161,"stars":29,"repoUrl":30,"updatedAt":3173},"checking-deploy-timing","correlate PostHog deployments with GitHub commits","Determine when a PostHog code change reached a given environment by reading the hidden GIT deploy annotations in the project and correlating them with the merge commit on GitHub. Use when PostHog staff ask \"when was X deployed\", \"is my change live in the US\u002FEU yet\", \"has my PR shipped\", \"did the fix roll out to prod-us\", or otherwise want to know whether\u002Fwhen a commit, PR, or feature went out to a region. Do not answer deploy-timing questions from event\u002Fdata volume alone — that only shows when data changed, not when code shipped.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3162,3165,3168,3171,3172],{"name":3163,"slug":3164,"type":13},"Deployment","deployment",{"name":3166,"slug":3167,"type":13},"Git","git",{"name":3169,"slug":3170,"type":13},"GitHub","github",{"name":3138,"slug":3139,"type":13},{"name":9,"slug":8,"type":13},"2026-06-28T07:46:59.53536",{"slug":3175,"name":3175,"fn":3176,"description":3177,"org":3178,"tags":3179,"stars":29,"repoUrl":30,"updatedAt":3191},"choosing-trend-or-slope-view","visualize trends and growth over time","Clarify how to visualize change over a time range before building a trend. Use whenever the user asks how much something changed, grew, dropped, improved, or regressed between two points or periods — \"how much did X change from A to B\", \"before vs after\", \"start vs end\", \"week over week\", \"compare this month to last\", \"change over time\" — or mentions a \"slope chart\" \u002F \"slopegraph\". Two readings of \"change\" need different charts: the whole trend (a line, every interval) versus just the two endpoints (a slope, start vs end). Ask which they want, then render it. Not for choosing a saved insight ChartDisplayType in the insight editor.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3180,3181,3184,3187,3188],{"name":3080,"slug":3081,"type":13},{"name":3182,"slug":3183,"type":13},"Charts","charts",{"name":3185,"slug":3186,"type":13},"Data Visualization","data-visualization",{"name":9,"slug":8,"type":13},{"name":3189,"slug":3190,"type":13},"Reporting","reporting","2026-06-18T08:18:57.960157",56,{"items":3194,"total":3351},[3195,3210,3220,3231,3244,3259,3275,3288,3300,3315,3325,3341],{"slug":3196,"name":3196,"fn":3197,"description":3198,"org":3199,"tags":3200,"stars":3207,"repoUrl":3208,"updatedAt":3209},"analyzing-expensive-users","analyze expensive users in AI observability","Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3201,3202,3205,3206],{"name":3080,"slug":3081,"type":13},{"name":3203,"slug":3204,"type":13},"Cost Optimization","cost-optimization",{"name":3138,"slug":3139,"type":13},{"name":9,"slug":8,"type":13},35568,"https:\u002F\u002Fgithub.com\u002FPostHog\u002Fposthog","2026-07-28T05:34:11.117757",{"slug":3211,"name":3211,"fn":3212,"description":3213,"org":3214,"tags":3215,"stars":3207,"repoUrl":3208,"updatedAt":3219},"auditing-endpoints","audit PostHog project endpoints","Audit every endpoint in a PostHog project for staleness, failed materialisations, and unused materialised versions. Use when the user asks \"what endpoints can I clean up?\", \"are any of my endpoints broken?\", \"which materialised versions are still being called?\", or wants a one-shot cleanup pass over the Endpoints product. Produces a prioritised report grouped by issue type, with recommended actions but does not modify anything without explicit confirmation.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3216,3217,3218],{"name":3080,"slug":3081,"type":13},{"name":3115,"slug":3116,"type":13},{"name":9,"slug":8,"type":13},"2026-06-08T08:08:33.693989",{"slug":3221,"name":3221,"fn":3222,"description":3223,"org":3224,"tags":3225,"stars":3207,"repoUrl":3208,"updatedAt":3230},"auditing-warehouse-source-health","audit PostHog data warehouse source health","Audit the health of a PostHog project's data warehouse sources and syncs — find every broken or degraded source connection, sync schema, and webhook channel. Use when the user asks \"why are my imports failing?\", \"what's broken with my sources?\", \"why is my warehouse data stale?\", or wants a one-shot triage of source\u002Fsync health before deciding where to dig in. Produces a prioritized report grouped by severity, with recommended next steps. For materialized-view health use `auditing-warehouse-view-health`; for a single failing sync use `diagnosing-failed-warehouse-syncs`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3226,3227,3228,3229],{"name":3115,"slug":3116,"type":13},{"name":21,"slug":22,"type":13},{"name":3138,"slug":3139,"type":13},{"name":9,"slug":8,"type":13},"2026-06-18T08:22:57.67984",{"slug":3232,"name":3232,"fn":3233,"description":3234,"org":3235,"tags":3236,"stars":3207,"repoUrl":3208,"updatedAt":3243},"auditing-warehouse-view-health","audit PostHog materialized view health","Audit the health of a PostHog project's materialized views (saved queries) — find every failed materialization and flag unused or stale materialized views that cost storage and compute. Use when the user asks \"which of my views are broken?\", \"why is this materialized view failing?\", \"are any of my views wasting compute?\", or wants a one-shot triage of view health. For source\u002Fsync health use `auditing-warehouse-source-health`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3237,3238,3239,3242],{"name":3115,"slug":3116,"type":13},{"name":21,"slug":22,"type":13},{"name":3240,"slug":3241,"type":13},"Performance","performance",{"name":9,"slug":8,"type":13},"2026-06-18T08:25:10.936787",{"slug":3245,"name":3245,"fn":3246,"description":3247,"org":3248,"tags":3249,"stars":3207,"repoUrl":3208,"updatedAt":3258},"authoring-error-tracking-alerts","author PostHog error tracking alerts","Author error tracking alerts that fire when an issue is created, reopened, or starts spiking. Use when the user asks to set up error notifications, route exceptions to Slack\u002Fwebhook\u002FLinear, or evaluate which error events are worth alerting on. Covers trigger-event selection, integration choice, dedup against existing alerts, and shipping with the canonical message body shape.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3250,3253,3256,3257],{"name":3251,"slug":3252,"type":13},"Alerting","alerting",{"name":3254,"slug":3255,"type":13},"Debugging","debugging",{"name":3138,"slug":3139,"type":13},{"name":9,"slug":8,"type":13},"2026-06-18T08:24:40.318583",{"slug":3260,"name":3260,"fn":3261,"description":3262,"org":3263,"tags":3264,"stars":3207,"repoUrl":3208,"updatedAt":3274},"authoring-log-alerts","author log alerts in PostHog","Author useful, low-noise log alerts on services in a PostHog project. Use when the user asks to set up alerts for their logs, suggest alerts they should add, or evaluate whether a service is worth monitoring. Covers service triage, baseline characterisation, threshold drafting, back-testing via simulate, and shipping with a notification destination.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3265,3266,3269,3270,3273],{"name":3080,"slug":3081,"type":13},{"name":3267,"slug":3268,"type":13},"Monitoring","monitoring",{"name":3138,"slug":3139,"type":13},{"name":3271,"slug":3272,"type":13},"Operations","operations",{"name":9,"slug":8,"type":13},"2026-07-18T05:10:54.430898",{"slug":3276,"name":3276,"fn":3277,"description":3278,"org":3279,"tags":3280,"stars":3207,"repoUrl":3208,"updatedAt":3287},"building-workflows","build and edit PostHog workflows","Build, edit, test, enable, and monitor PostHog workflows over MCP. Author the action\u002Fedge graph so it runs and opens cleanly in the visual editor, then change drafts surgically with patch operations. Use when asked to build, set up, automate, change, fix, or debug a workflow, campaign, broadcast, drip sequence, or event-triggered automation in the workflows product.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3281,3282,3283,3284],{"name":3135,"slug":3136,"type":13},{"name":3153,"slug":3154,"type":13},{"name":9,"slug":8,"type":13},{"name":3285,"slug":3286,"type":13},"Workflow Automation","workflow-automation","2026-07-28T05:34:12.167015",{"slug":3289,"name":3289,"fn":3290,"description":3291,"org":3292,"tags":3293,"stars":3207,"repoUrl":3208,"updatedAt":3299},"check-posthog-loading","inspect PostHog SDK loading across URLs","Inspect how the PostHog JavaScript SDK is loaded across a list of URLs. Use to confirm consistent installation across pages, find pages missing the snippet, detect mismatched API keys or hosts between pages, and verify the load method (head snippet vs deferred vs array.js).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3294,3295,3296,3297,3298],{"name":3080,"slug":3081,"type":13},{"name":3254,"slug":3255,"type":13},{"name":3098,"slug":3099,"type":13},{"name":3138,"slug":3139,"type":13},{"name":9,"slug":8,"type":13},"2026-05-07T05:56:19.828048",{"slug":3301,"name":3301,"fn":3302,"description":3303,"org":3304,"tags":3305,"stars":3207,"repoUrl":3208,"updatedAt":3314},"consuming-endpoints-from-client-code","integrate PostHog endpoints into client applications","Wire a PostHog endpoint into a client app or SDK. Covers fetching the OpenAPI spec, generating a typed client with openapi-generator or @hey-api\u002Fopenapi-ts, sending the right auth header, shaping the variables payload (HogQL code_name vs insight breakdown property), handling rate-limit and materialised-endpoint error responses. Use when the user says \"how do I call my endpoint\", \"generate a client for this\", or \"what auth header do I use\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3306,3309,3310,3311],{"name":3307,"slug":3308,"type":13},"API Development","api-development",{"name":3098,"slug":3099,"type":13},{"name":9,"slug":8,"type":13},{"name":3312,"slug":3313,"type":13},"SDK","sdk","2026-06-08T08:08:34.929454",{"slug":3316,"name":3316,"fn":3317,"description":3318,"org":3319,"tags":3320,"stars":3207,"repoUrl":3208,"updatedAt":3324},"copying-endpoints-across-projects","copy PostHog endpoints across projects","Copy a PostHog endpoint (a saved HogQL\u002Finsight query exposed as an API route) to another project in the same organization, or duplicate it under a new name in the same project. Use when the user wants to duplicate an endpoint, promote an endpoint from staging to production, replicate an endpoint's query\u002Fvariables\u002Ffreshness config in another workspace, or clone an endpoint to iterate on it. Unlike feature flags and experiments, endpoints have NO native cross-project copy tool — this skill covers the read-then-recreate flow (endpoint-get then endpoint-create), the active-project switching it requires, name-collision checks, and the safe defaults (land unmaterialised in the target, verify with endpoint-run). Does not cover editing endpoint versions (see managing-endpoint-versions) or authoring a brand-new endpoint from scratch (see creating-an-endpoint).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3321,3322,3323],{"name":3307,"slug":3308,"type":13},{"name":3271,"slug":3272,"type":13},{"name":9,"slug":8,"type":13},"2026-07-15T05:29:58.442727",{"slug":3326,"name":3326,"fn":3327,"description":3328,"org":3329,"tags":3330,"stars":3207,"repoUrl":3208,"updatedAt":3340},"creating-ai-subscription","schedule recurring AI-generated PostHog reports","Create a recurring AI-generated PostHog report — schedule a free-text prompt to run on a cron, with the LLM-synthesized markdown delivered to email or Slack on each tick. Use when the user wants a recurring AI summary of X on any cadence (daily, weekly, monthly, yearly) rather than a one-off report. (To attach an AI summary to an existing insight\u002Fdashboard subscription instead of a free-text prompt, see `managing-subscriptions` and its `summary_enabled` option.)\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3331,3332,3335,3336,3337],{"name":3135,"slug":3136,"type":13},{"name":3333,"slug":3334,"type":13},"Email","email",{"name":9,"slug":8,"type":13},{"name":3189,"slug":3190,"type":13},{"name":3338,"slug":3339,"type":13},"Slack","slack","2026-06-09T07:32:27.935712",{"slug":3342,"name":3342,"fn":3343,"description":3344,"org":3345,"tags":3346,"stars":3207,"repoUrl":3208,"updatedAt":3350},"creating-an-endpoint","create PostHog API endpoints","Create a PostHog endpoint with the right shape on the first try — covers query kind choice, name conventions, what to expose as variables (HogQL code_name vs insight breakdown), data_freshness_seconds, and whether to materialise on day one. Use when the user says \"create an endpoint\", \"expose this query as an API\", \"turn this insight into an endpoint\", or asks for help structuring a new endpoint. Steers away from common mistakes: materialising a query with cohort breakdowns or compare mode, inline-only variables on a materialised endpoint, unbounded date ranges, ambiguous names.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[3347,3348,3349],{"name":3080,"slug":3081,"type":13},{"name":3307,"slug":3308,"type":13},{"name":9,"slug":8,"type":13},"2026-06-08T08:08:29.624498",231]