Scopes
Scopes limit what a ShipHero Public API token can read or change. They allow an application or AI agent to access only the areas required for its work.
For example, a token with view:orders can read orders. A token with change:orders can create or modify orders. If the token does not need designated personally identifiable information, omit view:pii and protected fields will return null.
This page covers:
- How scopes work
- Available scopes
- Using scopes with the ShipHero Public API Skill
- Protecting PII
- Requesting a scoped token
- Inspecting the granted scopes
- Refreshing or revoking a scoped token
How scopes work
ShipHero Public API scopes use two prefixes:
view:grants read-only access to an area of the account.change:grants permission to create, update, or delete data in an area of the account.
Most queries require a view: scope, while operations that modify data require a change: scope. Use the scope documented for each operation rather than relying only on whether the GraphQL operation is written as a query or mutation.
Grant the smallest set of scopes that an integration needs. A read-only integration should not receive change: scopes. An integration that does not need personal data should not receive view:pii.
The token must contain every scope required by an operation. If a token without view:orders calls the orders query, the API returns a permission error:
{
"errors": [
{
"message": "Missing required scope(s): view:orders",
"operation": "orders",
"field": "orders",
"request_id": "...",
"code": 7
}
],
"data": {
"orders": null
}
}Some operations require more than one scope. For example, the Experimental API operation that creates an inbound shipment from an Advance Shipment Notice requires both change:inbound_shipments and change:purchase_orders.
Important
Scope restrictions apply to tokens issued through the OAuth flow described on this page and to tokens requested by the ShipHero Public API Skill. Tokens generated through https://public-api.shiphero.com/auth/token do not enforce scopes and retain full API access available to the user.
Developer users
Scopes are not supported for developer users. A developer user’s token has full API access, and its permissions cannot be limited by selecting scopes.
Use the OAuth flow below when an application needs a token restricted to selected areas or protected personal data.
Available scopes
View scopes
| Scope | Allows access to |
|---|---|
view:accounts | Account details, the authenticated user’s account context, and identifier conversion |
view:billing | Bills, charges, labor units, and fulfillment invoices |
view:data_exports | Recurring data exports and LakeHero data exports |
view:inbound_shipments | Inbound shipments, receiving summaries, location summaries, and images |
view:inventory | Inventory, locations, inventory changes, inventory snapshots, synchronization status, and cycle counts |
view:labor | Labor activity, workers, jobs, and picker or packer performance |
view:lots | Expiration lots |
view:orders | Orders, order history, and orders that can be merged |
view:pii | Fields containing protected personally identifiable information |
view:products | Products and warehouse product records |
view:purchase_orders | Purchase orders |
view:returns | Returns and return exchanges |
view:shipments | Shipments, shipping containers, carriers, shipping methods, and label quotes |
view:shipping_plans | Shipping plans |
view:users | Users |
view:vendors | Vendors |
view:warehouse_ops | Totes, picking and packing activity, and box configurations |
view:webhooks | Webhook configuration and delivery logs |
view:wholesale_orders | Wholesale orders and staging location candidates |
view:work_orders | Work orders |
Change scopes
| Scope | Allows access to |
|---|---|
change:accounts | Create warehouse profiles |
change:billing | Create, update, submit, recalculate, or delete bills and manage charges |
change:inbound_shipments | Create or update inbound shipments |
change:inventory | Change inventory and manage locations, cycle counts, totes, boxes, snapshots, and license plate numbers |
change:lots | Create, update, delete, or assign lots |
change:orders | Create or modify orders, line items, tags, holds, fulfillment, history, attachments, and merge status |
change:products | Create or modify products, warehouse products, kits, and assemblies |
change:purchase_orders | Create or modify purchase orders, fulfillment status, and attachments |
change:returns | Create or modify returns and exchanges, receive returned items, and add attachments |
change:shipments | Create shipments, containers, shipping labels, and barcode labels, or remove and void labels |
change:shipping_plans | Create shipping plans |
change:users | Update users |
change:vendors | Create or delete vendors and manage their product associations |
change:webhooks | Create, update, enable, disable, or delete webhooks |
change:wholesale_orders | Create, update, fulfill, pick, stage, pack, label, schedule, and generate documents for wholesale orders |
change:work_orders | Create work orders, manage fees, and assign pick locations |
Some operations covered by these scopes are available only through the Experimental API, including labor activity, carrier and shipping method lookups, label quotes, label printing or voiding, and inbound shipment creation from an Advance Shipment Notice. A scope does not provide access to an Experimental API operation unless the account is eligible to use that endpoint.
Using scopes with the ShipHero Public API Skill
The ShipHero Public API Skill requests the OAuth scopes needed to complete a task. You can restrict the request in your prompt.
For an inventory lookup that does not need personal data, tell the agent:
Use the ShipHero Public API Skill to check inventory for SKU ABC-123.
Request only view:inventory. Do not request view:pii.For order analysis without personal data:
Use the ShipHero Public API Skill to summarize orders created today.
Request view:orders, but do not request view:pii or any change scope.The agent will open the ShipHero OAuth authorization flow when it needs a token. Review the requested scopes before approving access. Without view:pii, protected fields are unavailable to the agent and return null if requested.
Protecting PII
view:pii is a field-level scope. It applies to protected fields inside otherwise permitted queries. Omitting it does not block the entire query. The API returns the protected fields as null and adds details to extensions.scope_warnings.
Important
view:pii controls fields that ShipHero designates as protected PII. Omitting it is not a universal filter for every field that might identify a person. Request only the fields your application needs and handle personal data according to your security and privacy requirements.
Representative protected fields include:
- Order email addresses, tax identifiers, shipping and billing contact details, and third-party shipping account numbers
- Warehouse invoice email addresses, phone numbers, and address details
- Inbound shipment booking contacts and receiving worker names
- Vendor email addresses and account numbers
- User hourly rates
- Billing customer names and fulfillment invoice card information
- Return label recipient and address details
The set of protected fields can grow as the schema changes. Applications should handle null on fields that require view:pii.
Order example without view:pii
This token has view:orders, but it does not have view:pii:
query GetOrder($id: String!) {
order(id: $id) {
data {
id
order_number
fulfillment_status
email
}
}
}The order fields remain available, while email is null. The response explains why the protected field was omitted:
{
"data": {
"order": {
"data": {
"id": "T3JkZXI6MTIzNDU=",
"order_number": "1001",
"fulfillment_status": "pending",
"email": null
}
}
},
"extensions": {
"scope_warnings": {
"Order.email": {
"code": "FIELD_SCOPE_MISSING",
"message": "Field 'email' was not resolved because scope(s) 'view:pii' are missing",
"field_id": "Order.email",
"parent_type": "Order",
"field": "email",
"required_scopes": ["view:pii"],
"occurrences": 1
}
}
}
}Warehouse address example without view:pii
This query requires view:accounts. It requests protected contact fields and non-protected region fields in the same address:
query {
account {
data {
warehouses {
address {
name
state
country
phone
}
}
}
}
}Without view:pii, name and phone return null. state and country remain available:
{
"data": {
"account": {
"data": {
"warehouses": [
{
"address": {
"name": null,
"state": "TX",
"country": "US",
"phone": null
}
}
]
}
}
},
"extensions": {
"scope_warnings": {
"Address.name": {
"code": "FIELD_SCOPE_MISSING",
"required_scopes": ["view:pii"],
"occurrences": 1
},
"Address.phone": {
"code": "FIELD_SCOPE_MISSING",
"required_scopes": ["view:pii"],
"occurrences": 1
}
}
}
}The warning entries also include the message, field identifier, parent type, and field name. They are shortened in this second response for readability.
Requesting a scoped token
ShipHero uses the OAuth 2.0 Authorization Code flow with Proof Key for Code Exchange, or PKCE. This flow does not use a client secret.
Use an OAuth client that supports PKCE with the following configuration:
| Setting | Value |
|---|---|
| Authorization endpoint | https://login.shiphero.com/authorize |
| Token endpoint | https://login.shiphero.com/oauth/token |
| Revocation endpoint | https://login.shiphero.com/oauth/revoke |
| Client ID | GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z |
| Audience | shiphero-public-api |
| Redirect URI | http://localhost:3000/callback |
| Response type | code |
| PKCE method | S256 |
Your OAuth client must be able to receive the browser callback on port 3000 of the computer where the authorization starts.
1. Choose scopes
Build a space-separated scope value. Include the following OpenID Connect scopes:
openididentifies the authorization request as OpenID Connect.profilegrants access to the user’s basic profile claims.offline_accessrequests a refresh token. Omit it only if the application will not refresh access.
Add only the Public API scopes that the application requires.
For example, a read-only order and product integration without PII requests:
openid profile offline_access view:orders view:productsAn integration that must read order contact information requests:
openid profile offline_access view:orders view:piiThe authorization request uses these parameters:
| Parameter | Value |
|---|---|
client_id | GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z |
redirect_uri | http://localhost:3000/callback |
response_type | code |
audience | shiphero-public-api |
scope | The chosen space-separated scope value |
code_challenge | The challenge generated from the PKCE verifier |
code_challenge_method | S256 |
state | A random value generated for this authorization request |
2. Start authorization
Configure the authorization request with the values above. Your OAuth client must also:
- Generate a cryptographically random PKCE code verifier.
- Create the
S256code challenge from that verifier. - Generate a random
statevalue. - Send the chosen space-separated scopes in the
scopeparameter. - Open the authorization request in the user’s browser.
Sign in to ShipHero and review the requested access. After approval, ShipHero redirects the browser to:
http://localhost:3000/callbackThe callback contains an authorization code and the original state. The OAuth client must verify that the returned state matches the value sent in the authorization request.
3. Exchange the authorization code
Send the authorization code and the original PKCE verifier to the token endpoint as form data. AUTHORIZATION_CODE is the code returned in the callback. PKCE_CODE_VERIFIER is the value generated before authorization.
curl --request POST 'https://login.shiphero.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z' \
--data-urlencode "code=${AUTHORIZATION_CODE}" \
--data-urlencode 'redirect_uri=http://localhost:3000/callback' \
--data-urlencode "code_verifier=${PKCE_CODE_VERIFIER}"Do not send a client_secret. This is a public OAuth client and uses PKCE instead of a client secret.
A successful response contains the tokens, their lifetime, and the granted scopes:
{
"access_token": "<REDACTED>",
"refresh_token": "<REDACTED>",
"id_token": "<REDACTED>",
"scope": "openid profile offline_access view:orders view:products",
"expires_in": 2419200,
"token_type": "Bearer"
}Store access and refresh tokens securely. Do not place them in URLs, source control, logs, or customer support messages.
Use the access token to call the GraphQL API:
POST /graphql HTTP/1.1
Host: public-api.shiphero.com
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/jsonInspecting the granted scopes
The authorization request asks for scopes. The token response confirms which scopes ShipHero granted. Check the response’s scope value before using the token.
A token with view:accounts can also check its granted Public API scopes through GraphQL:
query {
me {
data {
scopes
}
}
}Example response:
{
"data": {
"me": {
"data": {
"scopes": [
"view:accounts",
"view:orders",
"view:products"
]
}
}
}
}The access token is a JWT. You can also decode its payload locally to inspect the scope claim. Decoding a JWT does not verify its signature, so applications must still validate tokens before trusting their claims. Do not paste a live token into a public decoding website.
This sanitized payload belongs to a read-only token that cannot access PII:
{
"iss": "https://login.shiphero.com/",
"sub": "auth0|example-user",
"aud": [
"shiphero-public-api",
"https://shiphero.auth0.com/userinfo"
],
"iat": 1787169736,
"exp": 1789588936,
"scope": "openid profile offline_access view:orders view:products",
"azp": "GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z"
}Because view:pii is absent, protected fields return null.
This sanitized payload includes permission to read order PII:
{
"iss": "https://login.shiphero.com/",
"sub": "auth0|example-user",
"aud": [
"shiphero-public-api",
"https://shiphero.auth0.com/userinfo"
],
"iat": 1787169736,
"exp": 1789588936,
"scope": "openid profile offline_access view:orders view:pii",
"azp": "GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z"
}Requesting a scope does not guarantee that it will be granted. If a required scope is absent from the token response, do not use the token for that operation. Correct the requested permissions and complete authorization again.
Refreshing or revoking a scoped token
Refresh a token
If the token has offline_access, send its refresh token to the token endpoint:
curl --request POST 'https://login.shiphero.com/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'client_id=GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z' \
--data-urlencode "refresh_token=${REFRESH_TOKEN}"Replace the stored access token with the new one. If the response includes a new refresh token, replace the previous refresh token as well.
Refreshing a token does not add scopes. Complete the authorization flow again to change the granted scopes.
Revoke access
Revoke a refresh token when the application no longer needs access or when a token may have been exposed:
curl --request POST 'https://login.shiphero.com/oauth/revoke' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=GcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z' \
--data-urlencode "token=${REFRESH_TOKEN}"After revocation, remove the stored access token, refresh token, and ID token. An access token that was already issued can remain valid until its expiration time.