Skip to main content

Summary

API v2 is a stream-centred redesign. Three primary verbs frame the surface:
  • Create streams - POST /api/v2/streams (one-shot: handles + segments + schedule + delivery)
  • Update streams - PATCH /api/v2/streams/{id} (any setting, including full-replace pairs and inline segment creation)
  • Extract leads - GET /api/v2/streams/{id}/leads (per-stream) or GET /api/v2/leads (cross-stream polling)
Everything else (sync runs, re-tests, leads, budgets) is reached via stream sub-resources or the small set of identity / utility endpoints. v1 paths return 410 Gone with a pointer back here.

Vocabulary

  • Stream - a campaign. Owns N (tracked_account, segment) pairs and shared config (schedule, enrich, delivery, csv-email). The primary resource.
  • Segment - a reusable filter set (lead description + applied labels + gender + follower-count tiers). Code-side identifier is still segments.
  • Lead - a discovered profile that survived bot-filtering and (optionally) matched one or more segments. Renamed from “people of interest”.
  • Lookup - a one-shot enrichment or NL-to-segment operation for the Chrome extension. Distinct surface at /api/v2/lookups/*.

Endpoint mapping

Identity

Streams (new primary resource)

Leads

Account management

Tracked accounts no longer have a public “add” verb - adding happens via stream creation. Management verbs survive:

Segments

Editing a segment’s filters in place is intentionally NOT exposed (footgun risk for external integrators - mutating a segment affects classification across every stream that references it). Workflow: create a new segment via POST, then PATCH the affected streams to swap.

Sync settings

Lookups (Chrome extension surface)

Utility (unchanged path, sometimes shape)

Conceptual changes

Stream is a campaign

The keystone of the v2 redesign. One stream row owns N (tracked_account, segment) pairs and a shared config (schedule, enrich, delivery, csv-email). A segment can appear in multiple streams. A stream can contain multiple segments AND/OR multiple accounts in any combination.

One-shot stream creation

POST /api/v2/streams accepts handles (resolves via ScrapeBadger -> kaitoeasyapi, upserts tracked accounts, increments Stripe quantity) AND inline segment objects (creates new segments rows) in one call. Existing accounts/segments can be referenced by ID instead.

Full-replace pair updates

PATCH /api/v2/streams/{id} accepts a pairs array as full-replace semantics: server diffs against existing pairs, adds new (upserting accounts + creating inline segments), removes ones not in the new array.

Lead lifecycle

Leads carry a server-derived exported_at field. ?lifecycle=new filters for IS NULL (default). ?lifecycle=exported filters for IS NOT NULL. Exporting is done via POST /api/v2/leads/{id}/export.

matchConfidence enum

'full' / 'maybe' -> 'high' / 'medium' (column rewrite in migration 0113). Reflected in /api/v2/leads, /api/v2/streams/{id}/leads, webhook filter config.

Per-account billing visibility

GET /api/v2/me exposes per-account daily-sync slots + monthly re-test budgets, plus credit balance + low-balance flag, plus a totals rollup (“how much headroom do I have anywhere right now”). No standalone /api/v2/budget - it’s folded into /me.

Tracked accounts are second-class in the API

Adding accounts happens via stream creation. Removing pairs leaves orphan accounts (tracked but referenced by no active stream); orphans stay tracked + billed until explicitly slept via POST /api/v2/accounts/{id}/sleep. Account-level operations (sleep / wake / change-tier) survive as /api/v2/accounts/{id}/<verb>.

Webhook filter

Filter config supports {streamId?, accountId?, sourceType?, feedbackStatus?, matchConfidence?}. segmentId is removed. Existing subscriptions stored with segmentId filter silently match nothing post-deploy (the filter was a TODO that never actually applied anyway). Re-subscribe with streamId if you want filtering.

Paywall error codes

Every server gate that surfaces a paywall returns { error: '<code>', message: '<copy>' } with HTTP 403 (or 400 / 429 where appropriate). Branch on the code, not the message text.

Auth (unchanged)

API key auth via Authorization: Bearer <ctgo_…> header. Existing PATs continue working post-deploy. In-app UI calls (cookie session) work transparently on the hybrid-auth routes (/api/v2/streams*, /api/v2/leads*, /api/v2/segments, /api/v2/accounts/{id}/*, /api/v2/me, /api/v2/runs).

Rate limits (unchanged)

60 requests per minute per Bearer key. Cookie-session in-app calls are not rate-limited at this layer (session middleware gates them).