Skip to main content
GET
List Campaign Dispatches
Read every dispatch result for a single outbound campaign — the same data the dashboard’s CSV export produces, returned as JSON. Intended for scheduled jobs that pull the day’s results after dialing finishes. This endpoint is read-only. It performs no writes.

Request

Headers

string
required
Bearer token authentication with your Developer Key (dk_...).

Query parameters

string
Filter by dispatch status, comma-separated (e.g. completed,failed). Omit for no filtering. An unrecognized value returns 400 rather than silently matching nothing.
string
ISO 8601 timestamp with offset. Returns only records updated strictly after this instant — use it for incremental pulls.
string
Pagination cursor. Echo back the previous page’s next_cursor verbatim; do not parse or construct its contents.
integer
Maximum records per page. Defaults to 1000, capped at 5000.

Example

Response

Fields

dispatch_status values

evaluation

The content depends on the evaluation criteria configured for the project:
  • Pass/fail: "Pass", "Fail", or "Unknown" when undecidable
  • Numeric: the score as a string, e.g. "4.5"
  • Enum: the project’s own enum value as a string
null when the call has not been evaluated or the project defines no criteria.

variables

Dynamic fields whose keys come from each project’s variable settings — whatever the call extracted is what appears here. Adding a variable to a project is reflected automatically, with no API change.
If a call did not extract a given variable, that key is ABSENT from the record rather than present with an empty value. The CSV export pads every row to match its header; JSON does not.Write your client defensively — record.variables.customer_intent ?? "" — instead of assuming the key exists.
When a record has no extraction result at all, variables falls back to the seed fields the contact was imported with. That covers two cases, not one: contacts never dialed, and contacts that were dialed but produced no variables (no answer, immediate hangup).
This fallback is a deliberate difference from the CSV export, which builds its header from extracted variables plus the seed fields of never-dialed contacts only — so a dialed-but-no-extraction contact shows blank cells there while this API returns its seed fields.The two cases are distinguishable only at the extremes: session_id: null means never dialed, so those variables are always seed fields. For a record with a session_id, the API does not flag whether the values came from the call or from the import — treat them as “the best values we have for this contact”.

Pagination

Page with the cursor until has_more is false:
A page may return fewer records than limit: when variables payloads are large, the response is cut short to bound its size. This is reported the same way, with has_more: true and a next_cursor to continue from — so decide whether to keep paging from has_more, never from whether the page is full.
While a campaign is still dialing, a record can come back on more than one page. Pages are ordered by updated_at, which the dialer rewrites as a call progresses, so a record you already read can be pushed past your cursor and returned again later. Nothing is ever skipped — only repeated. Deduplicate by dispatch_id when paging through a running campaign; a pull that starts after dialing finishes is unaffected.
The cursor is an opaque string; pass it back verbatim. Its internal format is not part of the public contract and may change without breaking compatibility.

Daily incremental pull

The recommended scheduled usage: run once after dialing finishes, passing the last successful pull’s timestamp as updated_after to fetch only what changed.
+ must be encoded as %2B in a query string; otherwise it is parsed as a space and the offset is misread.
Prefer carrying forward the largest updated_at you saw on the previous run over using local midnight as the boundary — post-call evaluation writes can land after the day rolls over. Subtract a small safety margin from that watermark — a few seconds is plenty — and deduplicate by dispatch_id. updated_after is a strict > comparison, so a record written in the very millisecond your previous run read up to would otherwise never be picked up by any later run.

Errors

Notes

  • Scope: a Developer Key can only read campaigns under projects it has access to. Cross-project access returns 403.
  • Differences from the CSV export: same source and same fields, but the API returns raw values rather than display formatting — dispatch_status is completed, not Completed; duration_seconds is 96, not 1m 36s. Timestamps are always ISO 8601 in UTC.
  • Network allowlisting: allowlist the hostname api.pathors.com. The domain sits behind Cloudflare, so the resolved addresses are anycast and rotate — do not pin specific IPs. If your policy requires IP-based rules, use Cloudflare’s published IP ranges.