Ason supports a few different ways to authenticate, depending on who's calling: a human exploring the API, your backend calling on behalf of your platform, or configuring Ason to use your own IAM.
A User is a person with access to a Tenant - invited by email, with a role that determines what they can see and do. Users are separate from the System User your backend uses for machine-to-machine calls.
- Create tenants with your System User, not a personal account
- To give someone access: invite them (POST
/tenants/{orgId}/user_memberships/invitewith email + role) rather than creating a User directly - POST/usersexists but isn't usable by a System User token (returns 403). This invite flow is the only way in, even for someone who already has an Ason User on another tenant - there's no way to attach an existing user without inviting them - Accepting the invitation is what creates the User and membership - the invitee does that, not you, either through the link they're given (in the sandbox:
https://sandbox.asonpayroll.com/invitation/{invitationId}) or programmatically via POST/user_membership_invitations/{invitationId}with the invitee's own token - No cross-tenant linking: invite and accept separately per tenant - no bulk or shared invite exists
- Who can invite whom: an Owner can invite someone as Admin
- Removing a user: DELETE
/tenants/{orgId}/user_memberships/{userMembershipId}removes tenant access - deleting the user itself isn't possible - Changing name, email or password: not through Ason - those belong to your IAM provider. The only thing a user can change in Ason itself is their language/culture setting (PUT
/users/{userId}) - 2FA: governed entirely by your IAM/OIDC provider (e.g. Zitadel), not Ason
Roles
A role determines what a User can see and do:
- Administrator - full access to the whole tenant
- Manager - access limited to the specific payrolls they're assigned to
- Self Service - access limited to their own employee record only (linked to a specific Employee), landing directly on their own ESS documents
- Changing a role later: yes - PUT
/tenants/{orgId}/user_memberships/{userMembershipId}updates the role (and which employee it's linked to, for Self Service)
Interactive login (human)
The Quickstart and every Playground button here log you in interactively - a browser redirect to Ason's identity provider, a human clicking "Login". This is also how a User logs into the frontend day to day: right for exploring the API or for HR staff, wrong for your production backend, which needs to call Ason without a human in the loop - use a System User for that.
The browser redirects to Zitadel for the human to authenticate, then calls the Ason API with the resulting token - the same Bearer-token shape every request uses, interactive or not.
System User: machine-to-machine via Zitadel
Ason's identity provider is Zitadel. Interactive Users log in through Zitadel's normal OIDC flow, refreshing automatically like any browser login. Machine-to-machine access works differently - it's not a self-service OAuth2 flow you run yourself.
The flow:
- Ason provisions a system user for your tenant in Zitadel and generates a machine-to-machine token for it.
- Ason sends you that token directly - your backend never calls Zitadel itself.
- Your backend calls the Ason API with
Authorization: Bearer <token>- the same header every request uses. Ason's API treats a system user and a logged-in human identically: both are just a valid Zitadel token.
Ason talks to Zitadel on your behalf and hands you a token directly - the step that's conspicuously absent is your backend ever calling Zitadel itself. From there on, it's the same Bearer-token call as any interactive request.
Getting a system user
System users are provisioned by Ason, not self-service through the API - talk to us to get a token issued for your tenant.
Token lifetime
- The system user token is permanent - it doesn't expire, so there's no refresh flow to build. Store it once (config or secrets store) and reuse it.
- Interactive User sessions differ - they go through Zitadel's normal OIDC flow and refresh automatically
Should you also create Ason users?
- A system user authenticates your backend, not your end users - it doesn't decide what an individual employee or HR admin can see
- Whether you're embedding Ason or running its frontend as-is, the pattern is the same: your platform's IAM decides who can do what, your backend enforces it, and the backend (as the system user) talks to Ason on the user's behalf
- You generally don't need one Ason user per end user
Direct IAM Integration
Instead of proxying all requests through your backend to enforce access control, you can configure Ason to validate tokens directly against your own IAM.
End users authenticate straight against Ason using your own IAM's token - Ason validates it directly, no Zitadel or system-user hop involved. Your backend only shows up separately, out of band, to keep Ason's users, tenants and permissions in sync with your IAM.
- Requirements: a dedicated Ason PROD instance in Ason's Azure environment, configured to trust your IAM - a deeper integration than the system-user flow above, talk to us to set it up
- Result: a fully integrated authentication setup with centralized user management - Ason users and their permissions are set up directly in your dedicated instance, alongside your own IAM
Which one should I use?
- Exploring the API, or using the frontend as a human - interactive login, nothing to set up.
- Your backend calling Ason on behalf of your platform - System User. The recommended default for most integrations.
- Your own IAM should be the single source of truth Ason trusts directly - Direct IAM Integration, on a dedicated instance.
Where this shows up
- Features - what you can build on top of Ason, embedded or with its ready-to-use frontend
- API Reference - every endpoint accepts a system user token exactly like an interactive one
API Endpoints
| What | Call |
|---|---|
| Get the current user | GET /users |
| Update a user | PUT /users/{userId} |
| Create a user (not usable by a System User token in practice) | POST /users |
| List user memberships (who has access) | GET /tenants/{orgId}/user_memberships |
| List pending invitations | GET /tenants/{orgId}/user_memberships/invitations |
| Update a member's role | PUT /tenants/{orgId}/user_memberships/{userMembershipId} |
| Remove a member | DELETE /tenants/{orgId}/user_memberships/{userMembershipId} |
| Invite a user | POST /tenants/{orgId}/user_memberships/invite |
| Withdraw an invitation | DELETE /tenants/{orgId}/user_memberships/invitations/{invitationId} |
| Get invitation details | GET /user_membership_invitations/{invitationId} |
| Accept an invitation | POST /user_membership_invitations/{invitationId} |
