List a stage's leads
GET /
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
| Name | Type | Required | Description |
|---|---|---|---|
campaign_id | UUID | Yes | A campaign the key may reach. |
stage | string | Yes | The stage slug, as the board lists it. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
pipeline_id | UUID | No | The 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. |
limit | integer | No | Rows per page. 1 to 100. Default: 50. |
cursor | string | No | The next_cursor of the previous page, unchanged. Opaque: never build or edit one. Up to 4,096 characters. |
Response
Status: 200 OK
| Name | Type | Description |
|---|---|---|
data | array of objects | Leads in the stage, most recent arrival first. |
data. | UUID | The lead. Read it with GET /v1/leads/{lead_id} and its outcome with GET /v1/leads/{lead_id}/conversion. |
data. | string | null | First name. |
data. | string | null | Last name. |
data. | string | null | Phone number in E.164; a stored number that is not a valid phone number is returned as stored. |
data. | string | null | Email address. |
data. | object | null | The team member the lead's card on this pipeline is assigned to, or null. A follow-through card has its own assignee. |
data. | UUID | The member. |
data. | string | null | Their DialEcho profile name. |
data. | timestamp | null | When the card entered its current stage. |
data. | number | null | The deal value. A follow-through card shows the value of the deal it came from, as in DialEcho. |
data. | timestamp | null | When the lead was created in DialEcho. |
data. | string | null | The attribution.form_name the lead was sent with. |
next_cursor | string | null | Pass it back as cursor for the next page; null when there is none. |
has_more | boolean | true 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
| HTTP | Code | When |
|---|---|---|
| 403 | insufficient_scope | The key does not have leads:read. Checked before the query is validated. |
| 404 | not_found | The 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). |
| 409 | resync_required | The 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. |
| 422 | invalid_cursor | The cursor was not issued to this key for this request, was changed, or was sent with different parameters than the page it came from. |
| 422 | unknown_stage | {stage} is not a stage of that pipeline. error.valid_stages lists its slugs in board order. |
| 422 | invalid_request | campaign_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). |
| 503 | dependency_unavailable | The 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.
Related endpoints
- GET
/· Retrieve the pipelinev1/ campaigns/ {campaign_id}/ pipeline - GET
/· Retrieve a leadv1/ leads/ {lead_id} - GET
/· Retrieve a conversionv1/ leads/ {lead_id}/ conversion