REST API

Funnels

Manage the funnels defined on one of the current team's sites. A funnel is an ordered sequence of 2–8 inline steps — each step is its own match definition (match_type + match_value/scroll_threshold), the same shape a goal uses, but stored on the funnel itself rather than referencing a saved goal. Deleting or editing a goal never affects a funnel, and duplicate steps are allowed since they aren't shared records. A site is addressed by {siteKey} (UUID or domain). All endpoints require Authorization: Bearer {token}.

List funnels

GET /api/sites/{siteKey}/funnels

List a site's funnels, ordered by name, each with its step count. Not paginated — flat JSON array.

Path parameters

Parameter Type Required Description
siteKey string Yes Site UUID or domain.

Response — flat JSON array of:

Field Type Description
id string (uuid) Funnel id.
name string Funnel name.
strict_order bool Whether steps must be completed in order.
steps_count int Number of steps (present on list results).
created_at string (ISO 8601) | null When the funnel was created.

Status: 200 OK.

curl https://clickbase.so/api/sites/example.com/funnels \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"
await fetch('https://clickbase.so/api/sites/example.com/funnels', {
  headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
})
requests.get(
    'https://clickbase.so/api/sites/example.com/funnels',
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
)

Create a funnel

POST /api/sites/{siteKey}/funnels

Create a funnel from an ordered list of 2–8 inline steps. Array position is the step order (1-indexed).

Body parameters

Parameter Type Required Description
name string Yes Funnel name. Max 255 chars. Must be unique on the site.
steps array Yes Ordered list of 2–8 step objects, each {match_type, match_value, scroll_threshold}. See below.
strict_order bool No Whether steps must be completed in the given order. Default false.

Each step object:

Field Type Required Description
match_type string Yes One of page, event, scroll, outbound, download, button, form, copy (same set as a goal's match_type).
match_value string Conditional Page path (page/scroll), custom event name (event), or selector (autocapture). Required for page/scroll/event. A path gets a leading / if missing. Max 2048 chars. An event value cannot be a reserved name.
scroll_threshold int Conditional Scroll depth 0–100. Required for a scroll step, rejected on any other type.

A step's match operator is not settable through this endpoint — it always uses the type-dependent default (is for an event step, matches_pattern for every other type), same as an unset goal match_operator.

The CreateFunnel Action enforces the 2–8 step bound, that each step's value satisfies its type's rules, and funnel-name uniqueness — each violation returns 422. Unlike goal ids, duplicate step definitions within the same funnel are allowed.

Response — the created funnel, including its ordered steps:

Field Type Description
id string (uuid) Funnel id.
name string Funnel name.
strict_order bool Whether steps must be completed in order.
steps array Ordered {match_type, match_value, scroll_threshold} objects (present on create/update).
created_at string (ISO 8601) | null When the funnel was created.

Status: 201 Created.

curl -X POST https://clickbase.so/api/sites/example.com/funnels \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"name": "Checkout", "steps": [{"match_type": "event", "match_value": "signup"}, {"match_type": "event", "match_value": "purchase"}], "strict_order": true}'
await fetch('https://clickbase.so/api/sites/example.com/funnels', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    name: 'Checkout',
    steps: [
      { match_type: 'event', match_value: 'signup' },
      { match_type: 'event', match_value: 'purchase' },
    ],
    strict_order: true,
  }),
})
requests.post(
    'https://clickbase.so/api/sites/example.com/funnels',
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
    json={
        'name': 'Checkout',
        'steps': [
            {'match_type': 'event', 'match_value': 'signup'},
            {'match_type': 'event', 'match_value': 'purchase'},
        ],
        'strict_order': True,
    },
)

Update a funnel

PATCH /api/sites/{siteKey}/funnels/{funnel}

Update a funnel. In practice this is a full replace, not a partial patchname and steps are re-validated the same way as create (a missing/empty name and a steps array outside 2–8 both return 422), and strict_order is not merged with the current value: any request that omits it resets it to false. Always send the funnel's complete definition — name, all steps, and the intended strict_order — on every update, not just the field you changed.

Path parameters

Parameter Type Required Description
siteKey string Yes Site UUID or domain.
funnel string Yes Funnel id. A foreign/unknown id returns 404.

Body parameters

Parameter Type Required Description
name string Yes Funnel name. Must stay unique on the site.
steps array Yes Replacement ordered list of 2–8 step objects (same shape and rules as create).
strict_order bool No Whether steps must be completed in order. Omitting it resets it to false.

Response — the updated funnel (with steps). Status: 200 OK.

