Skip to main content
Building a partner app that acts on behalf of individual Yotel users (Salesforce, HubSpot, etc.)? You want OAuth 2.0, not an API key. API keys are for your own tenant’s automation.

Headers

Every request to /api/v1/* must send:
Authorization: Bearer yt_<env>_<secret>
Other headers:
HeaderRequiredPurpose
Content-Type: application/jsonon writesrequest body type
Idempotency-Key: <uuid>recommended on writesdedup on retry (see Idempotency)

Environments

PrefixBehavior
yt_live_…Real dialing, real PSTN charges, real webhooks to your production URL.
yt_test_…Sandbox: DB writes happen, webhooks fire, but no PSTN originate. Use during development + CI.
Environment is pinned on the key at issue time. You cannot promote a test key to live or vice versa; issue a new key in the target environment.

Rate limits

Every key has a rate_limit_per_min — default 600, configurable up to 100,000 for enterprise plans. The algorithm is a fixed per-minute window:
Request lands at minute M → INCR counter for M → if count > limit, 429.
Response headers on every 2xx:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 437
On 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 43
Retry-After is seconds until the next minute boundary. Well-behaved clients sleep for that duration and retry.

Burst behavior

At the minute boundary, 2× the limit can pass in a single second (last second of minute A + first second of minute B). This is a known property of fixed-window counters; if you need exact 600/min smoothing, throttle on your side with a client-side token bucket.

Redis outage fallback

If our rate-limit backend is unreachable, we fail open — your request isn’t rate-limited. This is deliberate: a DoS control shouldn’t block paying customers during our own outages. We still log the failure and rate-limit at the LB in emergencies.

Scopes

Reserved for v1.1 — the scopes field on a key is accepted but not enforced per-route yet. Safe to leave empty during v1. When scope enforcement lands, existing keys with empty scopes will be grandfathered into “all scopes granted.”

Rotating keys

Keys don’t expire. To rotate:
1

Issue a new key from the dashboard

Give it a label like Prod rotation 2026-Q2.
2

Deploy it to your backend

Roll the env var, restart services, verify traffic on the new key via dashboard → Usage → filter by key prefix.
3

Revoke the old key

Dashboard → Keys → old key → Revoke (with a reason for the audit trail). Revocation is immediate; subsequent requests with the old key return 401.
There’s no grace period after revoke. Plan your cutover so the new key is fully deployed before you revoke the old one.

Auth errors

401 Malformed key
{
  "detail": "Malformed API key"
}
Your Authorization value doesn’t parse as yt_<env>_…. Common cause: copy-paste lost the Bearer prefix.
401 Invalid key
{
  "detail": "Invalid API key"
}
The prefix is parseable but no active key matches. Either the key was revoked, mistyped, or never existed.
403 Scope denied (v1.1)
{
  "detail": "API key missing required scope: campaigns:write"
}
You’ll start seeing this when scope enforcement ships. Add the required scope to the key (or issue a new one with it).
429 Rate limited
{
  "detail": "Rate limit exceeded"
}
See rate limits section above.