Skip to main content

Authentication and Token Lifecycle

The public token journey is:

API key
-> POST /api/v1/auth/token
-> access token + refresh token
-> Bearer requests
-> POST /api/v1/auth/token/refresh
-> rotated token pair
-> POST /api/v1/auth/token/revoke

An API key is not a Bearer token. Keep API keys, access tokens, and refresh tokens out of URLs, browser bundles, source control, and logs.

Exchange an API key for a token pair

POST/api/v1/auth/token Open token exchange in the API Reference.

This anonymous operation accepts the API key in its JSON request body. Use the configured base URL and synthetic environment-variable values in examples:

curl -X POST "${MAILHUB_API_BASE_URL}/api/v1/auth/token" \
-H "Content-Type: application/json" \
-d '{"apiKey":"'"${MAILHUB_API_KEY}"'"}'

A successful response uses the public envelope and returns the values to retain for the session:

{
"success": true,
"data": {
"accountId": "<account-id>",
"apiKeyId": "<api-key-id>",
"accessToken": "<access-token>",
"refreshToken": "<refresh-token>",
"accessTokenExpiresAtUtc": "<utc-timestamp>",
"refreshTokenExpiresAtUtc": "<utc-timestamp>"
},
"errors": null
}

Retain the access token, refresh token, and their response expiry fields. The response fields are the source of truth; this portal does not publish token lifetime values.

Use the access token

Send the access token on protected public operations:

Authorization: Bearer <access-token>

Refresh once, then re-authenticate

POST/api/v1/auth/token/refresh Open token refresh in the API Reference.

Send the retained refresh token in the JSON request body. Like token exchange, this operation is anonymous — it carries no Authorization header.

curl -X POST "${MAILHUB_API_BASE_URL}/api/v1/auth/token/refresh" \
-H "Content-Type: application/json" \
-d '{"refreshToken":"'"${MAILHUB_REFRESH_TOKEN}"'"}'

A successful refresh returns the same fields as token exchange — a new access token and a new refresh token:

{
"success": true,
"data": {
"accountId": "<account-id>",
"apiKeyId": "<api-key-id>",
"accessToken": "<new-access-token>",
"refreshToken": "<new-refresh-token>",
"accessTokenExpiresAtUtc": "<utc-timestamp>",
"refreshTokenExpiresAtUtc": "<utc-timestamp>"
},
"errors": null
}

After a successful refresh, replace both stored tokens with the returned access token and refresh token before making another request. For a failed protected request, make at most one controlled refresh attempt. If refresh fails, re-authenticate with the API key. Do not create an infinite refresh or retry loop.

A failed refresh uses the standard error envelope — 400 for a request that could not be read, 401 for a refresh token that is missing, malformed, expired, or already revoked, and 422 when the supplied value does not pass validation. See Authentication Errors.

Revoke when access should end

POST/api/v1/auth/token/revoke Open token revocation in the API Reference.

Revocation takes the same anonymous, refresh-token body as refresh does.

curl -X POST "${MAILHUB_API_BASE_URL}/api/v1/auth/token/revoke" \
-H "Content-Type: application/json" \
-d '{"refreshToken":"'"${MAILHUB_REFRESH_TOKEN}"'"}'

A successful revocation returns the bounded operation result and nothing else:

{
"success": true,
"data": {
"isSuccess": true
},
"errors": null
}

Revoke the refresh session when access should end. Check the public response before discarding your locally retained credentials.

Sub-account scoping

The Sub-account must belong to the authenticated Account. Use the same optional scope consistently for one workflow, but do not assume it has one uniform effect across every operation:

  • Token exchange validates the supplied Sub-account but does not persist it in either token.
  • List Orders filters the list, and Order creation tags the new Order.
  • Rates and Label purchase evaluate the request against the selected Sub-account's carrier availability, which can narrow the carriers on offer. It does not change pricing, and it does not restrict record access.
  • Get/update an Order, download/cancel a Label, and refresh/revoke have no effect from a well-formed header.

A malformed header can return 400. Token exchange can return 404 when the Sub-account is unavailable to the Account and 409 when it is inactive. See Work with Sub-accounts for the complete operation-by-operation matrix and the API Reference for each operation's current header description.

Next step

Continue to Make Your First Request for the safe exchange-then-list flow.