Sandbox environment & fixtures
Audience: integrations that ship against
https://api.hiobuy.com(Workers gateway) — same base URL as production.
Plans: Free plans include self-service documentation and Sandbox access. Dedicated integration and fulfillment support are available on paid plans.
Auth: deterministic fixtures apply only to API keys prefixed with hio_test_* (key_type: test). hio_live_* production keys never read this page’s rules — they always hit upstream marketplaces / warehouses normally.
Related docs: Authentication · Response format · Errors · Rate limits · Products · Orders · Fulfillment · Webhooks
Important: Warehouse Fulfillment Testing
The Sandbox API (hio_test_*) is intended for mock and fixture-based integration testing.
If HIOBuy has provided your application with a WH-DEV Warehouse Developer Code, do not use the Sandbox API key to test your assigned warehouse configuration.
Use:
Production API Key (hio_live_*) + WH-DEV
WH-DEV is still a test warehouse configuration. Using a Production API key with WH-DEV does not mean that your application is performing live warehouse fulfillment.
| Configuration | Purpose |
|---|---|
hio_test_* | Mock / fixture API testing |
hio_live_* + WH-DEV | Assigned warehouse integration testing with test warehouse data |
hio_live_* + Production warehouse authorization | Live warehouse fulfillment |
Sandbox Fulfillment endpoints return predefined or simulated data. For example, sandbox warehouse locations, balances, packages, shipping routes, and package statuses may differ from the WH-DEV configuration assigned to your application.
If you are validating a WH-DEV integration, always use your Production API key.
Public API coverage (summary)
| Area | Sandbox |
|---|---|
| Global | response_format: upstream rejected (standard only). currency body field rejected as in prod. sandbox_trigger allowed for test keys (platform fixtures). Channel OAuth treated as authorized for hio_test_*. Quotas & rate limits skipped for test keys. |
/v1/products/* | Search, detail, parse, upload-image, search-by-image, freight estimate, batch-check, 1688 analytics endpoints mocked as documented below. OpenAPI-only extensions not in this site’s handoff set are not mocked. |
/v1/orders/* | Preview, create, detail, pay, cancel, logistics trace, list, purchase query mocked with magic / dynamic ids. |
/v1/fulfillment/* | Balance, quotes, shipments, inbounds, packages, unclaimed, returns, locations, VAS, service-requests, finance mocked with magic / dynamic ids (see below). |
/v1/support/tickets | List/detail/reply mocked with tkt_* fixtures. |
| Webhooks | Sandbox resources do not auto-emit webhooks. Use portal Send test (livemode: false) to verify your receiver and signature. |
Design goals
| Goal | Meaning |
|---|---|
| Repeatable | Same request → same response (no randomness in outcomes). |
| Documented | Every scenario has a stable id or keyword you can grep in this page. |
| Contract aligned | JSON matches standard schema fields from production; values differ. |
| Happy path default | Unknown ids fall back to generic success mocks. |
| Explicit failure | Use sb_* ids or sandbox_trigger to force errors / edge states. |
Naming: sb_\{domain\}_\{scenario\}
| Segment | Rule | Examples |
|---|---|---|
| Prefix | Always sb_ | sb_prod_not_found |
domain | Lowercase noun (prod, search, ord, shp, pkg, plat, …) | — |
scenario | snake_case | not_found, pay_declined |
Characters: [a-z0-9_], length ≤ ~48. Do not prefix real upstream ids with sb_ unless you intend the fixture. Some fulfillment resources also use stable non-sb_ ids (ret_*, ucp_*, svc_*, tkt_*, inv_*).
Two failure layers
Distinguish transport/API errors from HTTP 200 business states:
- API failure → HTTP 4xx/5xx +
error.code(e.g.NOT_FOUND,PAYMENT_DECLINED). - HTTP 200 with state → e.g.
variants[].stock = 0,status: wait_payment.
Product fixtures (high level)
POST /v1/products/detail
See Product detail. upstream format is unsupported in sandbox.
Trigger when product_id, parsed url, mi_id, or tao_password resolves to a sb_* id.
| Fixture id | Result | HTTP |
|---|---|---|
(any non-sb_ id) | Standard mock product | 200 |
sb_prod_not_found | NOT_FOUND | 404 |
sb_prod_offline | PRODUCT_UNAVAILABLE | 422 |
sb_prod_no_stock | Detail ok, stock: 0 | 200 |
curl https://api.hiobuy.com/v1/products/detail \
-H "Authorization: Bearer hio_test_..." \
-H "Content-Type: application/json" \
-d '{"channel":"1688","product_id":"sb_prod_not_found","language":"en"}'POST /v1/products/search
channel, keyword, page, page_size, language apply. 1688 filter / sort / price filters are accepted but ignored for mock rows.
keyword (exact) | Result |
|---|---|
| ordinary text | Mock list with hits |
sb_search_empty | items: [], total: 0 |
sb_search_not_found | Alias of empty |
Parse / upload-image / search-by-image / freight / analytics
| Endpoint | Sandbox note |
|---|---|
| Parse URL | URL query/path containing sb_* mirrors detail behavior. |
| Upload image | Always { "image_id": "img_sandbox_mock" } after validations pass. |
| Image search | Optional keyword: sb_search_empty yields empty list; pair with upload-image for flow tests. |
| Advanced — freight estimate | Default domestic freight mock 800 minor units aligned with preview. |
| Advanced — batch-check | Returns upstream-style envelope with mock mpProducts; use sb_prod_not_found in mi_id/item_id to populate exclusion lists. |
| Advanced — top keywords/list / daily-sales-trend | Use sb_search_empty on category_id / rank_id where documented to empty lists or 404 detail cases. |
Order & payment fixtures (magic ids)
POST /v1/orders/detail
order_id | Typical status | Notes |
|---|---|---|
sb_ord_unpaid | wait_payment | — |
sb_ord_paid | wait_shipment | — |
sb_ord_shipped | wait_receive | — |
sb_ord_completed | completed | — |
sb_ord_cancelled | cancelled | — |
sb_ord_refunding | wait_shipment (refund_status in progress) | — |
sb_ord_not_found | — | 404 |
POST /v1/orders/pay
order_id | Result | HTTP |
|---|---|---|
sb_ord_unpaid | Pays successfully → logical paid state | 200 |
sb_ord_pay_declined | PAYMENT_DECLINED | 402 |
sb_ord_pay_insufficient | PAYMENT_INSUFFICIENT_FUNDS | 402 |
sb_ord_paid | ORDER_ALREADY_PAID | 409 |
sb_ord_cancelled | ORDER_CANCELLED | 409 |
sb_ord_upstream_timeout | CHANNEL_UPSTREAM_ERROR | 502 |
Preview / create
Preview returns fixed illustrative totals (sample line ~4500 + freight ~800 in minor units). Create emits dynamic order_id values prefixed sbo_* persisted in sandbox storage — lines referencing offer_id: sb_prod_offline / sb_prod_no_stock return 422 like production semantics.
Cancel / logistics / list / purchase query
Per cancel, trace, detail list: unpaid sb_ord_* and dynamic sbo_* unpaid orders cancel; paid/shipped/etc. fixtures return ORDER_NOT_CANCELLABLE. Logistics returns packages for sb_ord_shipped / completed mocks; unpaid/paid-await-shipment return packages: []. Buyer list respects product_name: sb_ord_list_empty for empty mocks. Purchase query returns upstream envelopes; omit not-found pseudo ids accordingly.
Fulfillment fixtures
Note: The fixtures below are for Sandbox API testing only. They do not represent your application’s WH-DEV warehouse configuration. For WH-DEV testing, use a Production API key (
hio_live_*).
All endpoints below require a hio_test_* key. Dynamic ids from create (pkg_*, sbs_*, sbi_*, …) are persisted in sandbox storage for the key.
Balance · quotes · locations · VAS catalog
| Endpoint | Sandbox note |
|---|---|
GET /v1/fulfillment/balance | Mock available balance 500000 CNY minor units. |
POST /v1/fulfillment/shipping/quotes | Needs destination.country_code + weight or dimensions; legacy …/shipments/freight/estimate still works. |
GET /v1/fulfillment/locations | Sandbox: loc_wh_001 (Weihai), loc_sz_001 (Shenzhen, paused), loc_yw_001 (Yiwu). Live uses warehouse numeric id (e.g. "1") and Chinese country. |
GET /v1/fulfillment/value-added-services | Sandbox: PHOTO / WOODEN_FRAME etc. Live uses warehouse numeric code as a string (e.g. "1"). |
Shipments
Live warehouse Public id is the numeric PK as a string (e.g. "115"); order_sn is the warehouse order number. Sandbox create returns sbs_*; fixtures below use sb_shp_*.
| Fixture / id | Behavior |
|---|---|
sb_shp_unpaid | List + detail PENDING; intercept → SHIPMENT_NOT_INTERCEPTABLE |
sb_shp_shipped | Shipped detail: split boxes, first/last-mile legs, packing files; plus international tracking events. Intercept → REQUESTED |
sb_shp_not_found | Detail 404 |
sb_shp_pay_insufficient | POST …/shipments/sb_shp_pay_insufficient/pay → INSUFFICIENT_BALANCE |
sb_shp_not_cancellable | Cancel → SHIPMENT_NOT_CANCELLABLE; intercept is allowed (WAIT_SHIP) |
sb_shp_signed | Signed; intercept → SHIPMENT_NOT_INTERCEPTABLE |
sb_shp_intercepted | Detail SHIPPED + interception.status: INTERCEPTED; intercept again → SHIPMENT_INTERCEPTION_ALREADY_REQUESTED |
Dynamic sbs_* | Create / pay / list / detail / cancel / intercept against sandbox storage |
sb_parcel_not_found | Create → 422 PARCEL_NOT_FOUND |
List path: GET /v1/fulfillment/shipments. Tracking path: GET /v1/fulfillment/shipments/{id}/tracking (language query).
Inbounds (create / cancel)
Create returns dynamic package_id (pkg_*) — there is no separate inbound_id.
| Trigger value | Field | HTTP | error.code |
|---|---|---|---|
sb_inbound_shipped | tracking_number | 409 | INBOUND_TRACKING_ALREADY_SHIPPED |
sb_inbound_exists | tracking_number | 409 | INBOUND_TRACKING_ALREADY_EXISTS |
sb_inbound_bad_tracking | tracking_number | 422 | INVALID_TRACKING_NUMBER |
sb_inbound_external_exists | external_order_id | 409 | EXTERNAL_ORDER_ID_ALREADY_EXISTS |
sb_vas_missing | services[].service_code | 422 | VALUE_ADDED_SERVICE_NOT_FOUND |
sb_vas_unavailable | services[].service_code | 422 | VALUE_ADDED_SERVICE_UNAVAILABLE |
sb_vas_outbound | services[].service_code | 422 | VALUE_ADDED_SERVICE_INVALID_STAGE |
| Cancel id | HTTP | error.code |
|---|---|---|
Dynamic sbi_* (after create) | 200 | Cancelled; related package discarded |
Same sbi_* again | 409 | INBOUND_NOT_EDITABLE |
sb_inb_not_found | 404 | INBOUND_NOT_FOUND |
sb_inb_received | 409 | INBOUND_ALREADY_RECEIVED |
sb_inb_shipped / sb_inb_consolidated / sb_inb_cancelled | 409 | INBOUND_NOT_EDITABLE |
Packages · tracking · list
package_id | Detail / tracking |
|---|---|
Dynamic pkg_* | PENDING; tracking starts at INBOUND_CREATED |
sb_pkg_pending | PENDING |
sb_pkg_received | RECEIVED + PHOTO timeline |
sb_pkg_exception | RECEIVED + condition.EXCEPTION |
sb_pkg_shipped | SHIPPED full outbound timeline |
sb_pkg_not_found | 404 PACKAGE_NOT_FOUND |
Unclaimed packages
| Id / trigger | Behavior |
|---|---|
ucp_01K2ABCDEF1234567890 | Claimable; claim with SF123456789012 (list masked) |
ucp_01K2ABCDEF9876543210 | Claimable; YT738800001148 |
| Unknown tracking | 404 UNCLAIMED_PACKAGE_NOT_FOUND |
SF000ALREADY01 | 409 PACKAGE_ALREADY_CLAIMED |
SF000NOTCLAIM1 | 409 PACKAGE_NOT_CLAIMABLE |
Returns
| Id | Behavior |
|---|---|
ret_pending_001 | PENDING; fee=18; confirm succeeds |
ret_pending_002 | PENDING; fee=20; confirm with 18 → RETURN_FEE_CHANGED |
ret_returned_001 | RETURNED + tracking |
ret_cancelled_001 | CANCELLED |
sb_ret_not_found | 404 RETURN_NOT_FOUND |
sb_ret_fee_not_ready | Confirm → RETURN_FEE_NOT_READY |
sb_ret_insufficient | Confirm → INSUFFICIENT_BALANCE |
Create on sb_pkg_shipped | 409 PACKAGE_NOT_RETURNABLE |
Service requests
| Id | Behavior |
|---|---|
svc_photo_completed | PHOTO COMPLETED + images |
svc_inspection_failed | BASIC_INSPECTION FAILED |
svc_video_processing | VIDEO PROCESSING |
svc_reinforce_pending | PACKAGE_REINFORCEMENT PENDING |
svc_wooden_awaiting | WOODEN_FRAME awaiting confirmation |
sb_svc_not_found | 404 |
sb_svc_fee_not_ready / sb_svc_insufficient | Confirm edge cases |
Finance (transactions / invoices)
| Fixture | Note |
|---|---|
txn_01K2ABC001…006 | TOP_UP / VAS / RETURN / SHIPPING / REFUND / STORAGE |
inv_01K2ABC001 | ISSUED (downloadable) |
inv_01K2ABC002 | PENDING |
inv_01K2ABC003 | REJECTED |
See Fulfillment finance.
Support ticket fixtures
| Fixture id | Scenario |
|---|---|
tkt_01K2ABCDEF1234567890 | PACKAGE · NORMAL · WAITING_FOR_HIOBUY |
tkt_01K2ABCDEF9876543210 | RETURN · URGENT · WAITING_FOR_CUSTOMER |
tkt_01K2ABCDEFAPIRESOLVED | API · NORMAL · RESOLVED |
tkt_01K2ABCDEFSHIPCLOSED | SHIPMENT · NORMAL · CLOSED (no replies) |
tkt_not_found | 404 TICKET_NOT_FOUND |
See Support tickets.
Platform triggers: sandbox_trigger
Allowed only on hio_test_*; production keys ignore the field quietly.
sandbox_trigger | HTTP | error.code |
|---|---|---|
sb_plat_quota_exceeded | 429 | QUOTA_EXCEEDED |
sb_plat_rate_limited | 429 | RATE_LIMIT_EXCEEDED |
sb_plat_upstream_error | 502 | CHANNEL_UPSTREAM_ERROR |
Combine with benign ids so realistic bodies still validate:
{
"channel": "1688",
"product_id": "123456",
"language": "en",
"sandbox_trigger": "sb_plat_quota_exceeded"
}Special auth fixture sb_auth_channel_required paired with triggers can produce CHANNEL_AUTH_REQUIRED (403) for UI regressions despite the normal “authorized by default” rule.
Implementation status snapshot
| Phase | Coverage | Notes |
|---|---|---|
| Shipped | Products, procurement orders, fulfillment (balance/quotes/shipments/inbounds/packages/unclaimed/returns/locations/VAS/service-requests/finance), support tickets | Mirrors Public API subset on this site. |
| Not yet | Auto-emitted sandbox webhooks; unpublished OpenAPI-only catalogue extensions | Prefer portal Send test for webhook CI; do not assume all live events are enabled until announced. |
Quick copy cheat sheet
sb_prod_not_found → 404 product missing
sb_prod_offline → 422 unavailable
sb_prod_no_stock → 200 detail with zero stock
sb_search_empty → empty search/items
sb_ord_list_empty → empty buyer order list filter
img_sandbox_mock → stable upload-image id
sb_ord_unpaid → detail wait_payment • pay succeeds • cancel ok
sb_ord_pay_declined → pay 402 PAYMENT_DECLINED
sb_ord_paid → pay 409 ALREADY_PAID • trace maybe empty until shipped fixtures
sb_ord_shipped → detail wait_receive • trace populated
sb_ord_cancelled → cancelled state • pay conflicts
sb_shp_unpaid / sb_shp_shipped / sb_shp_signed / sb_shp_intercepted → shipment list / detail / intercept
sb_pkg_received / sb_pkg_shipped → package detail / fulfillment timeline
sb_inbound_exists → inbound create 409 tracking exists
ret_pending_001 → return confirm happy path
ucp_01K2ABCDEF1234567890 → unclaimed claimable
svc_photo_completed → VAS request completed
tkt_01K2ABCDEF1234567890 → support ticket open
sb_plat_quota_exceeded → sandbox_trigger → 429 QUOTA_EXCEEDEDWhen you add fixtures internally, mirror this numbering scheme in regression tests — never mutate standard JSON shapes inside mocks.
Get Support
Need integration help? Contact Developer Support at support@hiobuy.com · Response within 1–2 business days