AllScale_OpenAPI.postman_collection.json
{
"info": {
"_postman_id": "a11sc4le-0pen-4p1-0000-000000000001",
"name": "AllScale OpenAPI (v1)",
"description": "Ready-to-use Postman collection for AllScale's third-party OpenAPI.\n\n_Aligned with docs/openapi (Create v4 / Get v5 / Get-Status v2 β 2026-05-06)._\n\n**Setup**\n\n1. Import this collection **and** the companion environment file (`AllScale_OpenAPI.postman_environment.json`).\n2. Pick the environment (top-right dropdown).\n3. Fill in `api_key` and `api_secret` (shown once in the merchant dashboard β store them in the environment, not the collection).\n4. `base_url` defaults to the **sandbox** host; switch to the production host when ready.\n\n**How signing works**\n\nThe collection has a pre-request script at the root that runs before every request. It:\n\n- Reads `api_key` / `api_secret` from the active environment\n- Builds the canonical string `METHOD\\nPATH\\nQUERY\\nTIMESTAMP\\nNONCE\\nBODY_SHA256`\n- Signs it with HMAC-SHA256 + Base64\n- Upserts `X-API-Key`, `X-Timestamp`, `X-Nonce`, `X-Signature: v1=<sig>` headers\n\nYou should not need to touch the script for normal QA usage β just run a request.\n\n**Ordered happy-path**\n\n1. `Test / GET /v1/test/ping` β confirms connectivity + signing.\n2. `Test / POST /v1/test/post` β confirms body hashing.\n3. `Checkout Intent / Create β fiat` (or `Create β native stable-coin`) β creates an intent and auto-captures `checkout_intent_id` into the environment.\n4. `Checkout Intent / GET /v1/checkout_intents/{id}/status` β polls status.\n5. `Checkout Intent / GET /v1/checkout_intents/{id}` β full payload.\n\n**Pricing modes on Create** (pick exactly one per request):\n\n- `currency` (fiat IntEnum, Appendix A) β server FX-converts to the resolved settlement coin (USDT or USDC); use `accepted_stable_coins` to pick which.\n- `stable_coin` (IntEnum, Appendix B) β `1` (USDT) and `2` (USDC) are accepted; amount is stable-coin cents, no FX.\n\nSending both, or neither, returns `10001`.\n\n**Settlement:** USDT or USDC. `stable_coin_type` in the response matches the resolved coin (defaults to `1` USDT when neither `stable_coin` nor `accepted_stable_coins` is set).\n\n**Error codes** (from the server's unified envelope):\n\n| Code | Meaning |\n|-------|---------|\n| 0 | Success |\n| 10001 | Validation error |\n| 20001 | Missing auth headers |\n| 20002 | Bad signature |\n| 30001 | Forbidden (IP / ownership) |\n| 40001 | Rate limit exceeded |\n| 50001 | Checkout intent not found |\n| 50002 | Create checkout intent error (e.g. amount at or below the 0.1-coin floor) |\n| 90000 | Internal server error |\n",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"auth": {
"type": "noauth"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"// AllScale HMAC-SHA256 request signing.",
"// See docs/openapi/docs/API Doc - Auth.md for the canonical-string format.",
"",
"const apiKey = pm.environment.get('api_key') || pm.collectionVariables.get('api_key') || '';",
"const apiSecret = (pm.environment.get('api_secret') || pm.collectionVariables.get('api_secret') || '').trim();",
"",
"if (!apiKey || !apiSecret) {",
" console.warn('[AllScale] api_key or api_secret is empty β requests will be rejected (20001/20002).');",
"}",
"",
"// Method",
"const method = (pm.request.method || 'GET').toUpperCase();",
"",
"// Path β resolve {{var}} placeholders and :path params via pm.variables.replaceIn.",
"const urlObj = pm.request.url;",
"const rawPathSegments = (urlObj.path || []).map(function (seg) {",
" return pm.variables.replaceIn(String(seg));",
"});",
"let path = '/' + rawPathSegments.join('/');",
"// Postman treats a trailing slash in the URL as an empty segment; keep it so the canonical path",
"// matches what Starlette sees on the server (e.g. /v1/checkout_intents/).",
"",
"// Query β use Postman's resolved query string so encoding matches what actually goes on the wire.",
"let query = '';",
"try {",
" query = urlObj.getQueryString({ ignoreDisabled: true }) || '';",
"} catch (e) {",
" // Older Postman runtimes β fall back to manual build.",
" const parts = [];",
" (urlObj.query || []).each(function (q) {",
" if (q.disabled) return;",
" const k = pm.variables.replaceIn(q.key || '');",
" const v = pm.variables.replaceIn(q.value || '');",
" parts.push(k + '=' + v);",
" });",
" query = parts.join('&');",
"}",
"",
"// Body β only raw JSON bodies are signed with content. Other modes sign an empty body.",
"let body = '';",
"if (pm.request.body && pm.request.body.mode === 'raw' && pm.request.body.raw) {",
" body = pm.variables.replaceIn(pm.request.body.raw);",
"}",
"",
"// Canonical pieces",
"const bodyHash = CryptoJS.SHA256(body).toString(CryptoJS.enc.Hex);",
"const timestamp = Math.floor(Date.now() / 1000).toString();",
"",
"function uuidv4() {",
" if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {",
" return crypto.randomUUID();",
" }",
" // RFC 4122 v4 fallback",
" return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {",
" const r = Math.random() * 16 | 0;",
" const v = c === 'x' ? r : (r & 0x3 | 0x8);",
" return v.toString(16);",
" });",
"}",
"const nonce = uuidv4();",
"",
"const canonical = [method, path, query, timestamp, nonce, bodyHash].join('\\n');",
"const signature = CryptoJS.HmacSHA256(canonical, apiSecret).toString(CryptoJS.enc.Base64);",
"",
"pm.request.headers.upsert({ key: 'X-API-Key', value: apiKey });",
"pm.request.headers.upsert({ key: 'X-Timestamp', value: timestamp });",
"pm.request.headers.upsert({ key: 'X-Nonce', value: nonce });",
"pm.request.headers.upsert({ key: 'X-Signature', value: 'v1=' + signature });",
"",
"// Uncomment for debugging:",
"// console.log('[AllScale canonical]\\n' + canonical);",
"// console.log('[AllScale signature] v1=' + signature);",
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"// Collection-wide sanity checks.",
"pm.test('Response is JSON', function () {",
" pm.response.to.be.withBody;",
" pm.response.to.be.json;",
"});",
"",
"pm.test('Envelope has `code` and `request_id`', function () {",
" const body = pm.response.json();",
" pm.expect(body).to.have.property('code');",
" pm.expect(body).to.have.property('request_id');",
"});",
""
]
}
}
],
"variable": [
{
"key": "base_url",
"value": "{{base_url}}",
"type": "string"
}
],
"item": [
{
"name": "Test",
"description": "Smoke-test endpoints. Run these first to validate credentials, signing, and body hashing before touching real payment flows.",
"item": [
{
"name": "GET /v1/test/ping",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/v1/test/ping",
"host": ["{{base_url}}"],
"path": ["v1", "test", "ping"]
},
"description": "Health-check. Verifies that:\n\n- `api_key` / `api_secret` are valid\n- Your clock is in sync (Β±5 min window)\n- The signing pre-request script is producing a correct signature\n\nExpected: `{ \"code\": 0, \"payload\": { \"pong\": \"ok\" }, ... }`"
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('code == 0', function () {",
" pm.expect(pm.response.json().code).to.eql(0);",
"});",
"pm.test('payload.pong == \"ok\"', function () {",
" pm.expect(pm.response.json().payload.pong).to.eql('ok');",
"});"
]
}
}
]
},
{
"name": "GET /v1/test/fail",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/v1/test/fail",
"host": ["{{base_url}}"],
"path": ["v1", "test", "fail"]
},
"description": "Always returns `10001 Validation error`. Use it to verify your error-handling branch parses the envelope correctly."
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('code == 10001 (validation error)', function () {",
" pm.expect(pm.response.json().code).to.eql(10001);",
"});"
]
}
}
]
},
{
"name": "POST /v1/test/post",
"request": {
"method": "POST",
"header": [
{ "key": "Content-Type", "value": "application/json" }
],
"body": {
"mode": "raw",
"raw": "{\n \"everything\": \"is_fine\"\n}",
"options": {
"raw": { "language": "json" }
}
},
"url": {
"raw": "{{base_url}}/v1/test/post",
"host": ["{{base_url}}"],
"path": ["v1", "test", "post"]
},
"description": "Echo endpoint. Verifies body-hash signing β if this passes but real endpoints return `20002`, the client almost certainly isn't hashing the body the same way the server does.\n\nThe server echoes the body back under `payload.your_request_body`."
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"pm.test('code == 0', function () {",
" pm.expect(pm.response.json().code).to.eql(0);",
"});",
"pm.test('body was echoed', function () {",
" pm.expect(pm.response.json().payload.your_request_body.everything).to.eql('is_fine');",
"});"
]
}
}
]
}
]
},
{
"name": "Checkout Intent",
"description": "End-to-end checkout-intent lifecycle. Create β poll status β fetch full detail. `Create` captures the returned intent id into the `checkout_intent_id` environment variable so the two GETs work without manual copy-paste.\n\n**Two create flavors** (pick exactly one on each request):\n\n- **Fiat pricing** β send `currency` (IntEnum, Appendix A). The server FX-converts to the resolved settlement coin (USDT or USDC) at the current rate; `currency_rate` in the response is non-null. Use `accepted_stable_coins` (first entry wins) to pick the settlement coin; defaults to USDT.\n- **Native stable-coin pricing** β send `stable_coin` (IntEnum, Appendix B). `amount_cents` is stable-coin cents (`1000` = `10.00` of the chosen coin). No FX happens, so `currency_rate` in the response is `null`. Both `stable_coin = 1` (USDT) and `stable_coin = 2` (USDC) are accepted.\n\nSending both β or neither β of `currency`/`stable_coin` returns `10001`.",
"item": [
{
"name": "POST /v1/checkout_intents/ (Create β fiat)",
"request": {
"method": "POST",
"header": [
{ "key": "Content-Type", "value": "application/json" }
],
"body": {
"mode": "raw",
"raw": "{\n \"currency\": 1,\n \"amount_cents\": 100,\n \"order_id\": \"qa_order_{{$timestamp}}\",\n \"order_description\": \"QA test order\",\n \"user_id\": \"qa_user_001\",\n \"user_name\": \"QA Tester\",\n \"redirect_url\": \"https://example.com/checkout/allscale\",\n \"extra\": {\n \"source\": \"postman\"\n }\n}",
"options": {
"raw": { "language": "json" }
}
},
"url": {
"raw": "{{base_url}}/v1/checkout_intents/",
"host": ["{{base_url}}"],
"path": ["v1", "checkout_intents", ""]
},
"description": "Create a checkout intent priced in fiat. Server FX-converts the amount into the resolved settlement coin (USDT or USDC) at the current rate.\n\n**Field notes**\n\n- `currency` is the fiat IntEnum (1 = USD, 44 = EUR, β¦). Full table in `API Doc - Checkout Intent Routes - Create Checkout Intent.md` β Appendix A.\n- `amount_cents` is integer **fiat** cents. The resulting stable-coin amount must be greater than `0.1` (USDT or USDC); orders at or below the floor are rejected with `50002`.\n- Do **not** also send `stable_coin` on this request β sending both returns `10001`.\n- `redirect_url` must be `https://β¦` in production; sandbox also allows `http://`.\n- Settlement coin is **USDT or USDC**. To pin it on a fiat-priced order, send `accepted_stable_coins: [1]` (USDT) or `[2]` (USDC); the first entry picks the settlement coin used for FX conversion. `stable_coin_type` in the response matches whichever was resolved (defaults to `1` USDT).\n\nThe post-response test captures `payload.allscale_checkout_intent_id` into `{{checkout_intent_id}}` for the follow-up GETs."
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const body = pm.response.json();",
"pm.test('code == 0', function () {",
" pm.expect(body.code).to.eql(0);",
"});",
"pm.test('payload.allscale_checkout_intent_id exists', function () {",
" pm.expect(body.payload).to.have.property('allscale_checkout_intent_id');",
" pm.expect(body.payload.allscale_checkout_intent_id).to.be.a('string');",
"});",
"",
"if (body && body.payload && body.payload.allscale_checkout_intent_id) {",
" pm.environment.set('checkout_intent_id', body.payload.allscale_checkout_intent_id);",
" console.log('[AllScale] captured checkout_intent_id = ' + body.payload.allscale_checkout_intent_id);",
"}"
]
}
}
]
},
{
"name": "POST /v1/checkout_intents/ (Create β native stable-coin)",
"request": {
"method": "POST",
"header": [
{ "key": "Content-Type", "value": "application/json" }
],
"body": {
"mode": "raw",
"raw": "{\n \"stable_coin\": 1,\n \"amount_cents\": 1000,\n \"order_id\": \"qa_stable_{{$timestamp}}\",\n \"order_description\": \"QA native USDT order\",\n \"user_id\": \"qa_user_001\",\n \"user_name\": \"QA Tester\",\n \"redirect_url\": \"https://example.com/checkout/allscale\",\n \"extra\": {\n \"source\": \"postman\",\n \"pricing\": \"native_stable_coin\"\n }\n}",
"options": {
"raw": { "language": "json" }
}
},
"url": {
"raw": "{{base_url}}/v1/checkout_intents/",
"host": ["{{base_url}}"],
"path": ["v1", "checkout_intents", ""]
},
"description": "Create a checkout intent priced **natively in a stable coin** β no FX happens.\n\n**Field notes**\n\n- `stable_coin` is the StableCoin IntEnum (see Appendix B). Both `1` (USDT) and `2` (USDC) are accepted; other values are reserved and will be rejected.\n- `amount_cents` is **stable-coin cents** of the chosen `stable_coin`: `1000` = `10.00` of that coin. The example above (`stable_coin: 1`) produces a `10 USDT` order; switch to `stable_coin: 2` for the equivalent USDC order.\n- Do **not** also send `currency` β sending both, or neither, returns `10001`.\n- The response's `currency_rate` will be `null` (no FX). In the full GET, the fiat fields (`currency`, `currency_symbol`, `amount_cents`, `currency_rate`) will all be `null`; the authoritative amount is `amount_coins`.\n\nThe post-response test captures `payload.allscale_checkout_intent_id` into `{{checkout_intent_id}}` just like the fiat flavor."
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const body = pm.response.json();",
"pm.test('code == 0', function () {",
" pm.expect(body.code).to.eql(0);",
"});",
"pm.test('payload.allscale_checkout_intent_id exists', function () {",
" pm.expect(body.payload).to.have.property('allscale_checkout_intent_id');",
"});",
"pm.test('rate is null for native stable-coin pricing', function () {",
" pm.expect(body.payload.rate).to.be.oneOf([null, undefined]);",
"});",
"",
"if (body && body.payload && body.payload.allscale_checkout_intent_id) {",
" pm.environment.set('checkout_intent_id', body.payload.allscale_checkout_intent_id);",
" console.log('[AllScale] captured checkout_intent_id = ' + body.payload.allscale_checkout_intent_id);",
"}"
]
}
}
]
},
{
"name": "GET /v1/checkout_intents/{id}/status",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/v1/checkout_intents/{{checkout_intent_id}}/status",
"host": ["{{base_url}}"],
"path": ["v1", "checkout_intents", "{{checkout_intent_id}}", "status"]
},
"description": "Poll-friendly lightweight status check. `payload` is the status integer (see status enum in the doc).\n\nUse this instead of the full GET when all you need is the lifecycle state."
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const body = pm.response.json();",
"pm.test('code == 0', function () {",
" pm.expect(body.code).to.eql(0);",
"});",
"pm.test('payload is integer status', function () {",
" pm.expect(body.payload).to.be.a('number');",
"});"
]
}
}
]
},
{
"name": "GET /v1/checkout_intents/{id}",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/v1/checkout_intents/{{checkout_intent_id}}",
"host": ["{{base_url}}"],
"path": ["v1", "checkout_intents", "{{checkout_intent_id}}"]
},
"description": "Full checkout intent object β order metadata, currency/coin fields, on-chain tx hashes when payment has settled. Ownership is enforced: the intent must belong to the store identified by `api_key`, otherwise `30001 Forbidden`."
},
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"const body = pm.response.json();",
"pm.test('code == 0', function () {",
" pm.expect(body.code).to.eql(0);",
"});",
"pm.test('payload has core fields', function () {",
" const p = body.payload;",
" pm.expect(p).to.have.property('all_scale_checkout_intent_id');",
" pm.expect(p).to.have.property('currency');",
" pm.expect(p).to.have.property('amount_cents');",
" pm.expect(p).to.have.property('status');",
" pm.expect(p).to.have.property('tx_to');",
"});"
]
}
}
]
}
]
}
]
}Last updated