> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bafanglaicai88.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Describe only the public 123hub merchant API documented on this site. The primary endpoint is POST /public/api/multihub/v1.
> Preserve API method names, field names, header names, and error codes exactly as documented. Do not invent endpoints or parameters.
> Treat every amount as an integer in minor units unless a page explicitly states otherwise.
> Never expose, request, or fabricate a merchant secret key. The SDK pages contain reference implementations, not official SDK packages.

# Support errors and recovery

> Handle numeric ticket, refund, chargeback, attachment, idempotency, quota, and dependency errors

# Support errors and recovery

Ticket, refund, chargeback, and attachment methods return the standard signed API error envelope. Use the numeric `error.code` for program logic, `request_id` for support/reconciliation, and HTTP status only for the broad recovery class.

<Info>
  **Prerequisites:** capture the raw response bytes and `X-Data-Hash` header so
  you can verify `SHA512(rawResponseBody + secretKey)` before acting on an
  error.
</Info>

## Envelope

```json theme={null}
{
  "success": false,
  "error": {
    "code": 11005,
    "message": "An active refund request already exists",
    "details": null,
    "context": null
  },
  "request_id": "8ac3402a-a549-451f-9042-5c39026abc4e",
  "processing_time": 17
}
```

Error messages can become clearer over time. Numeric codes and public structured details are the stable contract; never parse a message to decide a money retry.

## HTTP recovery classes

|  HTTP | Class                                  | Default action                                                        |
| :---: | -------------------------------------- | --------------------------------------------------------------------- |
| `400` | Validation/authentication              | Correct the request/signature; do not retry unchanged                 |
| `403` | Scope or credential policy             | Use an authorized credential/policy                                   |
| `404` | Disabled method or tenant-safe absence | Verify rollout/identifier; do not probe ownership                     |
| `409` | State or idempotency conflict          | Query current state; preserve the original operation meaning          |
| `429` | Quota                                  | Honor retry headers and back off                                      |
| `503` | Dependency unavailable                 | Retry reads; retry mutations only with the same key and exact payload |

## Numeric codes

|    Code | Meaning                              | Recovery                                                                      |
| ------: | ------------------------------------ | ----------------------------------------------------------------------------- |
|  `1013` | Idempotency key payload mismatch     | Recover the original operation or use a new key for a genuinely new operation |
| `10001` | Ticket not found                     | Verify one public identifier and credential                                   |
| `10002` | Ticket already exists                | Use the existing public ticket returned by conflict details                   |
| `10003` | Invalid ticket state                 | Refresh the ticket                                                            |
| `10004` | Ticket/comment edit window expired   | Add a new comment                                                             |
| `10005` | Required verified attachment missing | Finish upload/finalize and retry ticket creation                              |
| `11001` | Refund not found                     | Verify one public identifier and credential                                   |
| `11002` | Payment not refund-eligible          | Refresh payment/refund state                                                  |
| `11003` | Invalid refund amount                | Send a positive digit-string no greater than `9223372036854775807`            |
| `11004` | Amount exceeds current remainder     | Query current history and submit a valid new operation                        |
| `11005` | Active refund request conflict       | Query the existing `pending`/`approved` request                               |
| `12001` | Chargeback not found                 | Verify the UUID and credential                                                |
| `13001` | Attachment not found                 | Refresh the visible parent attachment list                                    |
| `13002` | Upload expired                       | Prepare/upload again with new operation IDs                                   |
| `13003` | Attachment not ready                 | Wait for validation or replace a consumed/wrong-purpose ID                    |
| `13004` | Attachment rejected                  | Replace/correct the bytes; do not retry them                                  |

Common supporting codes include `1002` (method unavailable), `1005` (invalid method-specific request), `2002` (support dependency unavailable), `3000` (signature/replay failure), `3008` (scope/policy denied), and quota HTTP `429` errors.

## Handle errors safely

<CodeGroup>
  ```bash cURL / jq theme={null}
  BODY='{"method":"refund.get","params":{"identifiers":{"c_id":"refund-order-8401-1"}}}'
  SECRET='your_secret_key'
  HASH=$(printf '%s%s' "$BODY" "$SECRET" | openssl dgst -sha512 -r | awk '{print $1}')
  HTTP=$(curl -sS -o response.json -w '%{http_code}' \
    -H 'Content-Type: application/json' -H 'X-Data-Application-Id: 1' \
    -H "X-Data-Hash: $HASH" -H "X-Data-Timestamp: $(date +%s)" \
    -H "X-Data-Nonce: $(uuidgen)" --data-binary "$BODY" \
    'https://api.bafanglaicai88.com/public/api/multihub/v1')
  CODE=$(jq -r '.error.code // empty' response.json)
  REQUEST_ID=$(jq -r '.request_id // empty' response.json)
  case "$HTTP:$CODE" in
    503:2002) echo "retry exact mutation key after backoff request_id=$REQUEST_ID" ;;
    409:1013) echo "recover original operation; do not change payload" ;;
    404:1*) echo "resource unavailable for this credential" ;;
    *) echo "request failed http=$HTTP code=$CODE request_id=$REQUEST_ID" ;;
  esac
  ```

  ```python Python theme={null}
  def classify(response):
      body = response.json()
      code = body.get("error", {}).get("code")
      if response.status_code == 503 and code == 2002:
          return "retry_exact_key"
      if code == 1013:
          return "recover_original_operation"
      if response.status_code == 429:
          return "backoff_to_retry_header"
      if response.status_code in (400, 403, 404, 409):
          return "query_or_correct_no_blind_retry"
      return "escalate"
  ```

  ```javascript Node.js theme={null}
  function classify(response, body) {
    const code = body?.error?.code;
    if (response.status === 503 && code === 2002) return "retry_exact_key";
    if (code === 1013) return "recover_original_operation";
    if (response.status === 429) return "backoff_to_retry_header";
    if ([400, 403, 404, 409].includes(response.status)) return "query_or_correct";
    return "escalate";
  }

  // Persist body.request_id with your c_id/operation_id before retry decisions.
  ```
</CodeGroup>

## Retry matrix

| Operation            | Timeout/503 retry?                       | Required identity                         |
| -------------------- | ---------------------------------------- | ----------------------------------------- |
| Read/list/summary    | Yes, exponential backoff and fresh nonce | Same query/cursor                         |
| Ticket/refund create | Yes, exact payload                       | Same `c_id`                               |
| Other mutation       | Yes, exact payload                       | Same `operation_id`                       |
| Upload object POST   | Only while presigned session is valid    | Same returned form fields and exact bytes |
| Rejected attachment  | No                                       | Prepare a new valid file/session          |

<Warning>
  A transport timeout is an unknown outcome, not proof that a mutation failed.
  Never issue a refund with a new `c_id` until the original `c_id` has been
  queried or retried exactly.
</Warning>

## Best practices

* Alert on sustained `2002`, replay failures, and quota errors separately from business conflicts.
* Log `request_id`, method, public resource IDs, and your operation key; redact bodies, comments, hashes, presigned forms, and secrets.
* Treat resource-specific 404 responses as tenant-safe and never use them as an ownership oracle.
* See the complete platform-wide [Error Codes](/errors/error-codes) reference.
