Errors
AccruPay uses the standard GraphQL error envelope for all API-level failures. Transaction declines are a separate concern and are not represented as GraphQL errors.
GraphQL error envelope
When an operation fails at the API level, the response includes an errors array and data is null (or the failing field is null).
{
"errors": [
{
"message": "Authentication error.",
"extensions": {
"code": "UNAUTHORIZED"
}
}
],
"data": null
}
The extensions.code field is the machine-readable error code. Always branch on extensions.code, not on message — message text may change without notice.
extensions.code values
extensions.code is one of the fixed framework codes below, or a namespaced AppError key (@domain/REASON) for business-rule failures.
| Code | Triggers | Recommended action |
|---|---|---|
UNAUTHORIZED | Missing or invalid accrupay-api-secret header | Verify the API secret is set correctly and is not expired |
FORBIDDEN | Valid credentials but insufficient permissions for the requested resource | Check that the API key belongs to the correct merchant account and has access to the requested operation |
GRAPHQL_VALIDATION_FAILED | A required argument is missing, or a value is out of range / fails format validation | Check the message field for field-level detail; fix the input and retry |
GRAPHQL_PARSE_FAILED | The GraphQL document is malformed | Fix the query syntax |
@domain/REASON (AppError key) | A business rule was violated (e.g. resource not found, duplicate merchantInternalTransactionCode, provider not resolvable) | The key names the reason; read message and correct the request |
INTERNAL_SERVER_ERROR | Unexpected server-side failure | Retry with exponential backoff; if the issue persists, contact AccruPay support with the full error response |
Transaction declines are not API errors
A payment that is declined or fails at the provider level does not produce a GraphQL errors entry. The mutation resolves successfully and returns a transaction object. You must check transaction.status in the response data.
{
"data": {
"merchantApiServerPaymentMethodTransactionCreate": {
"id": "txn-uuid",
"status": "DECLINED",
"providerError": "Insufficient funds"
}
}
}
Transaction terminal statuses
| Status | Meaning | What to show the customer |
|---|---|---|
SUCCEEDED | Payment completed successfully | Confirmation — order fulfilled |
DECLINED | Card network or issuer declined the charge | Ask the customer to try a different payment method |
FAILED | Provider-side processing failure (e.g. timeout, invalid card data) | Generic error — try again or contact support |
CANCELED | Authorization was released without capture | No charge was made |
SUCCEEDED (action: REFUND) | Refund processed | Confirm refund amount in refundedAmount field |
Check the providerError field for the raw decline reason returned by the provider. This is useful for logging and support, but should not be shown verbatim to customers.
providerError field
Most transaction return types include a providerError field:
{
id
status
providerError
}
providerError is populated when status is DECLINED or FAILED. It contains the provider's raw reason string (e.g. "Do not honor", "Insufficient funds", "Invalid card number").
Use providerError for:
- Internal logging and alerting
- Support ticket context
- Reconciliation when a statement entry is unclear
Do not expose providerError directly to end customers. Map it to a user-friendly message in your application layer.
Network errors vs GraphQL errors
HTTP-level failures (5xx, network timeouts, DNS failures) do not produce a GraphQL errors array — the response body may be empty or non-JSON. Handle these separately:
| Scenario | Behavior | Recommendation |
|---|---|---|
HTTP 200 with errors[] | GraphQL error — parseable JSON | Branch on extensions.code |
HTTP 200 with data | Successful response | Check transaction.status for declines |
| HTTP 401 | Authentication rejected at the HTTP layer before GraphQL | Same as UNAUTHORIZED — check the API secret |
| HTTP 5xx | Server error before GraphQL processing | Retry with backoff |
| Network timeout / no response | Transport-level failure | Retry; use merchantInternalTransactionCode as idempotency key to avoid duplicate charges |
Idempotency and retries
merchantInternalTransactionCode is your idempotency key for charge mutations. If a request times out and you are unsure whether it was processed, do not retry with a new code — first query the transaction by merchantInternalTransactionCode to check whether it was created.
Related
- GraphQL Overview
- Server Payments — where transaction status matters most
- Error Reference — complete lookup table including SDK error classes