Skip to main content
flag
Mon - Fri   10:00 – 18:00

Getting Started

API Documentation / Getting started

Base URL for your account: https://cpaas.studio/api/v1

  1. Create an API key: Administration → API Key → Add Key. Copy the plain key shown in the modal — it will not be shown again.
  2. Upload an audio asset, or use one already approved in your Audio Library.
  3. Poll Audio Status until status is approved — required before the asset can be used in a broadcast (unless your key auto-approves).
  4. Check your balance for the module your key is scoped to.
  5. Send a broadcast.
  6. Poll the broadcast's number status until it settles.

Authentication

Every request must carry an API key, created from your account's Administration → API Key screen. The key is shown to you exactly once at creation. It can be supplied any of the following ways — the first one found wins, checked in this order:

# Transport Example
1 X-API-KEY header recommended X-API-KEY: cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2 Authorization: Bearer header Authorization: Bearer cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3 api_key / apikey / key parameter ?api_key=cpk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (query, form, or JSON body)

GET/POST equivalence

Every endpoint accepts both GET and POST for the exact same operation — never PUT, PATCH, or DELETE. Parameters can be sent as a GET query string, a POST form body, or a POST JSON body — all equivalent for every endpoint that isn't a file upload.

Response envelope

Every response — success or failure, on every endpoint — has one of these three shapes.

Success:

{
  "success": true,
  "message": "OK",
  "data": {"...": "..."},
  "request_id": "9c4a2e3e-9b7b-4a1e-8f39-2a3f6b7c9d10"
}

Failure (any code other than REQ_VALIDATION_FAILED):

{
  "success": false,
  "message": "A short, human-readable description of what went wrong.",
  "error": {"code": "SOME_ERROR_CODE", "details": null},
  "request_id": "9c4a2e3e-9b7b-4a1e-8f39-2a3f6b7c9d10"
}

Failure (REQ_VALIDATION_FAILED specifically):

{
  "success": false,
  "message": "The given data was invalid.",
  "error": {
    "code": "REQ_VALIDATION_FAILED",
    "fields": [
      {"field": "did", "code": "REQ_FIELD_REQUIRED", "message": "The did field is required."}
    ]
  },
  "request_id": "9c4a2e3e-9b7b-4a1e-8f39-2a3f6b7c9d10"
}

Rate limits

  • Two limits apply, and either can produce a SYS_RATE_LIMITED response with a Retry-After header:
  • Per API key — your normal usage limit, applied once your key has been validated.
  • Per source IP, for failed authentication only — a much lower limit that bounds invalid-key attempts. It does not apply once a request authenticates successfully.

Error codes

Codes marked Reserved are defined in the catalogue but not currently emitted by anything in this API.

AUTH_*

Code HTTP status Meaning What to do
AUTH_KEY_MISSING 401 No API key was found on the request, in any of the four transports. Check the key is actually being sent — see "Authentication".
AUTH_KEY_INVALID 401 The key doesn't match any active key. Covers both "no such key" and "key exists but the secret is wrong" — these are deliberately indistinguishable from the outside. Check the key was copied correctly; it's shown only once at creation, so a garbled copy may need a fresh key.
AUTH_KEY_EXPIRED 401 The key has passed its expiry date, or has been revoked/regenerated. Generate a new key from Administration → API Key.
AUTH_KEY_DISABLED 403 The key exists and hasn't expired, but is currently disabled. Re-enable it from Administration → API Key, or generate a new one.
AUTH_IP_NOT_ALLOWED 403 The key has an IP allowlist configured, and the request's source IP isn't on it. Add your server's IP to the key's allowlist, or clear the allowlist to permit all IPs.
AUTH_MODULE_NOT_ALLOWED 403 The key is scoped to a specific module and this endpoint belongs to a different one. Use a key scoped to "all", or the correct module, for this endpoint.
AUTH_ACCOUNT_INACTIVE 403 The account that owns this key isn't currently active. Contact your account administrator.

REQ_*

