PostHog Connector
Connect PostHog to Brevo through Tajo to sync product analytics data, user behavior events, and cohort memberships for data-driven marketing campaigns and personalized customer engagement.
Overview
| Property | Value |
|---|---|
| Platform | PostHog |
| Category | Product Analytics (Custom) |
| Setup Complexity | Medium |
| Official Integration | No |
| Data Synced | Events, Persons, Feature Flags, Cohorts |
| Auth Method | Personal API Key / Project Token |
Features
- Event sync - Forward PostHog analytics events to Brevo for behavioral targeting
- Person profile sync - Sync PostHog person properties to Brevo contact attributes
- Cohort-based segmentation - Map PostHog cohorts to Brevo contact lists
- Feature flag sync - Segment contacts by enabled feature flags
- Funnel data - Use conversion funnel data for targeted re-engagement
- Session replay metadata - Enrich contacts with session engagement metrics
Prerequisites
Before you begin, ensure you have:
- A PostHog account (Cloud or self-hosted)
- A Personal API Key from PostHog Settings
- Your Project API Key (token) from Project Settings
- A Brevo account with API access
- A Tajo account with connector permissions
Authentication
Personal API Key (Private Endpoints)
# Generate at https://app.posthog.com/settings/user-api-keysexport POSTHOG_PERSONAL_API_KEY=phx_your_personal_api_keyexport POSTHOG_PROJECT_TOKEN=phc_your_project_tokenexport POSTHOG_HOST=https://us.posthog.com # or https://eu.posthog.comexport TAJO_API_KEY=your_tajo_api_keyexport BREVO_API_KEY=your_brevo_api_key// Private API endpoints use Bearer authenticationconst headers = { 'Authorization': `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}`, 'Content-Type': 'application/json'};
// Public endpoints use the project tokenconst publicHeaders = { 'Content-Type': 'application/json'};// Token is passed in the request body for public endpointsAPI Key Security
Personal API keys provide full account access. Never expose them in client-side code. Use the Project API Key (token) for public endpoints like event capture and feature flag evaluation.
Configuration
Basic Setup
connectors: posthog: enabled: true host: "${POSTHOG_HOST}" personal_api_key: "${POSTHOG_PERSONAL_API_KEY}" project_token: "${POSTHOG_PROJECT_TOKEN}" project_id: "12345"
sync: persons: true events: true cohorts: true feature_flags: true schedule: "0 */3 * * *" # Every 3 hours
event_filters: - "$pageview" - "purchase_completed" - "signup_completed" - "feature_used"
lists: all_users: 25 active_users: 26 power_users: 27Field Mapping
field_mapping: email: email $name: FIRSTNAME $browser: BROWSER $os: OS $initial_referrer: REFERRAL_SOURCE total_events: EVENT_COUNT last_seen: LAST_ACTIVE_DATE signup_date: SIGNUP_DATE plan: SUBSCRIPTION_PLAN company: COMPANY cohort_names: POSTHOG_COHORTSAPI Endpoints
| Endpoint | Method | Description |
|---|---|---|
{host}/api/projects/{id}/persons/ | GET | List persons |
{host}/api/projects/{id}/events/ | GET | List events |
{host}/api/projects/{id}/cohorts/ | GET | List cohorts |
{host}/api/projects/{id}/feature_flags/ | GET | List feature flags |
{host}/api/projects/{id}/feature_flags/evaluation/ | POST | Evaluate flags |
{host}/api/projects/{id}/insights/ | GET | List saved insights |
{host}/api/projects/{id}/query/ | POST | Run HogQL queries |
{host}/i/v0/e | POST | Capture events (public) |
{host}/decide/?v=3 | POST | Feature flag decisions (public) |
Code Examples
Initialize Connector
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
await tajo.connectors.connect('posthog', { host: process.env.POSTHOG_HOST, personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY, projectToken: process.env.POSTHOG_PROJECT_TOKEN, projectId: '12345'});Sync Persons to Brevo
// Paginate through PostHog personslet nextUrl = `${posthogHost}/api/projects/${projectId}/persons/?` + new URLSearchParams({ limit: '100' });
while (nextUrl) { const response = await fetch(nextUrl, { headers: { 'Authorization': `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}` } });
const data = await response.json();
for (const person of data.results) { const email = person.properties.$email || person.properties.email; if (!email) continue;
await tajo.contacts.sync({ email, attributes: { FIRSTNAME: person.properties.$name || person.properties.name, LAST_ACTIVE_DATE: person.properties.$last_seen, SIGNUP_DATE: person.created_at, EVENT_COUNT: person.properties.$event_count, BROWSER: person.properties.$browser, OS: person.properties.$os, REFERRAL_SOURCE: person.properties.$initial_referrer }, listIds: [25] }); }
nextUrl = data.next;}Sync Cohorts as Brevo Lists
// Get PostHog cohorts and sync members to Brevo listsconst cohortsResponse = await fetch( `${posthogHost}/api/projects/${projectId}/cohorts/`, { headers: { 'Authorization': `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}` } });
const { results: cohorts } = await cohortsResponse.json();
for (const cohort of cohorts) { // Get persons in this cohort const personsResponse = await fetch( `${posthogHost}/api/projects/${projectId}/cohorts/${cohort.id}/persons/`, { headers: { 'Authorization': `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}` } } );
const { results: persons } = await personsResponse.json();
for (const person of persons) { const email = person.properties.$email || person.properties.email; if (email) { await tajo.contacts.update(email, { attributes: { POSTHOG_COHORTS: cohort.name } }); } }}Run HogQL Queries for Analytics
// Use HogQL to query analytics dataconst queryResponse = await fetch( `${posthogHost}/api/projects/${projectId}/query/`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ query: { kind: 'HogQLQuery', query: ` SELECT properties.$email AS email, count() AS event_count, max(timestamp) AS last_event FROM events WHERE event = 'purchase_completed' AND timestamp > now() - interval 30 day GROUP BY email HAVING event_count > 3 ORDER BY event_count DESC LIMIT 1000 ` } }) });
const queryResult = await queryResponse.json();
for (const row of queryResult.results) { await tajo.contacts.update(row[0], { attributes: { PURCHASE_COUNT_30D: row[1], LAST_PURCHASE: row[2] } });}Rate Limits
| Endpoint Category | Limit | Notes |
|---|---|---|
| Analytics endpoints | 240/min, 1,200/hr | GET persons, events, insights |
| Query endpoint | 2,400/hr | HogQL and custom queries |
| Feature flag evaluation | 600/min | Local evaluation endpoint |
| CRUD endpoints | 480/min, 4,800/hr | Create, update, delete operations |
| Public endpoints (capture) | Unlimited | Event capture, flag decisions |
Batch Exports
For large-scale event data exports, use PostHog’s batch exports feature instead of the API. Batch exports support S3, BigQuery, Snowflake, and other destinations.
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Invalid API key | Verify Personal API Key in settings |
| 400 Invalid project | Wrong project ID | Check project ID in PostHog URL |
| Empty persons list | No identified users | Ensure posthog.identify() is called |
| Missing properties | Properties not set | Verify $set calls in client SDK |
| Rate limit 429 | Too many requests | Implement backoff, check rate limit headers |
Debug Mode
connectors: posthog: debug: true log_level: verbose log_queries: true log_sync: trueBest Practices
- Identify users - Always call
posthog.identify()with email to enable person sync - Use cohorts for segmentation - Leverage PostHog’s behavioral cohorts for Brevo lists
- Batch API requests - Use pagination and batch processing for large datasets
- Use HogQL for complex queries - Extract custom analytics with SQL-like queries
- Set up batch exports - For large data volumes, prefer batch exports over API polling
- Filter relevant events - Sync only marketing-relevant events to reduce noise
Security
- Personal API Key - Scoped Bearer token authentication
- Project token - Public token for client-side operations only
- HTTPS only - All endpoints require TLS encryption
- IP allowlisting - Available for self-hosted instances
- Key scoping - Create API keys with specific permission scopes
- GitHub secret scanning - PostHog partners with GitHub for leaked key detection
Related Resources
Open-Source Implementation Map
No official open-source repository was found in the current Tajo connector catalog for PostHog. Keep this page focused on the verified public API contract and vendor documentation until an official schema, SDK, MCP server, or public integration repository is available.
Tajo Revamp Checklist
- Verify authentication and scope requirements against the vendor documentation before each connector release.
- Document primary sync objects, external IDs, pagination strategy, and rate limits explicitly.
- Add smoke tests from public API examples rather than undocumented behavior.
- Capture webhook signature verification and replay protection when the vendor supports webhooks.
- Record gaps where no official public repository or schema exists so future maintainers know what still needs source-backed validation.