Authentication Documentation
Secure your integration with TendedLoop using JWT tokens, API keys, OAuth single sign-on, or passwordless magic links.
Overview
TendedLoop supports four authentication methods, each designed for different use cases. Choose the method that best fits your integration scenario.
| Method | Best For | Token Lifetime |
|---|---|---|
| JWT Tokens | Web and mobile applications | 15 min (access) / 30 days (refresh) |
| API Keys | Server-to-server integrations | No expiry (revocable) |
| OAuth (SSO) | Enterprise single sign-on | Returns JWT tokens |
| Magic Links | Passwordless operator login | 15 min (link) / returns JWT |
JWT Authentication
JWT (JSON Web Token) is the primary authentication method for TendedLoop. Authenticate with your email and password to receive a short-lived access token and a long-lived refresh token.
Login
Send a POST request to /api/auth/login with your credentials. On success, you receive an access token (15-minute expiry) and a refresh token (30-day expiry).
cURL Login request
curl -X POST https://api.tendedloop.com/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "admin@yourcompany.com", "password": "your-password" }'
Response
{
"success": true,
"data": {
"user": {
"id": "clx1abc...",
"email": "admin@yourcompany.com",
"name": "Jane Admin",
"roles": ["ADMIN"]
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
}
Using the access token
Include the access token in the Authorization header of all subsequent requests:
curl https://api.tendedloop.com/api/dashboard/overview \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
Token refresh
When the access token expires, use the refresh token to obtain a new pair without re-authenticating:
curl -X POST https://api.tendedloop.com/api/auth/refresh \ -H "Content-Type: application/json" \ -d '{ "refreshToken": "eyJhbGciOiJIUzI1NiIs..." }'
Response
{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
JS Node.js example
const response = await fetch('https://api.tendedloop.com/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'admin@yourcompany.com', password: 'your-password', }), }); const { data } = await response.json(); const { accessToken, refreshToken } = data.tokens; // Use the access token for subsequent requests const dashboard = await fetch('https://api.tendedloop.com/api/dashboard/overview', { headers: { 'Authorization': `Bearer ${accessToken}` }, }); const result = await dashboard.json(); console.log(result.data);
API Keys
API keys provide a simple, long-lived authentication method ideal for server-to-server integrations, CI/CD pipelines, and backend services. Unlike JWT tokens, API keys do not expire automatically.
Creating an API key
- Navigate to Integrations > API Keys in the admin dashboard.
- Click Create API Key and assign a descriptive name.
- Select the scoped permissions (read, write, webhooks:manage).
- Copy the generated key immediately; it is only shown once.
Using an API key
Include your API key in the X-API-Key header:
curl https://api.tendedloop.com/api/dashboard/overview \
-H "X-API-Key: tl_k_a1b2c3d4e5f6..."
JS Node.js example
const response = await fetch('https://api.tendedloop.com/api/dashboard/overview', { headers: { 'X-API-Key': process.env.TENDEDLOOP_API_KEY, }, }); const { data } = await response.json(); console.log(data);
| Property | Details |
|---|---|
Header |
X-API-Key: <apiKey> |
| Expiry | No automatic expiry |
| Permissions | Scoped per key at creation time |
| Revocation | Revoke instantly from the dashboard |
OAuth (SSO)
TendedLoop supports Google and Microsoft OAuth for enterprise single sign-on. Users authenticate with their identity provider and receive JWT tokens for API access. On first login, a trial tenant is automatically created.
Google OAuth
Exchange a Google credential token for TendedLoop JWT tokens.
curl -X POST https://api.tendedloop.com/api/auth/google \ -H "Content-Type: application/json" \ -d '{ "credential": "eyJhbGciOiJSUzI1NiIs..." }'
Response
{
"success": true,
"data": {
"user": {
"id": "clx1abc...",
"email": "user@company.com",
"name": "Jane Smith",
"roles": ["ADMIN"]
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
},
"isNewUser": false
}
}
Microsoft OAuth
Exchange a Microsoft access token for TendedLoop JWT tokens.
curl -X POST https://api.tendedloop.com/api/auth/microsoft \ -H "Content-Type: application/json" \ -d '{ "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGci..." }'
Magic Links
Magic links provide passwordless authentication by sending a one-time login link via email. This is the primary auth method for operators and technicians who access TendedLoop from their mobile devices.
Step 1: Request a magic link
Send the user's email to request a magic link. The user receives an email with a unique login URL.
curl -X POST https://api.tendedloop.com/api/auth/magic-link/request \ -H "Content-Type: application/json" \ -d '{ "email": "technician@yourcompany.com" }'
Response
{
"success": true,
"data": {
"message": "Magic link sent to your email"
}
}
Step 2: Verify the token
When the user clicks the link in their email, the token is verified and JWT tokens are returned. The magic link token expires after 15 minutes.
curl https://api.tendedloop.com/api/auth/magic-link/verify?token=mlk_a1b2c3d4...
Response
{
"success": true,
"data": {
"user": {
"id": "clx1abc...",
"email": "technician@yourcompany.com",
"roles": ["TECHNICIAN"]
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
}
| Property | Details |
|---|---|
| Token expiry | 15 minutes after generation |
| Single use | Token is invalidated after first verification |
| No password required | User only needs access to their email inbox |
Role-Based Access
TendedLoop uses role-based access control (RBAC) to determine what each user can do. Users can hold multiple roles simultaneously, and the highest privilege wins when checking permissions.
| Role | Access Level | Capabilities |
|---|---|---|
| ADMIN | Full access | All features, settings, user management, billing, webhooks, API keys, routing rules |
| MANAGER | Read + analytics | Dashboards, feedback analytics, SLA monitoring, export, task management |
| TECHNICIAN | Task-only | View and complete assigned tasks, update task status, add task notes |
Multi-role users
A user can hold up to 3 roles (e.g., ["ADMIN", "MANAGER"]). The JWT payload includes a roles array. When an endpoint requires a specific role, TendedLoop checks if the user holds that role or a higher-privilege one.
// JWT payload structure { "userId": "clx1abc...", "tenantId": "clx2def...", "roles": ["ADMIN", "MANAGER"], "type": "access", "iat": 1707820000, "exp": 1707820900 }
Rate Limiting
Authentication endpoints are rate-limited to prevent brute-force attacks and abuse. When limits are exceeded, the API returns a 429 Too Many Requests response.
| Endpoint | Limit | Scope |
|---|---|---|
/api/auth/login |
Per email address | Prevents brute-force password attacks |
/api/auth/magic-link/request |
5 requests / 15 minutes | Per email address |
/api/auth/change-password |
Per user | Prevents password enumeration |
Rate limit response
{
"success": false,
"error": "Too many attempts. Please try again later.",
"code": "RATE_LIMITED"
}
X-System-Secret header with your system secret. This should never be used in production client-side code.
Error Codes
Authentication errors follow a consistent response format. Use the HTTP status code and the code field to handle errors programmatically.
Unauthorized
The request is missing a valid token, or the token has expired.
{
"success": false,
"error": "Invalid or expired token",
"code": "UNAUTHORIZED"
}
Fix: Refresh your access token using the refresh endpoint, or re-authenticate.
Forbidden
The token is valid, but the user does not have the required role to access this resource.
{
"success": false,
"error": "Insufficient permissions",
"code": "FORBIDDEN"
}
Fix: Ensure the authenticated user has the required role (ADMIN, MANAGER, or TECHNICIAN) for the endpoint.
Too Many Requests
Rate limit exceeded. Wait before retrying.
{
"success": false,
"error": "Too many attempts. Please try again later.",
"code": "RATE_LIMITED"
}
Fix: Implement exponential backoff in your retry logic. Avoid sending requests in tight loops.
Ready to integrate?
Start building with the TendedLoop API in minutes.