An integration receives HTTP 200 and records an order handover as accepted. The response body is actually an HTML login page. The network request completed, but the application never obtained the acceptance record it expected.
For a Shopify 3PL integration, keep transport success and the response contract as separate checks. This standalone example accepts one deliberately narrow contract: an application/json response containing accepted: true, the expected request reference and a nonempty receipt identifier.
Response.ok reports whether the status is in the 200–299 range. Response.json() parses the response body; successful parsing alone does not prove that its fields match an application's contract.
Validate before recording acceptance
The function checks the caller's expected reference, the HTTP result, the declared media type and the parsed payload. The local confirmed array represents an acceptance record. It changes only after all checks pass.
const assert = require('node:assert/strict'); async function readAcceptance(response, expectedRef) { if (typeof expectedRef !== 'string' || !expectedRef || expectedRef.trim() !== expectedRef) { throw new Error('Expected request reference required'); } if (!response.ok) throw new Error(`HTTP ${response.status}`); const media = (response.headers.get('content-type') || '') .split(';')[0].trim().toLowerCase(); if (media !== 'application/json') { throw new Error('Expected application/json'); } let body; try { body = await response.json(); } catch { throw new Error('Invalid JSON body'); } if (!body || typeof body !== 'object' || Array.isArray(body) || body.accepted !== true || body.requestRef !== expectedRef || typeof body.receiptId !== 'string' || !body.receiptId || body.receiptId.trim() !== body.receiptId) { throw new Error('Acceptance contract mismatch'); } return { requestRef: body.requestRef, receiptId: body.receiptId }; } async function main() { const confirmed = []; async function record(response, ref = 'batch-17') { const receipt = await readAcceptance(response, ref); confirmed.push(receipt); } const good = { accepted: true, requestRef: 'batch-17', receiptId: 'r-1' }; await record(Response.json(good, { status: 201 })); assert.deepEqual(confirmed, [{ requestRef: 'batch-17', receiptId: 'r-1' }]); const invalid = [ new Response('<html>Sign in</html>', { status: 200, headers: { 'content-type': 'text/html' } }), new Response('{', { headers: { 'content-type': 'application/json' } }), Response.json(good, { status: 403 }), new Response(null, { status: 204 }), Response.json(null), Response.json([]), Response.json({ ...good, accepted: 'true' }), Response.json({ ...good, requestRef: 'another-batch' }), Response.json({ ...good, receiptId: ' ' }), Response.json({ accepted: true, requestRef: 'batch-17' }) ]; for (const response of invalid) { const before = confirmed.length; await assert.rejects(() => record(response)); assert.equal(confirmed.length, before); } await assert.rejects(() => record(Response.json(good), '')); assert.equal(confirmed.length, 1); console.log('Acceptance response checks passed'); } main().catch(error => { console.error(error.message); process.exitCode = 1; }); Save this as acceptance-response.cjs and run node acceptance-response.cjs with Node.js 24. The checks use local Response objects and make no network calls. They exercise HTML returned with a successful status, malformed JSON, an HTTP error, an empty response and several payload mismatches. Every rejected response leaves the local acceptance record unchanged.
Keep the response contract specific to the operation
For drop shipping requests, a supplier acknowledgment and a warehouse acceptance may represent different events. A shopify fulfillment adapter should map the documented response to the event it actually proves. Do not convert any successful HTTP status into “dispatched.”
In a China fulfillment project, agree what the downstream receipt means and which request reference it confirms. A China 3PL integration may use another media type or payload shape; implement that documented contract explicitly. This example intentionally rejects everything outside its narrow JSON contract.
For a hypothetical specification titled “International Fulfillment for Folligen,” the same review starts with the operation and its expected acceptance fields, before choosing an adapter. That heading supplies a planning context, not an API schema.
I work with YX Fulfillment, whose confirmed workflow includes order processing and carrier handover. The code illustrates response validation for an integration discussion; it does not describe a deployed YX endpoint. Production work also needs authentication, timeouts, body-size limits, persistent records and operation-specific retry handling. An uncertain response to a write request needs a reconciliation decision before another write is attempted.
Warehouse workstations at YX Fulfillment.

![Path Planning and Autonomous Navigation — Ch.25: Future of Autonomous Navigation [KR]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjs5djnow7ktieu1tmsv0.png)

