API Response Formats
API Version: 5.24.0
OAuth 2.0 Token Response
Response from POST /api/oauth/token endpoint:
// Success Response (200 OK)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NTBlODQw...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NTBlODQw...",
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NTBlODQw..."
}
// Field descriptions:
// access_token - JWT for API authentication (1 hour expiry)
// token_type - Always "Bearer"
// expires_in - Seconds until access_token expires
// refresh_token - JWT for refreshing access token (30 days)
// id_token - JWT containing user identity and roleJWT Token Structures
Access Token Payload
Decoded JWT structure for access_token:
// Header
{
"alg": "HS256",
"typ": "JWT"
}
// Payload
{
"sub": "550e8400-e29b-41d4-a716-446655440000", // User UUID
"iss": "https://sso.doneisbetter.com", // Issuer
"aud": "your-client-id", // Audience (your app)
"exp": 1734274936, // Expiration (Unix timestamp)
"iat": 1734271336, // Issued at (Unix timestamp)
"jti": "token-unique-id" // JWT ID (for revocation)
}
// Usage:
Authorization: Bearer <access_token>
// Validation:
// - Check exp > current time
// - Verify iss matches expected issuer
// - Verify aud matches your client_idID Token Payload
Decoded JWT structure for id_token (contains user info + app role):
// Header
{
"alg": "HS256",
"typ": "JWT"
}
// Payload
{
"sub": "550e8400-e29b-41d4-a716-446655440000", // User UUID
"email": "user@example.com",
"name": "John Doe",
"role": "admin", // App-level role: 'user' or 'admin'
"iss": "https://sso.doneisbetter.com",
"aud": "your-client-id",
"exp": 1734274936,
"iat": 1734271336
}
// Usage:
// Decode (no verification needed if from /token endpoint)
const jwt = require('jsonwebtoken');
const userInfo = jwt.decode(id_token);
console.log(userInfo.email, userInfo.role);
// Store in your app's session
req.session.userId = userInfo.sub;
req.session.email = userInfo.email;
req.session.role = userInfo.role; // Use for authorizationRefresh Token Payload
Decoded JWT structure for refresh_token:
// Opaque to clients - treat as string, don't decode
// Internal structure (for reference only):
{
"sub": "550e8400-e29b-41d4-a716-446655440000",
"type": "refresh",
"client_id": "your-client-id",
"iss": "https://sso.doneisbetter.com",
"exp": 1736863336, // 30 days from issuance
"iat": 1734271336,
"jti": "refresh-token-unique-id"
}
// Usage:
// POST /api/oauth/token with grant_type=refresh_token
// Returns new access_token (and may rotate refresh_token)Standard API Response Format
Public and admin API endpoints return JSON in this format:
Success Response
// Example: POST /api/public/register
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "John Doe",
"createdAt": "2025-10-15T16:22:16.000Z"
}
// Example: GET /api/admin/users
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"name": "John Doe",
"createdAt": "2025-10-15T16:22:16.000Z",
"updatedAt": "2025-10-15T16:22:16.000Z"
}
]Error Response
// Standard error format
{
"error": {
"code": "APP_PERMISSION_DENIED",
"message": "User does not have permission to access this application",
"status": "pending" // Additional context (optional)
}
}
// OAuth error format (different!)
{
"error": "invalid_grant",
"error_description": "Authorization code expired or already used"
}HTTP Status Codes
The API uses standard HTTP status codes to indicate the result of requests:
200 OKRequest successful. Response body contains requested data.
201 CreatedResource successfully created (e.g., user registration).
302 FoundRedirect (used in OAuth authorization endpoint).
400 Bad RequestInvalid request parameters, malformed JSON, or validation errors.
401 UnauthorizedAuthentication required or invalid/expired token.
403 ForbiddenValid authentication but insufficient permissions (e.g., app permission denied).
404 Not FoundRequested resource does not exist.
429 Too Many RequestsRate limit exceeded. Check
Retry-Afterheader.500 Internal Server ErrorUnexpected server error occurred.
Response Headers
Important information is conveyed through response headers:
Content-Type: application/jsonAll API responses are JSON (except OAuth redirects).
X-RateLimit-Limit: 60Maximum requests allowed in current time window.
X-RateLimit-Remaining: 58Remaining requests in current time window.
X-RateLimit-Reset: 1627399287Unix timestamp when rate limit resets.
Retry-After: 60Seconds to wait before retrying (429 responses).
Set-Cookie: admin-session=...; HttpOnly; SameSite=LaxAdmin session cookie (admin login endpoint only).
Access-Control-Allow-Origin: https://yourapp.comCORS header indicating allowed origin.
Access-Control-Allow-Credentials: trueIndicates cookies are allowed in cross-origin requests.
App Permission Responses
Special response format for app permission management:
GET /api/admin/app-permissions/[userId]
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"apps": [
{
"clientId": "launchmass-client-id",
"name": "Launchmass",
"description": "Landing page builder",
"role": "admin", // Current role: 'none', 'user', or 'admin'
"status": "approved", // 'pending', 'approved', 'revoked', 'none'
"grantedAt": "2025-10-15T14:30:00.000Z",
"grantedBy": "admin-user-uuid",
"createdAt": "2025-10-15T12:00:00.000Z",
"updatedAt": "2025-10-15T14:30:00.000Z"
},
{
"clientId": "messmass-client-id",
"name": "Messmass",
"description": "Messaging platform",
"role": "none",
"status": "pending",
"createdAt": "2025-10-15T16:00:00.000Z",
"updatedAt": "2025-10-15T16:00:00.000Z"
}
]
}Permission Status Meanings
none- No permission record exists (never requested)pending- User requested access, awaiting admin approvalapproved- User has active access with assigned rolerevoked- Access was granted but has been revoked
Timestamp Format
All timestamps in API responses use ISO 8601 UTC with milliseconds:
// Format: YYYY-MM-DDTHH:MM:SS.sssZ
"createdAt": "2025-10-15T16:22:16.000Z"
"updatedAt": "2025-10-15T16:22:16.000Z"
"grantedAt": "2025-10-15T14:30:00.000Z"
// Parsing in JavaScript:
const date = new Date("2025-10-15T16:22:16.000Z");
console.log(date.toLocaleString()); // Local time
// Parsing in other languages:
// Python: datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
// Go: time.Parse(time.RFC3339Nano, timestamp)
// Java: Instant.parse(timestamp)Related Documentation
- Complete Endpoint Reference - All API endpoints
- Error Reference - All error codes and handling
- Authentication Guide - OAuth 2.0 flow details
- App Permissions - Permission system