Skip to main content
The v2 REST API uses a single, predictable error envelope across every endpoint. Whatever the failure — a missing auth token, a Zod validation problem, a 404, a downstream rate limit — the body shape is the same. Build your error-handling once and reuse it everywhere.
This page covers v2 REST endpoints. MCP tool errors follow the ADCP error spec and are returned in structuredContent rather than HTTP status codes.

Error envelope

Every non-success response has data: null and a populated error object:
{
  "data": null,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "field": "name",
    "details": {
      "issues": [
        { "path": "name", "message": "Required" }
      ]
    }
  }
}
FieldTypeAlways presentDescription
codestringyesMachine-readable error code (see table below)
messagestringyesHuman-readable message safe to surface to end users
fieldstringnoField path for validation errors (e.g. start_date, targeting.geos)
detailsobjectnoStructured payload — Zod issues, conflict resource ids, retry hints, etc
Successful responses have the inverse shape: { "data": <result>, "error": null }. List endpoints add a meta block (see Pagination).

HTTP status codes

StatusMeaningTypical code values
400Malformed requestBAD_REQUEST, VALIDATION_ERROR, CURRENCY_MISMATCH
401Missing or invalid authUNAUTHORIZED
403Authenticated but not allowedFORBIDDEN, ACCESS_DENIED, ALPHA_OPT_IN_REQUIRED, TOS_ACCEPTANCE_REQUIRED
404Resource doesn’t exist or isn’t visible to youNOT_FOUND
409Conflicting state (duplicate, wrong state, budget short)CONFLICT, PRICING_NOT_CONFIGURED, INSUFFICIENT_MEDIA_BUDGET, ROUTED_AGENT_REQUIRES_OPERATOR_AUTH
422Semantically invalid (rare — most things use 400)VALIDATION_ERROR
429Rate limit hitRATE_LIMITED (see Rate Limits)
500Unhandled server errorINTERNAL_ERROR
501Endpoint exists but not yet implementedNOT_IMPLEMENTED
503Upstream / dependency unavailableSERVICE_UNAVAILABLE

Common error codes

CodeWhen you’ll see it
VALIDATION_ERRORZod schema rejected the request body, query, or params
BAD_REQUESTGeneric 400 — usually a malformed param that didn’t reach Zod
UNAUTHORIZEDNo bearer token, expired token, or unknown API key
FORBIDDENAuth succeeded but you lack the role/permission
ACCESS_DENIEDResource exists but is owned by a different customer/seat
NOT_FOUNDResource ID doesn’t exist (or is hidden from your scope)
CONFLICTDuplicate resource, illegal state transition, or invariant violated
RATE_LIMITEDToo many requests — back off and retry after Retry-After
INTERNAL_ERRORUnhandled exception — safe to retry once
SERVICE_UNAVAILABLEA dependency (downstream agent, billing, signal provider) is temporarily down
Domain-specific codes you may encounter on campaign endpoints:
CodeMeaning
PRICING_NOT_CONFIGUREDAdvertiser has no pricing rule for the selected sales agent
CURRENCY_MISMATCHBudget currency doesn’t match the agent or storefront currency
INSUFFICIENT_MEDIA_BUDGETRequested budget is below the configured floor
ROUTED_AGENT_REQUIRES_OPERATOR_AUTHRouted sales agent needs operator credentials configured first
ALPHA_OPT_IN_REQUIREDFeature is in alpha — opt in via support before using
TOS_ACCEPTANCE_REQUIREDAccount must accept the latest terms of service before mutating

Validation errors

When request validation fails, code is VALIDATION_ERROR and details.issues enumerates every problem Zod found, with dotted field paths:
{
  "data": null,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "issues": [
        { "path": "name", "message": "Required" },
        { "path": "budget.amount", "message": "Number must be positive" },
        { "path": "flightDates.start", "message": "Invalid date" }
      ]
    }
  }
}
When a single-field check fails (e.g. a route guard), field is set instead:
{
  "data": null,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Start date must be in the future",
    "field": "start_date"
  }
}
Always render details.issues[].path in your UI — the user usually just needs to know which form field to fix.

Handling errors in client code

curl -i https://api.agentic.scope3.com/api/buyer/campaigns \
  -H "Authorization: Bearer $SCOPE3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"advertiserId": 123}'

# HTTP/1.1 400 Bad Request
# Content-Type: application/json
#
# {
#   "data": null,
#   "error": {
#     "code": "VALIDATION_ERROR",
#     "message": "Request validation failed",
#     "details": {
#       "issues": [
#         { "path": "name", "message": "Required" },
#         { "path": "budget", "message": "Required" }
#       ]
#     }
#   }
# }
Don’t pattern-match on message text — message strings may be reworded for clarity. Always branch on error.code (and on HTTP status as a fallback).

Retrying safely

RATE_LIMITED, INTERNAL_ERROR, and SERVICE_UNAVAILABLE are transient — retry GETs with exponential backoff. For creation/mutation requests after a 5xx, prefer to surface the error rather than auto-retry, since duplicate-create protection isn’t enforced server-side. VALIDATION_ERROR, NOT_FOUND, FORBIDDEN, ACCESS_DENIED, and CONFLICT are terminal — don’t retry until the input or state changes.