> ## 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 API fundamentals

> Authenticate, make idempotent support requests, and page through ticket, refund, and chargeback data

# Support API fundamentals

Ticket, refund, chargeback, and attachment methods use the same signed API endpoint as payments:

```http theme={null}
POST /public/api/multihub/v1
```

<Info>
  **Prerequisites:** create a server-side API credential with only the scopes
  your integration needs. New support scopes are not granted to existing
  credentials automatically.
</Info>

## Sign every request

Send the exact raw JSON body with the required application ID and hash. The
timestamp and nonce are an optional pair:

| Header                  | Value                                              |
| ----------------------- | -------------------------------------------------- |
| `X-Data-Application-Id` | Application identifier from Developer Center       |
| `X-Data-Hash`           | Lowercase `SHA512(rawBody + secretKey)` hex digest |
| `X-Data-Timestamp`      | Optional current Unix timestamp in seconds         |
| `X-Data-Nonce`          | Optional new unpredictable value for this request  |

Support methods never require timestamp/nonce. If you send replay proof, provide both headers with a fresh timestamp and nonce; partial, stale, and reused proofs are rejected when detected. Hash and send the same bytes: changing whitespace or key order after signing invalidates the request.

Responses use the existing envelope and are signed in the `X-Data-Hash` response header:

```json theme={null}
{
  "success": true,
  "result": {
    "items": []
  },
  "request_id": "0b71e25b-590b-4ed5-b51d-e2ba688cb03a",
  "processing_time": 18
}
```

Verify the response with `SHA512(rawResponseBody + secretKey)` before using its contents. Error responses use the same `request_id` and timing fields and are signed when credential verification succeeded; early authentication failures may not have a response signature.

## Signed request example

The following example lists tickets. The same helper works for every method in this guide.

<CodeGroup>
  ```bash cURL theme={null}
  BODY='{"method":"ticket.list","params":{"limit":25}}'
  SECRET='your_secret_key'
  HASH=$(printf '%s%s' "$BODY" "$SECRET" | openssl dgst -sha512 -r | awk '{print $1}')

  curl -X POST 'https://api.bafanglaicai88.com/public/api/multihub/v1' \
    -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 | tr '[:upper:]' '[:lower:]')" \
    --data-binary "$BODY"
  ```

  ```python Python theme={null}
  import hashlib, json, time, uuid, requests

  url = "https://api.bafanglaicai88.com/public/api/multihub/v1"
  secret = "your_secret_key"
  body = json.dumps({"method": "ticket.list", "params": {"limit": 25}}, separators=(",", ":"))
  response = requests.post(url, data=body, headers={
      "Content-Type": "application/json",
      "X-Data-Application-Id": "1",
      "X-Data-Hash": hashlib.sha512((body + secret).encode()).hexdigest(),
      "X-Data-Timestamp": str(int(time.time())),
      "X-Data-Nonce": str(uuid.uuid4()),
  }, timeout=30)
  response.raise_for_status()
  print(response.json())
  ```

  ```javascript Node.js theme={null}
  import crypto from "node:crypto";

  const url = "https://api.bafanglaicai88.com/public/api/multihub/v1";
  const secret = "your_secret_key";
  const body = JSON.stringify({ method: "ticket.list", params: { limit: 25 } });
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Data-Application-Id": "1",
      "X-Data-Hash": crypto
        .createHash("sha512")
        .update(body + secret)
        .digest("hex"),
      "X-Data-Timestamp": String(Math.floor(Date.now() / 1000)),
      "X-Data-Nonce": crypto.randomUUID(),
    },
    body,
  });
  console.log(await response.json());
  ```
</CodeGroup>

## Scopes

| Scope              | Grants                                                               |
| ------------------ | -------------------------------------------------------------------- |
| `tickets.create`   | Create tickets and prepare ticket attachments                        |
| `tickets.read`     | Read tickets, comments, and their attachments                        |
| `tickets.update`   | Edit a ticket description and reopen a ticket                        |
| `tickets.comment`  | Create or edit merchant comments and prepare comment attachments     |
| `refunds.create`   | Request a monetary refund                                            |
| `refunds.read`     | Read refund requests and refund comments                             |
| `refunds.comment`  | Add merchant refund comments                                         |
| `chargebacks.read` | Read chargeback history, evidence, and download evidence attachments |

