Fetch Workflow Step Stats
Changed onStats for one workflow step — the numbers the workflow builder shows under it. Every step type reports contacts: how many are at the step right now and how many left it inside the window. A send_email_step also reports email: engagement and attributed revenue for the email it sends. For every other step type email is null, so a delay or tag step never reads as an email that performed badly.
contacts.are_here is current occupancy — runs whose current step is this one — and is the one number that does not follow timerange. contacts.were_here counts contacts who left the step inside the window, once per pass through it.
Unlike a broadcast, a workflow step has no audience and no send window: it keeps sending for as long as the workflow runs and contacts keep reaching it. So there is nothing to report as queued or in progress, and the numbers are always for a window you choose. Defaults to a 30-day window ending now. Override with timerange_start and timerange_end. Values are clamped: a future end date is clamped to now, a window over 90 days is clamped to 90 days, and a start after the end resets to 30 days ending at timerange_end. Read timerange back from the response for the window actually used.
sends counts a contact once per pass through the step, so a looping workflow makes it larger than recipients. Rates are whole percentages rounded to the nearest integer — a share under 0.5% reads 0 next to a non-zero count, and the counts are authoritative. open_rate, click_rate, bounce_rate and unsubscribe_rate divide by recipients; click_to_open_rate divides unique clicks by unique opens. The workflow builder's percentages under a step divide by sends instead, so for a step that sent to a contact more than once the app shows a lower rate than this endpoint does for the same window; the counts are the same on both. Numerator and denominator are each counted inside the window on their own: an open inside the window counts even when the send it answers happened before the window. So a rate can exceed 100 when few new sends meet many opens of older emails, and it is 0 whenever its denominator is 0, however many opens there were. A step whose email last sent before the window reports zero sends and recipients while opens and clicks inside it still count. To compare like with like, widen the window to cover the sends you care about.
Revenue is attributed when the buyer's last tracked source within 30 days was a click on one of the email's links. Each attributed order contributes its first paid invoice, and only when that invoice was issued inside the window: renewals never count, and an order whose first payment predates the window contributes nothing even if a renewal was paid inside it. Only live-mode orders paid in the workspace currency are counted. Note the engagement counts do include test-mode sends, so the revenue-per-X figures divide live-only money by counts that may include test traffic.
The broadcast equivalent is GET /emails/broadcasts/{broadcast_id}/stats. Its performance and revenue blocks share this vocabulary at the top level, with delivered in place of sends/recipients/failed, and it takes no timerange — a broadcast reports on its own send window.
Request
- Base URLhttps://myworkspace.myclickfunnels.com/api/v2
- Auth
- HTTP bearer
- or OAuth 2Authorization codehttps://accounts.myclickfunnels.com/oauth/authorizeToken URLhttps://accounts.myclickfunnels.com/oauth/token
- TypeScriptA function with typed inputs. Its response is checked against the schema.
Path parameters
Query parameters
Start of the stats timerange (ISO 8601 only; any other value is a 422. Send an offset's + as %2B, since an unencoded + arrives as a space). Defaults to 30 days before timerange_end. If the window between start and end exceeds 90 days, start is automatically clamped to 90 days before end. If start is after end, the window resets to the default 30 days ending at timerange_end. See Timerange parameters (both endpoints) in the Stats Skill for the clamping rules and how to cover longer windows.
End of the stats timerange (ISO 8601 only; any other value is a 422. Send an offset's + as %2B, since an unencoded + arrives as a space). Defaults to the current time. Values in the future are automatically clamped to now.
Response
OK
Example response
{
"step": {
"id": 42,
"public_id": "RBxAQx",
"name": "Welcome email",
"step_type": "send_email_step"
},
"workflow": {
"id": 7,
"public_id": "zmXVGz",
"name": "New lead nurture"
},
"currency": "USD",
"timerange": {
"from": "2026-08-05T00:00:00Z",
"to": "2026-09-04T00:00:00Z"
},
"contacts": {
"are_here": 12,
"were_here": 1240
},
"email": {
"performance": {
"sends": 1240,
"recipients": 1180,
"failed": 4,
"bounced": 21,
"opened": 890,
"unique_opens": 604,
"clicked": 173,
"unique_clicks": 148,
"unsubscribed": 9,
"spam_complaints": 2,
"open_rate": 51,
"click_rate": 13,
"click_to_open_rate": 25,
"bounce_rate": 2,
"unsubscribe_rate": 1
},
"revenue": {
"total": "3480.00",
"orders_count": 24,
"per_recipient": "2.95",
"per_unique_open": "5.76",
"per_unique_click": "23.51"
}
}
}Changes
- info
endpoint added
- info