Skip to main content

Customers and Staff as One User

Customers and staff are not separate entities in Saleor. Both are represented by the same User GraphQL type. The role of a given user is determined by the isStaff field and by which mutations and permissions apply to them.

This page explains how the two roles relate at the API level: which fields are meaningful for each, which mutations to use to create or manage them, and how a user can move between roles.

One type, two roles​

Every account — whether a shopper or an administrator — is a User. The same fields (id, email, firstName, lastName, addresses, metadata, orders, etc.) are available on both. The difference is:

  • isStaff: Boolean! — true for staff members, false for customers. It is exposed publicly on the User type and is also included as a claim in the JWT issued by tokenCreate.
  • Role-specific fields — see below.
  • Permissions required to manage them — MANAGE_USERS for customers, MANAGE_STAFF for staff.

Fields that are meaningful only for staff​

The following fields exist on every User but are only populated for staff. For customers they return empty lists or false:

FieldMeaning
permissionGroupsPermission groups the user belongs to. Customers cannot belong to permission groups via the public API.
userPermissionsEffective permissions granted directly or via groups.
editableGroupsGroups the user is allowed to manage (used by the Dashboard).
accessibleChannelsChannels the staff user has access to via their permission groups.
restrictedAccessToChannelsWhether the staff user's access is limited to a subset of channels.

See Permissions for how permission groups and channel restrictions work.

Choosing a create mutation​

There are three mutations that produce a User. Pick the one that matches the caller and the resulting role:

MutationCallerRequired permissionResulting role
accountRegisterThe user themselves (storefront)NoneCustomer
customerCreateStaff or appMANAGE_USERSCustomer
staffCreateStaff or appMANAGE_STAFFStaff (isStaff: true)

accountRegister and customerCreate both produce a customer; the difference is who initiates the action and whether a password is set up-front or via an emailed link. See Manage Account as a Customer and Customer Management.

Promoting a customer to staff​

If staffCreate is called with an email that already belongs to a customer, the existing User is reused and promoted to staff. The same id, addresses, metadata, and order history are preserved — only isStaff flips to true and any provided groups are assigned.

There is no public mutation to demote a staff user back to a customer. To revoke staff access, use staffDelete or remove the user from all permission groups via staffUpdate.

End-to-end lifecycle: self-registration, then staff invite​

A common flow that causes confusion: a shopper signs up through the storefront, then is later invited by an administrator to become staff. Because both roles share the same User, the order history, addresses, and metadata gathered as a customer carry over after the promotion.

The table below lists, for each mutation in this flow, the asynchronous events emitted and how the corresponding email is delivered. The split is important:

  • Staff emails (invite / set password / password reset for staff) are sent by the core admin email plugin when it is configured. No external app is required.
  • Customer emails (account confirmation, set password, password reset for customers) are not sent by Saleor core. An external app must subscribe to the corresponding webhook and deliver the email itself — typically using the SMTP App or any other app you build or install.
#MutationAsync events emittedEmail
1accountRegisterCUSTOMER_CREATED; additionally ACCOUNT_CONFIRMATION_REQUESTED if enableAccountConfirmationByEmail is trueCustomer confirmation email. Not sent by core — requires an external app (e.g. SMTP App) subscribed to ACCOUNT_CONFIRMATION_REQUESTED.
2confirmAccountACCOUNT_CONFIRMEDNo email required for the confirmation step itself. If you want a "welcome" email, subscribe an external app to ACCOUNT_CONFIRMED.
3accountUpdate (customer edits own profile)CUSTOMER_UPDATED; CUSTOMER_METADATA_UPDATED if metadata changedNone by default.
4requestPasswordReset (while still a customer)ACCOUNT_SET_PASSWORD_REQUESTEDCustomer password reset email. Not sent by core — requires an external app subscribed to ACCOUNT_SET_PASSWORD_REQUESTED.
5setPasswordNo webhook event is emitted by this mutation itself.No email.
6staffCreate called with the existing customer's emailSTAFF_CREATED (the same User.id is reused; isStaff becomes true). Additionally STAFF_SET_PASSWORD_REQUESTED if redirectUrl is provided.Staff invite / set password email. Sent by the core admin email plugin when configured. The customer-facing ACCOUNT_SET_PASSWORD_REQUESTED event is not emitted in this case, so an external customer-email app does not receive a duplicate.
7staffUpdateSTAFF_UPDATEDNone by default.
8requestPasswordReset (now as a staff user)STAFF_SET_PASSWORD_REQUESTEDStaff password reset email. Sent by the core admin email plugin when configured.
9setPasswordNo webhook event is emitted by this mutation itself.No email.
Customer emails require an external app

Saleor core ships an "admin email" plugin that covers staff notifications (staff invite, staff password reset, etc.) out of the box. It does not send emails to customers. To deliver account confirmation, customer password reset, order confirmation, and other customer-facing emails, install an app such as the SMTP App and subscribe it to the relevant ACCOUNT_* and CUSTOMER_* webhook events.

Which event fires depends on isStaff at the time of the request

requestPasswordReset branches on the user's current role: it emits ACCOUNT_SET_PASSWORD_REQUESTED for customers and STAFF_SET_PASSWORD_REQUESTED for staff. Once a user has been promoted via staffCreate, all subsequent password-related notifications go through the staff event and the admin email plugin.

Choosing an update or delete mutation​

Update and delete mutations are split along the same lines as creation:

ActionSelf-serviceCustomer (by staff)Staff (by staff)
UpdateaccountUpdatecustomerUpdatestaffUpdate
DeleteaccountDelete, accountRequestDeletioncustomerDeletestaffDelete

The account* mutations always operate on the currently authenticated user and never change the user's role. The customer* and staff* mutations require the matching permission and only operate on users of the corresponding role.

Querying customers and staff​

The API exposes role-scoped list queries and a single by-identity lookup:

  • customers — requires MANAGE_USERS or MANAGE_ORDERS. Returns customer accounts. Staff users who have placed an order also appear in this list, because order history is tied to the underlying User.
  • staffUsers — requires MANAGE_STAFF. Returns staff accounts only.
  • user — lookup by id, email, or externalReference. The result is scoped by the caller's permissions: MANAGE_STAFF resolves staff, MANAGE_USERS or MANAGE_ORDERS resolves customers, holding both resolves any user.
  • me — returns the currently authenticated user regardless of role.

Dual-role behavior​

Because a staff member and a customer are the same kind of entity, a single User can act in both roles at once. A staff user who places an order keeps their isStaff: true but will also be returned by the customers query. Their order history, addresses, and metadata are all on the same record.

When building integrations, treat the role as a property of the user (isStaff) rather than as a separate identity. Do not assume that "appears in customers" implies "is not staff".

Authentication​

Customers and staff authenticate against the same endpoints and receive tokens of the same shape:

  • tokenCreate issues an access token and refresh token for either role. The decoded token includes the is_staff claim, but permissions are not embedded — they are resolved on each request from the user's groups and direct permissions.
  • tokenRefresh, tokenVerify, and tokensDeactivateAll work the same for both roles.

The passwordLoginMode shop setting can restrict password login for staff while leaving it available for customers. See Authentication for the full token lifecycle and JWT structure.

Permission groups are staff-only​

Permission groups (permissionGroupCreate, permissionGroupUpdate, permissionGroupDelete) require MANAGE_STAFF and only operate on staff members. Customers cannot be added to a permission group through the public API, and the permissionGroups field on a customer's User will always be empty.

If you need to grant a customer access to staff capabilities, promote them to staff via staffCreate first.