An overview of the SAVHN Developer Portal: the authentication flow, how API domains map to modules, how to obtain and use a bearer token, and how token scope is enforced.
Checking access...The SAVHN Developer Portal is the entry point for anyone building against the platform's REST API โ whether that's an internal integration, a partner-built extension, or a script automating a repetitive task. This page covers how authentication works and how the API surface is organized.
What the Developer Portal gives you
- Access credentials management (generating and revoking API tokens/keys for your organization).
- A categorized reference of every API domain (see the full API Reference page for the complete table).
- Environment information โ which base URL to target for your organization.
- Usage guidance for webhooks and common integration patterns (see Building Integrations with SAVHN).
Developer Portal access is itself permission-gated: only users with a role that includes developer/API access (typically Org Admins or a dedicated "Integration Manager" custom role) can generate credentials.
Authentication model
SAVHN's API uses bearer token authentication over HTTPS. There is no session-cookie mode for API clients โ every request outside the browser app must carry a valid bearer token.
Step 1: Obtain a token
Tokens are obtained by authenticating against the login endpoint with valid user credentials, or โ for long-lived server-to-server integrations โ by generating a dedicated API key from the Developer Portal, which is exchanged for tokens the same way.
POST /api/auth/login
Content-Type: application/json
{
"email": "integration-user@yourorg.com",
"password": "โขโขโขโขโขโขโขโข"
}
A successful response returns a bearer token to use on subsequent requests:
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"user": {
"id": "usr_2f9c1a",
"email": "integration-user@yourorg.com",
"orgId": "org_7b21d4"
}
}
}
Step 2: Use the token on every request
GET /api/crm/leads
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Every API domain โ /api/crm, /api/hrms, /api/finance, and so on โ expects the same Authorization: Bearer <token> header. There is no per-module authentication scheme; one token grants access to every domain your account is permitted to reach.
Step 3: Handle expiry and refresh
- Tokens are short-lived by design. When a token expires, requests receive a
401 Unauthorizedresponse and the client should re-authenticate. - For unattended integrations, prefer a dedicated API key generated from the Developer Portal over a human user's credentials, so token issuance doesn't depend on a person's password and can be revoked independently without disrupting that person's own login.
How tokens scope access
A bearer token inherits the exact permission set of the user (or API key identity) it was issued for:
- It is scoped to a single organization โ it cannot read or write data belonging to another tenant, regardless of what endpoint is called.
- It is scoped to that identity's role permissions โ an integration using a "Reports Viewer" identity cannot call write endpoints on Finance even with a valid token, and receives
403 Forbidden. - Module-level gating still applies โ if a module isn't enabled for the organization, its endpoints return
404 Not Foundor403 Forbiddenregardless of token validity.
This means the same RBAC model described in Role Management & Permissions governs API access, not a separate scopes system layered on top. This is a deliberate design choice: it keeps "what can this identity do" answerable in one place.
How the API is organized
Every module maps predictably to a REST domain under /api/. For example:
| Module | API domain |
|---|---|
| CRM | /api/crm |
| HRMS | /api/hrms |
| Payroll | /api/payroll |
| Finance | /api/finance |
| Projects | /api/projects |
| Tickets | /api/tickets |
| Meetings | /api/meetings |
| Documents | /api/documents |
| Client Portal | /api/client-portal |
Beyond the core business domains, there are dedicated domains for AI & Intelligence modules (/api/ai-studio, /api/ai-assistant, /api/business-brain, etc.), Industry Cloud-specific endpoints, and platform-level domains for content and operations. The full categorized list lives on the API Reference page โ start there once you're ready to build.
Choosing between a user token and a dedicated API key
The Developer Portal supports two ways of obtaining a bearer token, and picking the right one matters for maintainability:
| Approach | Best for | Trade-off |
|---|---|---|
User credential login (POST /api/auth/login with a person's email/password) |
Ad hoc scripts, local testing, short-lived exploration | Tied to a real person's account โ breaks if their password changes or they leave the organization |
| Dedicated API key generated in the Developer Portal | Any integration expected to run unattended or long-term | Independently named, scoped, and revocable; doesn't depend on any one person's login |
For anything beyond a quick one-off script, generate a dedicated API key tied to a purpose-built identity (e.g. "Integration: Finance sync") rather than a real employee's login, so that person leaving the organization or changing their password doesn't silently break production integrations.
Testing your authentication flow
Before building further, confirm the basic flow works end to end:
- Call
POST /api/auth/login(or exchange your generated API key) and confirm you receive a200with adata.tokenfield. - Call a low-risk read endpoint, such as
GET /api/users/me, with the token attached, and confirm you get your own identity's profile back rather than a401. - Deliberately call an endpoint you expect to be denied (e.g. a Payroll write endpoint using a Reports-only identity) and confirm you get
403 Forbiddenrather than succeeding โ this validates that scoping is working as expected before you build real logic around it.
Generating credentials
- Go to Developer Portal โ API Credentials.
- Click Generate New Key, name it descriptively (e.g. "Zapier integration โ invoices"), and choose the role/identity it should act as.
- The full key/secret is shown once at creation time โ store it securely, as it isn't retrievable again (you would need to revoke and regenerate).
- Use Revoke on any key immediately if it's compromised or no longer needed; revocation takes effect immediately and invalidates any tokens already issued from it.
Reading API errors effectively
Every error response follows the same envelope regardless of which domain produced it, which makes building a single error-handling path in your integration straightforward:
{
"success": false,
"error": {
"code": "PERMISSION_DENIED",
"message": "This identity does not have access to payroll:write.",
"requiredPermission": "payroll:write"
}
}
When debugging an unexpected error, check three things in order: (1) is the token itself valid and unexpired, (2) does the identity behind the token have the required role permission for the action, (3) is the target module actually enabled for the organization. In practice, the large majority of 401/403/404 responses trace back to one of these three, in that order of likelihood.
A minimal end-to-end example
Putting the pieces together, a minimal script that authenticates and reads a resource looks like this in outline form:
1. POST /api/auth/login with credentials -> receive bearer token
2. Store the token in memory (never persist it to disk in plaintext)
3. GET /api/crm/leads with Authorization: Bearer <token>
4. Handle 401 by re-authenticating; handle 403 by checking role/module state;
handle 429 by backing off per Retry-After
5. Parse `data` from the success envelope for your actual payload
This same shape applies regardless of which domain you're calling โ the authentication step happens once, and every subsequent request across every module reuses the same token until it expires.
Rate limits and good citizenship
- API usage is subject to per-organization rate limits to keep the platform responsive for all tenants; a
429 Too Many Requestsresponse includes aRetry-Afterheader. - Prefer webhooks over polling wherever a webhook exists for the event you care about (see Building Integrations with SAVHN) โ polling loops are the most common cause of avoidable rate-limit friction.
Next steps
Continue to Building Integrations with SAVHN for webhook patterns and common integration scenarios, or jump directly to the API Reference for the full endpoint catalog and REST conventions.
Related Modules
Stuck on this step? The team that built it can help.