Skip to main content

Error Handling

Use the following guidelines for handling errors in the system.

HTTP Status Code Summary

The API uses conventional HTTP status codes to indicate the success or failure of a request. In general: codes in the 2xx range indicate success, codes in the 4xx range indicate an error caused by the information provided (e.g., a missing required parameter, a failed validation, or a permission issue), and codes in the 5xx range indicate an error on our side.

Status CodeMeaning
200 — OKThe request succeeded.
400 — Bad RequestThe request data was invalid or malformed.
401 — UnauthorizedMissing or invalid authentication credentials.
403 — ForbiddenThe credentials are valid but are not permitted to access this resource.
404 — Not FoundThe requested resource does not exist.
409 — ConflictThe request conflicts with the current state of the resource (e.g., idempotency replay).
422 — Unprocessable EntityThe request was well-formed but could not be processed.
429 — Too Many RequestsThe rate limit has been exceeded. See API Throttling.
500 — Internal Server ErrorAn unexpected error occurred on our side. See Handling 500 Errors for Payments.
502 — Bad GatewayAn upstream dependency returned an invalid response.
503 — Service UnavailableThe service is temporarily unable to handle the request.
note

A payment decline is not an HTTP error — declines return 200. See Payment Errors for how to distinguish a successful API call from a successful payment.

Error Response Format

Every non-2xx response returns a JSON body with a consistent shape:

{
"type": "INVALID_REQUEST_DATA_ERROR",
"code": "400",
"message": "body must have required property 'amount'",
"argument_errors": {
"amount": "must have required property 'amount'"
}
}
FieldTypeDescription
typestringThe error category. One of INVALID_REQUEST_DATA_ERROR, API_ERROR, or SYSTEM_ERROR.
codestringThe HTTP status code as a string (e.g., "400").
messagestringA human-readable description of the error.
argument_errorsobject (optional)Present on validation errors. Maps each invalid field name to a description of why it failed.
stackstring (optional)Stack trace. Only included in non-production environments.

Error Types

typeTypically returned withMeaning
INVALID_REQUEST_DATA_ERROR400, 422The request failed validation (missing, malformed, or invalid field values).
API_ERROR4xxThe request was understood but could not be fulfilled (auth, not found, conflict, rate limit).
SYSTEM_ERROR5xxAn unexpected error occurred on our side.

Idempotency and Request Handling

All payment-related API calls have idempotency built into them. To leverage idempotency, pass the x-idempotency-key header with each API request. There is no harm in making the same API call multiple times with the same key. Note however that any changes to the request after the initial request will likely not be processed if the original request was successful.

API Throttling

Calls to the API are subject to throttling. We use window based timing that resets every 30 seconds. Production is currently limited to 300 requests per 30 seconds, with some endpoints for processing increased to 1500 per 30 seconds. These errors return a status code of 429.

Payment Errors

The system does not consider a payment decline an HTTP error. We do not use HTTP response codes to indicate failure of a successful call to the payment provider (card brands or banks). Inspect the status field returned to understand if the payment was successful or not. Common status values include:

  • succeeded: Payment completed successfully
  • failed: Payment was declined or failed
  • pending: Payment is being processed
  • created: Initial state, ready for payment attempt

Non-200 HTTP errors are used to indicate either a logic error (e.g., validation, configuration) or a system error.

Retries and Preventing Duplicates

There are several things that happen during the processing of a payment, from validation to fraud detection to persisting across our distributed system. While we take extreme measures to ensure that when things go wrong they are handled, there are times when unexpected behavior occurs. If you experience a 500 code error, often times it is best to not attempt the transaction again. Keep reading for best practices.

Best Practices

Create one Payment Intent per Checkout Experience

Creating one Payment Intent (PI) and using that PI to both collect payment data and attach payments to for an entire session is the correct way to use the API. If a payment fails, perhaps due to a decline or system error, continue to use the PI for new payment methods and new attempts. Do not create a new PI if the first payment attempt fails, as doing so will bypass a number of guards the system has in place to prevent issues such as duplicates. A great technique is to use your unique checkout cart id as the reference id on the PI (ex. cart_123456), and either a unique id or postfix per payment attempt (ex. cart_123456-1, cart_123456-2, etc.)

Always Use Idempotency Keys

Leverage idempotency keys to prevent duplicate charges to a customer. Each unique operation should have its own idempotency key that remains consistent across retries.

Handling 500 Errors for Payments

In the rare event that you receive a system error when creating a payment, you should fetch the Payment Intent by ID and inspect the status:

  • If the status is created, you can make additional payment attempts. Our team will be alerted to such errors and will investigate the primary issue, but feel free to reach out to support.
  • If the status is processing, this indicates an error late in the processing attempt. Best practice is to not process this payment again (assume the payment went through), and provide guidance to the end user to not attempt additional payments. This very specific error comes with special handling by our team and in almost all cases we can resolve the issue. You can make API calls to check on the status of the PI to see if it was processed by our team, or you can listen to webhooks for updates on when the payment gets processed.