voepy

Error reference

Every error from the voepy API returns the same shape:

{
  "error": {
    "code": "balance_insufficient",
    "message": "Tenant balance is exhausted; top up to place new calls."
  }
}

The HTTP status mirrors the category. Use code for branching logic — message is human-friendly but not stable across releases.

Categories

StatusCategoryWhat to do
400Client malformed the requestRead message, fix the body, retry.
401Missing / bad credentialsRe-check your API key.
402Payment requiredTop up the balance.
403Authorized but not allowedScope, quota, or compliance issue — read the code.
404Resource doesn't existThe ID is wrong, deleted, or belongs to another tenant.
409State conflictThe resource isn't in the state the action requires.
422Semantically invalidBody parses but is logically wrong.
429Rate-limitedBack off; respect Retry-After.
500Server bugRetry; if it persists, file a ticket.
502Upstream carrier failedOften retriable. Check message for hint.
503Service degradedBack off, retry.

Auth & access

CodeHTTPCause
authentication_required401No Authorization header, or the header isn't Bearer <key>.
invalid_credentials401Key not found, revoked, or expired.
tenant_suspended403Your tenant is paused (unpaid invoice, KYC issue, etc.).
forbidden403Key is valid but lacks the required scope (e.g. you used a calls:read key for POST /v1/calls).

Billing

CodeHTTPCause
balance_insufficient402Your balance + credit limit ≤ 0 for the requested action. Top up.
daily_spend_cap_exceeded403You hit your tenant's daily spend cap. Resets at UTC midnight.
concurrent_call_cap_exceeded403You hit your tenant's concurrent-call cap. Wait for in-flight calls to end.
auto_recharge_failed402The last auto-recharge attempt failed (usually a card decline). Re-attach a working card.
payment_method_required400Action needs a saved card and you have none.
subscription_invalid422Plan slug doesn't exist or isn't public.
topup_amount_too_low400amount_cents below the policy minimum.

Calls

CodeHTTPCause
from_number_not_owned403from isn't a tenant-owned active DID and caller_id_forwarding isn't enabled.
country_blocked403Destination country is on your quota's blocked list.
country_not_allowed403Destination country isn't on your allow-list (when an allow-list is configured).
prefix_blocked403Destination prefix matches your blocked-prefix list.
call_already_ended409You tried an action on a terminal call.
call_not_in_required_state409The action's "valid from" status doesn't match (e.g. bridge on a ringing call).
recording_not_ready409Recording exists but status != available — try again later.
idempotency_conflict409Same Idempotency-Key used for a different request body. Pick a new key.
unprocessable422Body fields are valid types but logically conflict.
upstream_error502Carrier-side failure. Retry with backoff; check the message.

Phone numbers

CodeHTTPCause
no_pricing_tier_configured422We don't have pricing for that country / number-type combination. Email support.
insufficient_balance402Order NRC can't be covered by your balance. Top up first.
order_invalid400Numbers aren't reservable (already sold, withdrawn, etc.).
number_not_yet_active409You tried to PATCH a pending number. Wait for activation.
messaging_profile_not_provisioned409messaging_enabled: true but your tenant has no messaging profile. Contact support.
number_not_found404DID id is wrong or doesn't belong to your tenant.
release_not_permitted409Number is the last DID on an active porting order or anchor for emergency-address compliance.

Webhooks

CodeHTTPCause
invalid_target_url400URL isn't HTTPS or isn't a valid URL.
event_types_required400You sent event_types: []. Use ["*"] to subscribe to everything.
subscription_not_found404Subscription id is wrong or belongs to another tenant.
signing_secret_unavailable404You're trying to read a signing_secret post-creation — it's only returned once. Delete and recreate.

Porting

CodeHTTPCause
porting_not_supported422The losing-carrier doesn't support hosted porting for these numbers.
csr_mismatch422Customer Service Record fields don't match what you submitted.
documents_required422LOA / bill upload missing.
porting_in_progress409Another porting order already exists for one of the numbers.

Conferences

CodeHTTPCause
conference_not_found404Conference id is wrong.
conference_full409Participant cap reached.
participant_not_found404Participant id isn't in the conference.
conference_action_invalid409Action isn't valid in the conference's current state (e.g. mute on a left participant).

Recordings / streams / transcription

CodeHTTPCause
recording_not_ready409status != available.
recording_already_stopped409stop_recording on a call with no active recording.
streaming_already_active409Tried to start a second stream with the same id on one leg.
streaming_not_found404stream_id doesn't match an active stream.
transcription_already_active409Already running on this leg.
transcription_not_active409transcription_stop with nothing running.

Generic

CodeHTTPCause
invalid_request400Body malformed, unknown field, or missing required. Read message.
not_found404Resource missing.
conflict409Generic state conflict not covered by a more specific code.
rate_limited429Slow down. Inspect Retry-After header.
service_unavailable503Service is degraded. Retry with backoff.
internal_error500Our bug. Retry and file a ticket if it persists.
upstream_error502Underlying carrier failed. Retry; check message.

Retry policy

StatusRetriable?Strategy
400, 401, 403, 404, 409, 422NoFix the request.
402After top-upTop up balance, then retry.
429YesHonor Retry-After; exponential backoff.
500, 502, 503YesExponential backoff with full jitter, max ~10 minutes.

Always send Idempotency-Key on retried mutations so retries don't double-charge or double-dial.

Where to look when something breaks

  1. Reproduce against the OpenAPI playground at https://api.voepy.com/api/docs/ — confirms request shape.
  2. Inspect webhook deliveries at GET /v1/webhook-deliveries if the issue is "I'm not receiving an event."
  3. Check audit log at GET /v1/audit for state-change history on your tenant.
  4. Look at the live balance + quota at GET /v1/account and GET /v1/quota for any 402 / 403 from spend-cap or balance.