List campaign contacts
curl --request GET \
--url https://api.goyappr.com/campaigns/{id}/leads \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.goyappr.com/campaigns/{id}/leads"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.goyappr.com/campaigns/{id}/leads', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.goyappr.com/campaigns/{id}/leads",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.goyappr.com/campaigns/{id}/leads"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.goyappr.com/campaigns/{id}/leads")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/campaigns/{id}/leads")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"lead_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"lead": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"phone_number": "+972501234567",
"name": "<string>",
"email": "jsmith@example.com",
"source": "api",
"tags": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"description": "<string>",
"color": "<string>",
"sort_order": 123,
"created_at": "2023-11-07T05:31:56Z"
}
],
"long_term_context": "<string>",
"metadata": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
},
"to_number_e164": "+972501234567",
"status": "pending",
"stop_hit": true,
"stop_reason": "<string>",
"stopped_by_disposition_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"attempt_count": 123,
"infra_retries_used": 123,
"next_attempt_at": "2023-11-07T05:31:56Z",
"last_disposition_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"last_disposition": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "<string>",
"color": "#22c55e",
"position": 123,
"is_protected": true,
"created_at": "2023-11-07T05:31:56Z"
},
"last_disconnect_reason": "<string>",
"last_status_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z"
}
],
"pagination": {
"total": 123,
"limit": 123,
"offset": 123
},
"campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"company_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": "<string>",
"code": "<string>"
}Contacts
List enrolled contacts
Enrolled contacts and their per-contact state, oldest enrollment first. Required
scope campaigns:read.
GET
/
campaigns
/
{id}
/
leads
List campaign contacts
curl --request GET \
--url https://api.goyappr.com/campaigns/{id}/leads \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.goyappr.com/campaigns/{id}/leads"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.goyappr.com/campaigns/{id}/leads', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.goyappr.com/campaigns/{id}/leads",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.goyappr.com/campaigns/{id}/leads"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.goyappr.com/campaigns/{id}/leads")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/campaigns/{id}/leads")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"lead_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"lead": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"phone_number": "+972501234567",
"name": "<string>",
"email": "jsmith@example.com",
"source": "api",
"tags": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"description": "<string>",
"color": "<string>",
"sort_order": 123,
"created_at": "2023-11-07T05:31:56Z"
}
],
"long_term_context": "<string>",
"metadata": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
},
"to_number_e164": "+972501234567",
"status": "pending",
"stop_hit": true,
"stop_reason": "<string>",
"stopped_by_disposition_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"attempt_count": 123,
"infra_retries_used": 123,
"next_attempt_at": "2023-11-07T05:31:56Z",
"last_disposition_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"last_disposition": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "<string>",
"color": "#22c55e",
"position": 123,
"is_protected": true,
"created_at": "2023-11-07T05:31:56Z"
},
"last_disconnect_reason": "<string>",
"last_status_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"created_at": "2023-11-07T05:31:56Z"
}
],
"pagination": {
"total": 123,
"limit": 123,
"offset": 123
},
"campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"company_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": "<string>",
"code": "<string>"
}Per-contact state for one campaign, oldest enrollment first. Each row carries the enrollment’s own state machine (status, attempts used, next retry time, why it stopped) with the underlying
lead and the last_disposition expanded to the full objects they reference.
Use this to answer “what happened to this specific person”. For aggregate counters use GET /campaigns/{id}/stats.
Required scope: campaigns:read.
Path parameters
| Parameter | Type | Notes |
|---|---|---|
id | uuid | Campaign ID. Must belong to the API key’s workspace. |
Query parameters
| Parameter | Type | Default | Notes |
|---|---|---|---|
status | string | — | Comma-separated list of contact statuses to keep. Whitespace around each value is trimmed. |
limit | integer | 50 | Page size. Clamped to 1–200. |
offset | integer | 0 | Rows to skip. Negative values are treated as 0. |
Contact statuses
| Status | Meaning |
|---|---|
pending | Waiting for its turn. |
scheduled | Waiting for next_attempt_at to elapse. |
dialing | A call is being placed or is in progress. |
awaiting_disposition | The call ended and is waiting to be categorized. Never re-dialed while in this state. |
completed_success | A stop rule fired — this contact is done. |
completed_failed | Finished without a successful outcome. |
exhausted | Reached max_attempts without hitting a stop rule. |
excluded | Removed by you, or retired when the campaign was stopped or archived. Terminal. |
dnc | On the do-not-call list. Never called again. |
Row fields
| Field | Type | Meaning |
|---|---|---|
id | uuid | Enrollment ID (not the lead ID). |
lead_id | uuid | The lead this enrollment points at. |
to_number_e164 | string | The number snapshotted at enrollment. Editing the lead afterwards cannot silently redirect a dial. |
status | string | See above. |
stop_hit | boolean | true once a stop rule retired this contact. Never un-set. |
stop_reason | string | null | Why it stopped — e.g. disposition, stop_disposition, no_answer, voicemail, disposition_timeout, dnc_blocked, or manual for an exclusion you made. |
stopped_by_disposition_id | uuid | null | The outcome that triggered the stop, kept even if that disposition is later deleted. |
attempt_count | integer | Dial attempts consumed, against max_attempts. |
infra_retries_used | integer | Retries spent on failures that never reached the person. These do not consume a dial attempt. |
next_attempt_at | timestamp | When this contact becomes eligible again. |
last_disposition_id | uuid | null | Most recent outcome. |
last_disconnect_reason | string | null | How the last call ended, when the carrier reported a reason. |
last_status_at | timestamp | null | When the state last changed. |
completed_at | timestamp | null | When this contact reached a terminal state. |
created_at | timestamp | When the contact was enrolled. |
lead | object | The full lead record. |
last_disposition | object | null | The full disposition record for last_disposition_id. |
Example request
# Everything, first page
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads" \
-H "Authorization: Bearer $YAPPR_API_KEY"
# Only contacts that finished without being reached
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads?status=exhausted,completed_failed&limit=200" \
-H "Authorization: Bearer $YAPPR_API_KEY"
# What is live right now
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads?status=dialing,awaiting_disposition" \
-H "Authorization: Bearer $YAPPR_API_KEY"
Example response
{
"data": [
{
"id": "c7d2e1f0-0000-4a55-8b12-000000000031",
"lead_id": "a1c3e5f7-0000-4b21-9c30-000000000021",
"to_number_e164": "+972501234567",
"status": "completed_success",
"stop_hit": true,
"stop_reason": "stop_disposition",
"stopped_by_disposition_id": "5c1d9a2e-0000-4b10-9f31-000000000011",
"attempt_count": 2,
"infra_retries_used": 0,
"next_attempt_at": "2026-07-27T08:12:00.000Z",
"last_disposition_id": "5c1d9a2e-0000-4b10-9f31-000000000011",
"last_disconnect_reason": "Hangup",
"last_status_at": "2026-07-27T08:41:12.220Z",
"completed_at": "2026-07-27T08:41:12.220Z",
"created_at": "2026-07-26T09:20:01.004Z",
"lead": {
"id": "a1c3e5f7-0000-4b21-9c30-000000000021",
"phone_number": "+972501234567",
"name": "Dana Levi",
"email": "dana@example.com",
"source": "api"
},
"last_disposition": {
"id": "5c1d9a2e-0000-4b10-9f31-000000000011",
"label": "Not Interested",
"color": "#f97316"
}
},
{
"id": "c7d2e1f0-0000-4a55-8b12-000000000032",
"lead_id": "a1c3e5f7-0000-4b21-9c30-000000000022",
"to_number_e164": "+972521112222",
"status": "scheduled",
"stop_hit": false,
"stop_reason": null,
"stopped_by_disposition_id": null,
"attempt_count": 1,
"infra_retries_used": 1,
"next_attempt_at": "2026-07-28T12:03:00.000Z",
"last_disposition_id": null,
"last_disconnect_reason": "No Answer",
"last_status_at": "2026-07-28T12:02:00.140Z",
"completed_at": null,
"created_at": "2026-07-26T09:20:01.004Z",
"lead": {
"id": "a1c3e5f7-0000-4b21-9c30-000000000022",
"phone_number": "+972521112222",
"name": "Yossi Mor",
"email": null,
"source": "api"
},
"last_disposition": null
}
],
"pagination": { "total": 412, "limit": 50, "offset": 0 },
"campaign_id": "b3f1c0d2-5a44-4f0e-9c11-7a2e8d3f0001",
"company_id": "fe493f11-0000-0000-0000-000000000001"
}
lead and last_disposition are abbreviated above; in a real response each is the complete object.
last_disposition is null while a call is still being categorized, and stays null for calls that never connected — a last_disconnect_reason with no disposition is the normal shape for an unanswered call.
Errors
| HTTP | Code | When |
|---|---|---|
| 401 | INSUFFICIENT_SCOPE | API key lacks campaigns:read. |
| 404 | — | No campaign with that ID in this workspace, or it has been archived. |
| 500 | — | The list query failed. Safe to retry. |
Authorizations
Your Yappr API key (e.g. ypr_live_...). Generate one in the dashboard under Settings → API Keys.
Path Parameters
Query Parameters
Comma-separated list of contact statuses to include.
Required range:
x <= 200