Account access

Updated May 26, 20265 min read

Carriyo separates who can access a tenant from what they can do. Who is the authenticated user or programmatic client. What is the combination of their role and the merchant / location scope on their access record. Both layers are enforced server-side, so the same rules apply whether someone is clicking around the Dashboard or firing requests at the API.

The next sections walk through how that plays out for Dashboard users first, then operators in the Fulfillment App, and finally programmatic API clients.

Signing in to the Dashboard

A user authenticates at the tenant level. The Dashboard prompts for their email, their identity provider verifies them, and Carriyo issues a session. Two identity-provider flavours are supported:

  • Password sign-in. Carriyo-managed credentials, the default for most tenants.
  • Single sign-on (SSO). SAML against the tenant's corporate identity provider. Enabled on the subscription as the Carriyo SSO add-on.

Whichever the provider, the same downstream access record applies: the user is bound to one or more tenants, and for each tenant, to a role plus a set of merchants and locations.

Roles

Roles are predefined and ship with the platform. Each role unlocks a set of capabilities (read, create, edit, delete per entity, plus action verbs like cancel, reassign, reprocess, retrigger):

RoleWhat it can do
ViewerRead-only across the merchants they're scoped to. No edits, no actions.
OperatorDay-to-day operational work: book shipments, reassign carriers, handle returns, manage manifests. Can't change configuration.
ManagerOperator plus configuration of merchant-level settings (CX, notification templates, products).
Account managerManager plus access to shared resources (locations, carrier accounts, shipping rules) across all merchants in the tenant. Typically the role for the customer's operations lead.
AdminAccount manager plus user management: invite users, assign roles, manage API clients.
IT ManagerViewer plus API client management. A narrow role for IT or integration owners who need to issue and rotate API credentials without broader operational access.
Super-adminReserved for the Carriyo team. End customers don't get this role.

Custom roles are possible on enterprise tenants, typically named for an org function (e.g. customer-service-manager, logistics-manager). A custom role is defined as a capability set plus a merchant / location scope.

Merchant and location scope

The role decides what kinds of actions the user can take. Scope decides which records those actions apply to.

Each access record carries:

  • An assigned merchant list. Empty means access to every merchant in the tenant; populated means restricted to that subset.
  • An optional assigned location list. Further narrows to specific fulfillment locations within the permitted merchants.
  • An optional country list. Some tenants further constrain by destination or pickup country.

These scopes apply to every entity the user touches. Per-merchant entities (orders, shipments, returns) are filtered by the merchant list before they reach the UI. Shared entities (locations, carrier accounts, rulesets) are shown only if the user has scope for at least one assigned merchant.

A common pitfall on multi-merchant tenants: creating a custom role without a merchant scope. The role is then effectively tenant-wide across every merchant, which usually isn't what the admin intended. Always pair a custom role with an explicit merchant list.

The Fulfillment App

The Fulfillment App is Carriyo's standalone operator app for warehouse floor work: picking, packing, dispatch. Its access model is similar to the Dashboard's but with one important constraint:

A Fulfillment App user is scoped to a single location at a time. The URL itself carries the active locationId, and everything the operator sees (fulfillment orders, picks, packs, stock) is filtered to that location.

A user can be granted access to multiple locations, but they switch context between them; they don't operate across locations simultaneously. Roles in the Fulfillment App are separate from the Dashboard roles. A user can be an admin on the Dashboard and a floor operator in the Fulfillment App, or hold access to only one of the two apps.

Programmatic access

Servers, integrations, and webhook receivers don't sign in interactively. They authenticate as API clients.

How API clients authenticate

API clients use OAuth 2.0 client credentials. Each client is provisioned for a specific tenant and gets:

  • A client_id (public).
  • A client_secret (rotate-able; not shown again after creation).
  • A scope set that controls what the client can do.

The client exchanges its credentials at the token endpoint for a short-lived access token, which it then sends as Authorization: Bearer <token> on every API request. The platform verifies the token against the issuing identity provider on every call.

Scopes

Permissions on the token are expressed as scopes, in three families:

  • Resource scopes. <verb>:<resource> pairs that gate which operations the client can perform. The verb is the HTTP method (read for GET, create for POST, update for PUT/PATCH, delete for DELETE) and the resource is the first path segment (shipments, orders, returns, webhooks, etc.). Example: read:shipments, create:orders, update:returns.
  • Tenant scope. tenant:<TENANT_ID> pins the client to a single tenant.
  • Merchant scopes. merchant:<MERCHANT_ID> for each merchant the client is allowed to act on, or merchant:ACCOUNT to grant account-level access (all merchants plus shared entities). The merchant scopes are projected into the downstream merchant request header.

A client typically holds exactly one tenant scope, one or more merchant scopes, and a list of resource scopes covering the operations the integration needs.

x-api-key is not authentication

Carriyo's API gateway issues an x-api-key per tenant for rate limiting and usage-plan enforcement only. It does not authenticate the caller. Requests must carry both the x-api-key header (for rate limiting) and the Bearer token (for authentication and authorization). UI-typed keys (used by the Dashboard's own calls) are rejected on machine-to-machine endpoints.

Client limits and lifecycle

The number of active API clients per tenant is governed by the subscription's api_client quota. Clients can be paused (INACTIVE), have their secret rotated, or have their scope set edited after creation. Deleting a client revokes its tokens immediately.

How scoping is enforced

Whether the caller is a user or an API client, the same enforcement machinery applies on every request:

  1. The token is verified (signature, expiry, issuer).
  2. The caller's tenant is pinned from the token's tenant claim.
  3. The caller's permitted merchant list is projected into a downstream merchant header.
  4. Per-merchant entities are filtered by that header before they reach the response.
  5. Shared-entity writes are validated against the caller's scope. For example, an operator can't reassign a shipment to a carrier account whose merchant overlap doesn't include the caller's merchant set.

The filtering happens server-side at every layer. There is no client-side trust. A token with no merchant scope is treated as having no access, not as having universal access.