Errors

HTTP status codes and error codes returned by the Teyra Pay API, and how to handle each one.

Teyra Pay uses standard HTTP status codes. 2xx means success, 4xx means the request has a problem you can fix, and 5xx means something went wrong on our side or with the payment network.

Every error has the same JSON shape:

{
  "error": {
    "code": "validation_error",
    "message": "Some fields are missing or invalid.",
    "details": {
      "fields": {
        "amount": "Expected integer, received float",
        "currency": "Invalid enum value. Expected 'KWD' | 'BHD' | 'OMR' | 'JOD' | 'SAR' | 'AED' | 'QAR' | 'EGP', received 'USD'"
      }
    }
  }
}
FieldDescription
codeStable, machine-readable code. Branch on this.
messageHuman-readable explanation, for your logs. It may change; don't parse it.
detailsOptional extra data. For validation_error, details.fields maps each invalid field to a message. Nested fields use a dotted path, such as customer.email or metadata.order_id.

Error codes

HTTPcodeMeaningWhat to do
401unauthorizedMissing, malformed, revoked or unknown API key.Check the Authorization: Bearer sk_… header and the key's mode.
403forbiddenThe key is valid but not allowed to do this.Check you're using the right key and account.
403merchant_not_activeLive request before the account is approved and activated, or the account is suspended.Use a test key, or complete onboarding.
404payment_not_foundThe payment doesn't exist, or it belongs to the other mode (test/live).Check the ID and the key's mode.
404refund_not_foundThe refund doesn't exist, or it belongs to the other mode.Check the ID and the key's mode.
404payment_link_not_foundThe payment link doesn't exist, or it belongs to the other mode.Check the ID and the key's mode.
404not_foundAny other object or path that doesn't exist, such as a webhook endpoint.Check the ID, the path and the key's mode.
409conflictA request with the same Idempotency-Key is still being processed.Wait a moment and retry with the same key.
409idempotency_conflictThis Idempotency-Key was already used with a different request.Use a new key for a new request. See Idempotency.
422validation_errorOne or more parameters are missing or invalid, including a live-mode URL that isn't https://, a payment_method your account doesn't support ("unsupported"), or, on some accounts, a three-decimal amount that doesn't end in 0.Read details.fields, fix the request. See Payment methods and your account.
422refund_exceeds_amountThe refund is more than what remains refundable. details.remaining holds what is left.Refund at most amount − amount_refunded.
422payment_not_refundableThe payment isn't succeeded, so it can't be refunded.Only refund succeeded payments.
429rate_limitedToo many requests with this key (1,200 per minute). details.retry_after_seconds and the Retry-After header say when to retry.Wait, then retry with the same Idempotency-Key. See Rate limits.
500internal_errorUnexpected error on our side.Retry with the same Idempotency-Key. Contact support if it persists.
502gateway_errorThe payment network returned an error or didn't respond.Retry with the same Idempotency-Key after a short delay.

New error codes may be added. Treat an unknown code according to its HTTP status.

Handling errors

const res = await fetch('https://api.teyra.co/v1/payments', options);
const body = await res.json();

if (!res.ok) {
  const { code, message, details } = body.error;
  if (code === 'validation_error') console.warn('Invalid fields', details?.fields);
  else if (res.status === 429 || res.status >= 500) await retryLater(); // keep the same Idempotency-Key
  throw new Error(`Teyra Pay ${res.status} ${code}: ${message}`);
}

Payment failures are not API errors

A declined card does not produce an HTTP error. Creating the payment succeeds (201), and the payment later moves to status: "failed" with failure_code and failure_message set. You learn about it from GET /v1/payments/:id or the payment.failed webhook.