Skip to main content
All errors follow a consistent JSON shape:
{
  "detail": "Human-readable description of what went wrong"
}
Some errors carry structured details:
{
  "detail": [
    {
      "loc": ["body", "predictive_target_abandon"],
      "msg": "must be between 0 and 0.030 (TRAI cap)",
      "type": "value_error"
    }
  ]
}
That’s Pydantic’s 422 format — field-level validation errors.

Status codes

StatusMeaning
200Success
201Created
204Success, no body
400Malformed request (rare — most validation uses 422)
401Missing or bad API key
403Key lacks the required scope (future)
404Resource doesn’t exist OR belongs to another tenant
409State conflict (e.g. /predictive/pause on a non-running campaign)
422Validation error — body failed a Pydantic constraint
429Rate-limited
500Our bug. Email support with the request timestamp.
502/503/504Upstream momentarily unavailable. Retry with backoff.

Tenant-scoped 404s

When you GET a resource that exists but belongs to another tenant, you get a 404, not a 403. We never want to confirm existence across tenant boundaries (otherwise an attacker could enumerate other customers’ campaign IDs).

Validation errors you’ll see often

predictive_target_abandon > 0.030

{
  "detail": [{
    "loc": ["body", "predictive_target_abandon"],
    "msg": "Input should be less than or equal to 0.03",
    "type": "less_than_equal"
  }]
}
TRAI caps abandon rate at 3.0%. We can’t accept a higher target.

Inverted ratio range

{
  "detail": "predictive_max_dial_ratio (1.5) must be >= predictive_min_dial_ratio (3.0)"
}
When you PATCH both ratio fields in the same payload, they must satisfy max >= min. PATCHing just one is always allowed (we defer to the DB CHECK).

Invalid phone

{"detail": "Invalid phone number"}
We only accept Indian numbers (10 digits, optional 0/91 prefix). For non-Indian use cases, contact us — cross-border dialing has its own compliance track.

Abandon rate exceeded (business-level)

Not an HTTP error — it’s a lifecycle event. When a predictive campaign’s abandon rate crosses its target for 60s sustained, the backend auto-pauses the campaign. You’ll see:
  • The campaign’s status flip to paused
  • A campaign.paused webhook with data.source = "abandon_rate_exceeded"
Re-enable via POST /api/campaigns/{id}/predictive/resume (dashboard JWT auth, not the v1 API key). See the operator runbook in your dashboard for guidance.