Static Sandbox
The static sandbox lets you build and test your integration against every API endpoint without hitting the real backend. Same auth, same schemas, same rate limits — but deterministic mock responses, zero side effects, and no dependency on backend health.
Use it to validate request shape, response parsing, header handling, auth flows, and 429 retry logic before you ever touch production.
Getting started
The static sandbox is a drop-in replacement for the production host. Keep your path, body, headers, and credentials exactly as they are — just change where you send the request.
| Environment | Host |
|---|---|
| Production | noon-api-gateway.noon.partners |
| Static Sandbox | noon-sandbox-api-gateway.noon.partners |
What stays the same as production
- Authentication. OAuth tokens, project codes, and partner IDs behave identically.
- Rate limits. Sandbox traffic counts against your per-project quota, so you can exercise real
429handling. - Request validation. Malformed requests are rejected with the same
400errors you'd see in production. Schema validation runs before the mock layer. - Response schemas. Every response is a real instance of the documented message, marshaled exactly as in production.
What's different
- No backend. Backend outages, latency spikes, and bugs never affect the sandbox.
- No side effects. Nothing is persisted, no events are emitted, no downstream systems are called.
- No business validation. Inventory, pricing, permissions, and similar checks are skipped.
- No simulated server errors. The sandbox never returns
5xx. You can observe400(bad request),401(bad auth), and429(rate-limited); everything else returns200.
When to use the static sandbox vs production
| Use the static sandbox to… | Use production to… |
|---|---|
| Wire up a new endpoint and verify your client deserializes the response | Run end-to-end tests that depend on real data |
Validate 400, 401, and 429 handling | Verify business rules (pricing, inventory, permissions) |
| Smoke-test in CI without flakiness from upstream outages | Test 5xx and other server-side error paths |
| Develop offline against a stable contract | Anything that needs a real side effect |
How responses are generated
For each field in the response, the sandbox walks through these checks in order and uses the first one that applies.
| Response field | Request has matching field? | Result |
|---|---|---|
| Any type | Yes | Copy the value as-is. Lists keep their length and order; nested objects are filled by re-running these same checks on each of their fields. |
| String | No | Fill with the uppercased field name (e.g. tag → "TAG"). If the field is optional, prefix it: OPTIONAL_TAG. |
| Anything else | No | Fill with the zero value for the type: 0 for numbers, false for booleans, [] for lists, {} for objects. If the field is optional, omit it from the response entirely. |
The standard status field is always filled with { "code": 0, "message": "OK" }, regardless of the request.
Examples
The endpoints below —
/dummy-service/v1/whoami,/dummy-service/v1/lookup,/dummy-service/v1/orders— are illustrative only. This services does not exist and the APIs shown below do not exist. They're here to demonstrate how the sandbox builds responses from requests. For the actual endpoints you can call, see the API reference.
Example 1 — empty request
A trivial endpoint that returns who you are. The request has no body, so the sandbox has nothing to copy from.
Endpoint: GET /dummy-service/v1/whoami
Request body: none.
Response schema:
| Field | Type | Required |
|---|---|---|
user_code | string | yes |
username | string | yes |
Static Sandbox response:
{
"user_code": "USER_CODE",
"username": "USERNAME"
}
How the sandbox built it: no request body means no fields to copy. Both response fields are required strings with no request counterpart, so each is filled with its uppercased name.
Example 2 — request fields echoed back
A flat lookup endpoint that shows how request fields flow into the response and how missing fields are filled by type.
Endpoint: POST /dummy-service/v1/lookup
Request schema:
| Field | Type | Required |
|---|---|---|
sku | string | yes |
locale | string | no |
Example request:
{
"sku": "ABC-123",
"locale": "en-AE"
}
Response schema:
| Field | Type | Required |
|---|---|---|
sku | string | yes |
locale | string | no |
title | string | yes |
price | number | yes |
in_stock | boolean | yes |
tags | list of strings | yes |
Static Sandbox response:
{
"sku": "ABC-123",
"locale": "en-AE",
"title": "TITLE",
"price": 0,
"in_stock": false,
"tags": []
}
How the sandbox built it: sku and locale match fields on the request, so they're copied verbatim. title is a required string with no counterpart → uppercased name. price, in_stock, and tags are non-string fields with no counterpart → zero values for their types.
Example 3 — nested objects, lists, and optional fields
An order-creation endpoint that exercises every rule at once: copying, recursion into nested objects, list preservation, optional-field handling, and the status rule.
Endpoint: POST /dummy-service/v1/orders
Request schema:
| Field | Type | Required |
|---|---|---|
customer | object | yes |
customer.email | string | yes |
items | list of objects | yes |
items[].sku | string | yes |
items[].qty | integer | yes |
Example request:
{
"customer": { "email": "[email protected]" },
"items": [
{ "sku": "ABC-123", "qty": 2 },
{ "sku": "XYZ-789", "qty": 1 }
]
}
Response schema:
| Field | Type | Required |
|---|---|---|
order_code | string | yes |
customer | object | yes |
customer.email | string | yes |
customer.name | string | yes |
customer.phone | string | no |
items | list of objects | yes |
items[].sku | string | yes |
items[].qty | integer | yes |
items[].line_total | number | yes |
status | object | yes |
Static Sandbox response:
{
"order_code": "ORDER_CODE",
"customer": {
"email": "[email protected]",
"name": "NAME",
"phone": "OPTIONAL_PHONE"
},
"items": [
{ "sku": "ABC-123", "qty": 2, "line_total": 0 },
{ "sku": "XYZ-789", "qty": 1, "line_total": 0 }
],
"status": { "code": 0, "message": "OK" }
}
How the Static Sandbox built it, field by field:
order_code— required string, no counterpart → uppercased name.customer— nested object, filled recursively.emailis copied from the request.nameis a required string with no counterpart → uppercased name.phoneis an optional string with no counterpart →OPTIONAL_prefix.items— list with two entries on the request, preserved in order. Inside each entry,skuandqtyare copied;line_totalis a required number with no counterpart →0.status— always succeeds.
FAQ
Which error responses can the sandbox return?
400 for malformed requests (same schema validation as production), 401 for invalid credentials, and 429 when you exceed your quota. The sandbox never returns 5xx. To exercise server-error paths, mock at your client layer.
Do sandbox requests count against my rate limit?
Yes. This is intentional — it means your 429 handling can be validated against the same quota you'll see in production.
Are responses stable across calls? Yes. The same request always produces the same response. Responses are a pure function of the request.
Can I tell from the response that I hit the sandbox? The host you called is the source of truth. Response bodies don't carry an environment marker, so make sure your client logs which host it's pointing at.