High Wire Payments

Errors and conventions

Every request convention, HTTP status code, response body shape and retry rule of the merchant API.

This page covers what is common to every endpoint: request conventions, status codes, the exact JSON shapes the API answers with, and what to do on each. The endpoint guides link here instead of repeating the shapes.

Conventions

TopicRule
Base URLhttps://api-payments-staging.highwirepayments.com/v1 on staging, https://api-payments.highwirepayments.com/v1 in production. See Environments.
AuthenticationHTTP Basic on every request. See Authentication.
FormatJSON request and response bodies. Send Content-Type: application/json. A request body is limited to 100 kB.
Success statusEvery POST answers 201 Created, including void, capture and link reissue. GET answers 200 OK.
AmountsIntegers in cents, the smallest unit of the currency: 7200 is $72.00. Never decimals, never strings.
CurrencyUSD only. Card charges carry no currency field, the hosted checkout requires "usd", and a retrieved payment always reports "usd".
IdentifiersPayment, refund, customer and payment-method ids are UUID v7 strings. Routes that take a payment id in the path answer 400 for anything that is not a UUID v7; the refund route answers 404 instead.
Card numbers in responsesMasked as the first six digits, eight x and the last four: 555555xxxxxxxx4444. A card number shorter than 16 digits shows only its last four. The full number is never echoed.
TimestampsISO 8601 in UTC with milliseconds, for example 2026-10-19T14:05:00.000Z.
Unknown fieldsSilently dropped, never rejected. A misspelled optional field is not an error, so check the response rather than relying on a rejection.
TypesValidated strictly. A number sent as a string ("7200") fails validation.
Optional fieldsOmit the ones you do not use. The hosted checkout also accepts null as "absent".
Versioning/v1 is the current stable version. Future versions will be served alongside it, with notice before any deprecation.

HTTP status codes

StatusMeaningBody
200A read succeeded.The payment, see Payment lifecycle.
201A write succeeded. For a charge this includes PENDING (accepted, settling) and REQUIRES_ACTION (the customer must authenticate).The endpoint's response object.
400Invalid input, a declined charge, refund, void or capture, a business rule, or a feature not enabled for the account.Validation error, Charge decline, Refund decline, Void and capture decline, Plain error or Code-carrying error.
401Missing or invalid credentials, or an inactive account.Plain error. See Authentication.
403The account is limited to refunds and voids and called another route.Plain error.
404Unknown id, another account's resource, or a payment that is not in a state the route can act on.Plain error. On charge-saved-method the body is a Charge decline.
409A rule refused the call before anything was charged, a hosted checkout link cannot be reissued, a request with the same Idempotency-Key is still in flight, or no account of yours can take the payment.Reason-coded conflict, Plain error or Code-carrying error.
422The Idempotency-Key was reused with a different request body.Plain error.
429A velocity check refused a card charge.Code-carrying error with code: "VELOCITY_LIMIT".
5xxThe gateway failed.Plain error. Retry with the same Idempotency-Key, unless the operation was accepted; see Server errors.

Response body shapes

Validation error

400, returned when a required field is missing or a value is malformed. fieldErrors maps each offending field path to its problems. Nested paths use dots and array indexes are numbers: deviceData.device.ip, lineItems.0.name.

{
  "statusCode": 400,
  "message": "Some fields are invalid, please check the errors",
  "fieldErrors": {
    "amount": ["amount must not be less than 1", "amount must be an integer number"],
    "cardNumber": ["cardNumber must be 12-19 digits with no spaces or separators"]
  }
}

A validation error is stored under the Idempotency-Key like any other 4xx: fix the body and send it under a new key, otherwise the corrected request answers 422.

Plain error

The default shape for everything that is neither a decline nor a validation failure: 401, 403, 404, business-rule 400s, malformed ids, idempotency 409/422 and 5xx.

{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Payment not found: 01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b"
}

message is human-readable. It is stable enough to log and to show to an operator, but branch on the status code and the route, not on the text. Messages you will meet:

StatusmessageWhere
400Validation failed (uuid v 7 is expected)A payment id in the path that is not a UUID v7.
400Idempotency-Key must be 1..255 characters, Idempotency-Key header may only appear once per requestAny idempotent route.
400Payment is not in a voidable state; only an in-flight or authorized (uncaptured) payment can be voided. Try to refund the payment instead.Void.
400This payment has already been captured.Capture.
400Payment has already been fully refunded, A refund is already in progress for this paymentRefunds.
401Missing Authorization header, Invalid credentialsAuthentication.
403Merchant account can only process refunds and voidsAuthentication.
404Payment not found: <id>, Payment not found or not refundable: <id>Unknown payment, another account's, or (on refund and capture) a payment without a successful capture or authorization.
409A request with this Idempotency-Key is already in progressIdempotency key.
409The checkout has expired; create a new oneHosted checkout.
422Idempotency-Key was previously used with a different request bodyIdempotency key.
500Internal server errorUnexpected failure.

Charge decline

400 from auth-and-capture, auth and charge-saved-method. The charge was well-formed and was refused: by your account limits, by fraud screening, or by the card's issuer. There is no statusCode in this body; the HTTP status carries it. The payment is recorded with status: DECLINED so it appears in your reporting, but a declined direct charge does not return its paymentId.

