Docs Reference

REST API

Every route of https://api.retafunnel.com/api/v1 with its scope and notes.

Everything the dashboard and the MCP server do is available over plain HTTPS at https://api.retafunnel.com/api/v1. Send the header Authorization: Bearer rfk_… on every request. Create API keys in the dashboard under Integrations → API keys and give each key only the scopes it needs: read (reports, sessions, read-only SQL), write (outcomes, segments, syncs) or setup (sites, funnels, test mode, simulations). Responses are JSON: { "data": … } on success, { "error": { "code", "message" } } on failure.

Core routes#

Method Path Scope Notes
GET /me any workspace, scopes
GET /schema read data dictionary JSON
GET /sites · POST /sites read / setup
GET /funnels · POST /funnels · PUT /funnels/:id read / setup
GET /segments · POST /segments · PUT /segments/:id · DELETE read / write body { name, target, rules, sync_meta?, sync_google? }
POST /segments/preview read { rules, target } → { count, sample[] }
POST /segments/:id/sync write { platform: 'meta'|'google' }
GET /sessions read query params: site_id, from, to, limit, cursor + any session_facts filter as filter[field][op]=value
GET /sessions/:id read facts + ordered events
GET /reports/overview read site_id?, funnel_id?, from, to
GET /reports/funnel read funnel_id, from, to, group_by?
GET /reports/video-retention read site_id, video_id, from, to
GET /reports/quiz-dropoff read quiz_id, site_id?, from, to
GET /reports/clicks read site_id, path?, from, to
GET /reports/scroll read site_id, path, from, to
POST /query read { sql } read-only over v_*, 10 s timeout, 10k rows
POST /outcomes write { email?, phone?, visitor_id?, name, value?, currency?, external_id?, occurred_at? }
GET /install/health read site_id → last event, counts by type last 24h, tracker versions seen
POST /install/test-event setup site_id → fires a synthetic test session (alias of simulate with scenario smoke)
PUT /sites/:id setup update name, allowed_origins, consent_mode
GET/POST /sites/:id/pages read / setup page_registry; POST body { url, page_type, elements, html? }, upsert by url; html is kept to derive test plans
GET /webhooks read Stripe, Calendly and generic webhook URLs (workspace token created lazily)
GET /segments/templates read DEFAULT_SEGMENT_TEMPLATES
GET/POST /connect-links · DELETE /connect-links/:id admin returns plaintext key once + connect_url
GET /integrations · PUT/DELETE /integrations/:provider · POST /integrations/meta/test read / admin secrets never returned
GET /sync-log read
GET/PUT /sites/:id/test-mode read / setup { until }; PUT body { minutes } (0 disables)
GET /sites/:id/test-plan read url → expected event checklist derived from page_registry + instrumented HTML (fetches the URL server-side if not registered)
POST /sites/:id/simulate setup { scenario: 'smoke'|'full_funnel'|'vsl_50_no_lead'|'quiz_abandon'|'lead_and_purchase', url? } → generates a test session through the real ingest path, returns session_id
GET /sites/:id/test-sessions read since → test sessions with facts and event list, each with expected_vs_received when a test plan exists
POST /sites/:id/simulate-outcome setup { name, email?, visitor_id?, value? } → runs the outcome path as test
POST /segments/:id/dry-run read what would be sent to Meta/Google (counts, sample hashed payload), nothing sent
DELETE /sites/:id/test-data setup purge test rows for the site

Webhooks (no API key, signature-verified): POST /outcomes/stripe/:workspace_token, POST /outcomes/calendly/:workspace_token, POST /outcomes/generic (uses API key). The workspace token lives in integrations.settings.webhook_token and is created by GET /webhooks.

Reports, funnels, sales and experiments#

Method Path Scope Notes
GET /funnels/templates read FUNNEL_TEMPLATES from shared
POST /funnels/from-template setup { site_id, template, name?, base_url? } → funnel (+ default segments, + page_registry hints)
GET /funnels/:id/checklist read Checklist (below)
POST /funnels/:id/launch setup sets status live if checklist has no failing required items (or force: true)
GET /reports/ads read site_id?, from, to, level=campaign|adset|ad, platform?
GET /reports/page read site_id, path, from, to
GET /reports/compare read kind=funnel|page, a=<id or path>, b=<id or path>, from, to → both reports plus deltas
GET /reports/sales read from, to, model, group_by
GET/POST /annotations · PUT/DELETE /annotations/:id read / write
GET/POST /experiments · PUT /experiments/:id · GET /experiments/:id/report read / write report includes two-proportion z-test p-value and uplift per variant vs control (first variant)
GET/POST /ad-entities read / setup POST upserts names manually; GET lists with unresolved ids first
POST /ad-entities/sync setup pulls names (and spend for the range) from Meta/Google for ids seen in the last 90 days
GET /persons · GET /persons/:id · PUT /persons/:id read / write leads list with status (lead/booked/customer), attribution first/last, outcomes; PUT for notes/external_ids. GET /persons/:id includes timeline: every session of the person in order (session_id, started_at, utm_*, campaign_id/adset_id/ad_id with names, variant, landing_path, max_video_pct, max_scroll_pct, quiz_last_step, is_lead_session, outcomes[]) and attribution: { first_touch, last_touch, linear: [{session_id, weight}] }
POST /outcomes/import write CSV text (email,phone,name,value,currency,occurred_at,external_id) → import batch with matched/unmatched rows
GET /outcomes/export read CSV of leads in range with visitor_id, email_hash, attribution, for pasting into a CRM
GET /ad-url-templates read per platform

Every report route accepts compare=previous and then returns { current, previous, delta }, where the previous period is the same length immediately before the requested one.

Full contract (types, shapes, MCP tools): docs/CONTRACTS.md on GitHub.

Last updated