Scope checks are always enforced for these methods. A credential can additionally restrict currencies or payment routes; payment-linked methods resolve the payment first and apply that policy to its actual currency and service.

## Identifiers and idempotency

Resources expose only public identifiers:

| Identifier | Meaning                                                               |
| ---------- | --------------------------------------------------------------------- |
| `c_id`     | Your reference, unique within your merchant account and resource type |
| `h_id`     | Server-generated UUID                                                 |

`ticket.create` and `refund.request` require `params.identifiers.c_id`. A successful response contains both `c_id` and `h_id`. Read methods accept exactly one of them, except chargebacks, which are addressed only by `h_id`.

State-changing methods other than ticket/refund creation require a top-level `operation_id`:

```json theme={null}
{
  "method": "ticket.reopen",
  "operation_id": "reopen-ticket-2407-v1",
  "params": {
    "identifiers": { "h_id": "47f04bbf-2b61-47ab-a7be-1730248145c9" }
  }
}
```

Keep the same `c_id` or `operation_id` when retrying the same logical mutation. The server stores the completed business result without a time-to-live:

* same key and same payload returns the original result;
* same key and different payload returns HTTP `409` with code `1013`;
* transport timeouts do not authorize changing the key;
* key scope is merchant + method, so do not reuse an operation key for a different method.

For `refund.request`, `amount` is an optional positive digit-string in minor units, up to `9223372036854775807`. Values above JavaScript's safe-integer limit remain strings in requests and responses; never coerce them to `number`.

<Warning>
  Never generate a new refund `c_id` merely because the first request timed out.
  Query with the original `c_id` first. A retry cannot create a second refund
  when the original request committed successfully.
</Warning>

## Cursor pagination

All list methods use opaque cursor pagination:

```json theme={null}
{
  "method": "refund.list",
  "params": {
    "limit": 25,
    "cursor": "opaque-value-from-the-previous-response"
  }
}
```

`limit` defaults to `25` and must be between `1` and `100`. Pass `next_cursor` unchanged to obtain the next page; `null` means the list is complete. Lists intentionally omit an expensive `total`. Use the corresponding `*.summary` method for counts grouped by status.

## Default quotas

Quotas are counted per merchant, application, and method.

| Method class             |    Default |
| ------------------------ | ---------: |
| Reads                    | 120/minute |
| Summaries                |  30/minute |
| Ticket/comment mutations |  30/minute |
| Attachment methods       |  20/minute |
| `refund.request`         |  10/minute |

HTTP `429` responses include the standard error envelope and retry headers. Do not retry mutations automatically unless you keep the same idempotency key and exact payload.

## Common errors

|   Code | HTTP | Meaning                                                             | Recovery                                                                     |
| -----: | :--: | ------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `1005` |  400 | Request does not match the method-specific DTO                      | Remove unknown fields and fix the reported path                              |
| `1013` |  409 | Idempotency key was reused with another payload                     | Recover the original result or choose a key for a genuinely new operation    |
| `3000` |  400 | Signature or replay proof is invalid                                | Sign the exact body and use a fresh timestamp/nonce                          |
| `3008` |  403 | Scope or credential policy denied the request                       | Use a credential with the required scope/policy                              |
| `1002` |  404 | Method is disabled or the application is not in the current rollout | Confirm enablement during onboarding                                         |
| `2002` |  503 | Support dependency is unavailable                                   | Retry reads with backoff; retry mutations only with the same key and payload |

For resource-specific codes, see [Error Codes](/errors/error-codes).

## Best practices

* Store `request_id`, `c_id`, `h_id`, method, and operation key with your local operation.
* Treat all not-found responses as final for that credential; cross-merchant resources are deliberately indistinguishable from missing resources.
* Never send `merchant_id`, application ID, actor, source, or `service_id` in a support request body.
* Use canonical dot-notation method names. Legacy payment aliases do not apply to support methods.
