Skip to content
View as Markdown

List a stage's leads ​

GET /v1/campaigns/{campaign_id}/pipeline/stages/{stage}/leads

Returns the campaign's leads in one stage, the most recent arrival first.

Scope: leads:read

{stage} is a stage slug from the board. Without pipeline_id it is a stage of the campaign's default pipeline. When the campaign has more than one pipeline, send pipeline_id for a stage of any pipeline but the default: slugs repeat across pipelines, so new without pipeline_id is always the default pipeline's new.

Rows are ordered by stage_entered_at, newest first, and rows already returned are never repeated. A lead that enters the stage while you page is at the top of the list, not on a later page: read the first page again for new arrivals.

The campaign must be one the key may reach, otherwise 404. Only leads the key's member can see are listed, and only on cards they may open: a card outside the assigned records their role limits them to is left out even when they see its lead through another card. Contact fields follow the member's field visibility as in the conversion report; form_name is attribution, so it is null when the role hides custom fields and Restricted when it masks them. assigned_member, stage_entered_at and deal_value are always shown.

Path parameters ​

NameTypeRequiredDescription
campaign_idUUIDYesA campaign the key may reach.
stagestringYesThe stage slug, as the board lists it.

Query parameters ​

NameTypeRequiredDescription
pipeline_idUUIDNoThe stage's pipeline, a pipelines[].id from the board. It must be one of the campaign's pipelines the member may open. Default: the campaign's default pipeline.
limitintegerNoRows per page. 1 to 100. Default: 50.
cursorstringNoThe next_cursor of the previous page, unchanged. Opaque: never build or edit one. Up to 4,096 characters.

Response ​

Status: 200 OK

NameTypeDescription
dataarray of objectsLeads in the stage, most recent arrival first.
data.lead_idUUIDThe lead. Read it with GET /v1/leads/{lead_id} and its outcome with GET /v1/leads/{lead_id}/conversion.
data.first_namestring | nullFirst name.
data.last_namestring | nullLast name.
data.phonestring | nullPhone number in E.164; a stored number that is not a valid phone number is returned as stored.
data.emailstring | nullEmail address.
data.assigned_memberobject | nullThe team member the lead's card on this pipeline is assigned to, or null. A follow-through card has its own assignee.
data.assigned_member.idUUIDThe member.
data.assigned_member.namestring | nullTheir DialEcho profile name.
data.stage_entered_attimestamp | nullWhen the card entered its current stage.
data.deal_valuenumber | nullThe deal value. A follow-through card shows the value of the deal it came from, as in DialEcho.
data.created_attimestamp | nullWhen the lead was created in DialEcho.
data.form_namestring | nullThe attribution.form_name the lead was sent with.
next_cursorstring | nullPass it back as cursor for the next page; null when there is none.
has_morebooleantrue while more pages may follow. Keep paging until it is false.

Example request ​

curl

bash
curl "https://development.agencydevworks.ai/v1/campaigns/cccccccc-0000-4000-8000-000000000001/pipeline/stages/appointment/leads?limit=2" \
  -H "Authorization: Bearer dei_your_key"

JavaScript

js
const response = await fetch("https://development.agencydevworks.ai/v1/campaigns/cccccccc-0000-4000-8000-000000000001/pipeline/stages/appointment/leads?limit=2", {
  headers: {
    "Authorization": "Bearer dei_your_key",
  },
});
const data = await response.json();

Example response ​

Status: 200 OK

json
{
  "data": [
    {
      "lead_id": "aaaaaaaa-0000-4000-8000-000000000001",
      "first_name": "Pat",
      "last_name": "Lee",
      "phone": "+14155550132",
      "email": "pat@example.com",
      "assigned_member": { "id": "ffffffff-0000-4000-8000-000000000001", "name": "Jordan Rivera" },
      "stage_entered_at": "2026-11-02T16:04:12Z",
      "deal_value": null,
      "created_at": "2026-11-02T15:55:55Z",
      "form_name": "quote-step-1"
    },
    {
      "lead_id": "aaaaaaaa-0000-4000-8000-000000000002",
      "first_name": "Sam",
      "last_name": null,
      "phone": "+18035550142",
      "email": null,
      "assigned_member": null,
      "stage_entered_at": "2026-11-01T15:30:00Z",
      "deal_value": null,
      "created_at": "2026-11-01T15:21:07Z",
      "form_name": null
    }
  ],
  "next_cursor": "v2.exampleOpaqueCursor",
  "has_more": true
}

Errors ​

HTTPCodeWhen
403insufficient_scopeThe key does not have leads:read. Checked before the query is validated.
404not_foundThe campaign does not exist or the key cannot reach it; or pipeline_id is not one of the campaign's pipelines the member may open (without pipeline_id: the campaign has no such default pipeline).
409resync_requiredThe cursor expired (after 30 days) or the key's or its member's access changed since it was issued. Start again from the first page.
422invalid_cursorThe cursor was not issued to this key for this request, was changed, or was sent with different parameters than the page it came from.
422unknown_stage{stage} is not a stage of that pipeline. error.valid_stages lists its slugs in board order.
422invalid_requestcampaign_id or pipeline_id is not a UUID, limit is outside 1 to 100, or cursor is over 4,096 characters. error.fields names it (campaign_id, query.pipeline_id, query.limit).
503dependency_unavailableThe report or the access check is temporarily unavailable. Retry the same request with backoff.

Every endpoint can also answer 401 invalid_api_key, 429 rate_limited and 503 dependency_unavailable. See Errors in the overview.