Errors

When a request fails, the Merchant API returns a non-2xx HTTP status and a consistent JSON error envelope. The code is a stable, machine-readable string you can branch on; the message is a human-readable explanation meant for logs and developers, not end users.

The error envelope

Every error has the same shape:

Error envelope

{
  "error": {
    "code": "invalid_amount",
    "message": "amount must be a decimal string"
  }
}

HTTP status codes

  • Name
    400
    Type
    Bad Request
    Description

    The request is malformed — invalid JSON body, a bad amount, or a missing required field.

  • Name
    401
    Type
    Unauthorized
    Description

    Authentication failed. See Authentication for the specific codes (missing_auth, stale_request, bad_signature, …).

  • Name
    403
    Type
    Forbidden
    Description

    Authenticated, but not allowed to perform this action.

  • Name
    404
    Type
    Not Found
    Description

    The resource does not exist, or is not owned by your account.

  • Name
    409
    Type
    Conflict
    Description

    The request conflicts with the resource's current state — e.g. refunding an order that is not refundable.

  • Name
    429
    Type
    Too Many Requests
    Description

    You are being rate limited. Back off and retry later.

  • Name
    500
    Type
    Internal Server Error
    Description

    Something went wrong on our side. Safe to retry idempotent requests.

  • Name
    503
    Type
    Service Unavailable
    Description

    A dependency is temporarily unavailable — e.g. an on-chain channel whose deposit-address pool is momentarily exhausted. Retry shortly.

Common error codes

These are the codes you are most likely to encounter across the Merchant API. Endpoint pages list the codes specific to each operation.

  • Name
    invalid_body
    Type
    400
    Description

    The request body is not valid JSON.

  • Name
    invalid_amount
    Type
    400
    Description

    An amount field is not a valid decimal string.

  • Name
    order_not_found
    Type
    404
    Description

    No order with that id belongs to your account.

  • Name
    not_refundable
    Type
    409
    Description

    Only a paid (or partially refunded) order can be refunded.

  • Name
    refund_unsupported
    Type
    409
    Description

    On-chain (non-custodial) orders settle directly to your address and cannot be refunded through the platform.

  • Name
    no_deposit_address
    Type
    503
    Description

    No deposit address is available for an on-chain charge right now — the channel's receiving-address pool is empty or exhausted. Retry shortly.

  • Name
    unknown_channel
    Type
    400
    Description

    The channel code is not enabled for your account.

  • Name
    channel_or_network_required
    Type
    400
    Description

    A charge needs either an explicit channel or a network (with currency) for routing.

  • Name
    no_route
    Type
    400
    Description

    No enabled channel collects the requested (network, currency) pair.

  • Name
    pay_currency_mismatch
    Type
    400
    Description

    On a routed charge, metadata.pay_currency contradicts the requested network — drop one of the two.

  • Name
    settlement_address_required
    Type
    400
    Description

    A live direct-settlement charge needs your settlement address for the order's chain kind configured first. See Account.

  • Name
    payment_mode_disabled
    Type
    403
    Description

    The channel's settlement mode is disabled for your account. Contact the platform operator.

Handling errors

Check the HTTP status first, then read error.code to decide what to do. Retry 5xx and 429 with backoff; treat 4xx (other than 429) as a request you must fix before retrying.

Node.js

const res = await pgFetch('POST', '/api/v1/orders/ord_2Zx9/refund', {
  amount: '10.00',
})

if (!res.ok) {
  const { error } = await res.json()
  switch (error.code) {
    case 'not_refundable':
      // order is not in a refundable state
      break
    case 'refund_unsupported':
      // on-chain order — refund off-platform
      break
    default:
      throw new Error(`${res.status} ${error.code}: ${error.message}`)
  }
}