Workflows::Step

Fetch Workflow Step Stats

Changed on

Stats 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.

get/workflows/steps/{step_id}/stats

Request

Path parameters

step_idstring required

Query parameters

timerange_startstring date-time

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.

timerange_endstring date-time

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

currencystring nullable

ISO 4217 currency code for all monetary values (e.g. USD, EUR). email.revenue counts only orders paid in this currency. null when the workspace has no currency set, in which case there is nothing to filter on and revenue sums every currency its orders were paid in.

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