curl -X PATCH https://clickbase.so/api/sites/example.com/funnels/{funnel} \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"name": "Checkout v2", "steps": [{"match_type": "event", "match_value": "signup"}, {"match_type": "event", "match_value": "purchase"}], "strict_order": true}'
await fetch(`https://clickbase.so/api/sites/example.com/funnels/${funnelId}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    name: 'Checkout v2',
    steps: [
      { match_type: 'event', match_value: 'signup' },
      { match_type: 'event', match_value: 'purchase' },
    ],
    strict_order: true,
  }),
})
requests.patch(
    f'https://clickbase.so/api/sites/example.com/funnels/{funnel_id}',
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
    json={
        'name': 'Checkout v2',
        'steps': [
            {'match_type': 'event', 'match_value': 'signup'},
            {'match_type': 'event', 'match_value': 'purchase'},
        ],
        'strict_order': True,
    },
)

Evaluate a funnel definition

POST /api/sites/{siteKey}/funnels/evaluate

Evaluate an ad hoc, unsaved funnel definition over a date range — the live preview the funnel builder uses while you're still editing steps. Never persists a Funnel row; read-only.

Body parameters

Parameter Type Required Description
steps array Yes Ordered list of 2–8 step objects (same shape and rules as create).
strict_order bool No Whether steps must be completed in order. Default false.
range string No Preset date range. See Conventions.
from string No Custom range start, Y-m-d. Requires to; takes precedence over range.
to string No Custom range end, Y-m-d. Requires from.

Dashboard filter dimensions (country, path, utm_source, etc. — see Conventions) are read from the query string, not the body. A goal or prop_key/prop_value filter is accepted but ignored: a funnel is already its own set of match conditions, so goal/property filtering on top of it is dropped before evaluation.

Response — computed, not persisted:

Field Type Description
steps array Per-step breakdown, in order (see below).
entering_visitors int Visitors who reached step 1.
never_entering_visitors int Visitors active in range who never reached step 1.
entering_visitors_percentage float entering_visitors as a percentage of all visitors in range.
never_entering_visitors_percentage float never_entering_visitors as a percentage of all visitors in range.

Each steps[] entry:

Field Type Description
label string Display label for the step.
visitors int Visitors who reached this step.
dropoff int Visitors lost between the previous step and this one.
dropoff_percentage float dropoff as a percentage.
conversion_rate float Cumulative conversion rate from step 1 to this step.
conversion_rate_step float Conversion rate from the previous step to this one.
revenue int Revenue (minor units) attributed at this step.
value int Goal value attributed at this step.
sources array Top traffic sources reaching this step: {label, domain, count, percent}.
countries array Top countries reaching this step: {label, count, percent}.

Status: 200 OK.

curl -X POST "https://clickbase.so/api/sites/example.com/funnels/evaluate?country=US" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"steps": [{"match_type": "event", "match_value": "signup"}, {"match_type": "event", "match_value": "purchase"}], "range": "last_30_days"}'
await fetch('https://clickbase.so/api/sites/example.com/funnels/evaluate?country=US', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    steps: [
      { match_type: 'event', match_value: 'signup' },
      { match_type: 'event', match_value: 'purchase' },
    ],
    range: 'last_30_days',
  }),
})
requests.post(
    'https://clickbase.so/api/sites/example.com/funnels/evaluate',
    params={'country': 'US'},
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
    json={
        'steps': [
            {'match_type': 'event', 'match_value': 'signup'},
            {'match_type': 'event', 'match_value': 'purchase'},
        ],
        'range': 'last_30_days',
    },
)

Funnel conversion (saved funnel)

GET /api/sites/{siteKey}/stats/funnels/{funnel}

Compute the conversion breakdown for a saved funnel over a date range — the same numbers the dashboard's funnel report shows. The funnel is resolved through the team's site, so a foreign/unknown funnel id returns 404. Where Evaluate runs an unsaved definition from the body, this reads a persisted Funnel by id.

Path parameters

Parameter Type Required Description
siteKey string Yes Site UUID or domain.
funnel string Yes Saved funnel UUID.

Query parameters — the shared date range (range, or from/to) and dashboard filters, see Conventions and filters. As with evaluate, a goal or prop_key/prop_value filter is accepted but ignored.

Response — identical shape to Evaluate a funnel definition (the steps[] breakdown plus the entering/never-entering visitor totals). Status: 200 OK.

curl "https://clickbase.so/api/sites/example.com/stats/funnels/{funnel}?range=last_30_days" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"
await fetch('https://clickbase.so/api/sites/example.com/stats/funnels/{funnel}?range=last_30_days', {
  headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
})
requests.get(
    f'https://clickbase.so/api/sites/example.com/stats/funnels/{funnel_id}',
    params={'range': 'last_30_days'},
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
)

Delete a funnel

DELETE /api/sites/{siteKey}/funnels/{funnel}

Delete a funnel and its steps. Cannot be undone. A foreign/unknown funnel id returns 404. Status: 204 No Content.

curl -X DELETE https://clickbase.so/api/sites/example.com/funnels/{funnel} \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"
await fetch(`https://clickbase.so/api/sites/example.com/funnels/${funnelId}`, {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
})
requests.delete(
    f'https://clickbase.so/api/sites/example.com/funnels/{funnel_id}',
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/json'},
)