{"openapi":"3.0.3","info":{"title":"Order Helper API","version":"1.0.0","description":"The Order Helper API lets you read your own store's Order Helper data from your\nown code: a spreadsheet sync, an internal dashboard, a warehouse system, or\nan AI assistant.\n\nIt is a JSON API over HTTPS. Every request is authenticated with a token\nyou create yourself inside the app, and every request only ever sees the\nstore that token belongs to \u2014 there is no way to ask for another store's\ndata, and no store id to pass.\n\n- **Base URL** \u2014 `https:\/\/api.sappo.xco.agency`\n- **Shape** \u2014 `GET https:\/\/api.sappo.xco.agency\/order-helper\/v1\/{resource}`\n- **Authentication** \u2014 `Authorization: Bearer mak_\u2026`\n- **Machine-readable reference** \u2014 [OpenAPI 3.0 document](https:\/\/api.sappo.xco.agency\/order-helper\/docs.json)\n\nResponses are always JSON. A successful resource response carries a `data`\narray and a `meta` object; a failed one carries a single `error` object and\nnothing else.\n\nNo endpoint changes your data: everything here is a read.\n\nThe full guide \u2014 authentication, paging, filtering, every error code and the\nrate limits \u2014 is at [https:\/\/api.sappo.xco.agency\/order-helper\/docs](https:\/\/api.sappo.xco.agency\/order-helper\/docs), as\nJSON at [https:\/\/api.sappo.xco.agency\/order-helper\/docs\/content.json](https:\/\/api.sappo.xco.agency\/order-helper\/docs\/content.json),\nand the MCP guide for AI assistants is at\n[https:\/\/api.sappo.xco.agency\/order-helper\/docs\/mcp](https:\/\/api.sappo.xco.agency\/order-helper\/docs\/mcp).","contact":{"name":"XCO Agency Support","email":"support@xco.agency"}},"servers":[{"url":"https:\/\/api.sappo.xco.agency","description":"Production"}],"tags":[{"name":"Requests","description":"Order-tracking lookups for the authenticated shop."},{"name":"Tracking","description":"Tracking events for orders that belong to the authenticated shop."}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"mak_\u2026","description":"Open the app in your Shopify admin, go to Settings \u2192 API Access, and create a token. The full token is shown once, at creation time; afterwards only its first 12 characters are stored in readable form. If that card is locked, API access is not included in your current plan and the app will not create a token until the plan changes. At most three tokens can be active at once, so revoke one you no longer use before creating another."}},"schemas":{"PaginatedMeta":{"type":"object","description":"Describes the page that was returned.","required":["page","pageSize","total"],"properties":{"page":{"type":"integer","description":"The 1-based page returned.","example":1},"pageSize":{"type":"integer","description":"Records per page, after the server cap was applied.","example":25},"total":{"type":"integer","description":"Records matching the filters, across all pages.","example":137}}},"PaginatedResponse":{"type":"object","description":"Every successful list response. `data` items carry the app's own field names.","required":["data","meta"],"properties":{"data":{"type":"array","description":"The records for this page, newest first.","items":{"type":"object","additionalProperties":true}},"meta":{"$ref":"#\/components\/schemas\/PaginatedMeta"}}},"Error":{"type":"object","description":"Every failure. Branch on `error.code`, not on `error.message`.","required":["error"],"properties":{"error":{"type":"object","required":["code","message","status"],"properties":{"code":{"type":"string","description":"Stable machine-readable code.","example":"INVALID_TOKEN"},"message":{"type":"string","description":"Human-readable explanation. Wording may change."},"status":{"type":"integer","description":"The HTTP status, repeated so it survives logging.","example":401}}}}}},"responses":{"Unauthorized":{"description":"No usable token was presented. Returned as `MISSING_TOKEN` when the `Authorization` header is absent or not a Bearer header, and as `INVALID_TOKEN` when the token does not exist, was revoked, has expired, or the store is suspended.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"},"example":{"error":{"code":"INVALID_TOKEN","message":"Token is invalid, expired, or revoked.","status":401}}}}},"Forbidden":{"description":"Not returned by any resource endpoint: no Merchant API resource is plan- or scope-restricted. Defined because the connector endpoint, where an app has one, can answer 403 \u2014 in the connector's own envelope rather than this one.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"NotFound":{"description":"Returned as `APP_NOT_FOUND` when the app handle in the path is not one Sappo serves, and as `RESOURCE_NOT_FOUND` when the app does not expose that resource.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"},"example":{"error":{"code":"RESOURCE_NOT_FOUND","message":"Resource '{resource}' is not available for 'order-helper'.","status":404}}}}},"UnprocessableEntity":{"description":"Not returned by any resource endpoint: unknown query parameters are ignored rather than rejected, and a resource endpoint takes no request body. The connector endpoint, where an app has one, returns it as `invalid_action` or `invalid_params` in the connector's own envelope.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"TooManyRequests":{"description":"The token exceeded its request budget. Carries `Retry-After` alongside the `X-RateLimit-*` headers.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many requests. Please slow down.","status":429}}}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed per window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window. Always 0 on this response.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix timestamp when the window resets.","schema":{"type":"integer"}}}},"InternalServerError":{"description":"The request was accepted but failed while being handled. Reported to us automatically.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"},"example":{"error":{"code":"INTERNAL_ERROR","message":"An internal error occurred.","status":500}}}}}}},"paths":{"\/order-helper\/v1\/requests":{"get":{"tags":["Requests"],"summary":"List tracking requests","description":"Order-tracking lookups for the authenticated shop.","operationId":"listTrackingRequests","parameters":[{"name":"page","in":"query","required":false,"description":"1-based page number. Values below 1 are treated as 1.","schema":{"type":"integer","minimum":1,"default":1}},{"name":"page_size","in":"query","required":false,"description":"Records per page. Values above 100 are reduced to 100. Echoed back as `meta.pageSize`.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"A page of requests.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/PaginatedResponse"},"example":{"data":[],"meta":{"page":1,"pageSize":25,"total":0}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/TooManyRequests"},"500":{"$ref":"#\/components\/responses\/InternalServerError"}},"security":[{"BearerAuth":[]}]}},"\/order-helper\/v1\/tracking":{"get":{"tags":["Tracking"],"summary":"List tracking events","description":"Tracking events for orders that belong to the authenticated shop.","operationId":"listTrackingEvents","parameters":[{"name":"page","in":"query","required":false,"description":"1-based page number. Values below 1 are treated as 1.","schema":{"type":"integer","minimum":1,"default":1}},{"name":"page_size","in":"query","required":false,"description":"Records per page. Values above 100 are reduced to 100. Echoed back as `meta.pageSize`.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"A page of tracking.","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/PaginatedResponse"},"example":{"data":[],"meta":{"page":1,"pageSize":25,"total":0}}}}},"401":{"$ref":"#\/components\/responses\/Unauthorized"},"404":{"$ref":"#\/components\/responses\/NotFound"},"429":{"$ref":"#\/components\/responses\/TooManyRequests"},"500":{"$ref":"#\/components\/responses\/InternalServerError"}},"security":[{"BearerAuth":[]}]}}}}