Skip to content
Need help with our api? Ask in Community
Scopes

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

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

ScopeAllows access to
view:accountsAccount details, the authenticated user’s account context, and identifier conversion
view:billingBills, charges, labor units, and fulfillment invoices
view:data_exportsRecurring data exports and LakeHero data exports
view:inbound_shipmentsInbound shipments, receiving summaries, location summaries, and images
view:inventoryInventory, locations, inventory changes, inventory snapshots, synchronization status, and cycle counts
view:laborLabor activity, workers, jobs, and picker or packer performance
view:lotsExpiration lots
view:ordersOrders, order history, and orders that can be merged
view:piiFields containing protected personally identifiable information
view:productsProducts and warehouse product records
view:purchase_ordersPurchase orders
view:returnsReturns and return exchanges
view:shipmentsShipments, shipping containers, carriers, shipping methods, and label quotes
view:shipping_plansShipping plans
view:usersUsers
view:vendorsVendors
view:warehouse_opsTotes, picking and packing activity, and box configurations
view:webhooksWebhook configuration and delivery logs
view:wholesale_ordersWholesale orders and staging location candidates
view:work_ordersWork orders

Change scopes

ScopeAllows access to
change:accountsCreate warehouse profiles
change:billingCreate, update, submit, recalculate, or delete bills and manage charges
change:inbound_shipmentsCreate or update inbound shipments
change:inventoryChange inventory and manage locations, cycle counts, totes, boxes, snapshots, and license plate numbers
change:lotsCreate, update, delete, or assign lots
change:ordersCreate or modify orders, line items, tags, holds, fulfillment, history, attachments, and merge status
change:productsCreate or modify products, warehouse products, kits, and assemblies
change:purchase_ordersCreate or modify purchase orders, fulfillment status, and attachments
change:returnsCreate or modify returns and exchanges, receive returned items, and add attachments
change:shipmentsCreate shipments, containers, shipping labels, and barcode labels, or remove and void labels
change:shipping_plansCreate shipping plans
change:usersUpdate users
change:vendorsCreate or delete vendors and manage their product associations
change:webhooksCreate, update, enable, disable, or delete webhooks
change:wholesale_ordersCreate, update, fulfill, pick, stage, pack, label, schedule, and generate documents for wholesale orders
change:work_ordersCreate 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:

SettingValue
Authorization endpointhttps://login.shiphero.com/authorize
Token endpointhttps://login.shiphero.com/oauth/token
Revocation endpointhttps://login.shiphero.com/oauth/revoke
Client IDGcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z
Audienceshiphero-public-api
Redirect URIhttp://localhost:3000/callback
Response typecode
PKCE methodS256

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:

  • openid identifies the authorization request as OpenID Connect.
  • profile grants access to the user’s basic profile claims.
  • offline_access requests 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:products

An integration that must read order contact information requests:

openid profile offline_access view:orders view:pii

The authorization request uses these parameters:

ParameterValue
client_idGcdtO6dRNDOWmmoqa5vtGHlSTjeG3I0Z
redirect_urihttp://localhost:3000/callback
response_typecode
audienceshiphero-public-api
scopeThe chosen space-separated scope value
code_challengeThe challenge generated from the PKCE verifier
code_challenge_methodS256
stateA random value generated for this authorization request

2. Start authorization

Configure the authorization request with the values above. Your OAuth client must also:

  1. Generate a cryptographically random PKCE code verifier.
  2. Create the S256 code challenge from that verifier.
  3. Generate a random state value.
  4. Send the chosen space-separated scopes in the scope parameter.
  5. 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/callback

The 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/json

Inspecting 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.