Code HTTP status Meaning What to do
REQ_VALIDATION_FAILED 422 One or more parameters failed validation. error.fields lists each failed field with its own code and message. Fix the named field(s) and retry.
REQ_NOT_FOUND 404: the URL doesn't match any endpoint in this API. 405: the URL matches an endpoint but the HTTP verb isn't GET/POST. Also the fallback for any unmatched/foreign route. Check the URL and method against this document.
REQ_NUMBERS_LIMIT_EXCEEDED 422 Voice Broadcast's numbers array exceeds the per-request cap (1,000 by default). Split the recipient list into multiple requests.
REQ_SCHEDULE_IN_PAST 422 schedule_at resolved to a time that's already passed by the moment the request was actually processed. Resend with a later schedule_at, or omit it to send immediately.
REQ_SCHEDULE_INVALID_FORMAT 422 schedule_at isn't a value the date parser accepts. Use ISO-8601, with or without a UTC offset.
REQ_AUDIO_SELECTION_INVALID 422 Neither or both of audio_uid/audio_reference were supplied where exactly one is required. Supply exactly one of audio_uid, audio_reference.
REQ_TARGET_SELECTION_INVALID (Reserved) Defined in the catalogue but not currently emitted anywhere in this API — no live endpoint has a target-selection ambiguity of this shape yet. Not applicable today.
REQ_FIELD_REQUIRED Field-level only — never a top-level error.code. Appears as one entry inside error.fields when that field was missing. Supply the named field.
REQ_FIELD_INVALID_FORMAT Field-level only. The default field-level code for any validation rule not covered by a more specific one. Fix the named field's format.
REQ_FIELD_OUT_OF_RANGE Field-level only. The field failed a min/max/between/size rule. Adjust the named field's value/length within range.
REQ_FIELD_UNKNOWN_VALUE Field-level only. The field's value isn't one of the accepted enumerated values. Use one of the documented allowed values for that field.
REQ_FIELD_NOT_PERMITTED Field-level only. The field was supplied but this endpoint doesn't accept it. Remove the named field from your request.
REQ_IDEMPOTENCY_KEY_CONFLICT 409 Voice Broadcast only. The Idempotency-Key header you sent was already used, but with a different request body than this one. Use a fresh Idempotency-Key for a genuinely new request, or resend the exact original body if you meant to replay it.

AUDIO_*

Code HTTP status Meaning What to do
AUDIO_NOT_FOUND 404 audio_uid/reference doesn't match any of your audio assets. Check audio_uid/reference against Audio Status.
AUDIO_NOT_APPROVED 422 The audio asset hasn't been approved, or hasn't been delivered to the operator yet. Poll Audio Status until status is approved, then retry.
AUDIO_DISABLED 422 The audio asset has been turned off via Audio Enable/Disable. Re-enable it, or select a different asset.

BCAST_*

Code HTTP status Meaning What to do
BCAST_NOT_FOUND 404 id/reference doesn't match any of your broadcasts. Check the id/reference against the response from Voice Broadcast.
BCAST_DID_INVALID 422 did doesn't match any of your account's active, voice.obd-enabled DIDs. Check the number against your account's DID list.
BCAST_NO_VALID_NUMBERS 422 Every recipient number submitted was invalid or a duplicate. Check error.details.rejected_numbers for the reason per number, then retry with valid ones.
BCAST_ALL_NUMBERS_INVALID 422 A more specific case of BCAST_NO_VALID_NUMBERS: every rejected number failed specifically because it could not be normalized to a valid number. Reformat every number in the request and resend.
BCAST_ALL_NUMBERS_DUPLICATE (Reserved) Provably unreachable today — numbers deduplication always keeps a number's first occurrence as valid, so a submission can never reach "all numbers rejected" with duplicate as the only reason. Not applicable today.

BILLING_*

Code HTTP status Meaning What to do
BILLING_INSUFFICIENT_CREDITS 402 Your voice.obd wallet balance doesn't cover the estimated cost of the request. Top up your voice.obd balance, or reduce the recipient count, then retry.
BILLING_NO_ACTIVE_PLAN (Reserved) Every endpoint in this API today is wallet-billed; this code exists for a future plan-billed scope that isn't reachable from any published endpoint yet. Not applicable today.
BILLING_PLAN_QUOTA_EXHAUSTED (Reserved) Reserved, same reason as BILLING_NO_ACTIVE_PLAN. Not applicable today.

SYS_*

Code HTTP status Meaning What to do
SYS_RATE_LIMITED 429 Too many requests. A Retry-After header on the response says how many seconds to wait. Wait for the Retry-After duration, then retry.
SYS_PROVIDER_ERROR 502 The call succeeded on our side but failed talking to the underlying voice provider/operator. Safe to retry after a short delay. If it persists, contact support with the request_id.
SYS_PROVIDER_TIMEOUT (Reserved) Reserved for a possible future endpoint that calls the operator synchronously in the request/response cycle; nothing in this API does that today. Not applicable today.
SYS_PROVIDER_REJECTED (Reserved) Reserved, same reason as SYS_PROVIDER_TIMEOUT. Not applicable today.
SYS_INTERNAL_ERROR 500 An unexpected error on our side. You will never see a stack trace or debug output here. Contact support with the request_id.