Skip to main content

setting-up-a-data-warehouse-source

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.

Informations de source

Dépôt
PostHog/ai-plugin
Dernière activité de la source
5 octobre 2026 à 12:50
Langue détectée de SKILL.md
anglais
Étoiles
88
Forks
18

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
2 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
setting-up-a-data-warehouse-source
description
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.
# Setting up a data warehouse source Use this skill when the user wants to connect an external data source to PostHog's data warehouse for the first time. **Default to the one-step flow:** `data-warehouse-source-setup` validates credentials, discovers every table, enables them with sensible sync defaults (incremental where possible), and creates the source in a single call — no `schemas` array to assemble. For credentials, hand the user a secure browser link with `data-warehouse-source-connect-link` instead of collecting secrets in chat. Only drop to the manual `wizard → db-schema → create` flow when the user wants to hand-pick which tables sync or set non-default sync types per table. ## When to use this skill - The user wants to connect a new source: "connect Stripe", "import my Postgres orders table", "sync Hubspot contacts" - The user isn't sure what source types PostHog supports - The user has credentials but doesn't know how to structure the `schemas` payload - The user wants guidance on which sync method to pick per table ## Available tools | Tool | Purpose | | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | `data-warehouse-source-connect-link` | **Preferred for credentials** — get a secure browser/OAuth link so the user authenticates without pasting secrets in chat | | `data-warehouse-source-setup` | **Preferred to create** — one call: validate creds, discover tables, apply sync defaults, create the source | | `external-data-sources-wizard` | Discover which source types exist and what fields each needs (advanced flow) | | `external-data-sources-db-schema` | Validate credentials and list tables with available sync methods per table (advanced flow) | | `external-data-sources-create` | Advanced create — requires a `schemas` array built from the db-schema response | | `external-data-sources-check-cdc-prerequisites-create` | Postgres CDC pre-flight check (optional, only for Postgres CDC) | | `external-data-sources-webhook-info-retrieve` | Check if a source supports webhooks and whether one has been registered | | `external-data-sources-create-webhook-create` | Register a webhook with the external service after source creation | | `external-data-sources-update-webhook-inputs-create` | Supply the signing secret manually when auto-registration failed | | `external-data-sources-list` | After creation, confirm the source is listed and see its initial status | | `external-data-schemas-list` | See per-table sync status once the source is created | ## Pre-flight: credential gotchas that cause most failures Surface these **before** collecting credentials — they're the top reasons setup fails on the first try. Validating against them up front avoids burning credential prompts on retries. - **The host must be reachable from PostHog's network.** `localhost`, `127.0.0.1`, and private/RFC-1918 hosts (`10.x`, `192.168.x`, `172.16–31.x`) are rejected — PostHog runs the connection from its own infrastructure, not the user's machine. Serverless/managed Postgres (Neon, Supabase, RDS behind strict rules) often also needs PostHog's egress IPs allowlisted first. If the DB isn't publicly reachable, route to the browser deep-link (`data-warehouse-source-connect-link`) or an SSH tunnel rather than collecting credentials that can't validate. - **Supabase is Postgres — don't collect it twice.** Use the **Session pooler** connection, not the direct host (the direct host is IPv6-only). The pooler host looks like `aws-0-<region>.pooler.supabase.com`, the **username** must be `postgres.<project-ref>`, and the **port is 6543** (not 5432). The password is the **database** password (Settings → Database), which is distinct from the `anon`/`service_role` JWT keys and from the Supabase account password. If `SUPABASE_URL` is in the project env, derive the project ref from `db.<ref>.supabase.co` to pre-fill these instead of asking the user to guess. - **Many SaaS sources need a specific key type or plan** — get the right one before the create call fails: - **Stripe** — a _restricted_ key (`rk_live_…`), not the standard secret key (`sk_live_…`). - **RevenueCat** — a v2 secret key (`sk_…`) with the read scopes enabled. - **Sentry** — an internal-integration token, not a DSN and not a personal auth token. - **Convex** — requires the Professional plan. - **Twilio** — API Key SID + Secret, not the account auth token. - **Mailchimp** — the key carries its datacenter suffix (`key-usX`). - For send-only services (Resend, Mailgun), the key already in the project env is often restricted; the warehouse import needs a full/read-access key. - **Never pass an unresolved secret reference.** If a credential field is still a `{"secretRef": ...}` object, PostHog can't resolve it — the create/db-schema/setup calls reject it with a clear error. Resolve it to the real value first, or collect credentials via `data-warehouse-source-connect-link` and pass the resulting `credential_id`. ## Recommended: one-step setup Most setups should use this path — it avoids the most common failures (skipping db-schema, malformed `schemas`, missing required fields). 1. **Discover the source type and its fields** (optional): `external-data-sources-wizard` lists every source type and the credential fields each needs. Use it to know what to ask the user for; skip it if the source type is obvious. 2. **Collect credentials securely**: call `data-warehouse-source-connect-link({source_type})`. It returns a `connect_url` to a minimal connect page rendering the source's full connection form — the user authorizes via OAuth or enters credentials there, whichever the source offers (the response's `auth_method` tells you which to expect). The page validates the details against a live connection and stashes them encrypted in a temporary store — it does NOT create the source. After the user confirms they're done, find the stored credential id via `data-warehouse-stored-credentials-list` (filter by `source_type`, newest first; the page also shows the id to the user) and pass `{"credential_id": <id>}` to setup. Stored credentials are single-use — deleted as soon as setup consumes them — and expire after 24 hours. Never ask the user to paste raw database passwords, API keys, or OAuth tokens into the chat. 3. **Create in one call**: `data-warehouse-source-setup({source_type, payload, prefix})`. The server validates credentials, discovers all tables, enables them with sync defaults (incremental where a tracking column exists, else append, else full_refresh — never CDC), sets `created_via=mcp`, and creates the source. The `payload` carries a credential reference (`{"credential_id": ...}` or the OAuth integration id key) — or inline credentials for headless automation; no `schemas` array is needed. On success you get the new source `id`; call `external-data-schemas-list` to show the user what was enabled and how each table will sync. Notes specific to this path: - **All discovered tables are enabled.** That's intended (incremental defaults keep ongoing cost low), but flag row counts for very large tables and offer the advanced flow if the user wants to sync only a subset. - **Webhooks are auto-registered** for sources that support them (currently Stripe). Check the `webhook` key in the setup response: on success, webhook-capable tables sync in real time and webhook-only tables (e.g. Stripe Discount) are enabled too; on failure (e.g. the API key can't create webhooks), tables keep the polling defaults and webhook-only tables stay disabled — relay the `webhook.error` to the user and offer Step 6 to register manually. If `webhook.pending_inputs` is non-empty, collect those values and submit via `external-data-sources-update-webhook-inputs-create`. CDC is never chosen automatically; use the advanced flow + CDC steps for near-real-time Postgres. - Inline credentials in `payload` still work for headless/automation, but prefer the connect-link handoff above. ## Advanced: hand-pick tables (three-step flow) Use this when the user wants to choose exactly which tables sync or set non-default sync types. Don't try to shortcut to `external-data-sources-create` — you need the db-schema response to build a valid `schemas` payload. ```text ┌────────────────────┐ │ 1. wizard │ What source types exist? What fields does each need? └────────┬───────────┘ ▼ ┌────────────────────┐ │ 2. db-schema │ Validate creds. List tables + available sync methods per table. └────────┬───────────┘ ▼ ┌────────────────────┐ │ 3. create │ Send source_type + credentials + schemas[] to actually create. └────────────────────┘ ``` ## Workflow ### Step 1 — Discover the source type Call `external-data-sources-wizard` **with `source_type` set to the kind(s) you need** (comma-separated, e.g. `Postgres,Stripe`). The unfiltered response describes every supported source and is hundreds of KB — large enough to blow your context budget. Only omit `source_type` when you genuinely need to enumerate every available type, and when you do, pass `fields: ['*.name', '*.caption']` so the response carries just the names instead of every source's config field definitions. The response is a dict keyed by source type. Each entry describes: - `name` — the canonical source_type string you'll pass to later calls (e.g. `"Postgres"`, `"Stripe"`, `"Hubspot"`). - `caption` — human-readable description. - `fields` — the config fields needed (host, port, database, api_key, client_id/secret, ...). Each has `name`, `type` (input, password, switch, select, file-upload), and `required`. - `featured`, `unreleasedSource` — use to gauge readiness. Skip sources marked `unreleasedSource: true` unless the user explicitly asked for a preview. Match the user's request to a source. If they said "Postgres", look up `Postgres`. If they said something ambiguous like "database", present the top relevant matches (Postgres, MySQL, MongoDB, BigQuery, Snowflake, Redshift) and let them pick. For OAuth-based sources (Hubspot, Salesforce, Google Ads), the wizard entry hints at an OAuth flow. These typically need the user to authorize in the PostHog UI rather than pasting credentials — explain this and direct them to the source setup page rather than trying to collect tokens in chat. OAuth is about _authentication_, not about how data flows; OAuth sources still use polling bulk sync, not webhooks. Gather the required credentials from the user. Never ask for more fields than the wizard entry says are required — asking for an unnecessary `port` when the source doesn't need one confuses users. ### Step 2 — Validate credentials and discover tables Call `external-data-sources-db-schema` with `source_type` plus all credential fields. This does two things at once: 1. Validates the credentials against the live source. Returns 400 with a `message` if anything is wrong (bad host, wrong password, permission denied). Show the error verbatim — it's often actionable ("password authentication failed for user 'x'"). 2. If valid, returns an array of table entries. Each entry: ```text { "table": "orders", "should_sync": false, "rows": 1_250_000, "incremental_available": true, # can do sync_type=incremental "append_available": true, # can do sync_type=append "cdc_available": true, # can do sync_type=cdc (null = not enabled for team) "supports_webhooks": false, # can do sync_type=webhook for real-time push "incremental_fields": [ # candidates: usually updated_at, created_at, id {"field": "updated_at", "type": "datetime", "label": "updated_at", ...}, {"field": "created_at", "type": "datetime", ...}, {"field": "id", "type": "integer", ...} ], "detected_primary_keys": ["id"], "available_columns": [{"field": "id", "type": "integer", "nullable": false}, ...], "description": "..." } ``` Present this to the user. Don't dump the raw JSON — summarize: which tables were found, row counts, and the default sync method recommendation per table (see [sync-type decision guide](./references/sync-types.md)). ### Step 3 — Confirm per-table sync configuration For each table the user wants to sync, pick a sync_type. See the [sync-type decision guide](./references/sync-types.md) for detailed rules, but the short version is: - **Small / dimension tables (<50k rows, no natural ordering column):** `full_refresh` — simple and always correct. - **Large tables with an `updated_at` / `modified_at`:** `incremental` — much cheaper per sync. - **Append-only immutable tables (logs, events):** `append` if available — preserves history. - **Postgres with CDC enabled and you need near-real-time:** `cdc` — requires primary keys and Postgres prerequisites. - **Sources that support webhooks (currently Stripe):** for near-real-time ingestion set `sync_type: "webhook"` on the tables where `supports_webhooks: true`, then register the webhook as a post-create step (see step 6 below). Tables that don't support webhooks on the same source still need a bulk sync_type. For each schema that will use `incremental`/`append`/`cdc`, you also need: - `incremental_field` — which column to track for high-water-mark ordering. Pick from the `incremental_fields` list returned by db-schema. Prefer `updated_at` over `created_at` (updated_at catches late-arriving updates; created_at misses them). For integer-only tables, use the monotonically increasing primary key. - `incremental_field_type` — must match the chosen field's type (`datetime`, `timestamp`, `date`, `integer`, `numeric`, `objectid`). - `primary_key_columns` — required for CDC. Use `detected_primary_keys` from db-schema. ### Step 4 — Pick a good prefix The source's `prefix` is prepended to table names in HogQL. Tables end up as `{prefix}_{table_name}`. - Default to the source type lowercased if there's only one source of that type: `stripe`, `postgres`. - If the user already has a Postgres source, pick something distinguishing: `postgres_prod`, `postgres_analytics`. - Use lowercase, underscore-separated. The prefix becomes part of every HogQL query the user writes. Confirm the prefix with the user before creating — changing it later is possible but renames every table. ### Step 5 — Create the source Call `external-data-sources-create` with: ```json { "source_type": "Postgres", "prefix": "postgres_prod", "payload": { "host": "...", "port": "5432", "dbname": "...", "user": "...", "password": "...", "schema": "public", "schemas": [ { "name": "orders", "should_sync": true, "sync_type": "incremental", "incremental_field": "updated_at", "incremental_field_type": "datetime", "primary_key_columns": ["id"] }, { "name": "users", "should_sync": true, "sync_type": "full_refresh" }, { "name": "audit_log", "should_sync": false } ] } } ``` Rules for the `schemas` array: - Every table returned by db-schema should be included, even ones the user doesn't want (set `should_sync: false`). Tables the user didn't mention default to `should_sync: false`. - `sync_type` is required only when `should_sync: true`. - `incremental_field` / `incremental_field_type` must be present when `sync_type` is `incremental` or `append`. - `primary_key_columns` must be present when `sync_type` is `cdc`. On success you'll get back a source with a new `id`. The first sync is triggered automatically. ### Step 6 — Register a webhook (only when any schema is `sync_type: "webhook"`)
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub