Campaign results
Read persisted campaign outcomes for one WhatsApp number.
Reviewed
Use campaigns:read to read existing campaigns for the key's number while Logic Flow continues handling replies. Grant this scope explicitly to an existing or new key; the key manager needs broadcast-read authority. It provides no campaign creation, scheduling, retarget action or send permission.
Read a campaign
| Route | Response |
|---|---|
GET /v1/developer/campaigns | {items, nextCursor} |
GET /v1/developer/campaigns/:id | {campaign} |
GET /v1/developer/campaigns/:id/recipients | {items, nextCursor} |
Summaries include id, name, status, templateId, pauseCode, recipient and delivery counters, quick-reply counters and lifecycle timestamps. The exact counters are totalRecipients, admittedCount, quotaWaitingCount, providerAcceptedCount, sentCount, deliveredCount, readCount, repliedCount, failedCount, quickReplyCount and quickReplyUniqueCount. Timestamps are scheduledAt, startedAt, completedAt, sendCompletedAt, outcomeSettledAt, metricsReconciledAt, createdAt and updatedAt; all except creation/update may be null. These are persisted broadcast values. Provider acceptance is distinct from delivery, and send completion is distinct from settled outcomes.
Detail adds failureCohorts and settlementCohorts, keyed by the corresponding enums. Zero-count keys can be absent. These resources do not attribute orders, revenue, conversions or business costs.
Page through current results
Both list routes accept limit from 1 to 100 (default 50) and an opaque after cursor up to 512 characters. Follow nextCursor as after until null. Results use ascending immutable IDs. Keep filters and the parent campaign unchanged: the cursor binds workspace, number, resource and filters. Changing the page size is allowed. A malformed or wrong-context cursor returns 400 invalid_cursor.
Campaign list optionally filters status: DRAFT, SCHEDULED, QUEUED, SENDING, PAUSED, COMPLETED, FAILED or CANCELLED. Recipient lists optionally filter:
| Field | Values |
|---|---|
status | WAITING_QUOTA, ADMITTED, QUEUED, SEND_UNCERTAIN, PROVIDER_ACCEPTED, SENT, DELIVERED, READ, REPLIED, FAILED, SKIPPED, OPTED_OUT |
settlementState | PENDING, SETTLED_SUCCESS, SETTLED_FAILURE, UNCERTAIN |
failureClass | MARKETING_LIMIT, PAIR_RATE_LIMIT, SENDER_QUALITY, MARKETING_SUPPRESSED, WHATSAPP_UNREACHABLE, PERMANENT_BOUNCE, TEMPLATE_PAUSED, SHARED_CONFIGURATION, TEMPORARY_UNDELIVERED, EXPIRED_UNDELIVERED |
retargetEligible | true or false |
Pages show current state, not a fixed export. Concurrent status changes can move rows in or out of a filter. Reconcile current campaign and recipient results when completeness matters. All requests repeat workspace and exact number checks; a foreign, sibling-number or absent campaign returns 404 campaign_not_found.
Interpret recipient results
Each recipient exposes id, nullable authorized contactId, status, settlementState, nullable failureClass and normalizedFailureCode, retargetEligible, nextEligibleAt, queuedAt, providerAcceptedAt, sentAt, deliveredAt, readAt, repliedAt, failedAt and settledAt. All timestamps except queuedAt may be null. No phone number, provider message ID, raw provider failure, rendered variables or stored recipient snapshot is returned.
SEND_UNCERTAIN and UNCERTAIN require reconciliation; they do not authorize another send. Eligibility fields report the existing broadcast decision, not permission to launch a retarget campaign. Contact IDs remain workspace identities; preserve the number and campaign identity when storing recipient results across several keys.
Endpoint details
Every request uses Authorization: Bearer $WAARU_API_KEY and a connected number in Developer API mode. The scope below is required. IDs and payloads are illustrative; replace them with your own permitted resources. See authentication and common errors, size limits and retries.
GET /v1/developer/campaigns
Required scope: campaigns:read
Requires campaigns:read. Read this number's campaign summaries. Requires an explicitly granted campaigns:read scope; existing keys gain none. Keep filters unchanged across opaque cursor pages. No schedule, send or revenue-attribution authority.
| Parameter | Location | Required | Constraints |
|---|---|---|---|
limit | query | No | {"type":"integer","minimum":1,"maximum":100,"default":50} |
after | query | No | {"type":"string","maxLength":512} Use nextCursor from the previous response; keep filters unchanged. |
status | query | No | {"type":"string","enum":["DRAFT","SCHEDULED","QUEUED","SENDING","PAUSED","COMPLETED","FAILED","CANCELLED"]} |
Request
curl --fail-with-body --request GET 'https://api.waaru.app/v1/developer/campaigns' \
--header "Authorization: Bearer $WAARU_API_KEY"Send no request body.
Response: HTTP 200
{
"items": [
{
"id": "campaign_example",
"name": "Workshop reminder",
"status": "COMPLETED",
"templateId": "template_example",
"pauseCode": null,
"totalRecipients": 1,
"admittedCount": 1,
"quotaWaitingCount": 0,
"providerAcceptedCount": 1,
"sentCount": 1,
"deliveredCount": 1,
"readCount": 0,
"repliedCount": 0,
"failedCount": 0,
"quickReplyCount": 0,
"quickReplyUniqueCount": 0,
"scheduledAt": null,
"startedAt": null,
"completedAt": null,
"sendCompletedAt": null,
"outcomeSettledAt": null,
"metricsReconciledAt": null,
"createdAt": "2026-10-18T09:00:00.000Z",
"updatedAt": "2026-10-18T09:00:00.000Z"
}
],
"nextCursor": null
}| Field | Required | Type and constraints |
|---|---|---|
items | Yes | {"maxItems":100,"type":"array"} Nested fields are in the complete schema below. |
nextCursor | Yes | {"anyOf":[{"type":"string","minLength":1,"maxLength":512},{"type":"null"}]} |
Response body: complete JSON schema
Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.
{
"type": "object",
"properties": {
"items": {
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1,
"maxLength": 191
},
"name": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"DRAFT",
"SCHEDULED",
"QUEUED",
"SENDING",
"PAUSED",
"COMPLETED",
"FAILED",
"CANCELLED"
]
},
"templateId": {
"type": "string",
"minLength": 1,
"maxLength": 191
},
"pauseCode": {
"anyOf": [
{
"type": "string",
"enum": [
"MANUAL",
"QUOTA",
"MISSED_SCHEDULE",
"QUALITY",
"TEMPLATE",
"SHARED_CONFIGURATION",
"PROVIDER_LIMIT",
"QUEUE_BACKPRESSURE",
"WORKSPACE_BLOCKED"
]
},
{
"type": "null"
}
]
},
"totalRecipients": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"admittedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"quotaWaitingCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"providerAcceptedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"sentCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"deliveredCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"readCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"repliedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"failedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"quickReplyCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"quickReplyUniqueCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"scheduledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"startedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"sendCompletedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"outcomeSettledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"metricsReconciledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
}
},
"required": [
"id",
"name",
"status",
"templateId",
"pauseCode",
"totalRecipients",
"admittedCount",
"quotaWaitingCount",
"providerAcceptedCount",
"sentCount",
"deliveredCount",
"readCount",
"repliedCount",
"failedCount",
"quickReplyCount",
"quickReplyUniqueCount",
"scheduledAt",
"startedAt",
"completedAt",
"sendCompletedAt",
"outcomeSettledAt",
"metricsReconciledAt",
"createdAt",
"updatedAt"
],
"additionalProperties": false
}
},
"nextCursor": {
"anyOf": [
{
"type": "string",
"minLength": 1,
"maxLength": 512
},
{
"type": "null"
}
]
}
},
"required": [
"items",
"nextCursor"
],
"additionalProperties": false
}Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.
GET /v1/developer/campaigns/{id}
Required scope: campaigns:read
Requires campaigns:read. Read one authorized campaign and retained settlement/failure cohort counts. Counters describe operational observations, not orders, conversions or revenue.
| Parameter | Location | Required | Constraints |
|---|---|---|---|
id | path | Yes | {"type":"string","minLength":1,"maxLength":191} |
Request
curl --fail-with-body --request GET 'https://api.waaru.app/v1/developer/campaigns/id_example' \
--header "Authorization: Bearer $WAARU_API_KEY"Send no request body.
Response: HTTP 200
{
"campaign": {
"id": "campaign_example",
"name": "Workshop reminder",
"status": "COMPLETED",
"templateId": "template_example",
"pauseCode": null,
"totalRecipients": 1,
"admittedCount": 1,
"quotaWaitingCount": 0,
"providerAcceptedCount": 1,
"sentCount": 1,
"deliveredCount": 1,
"readCount": 0,
"repliedCount": 0,
"failedCount": 0,
"quickReplyCount": 0,
"quickReplyUniqueCount": 0,
"scheduledAt": null,
"startedAt": null,
"completedAt": null,
"sendCompletedAt": null,
"outcomeSettledAt": null,
"metricsReconciledAt": null,
"createdAt": "2026-10-18T09:00:00.000Z",
"updatedAt": "2026-10-18T09:00:00.000Z",
"failureCohorts": {},
"settlementCohorts": {
"SETTLED_SUCCESS": 1
}
}
}| Field | Required | Type and constraints |
|---|---|---|
campaign | Yes | {"type":"object","required":["id","name","status","templateId","pauseCode","totalRecipients","admittedCount","quotaWaitingCount","providerAcceptedCount","sentCount","deliveredCount","readCount","repliedCount","failedCount","quickReplyCount","quickReplyUniqueCount","scheduledAt","startedAt","completedAt","sendCompletedAt","outcomeSettledAt","metricsReconciledAt","createdAt","updatedAt","failureCohorts","settlementCohorts"],"additionalProperties":false} Nested fields are in the complete schema below. |
Response body: complete JSON schema
Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.
{
"type": "object",
"properties": {
"campaign": {
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1,
"maxLength": 191
},
"name": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"DRAFT",
"SCHEDULED",
"QUEUED",
"SENDING",
"PAUSED",
"COMPLETED",
"FAILED",
"CANCELLED"
]
},
"templateId": {
"type": "string",
"minLength": 1,
"maxLength": 191
},
"pauseCode": {
"anyOf": [
{
"type": "string",
"enum": [
"MANUAL",
"QUOTA",
"MISSED_SCHEDULE",
"QUALITY",
"TEMPLATE",
"SHARED_CONFIGURATION",
"PROVIDER_LIMIT",
"QUEUE_BACKPRESSURE",
"WORKSPACE_BLOCKED"
]
},
{
"type": "null"
}
]
},
"totalRecipients": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"admittedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"quotaWaitingCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"providerAcceptedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"sentCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"deliveredCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"readCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"repliedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"failedCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"quickReplyCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"quickReplyUniqueCount": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"scheduledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"startedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"sendCompletedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"outcomeSettledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"metricsReconciledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
"failureCohorts": {
"type": "object",
"propertyNames": {
"type": "string",
"enum": [
"MARKETING_LIMIT",
"PAIR_RATE_LIMIT",
"SENDER_QUALITY",
"MARKETING_SUPPRESSED",
"WHATSAPP_UNREACHABLE",
"PERMANENT_BOUNCE",
"TEMPLATE_PAUSED",
"SHARED_CONFIGURATION",
"TEMPORARY_UNDELIVERED",
"EXPIRED_UNDELIVERED"
]
},
"additionalProperties": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"settlementCohorts": {
"type": "object",
"propertyNames": {
"type": "string",
"enum": [
"PENDING",
"SETTLED_SUCCESS",
"SETTLED_FAILURE",
"UNCERTAIN"
]
},
"additionalProperties": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
}
},
"required": [
"id",
"name",
"status",
"templateId",
"pauseCode",
"totalRecipients",
"admittedCount",
"quotaWaitingCount",
"providerAcceptedCount",
"sentCount",
"deliveredCount",
"readCount",
"repliedCount",
"failedCount",
"quickReplyCount",
"quickReplyUniqueCount",
"scheduledAt",
"startedAt",
"completedAt",
"sendCompletedAt",
"outcomeSettledAt",
"metricsReconciledAt",
"createdAt",
"updatedAt",
"failureCohorts",
"settlementCohorts"
],
"additionalProperties": false
}
},
"required": [
"campaign"
],
"additionalProperties": false
}Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.
GET /v1/developer/campaigns/{id}/recipients
Required scope: campaigns:read
Requires campaigns:read. Read paginated retained recipient outcomes for this number's campaign without phone numbers or provider identifiers. Keep filters unchanged; later status changes can change a filtered cohort, so reconcile current results. Retarget evidence does not authorize a new send.
| Parameter | Location | Required | Constraints |
|---|---|---|---|
id | path | Yes | {"type":"string","minLength":1,"maxLength":191} |
limit | query | No | {"type":"integer","minimum":1,"maximum":100,"default":50} |
after | query | No | {"type":"string","maxLength":512} Use nextCursor from the previous response; keep filters unchanged. |
status | query | No | {"type":"string","enum":["WAITING_QUOTA","ADMITTED","QUEUED","SEND_UNCERTAIN","PROVIDER_ACCEPTED","SENT","DELIVERED","READ","REPLIED","FAILED","SKIPPED","OPTED_OUT"]} |
settlementState | query | No | {"type":"string","enum":["PENDING","SETTLED_SUCCESS","SETTLED_FAILURE","UNCERTAIN"]} |
failureClass | query | No | {"type":"string","enum":["MARKETING_LIMIT","PAIR_RATE_LIMIT","SENDER_QUALITY","MARKETING_SUPPRESSED","WHATSAPP_UNREACHABLE","PERMANENT_BOUNCE","TEMPLATE_PAUSED","SHARED_CONFIGURATION","TEMPORARY_UNDELIVERED","EXPIRED_UNDELIVERED"]} |
retargetEligible | query | No | {"type":"boolean"} |
Request
curl --fail-with-body --request GET 'https://api.waaru.app/v1/developer/campaigns/id_example/recipients' \
--header "Authorization: Bearer $WAARU_API_KEY"Send no request body.
Response: HTTP 200
{
"items": [
{
"id": "recipient_example",
"contactId": "contact_example",
"status": "DELIVERED",
"settlementState": "SETTLED_SUCCESS",
"failureClass": null,
"normalizedFailureCode": null,
"retargetEligible": false,
"nextEligibleAt": null,
"queuedAt": "2026-10-18T09:00:00.000Z",
"providerAcceptedAt": "2026-10-18T09:00:00.000Z",
"sentAt": "2026-10-18T09:00:00.000Z",
"deliveredAt": "2026-10-18T09:00:00.000Z",
"readAt": null,
"repliedAt": null,
"failedAt": null,
"settledAt": "2026-10-18T09:00:00.000Z"
}
],
"nextCursor": null
}| Field | Required | Type and constraints |
|---|---|---|
items | Yes | {"maxItems":100,"type":"array"} Nested fields are in the complete schema below. |
nextCursor | Yes | {"anyOf":[{"type":"string","minLength":1,"maxLength":512},{"type":"null"}]} |
Response body: complete JSON schema
Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.
{
"type": "object",
"properties": {
"items": {
"maxItems": 100,
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1,
"maxLength": 191
},
"contactId": {
"anyOf": [
{
"type": "string",
"minLength": 1,
"maxLength": 191
},
{
"type": "null"
}
]
},
"status": {
"type": "string",
"enum": [
"WAITING_QUOTA",
"ADMITTED",
"QUEUED",
"SEND_UNCERTAIN",
"PROVIDER_ACCEPTED",
"SENT",
"DELIVERED",
"READ",
"REPLIED",
"FAILED",
"SKIPPED",
"OPTED_OUT"
]
},
"settlementState": {
"type": "string",
"enum": [
"PENDING",
"SETTLED_SUCCESS",
"SETTLED_FAILURE",
"UNCERTAIN"
]
},
"failureClass": {
"anyOf": [
{
"type": "string",
"enum": [
"MARKETING_LIMIT",
"PAIR_RATE_LIMIT",
"SENDER_QUALITY",
"MARKETING_SUPPRESSED",
"WHATSAPP_UNREACHABLE",
"PERMANENT_BOUNCE",
"TEMPLATE_PAUSED",
"SHARED_CONFIGURATION",
"TEMPORARY_UNDELIVERED",
"EXPIRED_UNDELIVERED"
]
},
{
"type": "null"
}
]
},
"normalizedFailureCode": {
"anyOf": [
{
"type": "string",
"maxLength": 64
},
{
"type": "null"
}
]
},
"retargetEligible": {
"type": "boolean"
},
"nextEligibleAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"queuedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
"providerAcceptedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"sentAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"deliveredAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"readAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"repliedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"failedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
},
"settledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"contactId",
"status",
"settlementState",
"failureClass",
"normalizedFailureCode",
"retargetEligible",
"nextEligibleAt",
"queuedAt",
"providerAcceptedAt",
"sentAt",
"deliveredAt",
"readAt",
"repliedAt",
"failedAt",
"settledAt"
],
"additionalProperties": false
}
},
"nextCursor": {
"anyOf": [
{
"type": "string",
"minLength": 1,
"maxLength": 512
},
{
"type": "null"
}
]
}
},
"required": [
"items",
"nextCursor"
],
"additionalProperties": false
}Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.
Next: Message changes and current-state reconciliation or authentication and scopes.
Template discovery
Read cached template lifecycle state and available Flow launch descriptors, then construct eligible template sends without exposing provider tokens.
Activity reports
Inspect seven UTC dates of key-specific API requests and number-specific webhook delivery health without customer payloads.