{
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444",
  "cardExpiration": "1133",
  "cardholderName": "Jordan Example",
  "status": "DECLINED",
  "declinedReason": "Card declined"
}
FieldNotes
cardholderNameOnly on the raw-card routes.
paymentId, customerId, paymentMethodIdOnly on charge-saved-method declines, where the declined attempt counts toward the card's attempt limit.
declinedReasonOne of a fixed set of short reasons, listed under Declines. Log it and decide on status; show the shopper a generic message.

charge-saved-method also uses this shape, without card fields, for an unknown payment method (404 with declinedReason: "Payment method not found.") and for ownership problems (400), see Saved payment methods.

Refund decline

400 from refund when the processor refused the refund or your daily refund limit blocked it. status is the refund's status, not the payment's.

{
  "paymentId": "01928e3e-7b6c-7c8e-9b3a-5b9f8b9b9b9b",
  "amount": 500,
  "cardNumber": "555555xxxxxxxx4444",
  "status": "FAILED",
  "declinedReason": "Amount exceeds daily refund limit"
}

cardNumber is null for wallet payments, which carry no card number. status is CANCELED only in the rare case where the refund was already cancelled on the processor's side before the call finished; no money moved either way.

Void and capture decline

400 from void and capture when the processor refused the operation. Here status: "DECLINED" describes the attempt, not the payment: read the payment to see where it stands.

{
  "paymentId": "01929a10-5d2e-7f3b-8c4d-1e2f3a4b5c6d",
  "amount": 7200,
  "cardNumber": "555555xxxxxxxx4444",
  "status": "DECLINED",
  "declinedReason": "Capture declined"
}

Reason-coded conflict

409 with a machine-readable reason. The rule fired before anything was charged or recorded, and the refusal is not stored under the Idempotency-Key, so the same key can be sent again once the condition clears.

{
  "statusCode": 409,
  "message": "This saved payment method has reached its reuse limit.",
  "reason": "REUSE_LIMIT"
}
reasonRouteMeaning
ORIGINAL_NOT_SETTLED, METHOD_NOT_READY, ORIGINAL_NOT_ELIGIBLE, WINDOW_CLOSED, REUSE_LIMIT, ATTEMPT_LIMIT, AMOUNT_LIMITcharge-saved-methodThe reuse is outside its bounds. See Reuse refusals.
PAYMENT_DISPUTEDrefundThe payment has an open or lost dispute. See Refunds and disputes.

A 409 without reason is a plain error: an Idempotency-Key still in flight, or a hosted checkout link that cannot be reissued.

Code-carrying errors

These errors carry a stable code.

HW_UNSUPPORTED (400): the hosted checkout is not enabled for your account. Nothing is written.

{
  "statusCode": 400,
  "error": "Bad Request",
  "code": "HW_UNSUPPORTED",
  "message": "Hosted checkout is not enabled for this account"
}

HW_NO_ACTIVE_PROVIDER_ACCOUNT (409): no account of yours takes new payments. On a payment that already exists, HW_PROVIDER_ACCOUNT_REFUND_ONLY, HW_PROVIDER_ACCOUNT_RESTRICTED, HW_PROVIDER_ACCOUNT_DISABLED or HW_PROVIDER_ACCOUNT_UNRESOLVED (409): the account it was created on no longer takes payments. Neither is stored under an Idempotency-Key, so the same key can be retried once High Wire has resolved the account.

{
  "statusCode": 409,
  "error": "Conflict",
  "code": "HW_NO_ACTIVE_PROVIDER_ACCOUNT",
  "message": "No active provider account can take this payment."
}

VELOCITY_LIMIT (429): a velocity check refused a card charge. Nothing is charged and no payment is recorded. There is no Retry-After header. See Account limits.

{
  "statusCode": 429,
  "message": "Velocity limit exceeded",
  "code": "VELOCITY_LIMIT"
}

Server errors

A 5xx is a plain error. The idempotency record for the call is discarded, so retry with the same Idempotency-Key; a retry under the same key cannot charge twice. A retry can answer a plain 400 such as This payment has already been captured. when the first attempt did go through; read the payment to confirm.

A 500 whose message says the operation was accepted (Capture accepted; finalization pending. Poll the payment., and the same for a void or a refund) means exactly that: do not retry; poll the payment until it reflects the operation.

Telling the shapes apart

Check the body in this order; the first match wins.

  1. fieldErrors present: a validation error.
  2. declinedReason present: a decline. status says which kind: DECLINED for a charge, void or capture; FAILED or CANCELED for a refund.
  3. reason present: a reason-coded conflict.
  4. code present: a code-carrying error.
  5. Otherwise a plain error; use statusCode.

Retrying

SituationWhat to do
Network timeout or connection error on a writeRetry with the same Idempotency-Key and body.
5xxRetry with the same key; then read the payment. If the message says the operation was accepted, do not retry: poll the payment. See Server errors.
409 without reasonThe first call is still running. Retry the same key after a moment to get its outcome.
409 with reasonAct on the reason. Some clear on their own (ORIGINAL_NOT_SETTLED, METHOD_NOT_READY, PAYMENT_DISPUTED once won); the others need a new card or a smaller amount.
422A client bug: the key was reused for a different operation. Use a new key for the new operation.
429Stored under the key. Do not retry the same request; a new attempt needs a new key, later.
400 declineStored under the key. A new attempt is a new operation with a new key.
400 validationStored under the key. Fix the body and use a new key.
401, 403Fix the credentials or contact High Wire; nothing was stored.
201 with status: PENDINGDo not retry. Poll the payment; see Polling.

On this page