All docs
4 min read

Funnels

Multi-step lead-gen funnels - screens, blocks, scoring, A/B variants, server-side pixel + CAPI dispatch. Same token model as the rest of the API.

Base URL: https://formspring.io/api/v1

Method Path Ability
GET /funnels funnels:read
POST /funnels funnels:write
GET /funnels/{funnel} funnels:read
PUT /funnels/{funnel} funnels:write
DELETE /funnels/{funnel} funnels:write
POST /funnels/{funnel}/publish funnels:publish
POST /funnels/{funnel}/unpublish funnels:publish
GET /funnels/{funnel}/analytics funnels:analytics
GET /funnels/{funnel}/sessions funnels:analytics

Funnels require a paid plan - 402 on Free.

The {funnel} path parameter accepts the funnel's slug.

List funnels

http
GET /funnels
GET /funnels?project_id={project_public_id}

Returns all active funnels for the current team. Optional project_id query scopes to a single project.

Each row carries screens_count, sessions_count, and submissions_count so the UI can render a triage table without a second round-trip.

Create a funnel

http
POST /funnels
Content-Type: application/json

{
  "name": "Spring 2026 - homebuyer guide",
  "slug": "homebuyer-guide"
}
  • name required (max 120 chars)
  • slug - URL slug under /u/{slug}. Auto-generated when omitted.

Created funnels start unpublished with an empty screens array. Add screens + blocks via PUT /funnels/{funnel} or through the dashboard builder.

Response 201: FunnelResource.

Show a funnel

http
GET /funnels/{funnel}

The show response eager-loads screens and each screen's blocks array so a single call hydrates the whole builder document.

Update a funnel

http
PUT /funnels/{funnel}
Content-Type: application/json

{
  "name": "Homebuyer Guide - A/B variant B",
  "tracking": {
    "ga4_id": "G-XXXXXXX",
    "meta_pixel_id": "1234567890"
  },
  "ab_test_config": {
    "enabled": true,
    "variants": [{ "key": "a", "weight": 50 }, { "key": "b", "weight": 50 }]
  },
  "screens": [
    { "position": 0, "kind": "welcome", "config": { "heading": "Find your home" } },
    { "position": 1, "kind": "question", "config": { "prompt": "Bedrooms?" } },
    { "position": 2, "kind": "form-collect", "config": { "fields": ["email"] } },
    { "position": 3, "kind": "thank-you", "config": { "heading": "Thanks!" } }
  ]
}

Sending screens replaces the whole screen array in a single transaction. Blocks within each screen are managed through screen config or the MCP add_block / set_logic tools.

Allowed screen kinds are listed in Funnel::SCREEN_KINDS (welcome, question, form-collect, thank-you, redirect, …).

Publish / unpublish

http
POST /funnels/{funnel}/publish
POST /funnels/{funnel}/unpublish

Publishing flips is_published = true + stamps published_at. Unpublishing flips is_published = false; the public /u/{slug} URL stops serving the funnel.

These two endpoints require the dedicated funnels:publish ability (separate from funnels:write so admins can issue tokens that edit funnel content without going-live capability).

Delete a funnel

http
DELETE /funnels/{funnel}

Soft-deletes the funnel and flips is_published off. Sessions are preserved for audit history; screens/blocks/scoring rules cascade away on permanent purge from the admin panel.

Analytics

http
GET /funnels/{funnel}/analytics

Returns the aggregated funnel summary - total sessions, completion rate, step-by-step drop-off, A/B-variant attribution. Requires funnels:analytics.

http
GET /funnels/{funnel}/sessions

Returns the 100 most-recent funnel sessions (started, completed, current screen, A/B variant, UTM context). Capped server-side; for full export use the dashboard CSV stream.

Errors

Status When
402 Plan doesn't include funnels
403 Token missing required ability
404 Funnel doesn't belong to the current team

See also