Skip to content

feat(data-warehouse): implement tremendous import source - #69377

Merged
Gilbert09 merged 4 commits into
masterfrom
tom/dwh-tremendous
Jul 9, 2026
Merged

feat(data-warehouse): implement tremendous import source#69377
Gilbert09 merged 4 commits into
masterfrom
tom/dwh-tremendous

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

Tremendous (rewards and incentives payouts: gift cards, prepaid cards, cash) was scaffolded as a warehouse source but had no sync logic, so users couldn't pull their payouts data into the Data warehouse to analyze incentive programs alongside product data.

Changes

Fills in the tremendous source following the standard source.py / settings.py / tremendous.py split:

  • Endpoints (settings.py): orders, rewards, invoices, members, campaigns, products, funding_sources. Primary key is id everywhere. orders/rewards/invoices partition by created_at (stable creation timestamp).
  • Incremental sync: only orders exposes a server-side timestamp filter (created_at[gte], ISO 8601), confirmed against Tremendous's official OpenAPI spec, so it's the only incremental endpoint. Everything else is full refresh. balance_transactions also filters on created_at but its rows carry no id (no usable primary key), so it's deliberately excluded for now.
  • Transport (tremendous.py): requests via make_tracked_session(), tenacity retries on 429/5xx, offset/limit pagination (per-endpoint page caps from the spec: orders/rewards 500, invoices 10). Tremendous lists are creation-date DESC with no sort param, so sort_mode="desc" and the incremental watermark finalizes at the end of a completed sync. members/campaigns/products/funding_sources are unpaginated single-response collections.
  • Resumable (ResumableSource): offset saved to Redis after each yielded page, so a heartbeat-timeout retry resumes mid-collection instead of restarting; re-pulled rows dedupe on the primary key.
  • Source config: environment select (production vs sandbox, separate hosts and API keys) + secret API key field, get_non_retryable_errors for 401/403 on both hosts, org-wide single-probe validate_credentials, canonical_descriptions.py from the official API reference, lists_tables_without_credentials = True so public docs render the table catalog.
  • SOURCES.md: moved tremendous from Scaffolded into the Implemented table (HTTP / requests / tracked).
  • generated_configs.py: regenerated; diff kept to the TremendousSourceConfig class only.

Note

Webhooks are intentionally not wired up. Tremendous allows exactly one webhook endpoint per organization, so auto-creating ours would clobber any webhook the customer already relies on. Pull-only for now; noted in a code comment.

The source stays behind unreleasedSource=True with releaseStatus=ALPHA for a staged rollout. Enum/schema wiring (types.py, schema-general.ts, schema_enums.py) and the icon landed with the scaffold; no migrations needed.

Endpoint behavior (pagination params, response shapes, filter semantics, per-endpoint limit caps) was verified against Tremendous's published OpenAPI spec and unauthenticated probes of the live sandbox API (all endpoints return 401, confirming paths). I couldn't run authenticated smoke tests without an API key, so filter behavior follows the spec; the conservative bits (inclusive gte re-fetch deduped by merge, short-page termination) are documented in code comments.

How did you test this code?

Automated only (no Tremendous credentials available for a live sync):

  • test_tremendous.py (transport): offset pagination advances and terminates on a short page (guards infinite-loop/missed-page regressions), resume starts from the saved offset instead of page one, state saves only after a yielded page (crash-safety ordering), created_at[gte] is sent on every page when incremental and never on full refresh (guards silent full re-syncs and windowed full refreshes), unpaginated endpoints fetch once with no params, retryable vs terminal status mapping, environment-to-host routing, SourceResponse shape (sort_mode="desc", stable partition keys).
  • test_tremendous_source.py (source class): only orders advertises incremental, documented-tables rendering via the placeholder config path, non-retryable error keys match real 401/403 URLs on both hosts but not transient 429/5xx, credential/environment plumbing, a stale watermark is dropped on full-refresh runs, unknown schema rejected.
  • All 63 pass, plus the sources package guard tests (categories, generated configs) - 1368 passed. ruff check/format clean, hogli ci:preflight --fix reports 0 failures, makemigrations --check reports no changes.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

The posthog.com doc (contents/docs/cdp/sources/tremendous.md) is written per the docs template and validated with audit_source_docs against a posthog.com checkout (no Tremendous findings), but it lives in the posthog.com repo and needs to land there separately:

