Skip to content

feat(data-warehouse): implement justcall import source - #67824

Merged
talyn-app[bot] merged 6 commits into
masterfrom
posthog-code/justcall-source
Jul 3, 2026
Merged

feat(data-warehouse): implement justcall import source#67824
talyn-app[bot] merged 6 commits into
masterfrom
posthog-code/justcall-source

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

PostHog can't pull data from JustCall, a cloud phone, SMS/WhatsApp, and sales-dialer platform. Teams that run their support or sales calls through JustCall have no way to join that telephony activity with the rest of their product and customer data in PostHog. The connector was scaffolded (registered with unreleasedSource=True and an empty source.py) but had no sync logic.

Changes

Implements the justcall source end-to-end as a ResumableSource over JustCall's REST/JSON v2.1 API, following the implementing-warehouse-sources skill's source.py / settings.py / justcall.py split.

Endpoints synced: calls, texts, contacts, users, phone_numbers, and sales_dialer_calls.

  • Auth: API key + secret sent raw in the Authorization header (api_key:api_secret), matching JustCall v2.1.
  • Incremental: calls, texts, and sales_dialer_calls sync incrementally via JustCall's server-side from_datetime filter, requesting rows ascending with sort=datetime so the watermark advances monotonically. The cursor is the account-timezone *_user_date field (day-granular; from_datetime re-fetches the boundary day and the primary-key merge dedupes the overlap). contacts, users, and phone_numbers are full refresh because JustCall exposes no server-side time filter for them.
  • Pagination: page-based. Each request rebuilds the query so from_datetime / sort / order stay attached on every page (JustCall's next_page_link isn't followed because I couldn't verify it preserves the filter on later pages, and dropping it would re-walk full history each incremental sync). A short page ends pagination; resume state checkpoints the current page after each yielded batch.
  • Tracked transport: all outbound HTTP goes through make_tracked_session().
  • Adds get_non_retryable_errors (401/403), canonical_descriptions.py from the official API docs, and sets lists_tables_without_credentials = True so the public docs render the table catalog.
  • Ships behind unreleasedSource=True with releaseStatus=alpha.
  • Updates SOURCES.md (moves justcall into the Implemented table) and adds the user-facing posthog.com doc (see agent context).

The existing frontend/public/services/justcall.png icon is reused.

How did you test this code?

Automated tests I (the agent) actually ran, all passing in the project venv:

  • tests/test_justcall.py (transport) - _format_cursor coercion, _build_params (incremental vs full-refresh, per-endpoint order casing), _build_url, validate_credentials status mapping, and get_rows behavior: page-forward pagination stopping on a short page, checkpoint-per-page resume state, resuming from saved page, and that the from_datetime filter is attached only for time-filterable endpoints.
  • tests/test_justcall_source.py (source class) - source_type, config fields/labels, non-retryable error matching, get_schemas (only calls/texts/sales_dialer_calls advertise incremental), credential plumbing, resumable-manager binding, source_for_pipeline argument plumbing, documented-tables rendering, and canonical-description coverage of every endpoint.
  • Repo suites that cover the new source: test_source_categories.py (1312 passed) and test_source_config_generator.py (snapshot passed).

I did not run a live end-to-end sync - that needs a real JustCall account. See the uncertainty note below.

Automatic notifications

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

Docs update

User-facing doc written per the documenting-warehouse-sources skill at contents/docs/cdp/sources/justcall.md (sourceId JustCall, alpha callout, <SourceParameters /> + <SourceTables />). It lives in the posthog.com repo, which isn't checked out in this environment, so it still needs to land there. docsUrl in get_source_config already points at https://posthog.com/docs/cdp/sources/justcall, and audit_source_docs couldn't be run here (no posthog.com checkout).

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Authored by Claude (Claude Code). Skills invoked: implementing-warehouse-sources and documenting-warehouse-sources.

Key decisions:

  • Reference: modeled on the existing aircall source (also telephony, API key + token, ResumableSource), diverging where JustCall differs (raw Authorization header, data + next_page_link envelope, 0-indexed pages, split UTC/user-timezone date fields).
  • Scope: shipped ResumableSource only. JustCall's webhook API is a natural follow-up, but full webhook support (management + signature verification) is unverifiable without account credentials, so I kept the first cut to the resumable pull path.
  • API verification: I confirmed base URL, the Authorization: api_key:api_secret header shape, and the 401 envelope with unauthenticated curl, and pulled query params / object fields / the data + next_page_link envelope from JustCall's v2.1 reference docs. I could not run the future-date-cutoff smoke test the skill recommends for confirming from_datetime actually filters (no credentials). I enabled incremental anyway for the three endpoints where from_datetime is a documented, load-bearing core filter, requesting ascending explicitly and preserving the filter on every page so the failure mode is bounded (extra API cost, not watermark corruption). This is called out in a code comment.
  • Generated config: pnpm run generate:source-configs needs a database, which isn't available here, so I hand-wrote the two-field JustCallSourceConfig to exactly match the generator's deterministic output (verified against AircallSourceConfig). CI regenerates it. Same for schema:build - the JustCall enum value already exists in schema-general.ts / schema_enums.py, so no schema regeneration was needed.

Agent-authored - please review; do not self-merge.

Implements the JustCall (cloud phone, SMS, and sales-dialer) data warehouse
import source end-to-end as a ResumableSource over JustCall's REST/JSON v2.1 API.

Endpoints: calls, texts, contacts, users, phone_numbers, and sales_dialer_calls.
Calls, texts, and sales dialer calls sync incrementally via JustCall's
`from_datetime` server-side filter (ascending, day-granular watermark on the
account-timezone `*_user_date` field); the remaining endpoints are full refresh.

All outbound HTTP goes through the tracked session. Pagination rebuilds the page
query each request so the time filter stays attached on every page, and resume
state checkpoints the current page after each yielded batch. Ships behind
`unreleasedSource=True` with `releaseStatus=alpha`.

Generated-By: PostHog Code
Task-Id: 9952b8ef-a3ac-4fd4-839b-f4551df47f94
@github-actions

github-actions Bot commented Jul 2, 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 2, 2026

Copy link
Copy Markdown
Contributor

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

@Gilbert09
Gilbert09 marked this pull request as ready for review July 2, 2026 13:39
Copilot AI review requested due to automatic review settings July 2, 2026 13:39
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 2, 2026 13:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Gilbert09 added 3 commits July 2, 2026 14:53
Compute the incremental fields once per endpoint instead of three repeated
dict lookups, and drop the redundant list() around the ENDPOINTS tuple.
Addresses a Greptile review nit; behavior is unchanged.

Generated-By: PostHog Code
Task-Id: 9952b8ef-a3ac-4fd4-839b-f4551df47f94
…odies

Register the JustCall API key/secret (and the combined Authorization header
value) for value-based redaction on a single reused tracked session, so the
raw credentials can't leak into request logs or captured HTTP samples on a
failed sync. Stop logging the verbatim upstream response body on error — these
endpoints return calls, texts, and contacts, so the raw payload could copy
customer PII or echoed credentials into logs; status and endpoint are enough
to debug.

Generated-By: PostHog Code
Task-Id: 290b0f1a-e63a-4c56-a24f-386f63407b22
Generated-By: PostHog Code
Task-Id: 290b0f1a-e63a-4c56-a24f-386f63407b22
@veria-ai

veria-ai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

Gilbert09 added 2 commits July 3, 2026 03:56
The api_key is part of the Authorization credential sent to JustCall.
Marking it as a plain text, non-secret field meant it was classified as
non-sensitive and returned by the source settings API, letting anyone who
can view the source config copy stored credential material. Treat it like
the api_secret (PASSWORD type, secret=True), matching the convention used
by every other API-key source, and lock it in with a parametrized test.

Generated-By: PostHog Code
Task-Id: c9d9ce24-26dc-4843-afa3-a35bdb05a6fc
Generated-By: PostHog Code
Task-Id: c9d9ce24-26dc-4843-afa3-a35bdb05a6fc
@talyn-app
talyn-app Bot merged commit 51859b8 into master Jul 3, 2026
211 checks passed
@talyn-app
talyn-app Bot deleted the posthog-code/justcall-source branch July 3, 2026 03:38
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 3, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-03 04:01 UTC Run
prod-us ✅ Deployed 2026-07-03 04:12 UTC Run
prod-eu ✅ Deployed 2026-07-03 04:13 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.

3 participants