API keys, authentication, and the error envelope
Creating keys, Bearer auth, pagination and rate limits, the standard error shape, and a first request in curl.
Everything the dashboard does is available over the REST API at /api/v1. Keys are created under Settings → API keys — name the key, and copy the secret when it's shown, because it's displayed exactly once and stored hashed. Keys are scoped to the workspace they're created in and act with that workspace's data only.
Authentication
Pass the key as a Bearer token on every request:
curl https://app.sendcanyon.com/api/v1/campaigns \
-H "Authorization: Bearer sc_live_9f2ab..." \
-H "Content-Type: application/json"{
"data": [
{
"id": "6b9d2c1e-8a44-4f0b-9c3a-2f1d5e7a0b8c",
"name": "Q3 outbound — RevOps leaders",
"status": "running",
"created_at": "2026-07-02T09:14:33Z"
}
],
"next_cursor": "eyJpZCI6IjZiOWQi..."
}Pagination and rate limits
List endpoints use cursor pagination: pass ?cursor= from the previous response's next_cursor until it comes back null, with ?limit= up to 100. Requests are rate-limited per key; every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers, and exceeding the limit returns 429 with a Retry-After header. Back off exponentially on 429 — the limit is per key, so a hot loop starves your own integration.
The error envelope
Every error, from any endpoint, has the same shape — an error object with a stable machine-readable code and a human-readable message:
{
"error": {
"code": "not_found",
"message": "No campaign with that id exists in this workspace."
}
}| HTTP | code | Meaning |
|---|---|---|
| 400 | validation_error | A field failed validation; the message names the field |
| 401 | unauthorized | Missing, malformed, or revoked API key |
| 403 | forbidden | Key is valid but the role can't perform this mutation |
| 404 | not_found | No such resource in this workspace (including other workspaces' ids) |
| 409 | conflict | State conflict, e.g. launching an already-running campaign |
| 422 | limit_exceeded | A plan limit blocks the action; upgrade or reduce usage |
| 429 | rate_limited | Too many requests for this key; honor Retry-After |
| 500 | internal_error | Our fault; safe to retry with backoff |
What's on the API
- Domains: create, list, fetch generated DNS records, trigger verification.
- Mailboxes: connect, test-connection, warmup start/stop/status.
- Contacts and lists: CRUD, CSV import, validation, suppressions.
- Campaigns: CRUD, launch/pause/resume/duplicate, per-campaign stats.
- Messages and events: read endpoints for everything sent and everything observed.
- Analytics, billing state, and outbound webhook configuration.
All request and response bodies are JSON; all timestamps are UTC ISO 8601; all ids are UUIDs. Mutating endpoints validate strictly and reject unknown fields with validation_error rather than ignoring them.