Documentation menu

Funnels

Ordered multi-step paths through the site, plus their computed drop-off statistics.

6 endpoints, all relative to https://analytics.appfor.you. Shared rules live in Conventions, Rate limits and Error codes.

List funnels

GET/api/v2/sites/{siteId}/funnelsscope: read

Definitions only — no computed results. Available to the site owner and accepted team members of any role.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.

Query parameters

NameTypeDescription
limitintegerRows to return. Default 50, minimum 1, maximum 500.
offsetintegerRows to skip, for paging. Default 0.

Example request

curl -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/funnels"

Example response

{
  "funnels": [
    {
      "funnelId": "6512bb02bcf86cd7994390b2",
      "siteId": "6507f1f77bcf86cd799439011",
      "name": "Checkout",
      "steps": [
        { "name": "Cart", "pathname": "/cart" },
        { "name": "Payment", "pathname": "/checkout" },
        { "name": "Done", "pathname": "/thank-you" }
      ],
      "createdAt": "2025-02-10T08:00:00.000Z",
      "updatedAt": "2025-02-10T08:00:00.000Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

Create a funnel

POST/api/v2/sites/{siteId}/funnelsscope: write

Each step needs a non-empty name and pathname. Order matters — step 1 is the entry point used as the conversion baseline.

  • Viewers receive 403.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.

Request body

FieldTypeRequiredDescription
namestringYesFunnel display name.
stepsArray<{ name, pathname }>YesOrdered steps. Minimum 2 items.

Example request

curl -X POST "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/funnels" \
  -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Checkout", "steps": [ { "name": "Cart", "pathname": "/cart" }, { "name": "Payment", "pathname": "/checkout" }, { "name": "Done", "pathname": "/thank-you" } ] }'

Example response (201)

{
  "funnelId": "6512bb02bcf86cd7994390b2",
  "siteId": "6507f1f77bcf86cd799439011",
  "name": "Checkout",
  "steps": [
    { "name": "Cart", "pathname": "/cart" },
    { "name": "Payment", "pathname": "/checkout" },
    { "name": "Done", "pathname": "/thank-you" }
  ],
  "createdAt": "2025-03-21T09:00:00.000Z",
  "updatedAt": "2025-03-21T09:00:00.000Z"
}

Get a funnel

GET/api/v2/sites/{siteId}/funnels/{funnelId}scope: read

No computed results here — use the funnel stats sub-resource for those.

  • Returns 404 "Funnel not found" for malformed or foreign ids.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
funnelIdstringIdentifier of the funnel.

Example request

curl -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/funnels/6512bb02bcf86cd7994390b2"

Example response

{
  "funnelId": "6512bb02bcf86cd7994390b2",
  "siteId": "6507f1f77bcf86cd799439011",
  "name": "Checkout",
  "steps": [
    { "name": "Cart", "pathname": "/cart" },
    { "name": "Payment", "pathname": "/checkout" }
  ],
  "createdAt": "2025-02-10T08:00:00.000Z",
  "updatedAt": "2025-02-10T08:00:00.000Z"
}

Update a funnel

PATCH/api/v2/sites/{siteId}/funnels/{funnelId}scope: write

At least one field is required. `steps` REPLACES the entire array — it is not merged — and the replacement must still contain at least 2 steps.

  • Viewers receive 403.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
funnelIdstringIdentifier of the funnel.

Request body

Every field is optional, but at least one must be supplied.

FieldTypeRequiredDescription
namestringNoNew funnel name.
stepsArray<{ name, pathname }>NoFull replacement step list, minimum 2 items.

Example request

curl -X PATCH "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/funnels/6512bb02bcf86cd7994390b2" \
  -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Checkout v2" }'

Example response

{
  "funnelId": "6512bb02bcf86cd7994390b2",
  "siteId": "6507f1f77bcf86cd799439011",
  "name": "Checkout v2",
  "steps": [
    { "name": "Cart", "pathname": "/cart" },
    { "name": "Payment", "pathname": "/checkout" }
  ],
  "updatedAt": "2025-03-21T09:25:00.000Z"
}

Delete a funnel

DELETE/api/v2/sites/{siteId}/funnels/{funnelId}scope: adminDestructive

Deleting a funnel destroys the step definition and every drop-off report built on it. Pass `confirm=<funnelId>` as a query parameter or as a `confirm` field in the JSON body — it must equal the 24-character funnel id from the URL, not the funnel name.

Irreversible. Requires the admin scope and confirm={funnelId} — see Destructive operations.

  • Viewers receive 403; an unknown id returns 404.
  • The confirmation is checked AFTER the scope and site-access checks but BEFORE the id is validated, so a wrong `confirm` with a non-existent id returns 400, never a 404.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
funnelIdstringIdentifier of the funnel.

Query parameters

NameTypeDescription
confirmstringDestructive-operation confirmation. Must exactly equal the `funnelId` in the URL. May be sent as a `confirm` field in the JSON body instead.

Request body

Optional — send `{ "confirm": "<funnelId>" }` instead of the query parameter if you prefer.

FieldTypeRequiredDescription
confirmstringNoMust equal the funnelId in the URL.

Example request

curl -X DELETE "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/funnels/6512bb02bcf86cd7994390b2?confirm=6512bb02bcf86cd7994390b2" \
  -H "Authorization: Bearer $ANALYTICS_API_KEY"

Example response

{
  "deleted": true,
  "funnelId": "6512bb02bcf86cd7994390b2"
}

Funnel results

GET/api/v2/sites/{siteId}/funnels/{funnelId}/statsscope: read

For each step: the number of unique sessions that hit the step pathname, the drop-off rate versus the previous step, and the conversion rate versus step 1. `overallConversionRate` is first-step to last-step. Percentages are rounded to one decimal. Same maths as the dashboard.

  • The resolved window is echoed as a FLAT `range` string alongside top-level `since` and `until`, not as a `range` object. This and `GET /goals/{goalId}` are the only two endpoints that still do.
  • An unusable window (unparseable `from`/`to`, or one that ends before it starts) returns 400, matching the goal endpoints, rather than silently falling back to the `range` preset.
  • Date-only `from`/`to` cover WHOLE days: `to=2025-03-21` runs through `2025-03-21T23:59:59.999Z`, so `from=X&to=X` is a full 24-hour day.

Path parameters

siteIdstringThe Mongo _id returned as `siteId` by GET /api/v2/sites, or the public `trackingId` embedded in the tracking snippet. Both are accepted everywhere.
funnelIdstringIdentifier of the funnel.

Query parameters

NameTypeDescription
rangestringRelative window: `24h`, `7d`, `30d`, `90d` or `365d`. Default `30d`. Overridden by `from`/`to`.
fromstring (ISO-8601 or YYYY-MM-DD)Explicit window start, overriding `range`. A date-only `YYYY-MM-DD` value means that day from `T00:00:00.000Z`; a value carrying a time is an instant and is used verbatim. Supplying only `from` gives `[from, now]`. An unparseable value, or a window that ends before it starts, returns 400.
tostring (ISO-8601 or YYYY-MM-DD)Explicit window end, overriding `range`. A date-only `YYYY-MM-DD` value covers the WHOLE day, through `T23:59:59.999Z` — the final day is included, and `from=X&to=X` is a full 24 hours rather than the zero-width window it used to be. A value carrying a time is used verbatim. An end in the future is clamped to now, except where clamping would invert the window. Supplying only `to` gives `[to - range, to]`. An unparseable value, or a window that ends before it starts, returns 400.

Example request

curl -H "Authorization: Bearer $ANALYTICS_API_KEY" \
  "https://analytics.appfor.you/api/v2/sites/6507f1f77bcf86cd799439011/funnels/6512bb02bcf86cd7994390b2/stats?range=30d"

Example response

{
  "funnel": { "funnelId": "6512bb02bcf86cd7994390b2", "name": "Checkout" },
  "range": "30d",
  "since": "2025-02-19T00:00:00.000Z",
  "until": "2025-03-21T00:00:00.000Z",
  "steps": [
    { "name": "Cart", "pathname": "/cart", "sessions": 1000, "dropoffRate": 0, "conversionRate": 100 },
    { "name": "Payment", "pathname": "/checkout", "sessions": 620, "dropoffRate": 38, "conversionRate": 62 },
    { "name": "Done", "pathname": "/thank-you", "sessions": 415, "dropoffRate": 33.1, "conversionRate": 41.5 }
  ],
  "overallConversionRate": 41.5
}