Account access
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):
| Role | What it can do |
|---|---|
| Viewer | Read-only across the merchants they're scoped to. No edits, no actions. |
| Operator | Day-to-day operational work: book shipments, reassign carriers, handle returns, manage manifests. Can't change configuration. |
| Manager | Operator plus configuration of merchant-level settings (CX, notification templates, products). |
| Account manager | Manager 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. |
| Admin | Account manager plus user management: invite users, assign roles, manage API clients. |
| IT Manager | Viewer 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-admin | Reserved 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 (readfor GET,createfor POST,updatefor PUT/PATCH,deletefor 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, ormerchant:ACCOUNTto grant account-level access (all merchants plus shared entities). The merchant scopes are projected into the downstreammerchantrequest 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:
- The token is verified (signature, expiry, issuer).
- The caller's tenant is pinned from the token's tenant claim.
- The caller's permitted merchant list is projected into a
downstream
merchantheader. - Per-merchant entities are filtered by that header before they reach the response.
- 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.