NowgateDocs

Errors

Every error is RFC 9457 problem details with a stable code you can branch on.

On this page

When a request fails, the response has a 4xx or 5xx status and an application/problem+json body:

{
  "type": "https://docs.nowgate.io/errors/amount_too_precise",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "USDC has 6 decimal places; amounts are never rounded.",
  "instance": "/v1/payments",
  "code": "amount_too_precise",
  "request_id": "req_01jc3xb1n6f0q2w8e4r7t5y9v3",
  "errors": [
    {
      "pointer": "/amount",
      "code": "amount_too_precise",
      "detail": "USDC has 6 decimal places; amounts are never rounded."
    }
  ]
}
FieldMeaning
codeA stable, machine-readable code. Branch on this, never on title or detail.
typeA link to the code's page in these docs.
detailWhat exactly was wrong, in a sentence for humans.
errorsField problems, each with a JSON pointer into your request.
request_idQuote it when you contact support. It is also in the X-Request-Id header.

In the SDKs#

Each SDK turns a problem into an exception of its status's class, carrying the whole problem: status, code, title, detail, type, instance, request_id, retry_after (from Retry-After, in seconds) and errors with their pointers. PHP spells them in camelCase, and the code is errorCode there, since an exception's own code is a number.

try {
  await nowgate.payments.create({ network: "tron", asset: "USDT", amount: "25.0000001" });
} catch (err) {
  if (!(err instanceof InvalidRequestError)) throw err;
  // err.code is "amount_too_precise"; each field's problem has its JSON pointer
  pointer = err.errors[0]?.pointer;
  assert.equal(err.code, "amount_too_precise");
}
WhenTypeScriptPythonPHP (Nowgate\Exception\…)
every SDK errorNowgateErrorNowgateErrorNowgateException
any problem the API answers withApiErrorApiErrorApiException
401AuthenticationErrorAuthenticationErrorAuthenticationException
403PermissionErrorPermissionDeniedErrorPermissionException
404NotFoundErrorNotFoundErrorNotFoundException
409ConflictErrorConflictErrorConflictException
400, 405, 413, 414, 415, 422InvalidRequestErrorInvalidRequestErrorInvalidRequestException
429RateLimitErrorRateLimitErrorRateLimitException
5xxServerErrorServerErrorServerException
no answer: refused, reset or timed outConnectionErrorAPIConnectionErrorConnectionException
a webhook that doesn't verifySignatureVerificationErrorSignatureVerificationErrorSignatureVerificationException

An answer that isn't problem JSON, such as a proxy's error page, becomes the exception of its status with the code http_<status> and the first 500 bytes of the body as its detail. The SDKs already retry what may succeed on a retry: see Idempotency and retries.

Retrying#

Most errors fail the same way if you send the same request again. Retry, with the same Idempotency-Key, only after 409, 429 and 5xx errors, and honor Retry-After when present. When 5xx errors go on, status.nowgate.io says whether Nowgate knows of an incident, and when it's resolved.

Rate limits#

When a limit is reached, the API answers 429 with the rate_limited code, and Retry-After gives the seconds to wait. The limits:

LimitDefault
Reads (GET) per API key25 a second, in bursts of up to 50
Writes (every other method) per API key10 a second, in bursts of up to 20
Requests per client address50 a second, in bursts of up to 100
Requests with a refused API key, per address20 a minute

A burst is how many requests may arrive at once. After it, requests are allowed at the steady rate. Each key counts on its own, so a busy integration doesn't slow down another one. An IPv6 address counts together with the rest of its /64.

An address that keeps presenting keys that are refused with 401 must wait before any of its keys is checked again, even a valid one, so fix the key rather than retrying it. Spread bulk work over time, and after a 429, wait as long as Retry-After says.

All codes#

CodeStatusMeaning
address_already_given409This refund's address was given already.
amount_invalid422The amount is not a valid decimal string.
amount_too_large422The fiat price is above the largest Nowgate converts.
amount_too_precise422The amount has more decimal places than the asset supports.
amount_too_small422The amount is below the asset's minimum payment.
api_key_wrong_environment401The API key belongs to the other environment.
asset_disabled422This asset is temporarily not accepting new payments.
asset_not_supported422Nowgate doesn't support this network and asset pair.
attempt_limit429Too many payments were started on this link, or from this address, for now.
authentication_required401The request has no API key.
currency_not_supported422Nowgate doesn't price payments in this currency.
destination_disabled409The payout destination is disabled.
emails_unavailable422This deployment sends no email, so it can't email the subscriber.
external_id_taken409Another payout of yours already has this external_id.
fee_estimate_unavailable503The network's fee can't be estimated right now, so the payout can't be quoted.
idempotency_key_in_progress409Another request with the same Idempotency-Key is still being processed.
idempotency_key_required400A POST request has no Idempotency-Key header.
idempotency_key_reused422The Idempotency-Key was already used for a different request.
insufficient_balance409Your available balance doesn't cover the payout and its fees.
insufficient_scope403The API key isn't allowed to do this.
internal_error500Something went wrong on Nowgate's side.
invalid_api_key401The API key is not valid.
invalid_id400An ID in the request is not a valid Nowgate ID of the expected kind.
invalid_items422Some items of the batch are invalid, so nothing was created.
invalid_request400The request is malformed.
invoice_not_payable409The invoice can't be paid: it is paid already, or void.
ip_not_allowed403The request came from an IP address the API key doesn't allow.
link_inactive409The payment link is no longer active.
merchant_pending403The merchant account is waiting for activation.
merchant_suspended403The merchant account is suspended.
method_not_allowed405This path doesn't support the HTTP method used.
not_found404There is nothing at this address, or nothing your key can see.
not_refundable409Nothing of this payment can be refunded.
payout_limit_exceeded422The payout would pass one of your payout limits.
payout_not_cancelable409The payout can't be canceled any more.
plan_archived409The plan is archived: it takes no new subscriptions.
pool_exhausted503No deposit address is ready for this network yet.
rate_limited429Too many requests in a short time.
rate_unavailable503The asset can't be priced in the currency right now.
refund_exceeds_refundable422The refund is more than the payment has left to refund.
request_too_large413The request body is too large.
subscription_canceled409The subscription is canceled, so it can't change.
subscription_limit429Your account reached its daily limit of new subscriptions.
too_many_destinations409You have the most enabled payout destinations allowed.
unknown_field400The body contains a field this operation doesn't accept.
unsupported_media_type415The body isn't sent as JSON.
uri_too_long414The URL is too long.
validation_failed422The request is well-formed JSON, but some values are not acceptable.