tremendous.md for posthog.com
---
title: Linking Tremendous as a source
sidebar: Docs
showTitle: true
availability:
  free: full
  selfServe: full
  enterprise: full
sourceId: Tremendous
---

import SourceSetupIntro from "../_snippets/source-setup-intro.mdx"
import SyncModes from "../_snippets/sync-modes.mdx"
import TroubleshootingLink from "../_snippets/dw-troubleshooting-link.mdx"
import AlphaRelease from "../_snippets/alpha-release.mdx"

<AlphaRelease />

The Tremendous connector syncs your rewards and payouts data, such as orders, rewards, invoices, and campaigns, into the PostHog Data warehouse, so you can analyze your incentive programs alongside your product data.

## Prerequisites

You need a Tremendous account with access to create an API key. Tremendous has separate sandbox and production environments with separate API keys, and production API access must be approved by Tremendous first.

## Adding a data source

<SourceSetupIntro />

When linking Tremendous, you'll need:

- **Environment** – whether to sync from your **Production** or **Sandbox** Tremendous environment. Make sure the API key you provide matches the selected environment.
- **API key** – create an API key under **Team settings****Developers** in [Tremendous](https://www.tremendous.com).

## Sync modes

<SyncModes />

The `orders` table supports incremental syncs on the `created_at` field. All other tables are full refresh only, as the Tremendous API doesn't expose a server-side timestamp filter for them.

## Configuration

<SourceParameters />

## Supported tables

<SourceTables />

## Troubleshooting

- If you see an authentication error, your API key may be invalid, revoked, or for the wrong environment. Sandbox and production keys are not interchangeable — generate a key for the selected environment under **Team settings****Developers**, then reconnect.

<TroubleshootingLink />

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Authored with Claude Code from a task brief. Skills invoked: /implementing-warehouse-sources (architecture contract, incremental/resumable rules), /writing-tests (test value gate), /documenting-warehouse-sources (doc template + audit).

Decisions along the way: chose ResumableSource without WebhookSource because of the one-webhook-per-org constraint; excluded balance_transactions (no id on rows); declared sort_mode="desc" after confirming from the OpenAPI spec that lists are creation-DESC with no sort param; per-endpoint page caps taken from the spec (invoices is capped at 10, not 500). The generator rewrote unrelated classes in generated_configs.py, so its diff was manually restricted to the Tremendous class.


Created with PostHog Code

Implements the Tremendous warehouse source end-to-end: seven endpoint catalog
(orders, rewards, invoices, members, campaigns, products, funding_sources),
resumable offset pagination, incremental sync on orders via created_at[gte],
sandbox/production environment select, canonical table descriptions, and tests.

Generated-By: PostHog Code
Task-Id: 328131bb-785b-4a7e-bc74-ff7a15cb7459
@Gilbert09 Gilbert09 self-assigned this Jul 8, 2026
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 8, 2026 16:18
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(data-warehouse): implement tremendo..." | Re-trigger Greptile

…t config

The endpoint name already lives in the TREMENDOUS_ENDPOINTS dict key and nothing
reads the field. Flagged by review.

Generated-By: PostHog Code
Task-Id: 328131bb-785b-4a7e-bc74-ff7a15cb7459
@trunk-io

trunk-io Bot commented Jul 8, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

Gilbert09 added 2 commits July 8, 2026 22:57
Credentialed Tremendous requests carry a Bearer API key. With redirects
enabled, a 30x response (or an on-path actor inducing one) would replay the
authenticated request to a different host, leaking the key past the connector's
outbound trust boundary. Pass allow_redirects=False on both the sync session and
the credential-validation probe so requests stay pinned to the validated host.

Generated-By: PostHog Code
Task-Id: 6636b133-8f49-4793-b252-8ed55fba633e
Generated-By: PostHog Code
Task-Id: 6636b133-8f49-4793-b252-8ed55fba633e
@Gilbert09
Gilbert09 merged commit abaca2c into master Jul 9, 2026
222 checks passed
@Gilbert09
Gilbert09 deleted the tom/dwh-tremendous branch July 9, 2026 10:59
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 9, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-09 12:44 UTC Run
prod-us ✅ Deployed 2026-07-09 12:58 UTC Run
prod-eu ✅ Deployed 2026-07-09 13:04 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants