Work with Sub-accounts
Outcome
Use this guide to determine when the optional X-SubAccount-Id header affects
an operation, keep scope explicit where it matters in a workflow, and avoid
relying on the header where it has no effect. The matrix below is the
authoritative public guide; the API Reference remains the complete schema
source.
What a Sub-account means in this API
A Sub-account is an application scope beneath the authenticated Mailhub
Account. The Account is the authentication and ownership boundary; the
Sub-account is an optional, per-request scope inside it, and some public
operations allow the caller to select that scope through X-SubAccount-Id. See
Account and Sub-account Scope for the concept
this guide applies.
Some existing Mailhub product screens may use the term Subcompany. Public API documentation uses Sub-account.
When to use X-SubAccount-Id
The header is optional for every current public operation. When an operation's matrix row supports it, omitting the header uses the authenticated Account context. Send an authorized value only for the operation-specific effect shown below; it is not a universal scope switch.
Header syntax
The public contract declares an optional UUID string header:
X-SubAccount-Id: <SUB_ACCOUNT_ID>
Public-operation behavior matrix
| Operation | Method and path | Header effect | What the caller should do |
|---|---|---|---|
| Login | POST /api/v1/auth/token | Optional: validates the selected Sub-account for token exchange but does not persist that scope in either returned token. | Use only when the token exchange itself needs that validation; do not rely on it to scope later requests. |
| Refresh token | POST /api/v1/auth/token/refresh | The operation does not read the header; a well-formed value does not change behavior. | Omit it; do not use it to establish request scope. |
| Logout | POST /api/v1/auth/token/revoke | The operation does not read the header; a well-formed value does not change behavior. | Omit it; do not use it to establish request scope. |
| Cancel shipping label | DELETE /api/v1/labels/{labelId} | Sending X-SubAccount-Id does not change the behavior of this operation. | Omit it; do not rely on it to scope this operation. |
| Download shipping label | GET /api/v1/labels/{labelId}/download | Sending X-SubAccount-Id does not change the behavior of this operation. | Omit it; do not rely on it to scope this operation. |
| List orders | GET /api/v1/orders | Optional: filters the Order list to the selected Sub-account. | Send it when listing that Sub-account; omit it to use the authenticated Account context. |
| Create an Order | POST /api/v1/orders | Optional: tags the newly created Order and its Shipments with the selected Sub-account. | Send it only when creating in that Sub-account. |
| Get order by ID | GET /api/v1/orders/{orderId} | Optional: restricts the lookup to the selected Sub-account. An Order that belongs to a different Sub-account is reported as 404 Not Found. | Send it only when the Order belongs to that Sub-account; omit it to look up Orders across the authenticated Account. |
| Updates order status. | PATCH /api/v1/orders/{orderId}/status | Sending X-SubAccount-Id does not change the behavior of this operation. | Omit it; do not rely on it to scope this operation. |
| Create shipping label | POST /api/v1/shipments/{shipmentId}/labels | Optional: evaluates the request against the selected Sub-account, which can narrow the carriers available to it. It does not change pricing and does not restrict resource access. | Send it when the request should use that Sub-account’s carrier availability; omit it to use the authenticated Account context. |
| Get shipment rates | POST /api/v1/shipments/{shipmentId}/rates | Optional: evaluates the request against the selected Sub-account, which can narrow the carriers available to it. It does not change pricing and does not restrict resource access. | Send it when the request should use that Sub-account’s carrier availability; omit it to use the authenticated Account context. |
| Create an Order with Rates | POST /api/v2/orders | Optional: tags the newly created Order and its Shipments with the selected Sub-account. | Send it only when creating in that Sub-account. |
Account-scoped example
Use the default Account context by omitting the optional header from the read-only List Orders request:
curl -s "${MAILHUB_API_BASE_URL}/api/v1/orders" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}"
Sub-account-scoped example
For List Orders, the same read-only request can filter to an authorized Sub-account:
curl -s "${MAILHUB_API_BASE_URL}/api/v1/orders" \
-H "Authorization: Bearer ${MAILHUB_ACCESS_TOKEN}" \
-H "X-SubAccount-Id: <SUB_ACCOUNT_ID>"
Carrier operations
For Get Shipment Rates and Create Shipping Label, sending the header evaluates the request against the selected Sub-account, which can narrow the carriers available to it. A Rate you expect may therefore be absent when the header is sent and present when it is omitted.
The header does not change pricing, and it does not decide which Shipments, Rates, or Labels you may act on — that is enforced by Account ownership, whether or not you send it.
Reading a single Order
For Get Order the matrix records FiltersResourceAccess. Sending
X-SubAccount-Id restricts the lookup to that Sub-account: an Order belonging to
a different Sub-account is reported as 404 Not Found, while the same request
without the header returns it. Omit the header to look up Orders across the
authenticated Account.
This is a scoping filter, not an authorization boundary — which Orders you may reach at all is still decided by Account ownership.
When not to use it
For Update Order Status, Download Label, and Cancel Label, the matrix
records NoEffect: Sending X-SubAccount-Id does not change the
behavior of this operation. Refresh and revoke do not read the header, and
token exchange validates a supplied value without persisting it in either
returned token.
Errors and inaccessible scope
A malformed header value is rejected with the current public 400 error
envelope. Current verification records 404 for a nonexistent or inaccessible
Sub-account and 409 when it is inactive; inspect the current operation
response and correct the selected scope rather than retrying unchanged.
How far that checking goes depends on the operation class, so do not assume one pipeline everywhere.
- A malformed value is rejected everywhere. The GUID-format check runs
globally, ahead of routing, so any public operation answers
400for a value that is not a GUID — including the ones that never read the header. - Protected resource operations — the ones whose matrix row gives the header
an effect — resolve the Sub-account before running: one that does not belong
to your authenticated Account is reported as
404, and an inactive one as409. Only then does the operation run in that scope, and what the scope changes is still per-operation. Consult the matrix above; do not infer404or409for a row that does not record them. - Token exchange validates a supplied Sub-account for existence and active
state (
404/409) but does not persist the result: the scope is never embedded in the returned access or refresh token, and has no effect on any later request. - Refresh and revoke do not read the header at all. A malformed value still
fails with
400from the global check, but a well-formed one is simply ignored — these operations never look a Sub-account up, so they answer neither404nor409for one.
Omitting the header is not an error: the request runs in the whole Account context. Every one of these failures is caller-correctable, so correct the value or the scope instead of retrying it unchanged. Account ownership is enforced either way, whether or not the header is sent.
See Authentication Errors and Validation Errors for general error handling.
Consistency across multi-step workflows
Keep Order, Shipment, Rate, and Label identifiers separate. For each step — create/list/read an Order, request Rates, buy a Label, then download or cancel it — consult the matrix and apply the header only where its row gives it an effect. A scope used for an Order does not make a later Label operation scoped automatically.
Security and production guidance
- Treat Sub-account identifiers as sensitive application identifiers.
- Do not log tokens or secrets.
- Validate application-side scope selection and apply least privilege in your application.
- Keep scope explicit in internal logs or audit context; do not cache one user's selected Sub-account globally.
- Derive behavior per operation from this matrix and the API Reference.
Next steps
- Account and Sub-account Scope
- Authentication
- Order-to-Label Workflow
- Get Shipping Rates
- Buy a Label
- Download a Label
- Cancel a Label
- API Reference
- API Playground guide — select a Sub-account in the browser tool and observe the carrier-availability effect on Rates and Label purchase against a configured Sandbox.