Admin API¶
The Admin API provides endpoints for tenant management, user administration, and API key management. These endpoints require elevated permissions.
Overview¶
CalcBridge's admin system enables:
- Tenant Management: Create and configure organizations
- User Administration: Manage users, roles, and permissions
- API Key Management: Create and revoke API keys for service integrations
- System Configuration: Configure tenant-specific settings
Authorization¶
Admin endpoints require one of the following:
- Owner role: Full access to tenant administration
- Admin role: Limited access (cannot delete tenant or transfer ownership)
- System API key: For automated tenant provisioning (Enterprise only)
Endpoints¶
Tenant Management¶
Get Current Tenant¶
Retrieve information about the current tenant.
Example Request¶
Response¶
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Acme Capital",
"slug": "acme-capital",
"subscription_tier": "premium",
"settings": {
"default_currency": "USD",
"timezone": "America/New_York",
"date_format": "MM/DD/YYYY",
"max_workbook_size_mb": 100,
"max_users": 25
},
"usage": {
"users": 12,
"workbooks": 45,
"api_calls_this_month": 15420,
"storage_used_mb": 256
},
"created_at": "2025-06-15T10:00:00Z",
"updated_at": "2026-01-20T15:30:00Z"
}
Update Tenant¶
Update tenant settings.
Request Body¶
| Field | Type | Description |
|---|---|---|
name | string | Organization name |
settings | object | Tenant settings |
Settings Object¶
| Field | Type | Description |
|---|---|---|
default_currency | string | Default currency code (USD, EUR, GBP) |
timezone | string | IANA timezone identifier |
date_format | string | Date display format |
number_format | string | Number format locale |
Example Request¶
import requests
response = requests.patch(
"https://api.calcbridge.io/api/v1/admin/tenant",
headers={"Authorization": f"Bearer {token}"},
json={
"name": "Acme Capital Partners",
"settings": {
"timezone": "Europe/London",
"date_format": "DD/MM/YYYY"
}
}
)
tenant = response.json()
print(f"Updated: {tenant['name']}")
const response = await fetch('https://api.calcbridge.io/api/v1/admin/tenant', {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Acme Capital Partners',
settings: {
timezone: 'Europe/London',
date_format: 'DD/MM/YYYY'
}
})
});
const tenant = await response.json();
console.log('Updated:', tenant.name);
Response¶
Returns the updated tenant object.
User Management¶
List Users¶
Get all users in the current tenant.
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
page_size | integer | 20 | Items per page |
role | string | - | Filter by role |
status | string | - | Filter by status (active, inactive) |
search | string | - | Search by name or email |
Example Request¶
Response¶
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john@example.com",
"full_name": "John Smith",
"role": "analyst",
"status": "active",
"last_login": "2026-01-25T09:00:00Z",
"created_at": "2025-08-10T10:00:00Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"email": "jane@example.com",
"full_name": "Jane Doe",
"role": "analyst",
"status": "active",
"last_login": "2026-01-24T16:30:00Z",
"created_at": "2025-09-15T14:00:00Z"
}
],
"total": 8,
"page": 1,
"page_size": 20,
"total_pages": 1
}
Invite User¶
Invite a new user to the tenant.
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User email address |
full_name | string | Yes | User's full name |
role | string | Yes | Role: owner, admin, analyst, viewer |
send_email | boolean | No | Send invitation email (default: true) |
Example Request¶
import requests
response = requests.post(
"https://api.calcbridge.io/api/v1/admin/users/invite",
headers={"Authorization": f"Bearer {token}"},
json={
"email": "newuser@example.com",
"full_name": "New User",
"role": "analyst",
"send_email": True
}
)
invitation = response.json()
print(f"Invited: {invitation['email']}")
print(f"Invitation expires: {invitation['expires_at']}")
const response = await fetch('https://api.calcbridge.io/api/v1/admin/users/invite', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'newuser@example.com',
full_name: 'New User',
role: 'analyst',
send_email: true
})
});
const invitation = await response.json();
console.log('Invited:', invitation.email);
Response¶
{
"id": "770e8400-e29b-41d4-a716-446655440010",
"email": "newuser@example.com",
"full_name": "New User",
"role": "analyst",
"status": "pending",
"invitation_token": "inv_abc123xyz...",
"expires_at": "2026-02-01T10:30:00Z",
"created_at": "2026-01-25T10:30:00Z"
}
Error Responses¶
| Status | Error | Description |
|---|---|---|
400 | Invalid role | Unknown role value |
409 | User already exists | Email already registered |
403 | User limit exceeded | Subscription user limit reached |
Get User¶
Retrieve a specific user's details.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
user_id | string (UUID) | User identifier |
Example Request¶
Response¶
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john@example.com",
"full_name": "John Smith",
"role": "analyst",
"status": "active",
"permissions": {
"workbooks": ["read", "write", "delete"],
"calculations": ["execute"],
"compliance": ["read", "write"],
"scenarios": ["read", "write"],
"reports": ["read", "generate"],
"admin": []
},
"last_login": "2026-01-25T09:00:00Z",
"login_count": 145,
"created_at": "2025-08-10T10:00:00Z",
"updated_at": "2026-01-25T09:00:00Z"
}
Update User¶
Update a user's role or status.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
user_id | string (UUID) | User identifier |
Request Body¶
| Field | Type | Description |
|---|---|---|
role | string | New role |
status | string | Status: active, inactive |
full_name | string | User's full name |
Example Request¶
const response = await fetch(
`https://api.calcbridge.io/api/v1/admin/users/${userId}`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
role: 'admin',
status: 'active'
})
}
);
const user = await response.json();
console.log(`Updated ${user.full_name} to role: ${user.role}`);
Response¶
Returns the updated user object.
Remove User¶
Remove a user from the tenant.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
user_id | string (UUID) | User identifier |
Example Request¶
Response¶
Returns 204 No Content on success.
Error Responses¶
| Status | Error | Description |
|---|---|---|
403 | Cannot remove owner | Tenant owner cannot be removed |
403 | Cannot remove self | Cannot remove your own account |
404 | User not found | Invalid user_id |
API Key Management¶
List API Keys¶
Get all API keys for the current tenant.
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
page_size | integer | 20 | Items per page |
status | string | - | Filter by status (active, revoked) |
Example Request¶
Response¶
{
"items": [
{
"id": "880e8400-e29b-41d4-a716-446655440020",
"name": "Production Integration",
"key_prefix": "cb_live_a1b2c3",
"permissions": ["workbooks:read", "calculations:execute", "compliance:read"],
"status": "active",
"last_used": "2026-01-25T10:15:00Z",
"usage_count": 5420,
"expires_at": null,
"created_by": "admin@example.com",
"created_at": "2025-10-01T10:00:00Z"
},
{
"id": "880e8400-e29b-41d4-a716-446655440021",
"name": "Development Testing",
"key_prefix": "cb_test_x9y8z7",
"permissions": ["workbooks:read", "workbooks:write", "calculations:execute"],
"status": "active",
"last_used": "2026-01-24T16:45:00Z",
"usage_count": 892,
"expires_at": "2026-06-01T00:00:00Z",
"created_by": "admin@example.com",
"created_at": "2025-12-01T14:00:00Z"
}
],
"total": 3,
"page": 1,
"page_size": 20,
"total_pages": 1
}
Create API Key¶
Create a new API key.
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Key name (1-200 chars) |
permissions | array[string] | Yes | Permission scopes |
expires_at | string (ISO 8601) | No | Expiration date (null for no expiry) |
environment | string | No | Environment: live, test (default: live) |
Available Permissions¶
| Permission | Description |
|---|---|
workbooks:read | List and view workbooks |
workbooks:write | Create, update, delete workbooks |
calculations:execute | Run formula evaluations |
compliance:read | View compliance results |
compliance:write | Run compliance tests, configure thresholds |
scenarios:read | View scenarios |
scenarios:write | Create and run scenarios |
reports:read | View reports |
reports:generate | Generate and export reports |
alerts:read | View alerts |
alerts:write | Configure alerts |
mappings:read | View mappings |
mappings:write | Create and update mappings |
admin:users | Manage users (admin only) |
admin:api-keys | Manage API keys (admin only) |
Example Request¶
curl -X POST https://api.calcbridge.io/api/v1/admin/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline",
"permissions": [
"workbooks:read",
"workbooks:write",
"calculations:execute",
"compliance:read"
],
"expires_at": "2027-01-01T00:00:00Z",
"environment": "live"
}'
import requests
response = requests.post(
"https://api.calcbridge.io/api/v1/admin/api-keys",
headers={"Authorization": f"Bearer {token}"},
json={
"name": "CI/CD Pipeline",
"permissions": [
"workbooks:read",
"workbooks:write",
"calculations:execute",
"compliance:read"
],
"expires_at": "2027-01-01T00:00:00Z",
"environment": "live"
}
)
api_key = response.json()
# IMPORTANT: Save the full key now - it won't be shown again!
print(f"API Key: {api_key['key']}")
const response = await fetch('https://api.calcbridge.io/api/v1/admin/api-keys', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'CI/CD Pipeline',
permissions: [
'workbooks:read',
'workbooks:write',
'calculations:execute',
'compliance:read'
],
expires_at: '2027-01-01T00:00:00Z',
environment: 'live'
})
});
const apiKey = await response.json();
// IMPORTANT: Save the full key now - it won't be shown again!
console.log('API Key:', apiKey.key);
Response¶
{
"id": "880e8400-e29b-41d4-a716-446655440022",
"name": "CI/CD Pipeline",
"key": "cb_live_abc123def456ghi789jkl012mno345pqr678stu901vwx234yz",
"key_prefix": "cb_live_abc123",
"permissions": [
"workbooks:read",
"workbooks:write",
"calculations:execute",
"compliance:read"
],
"environment": "live",
"status": "active",
"expires_at": "2027-01-01T00:00:00Z",
"created_by": "admin@example.com",
"created_at": "2026-01-25T10:30:00Z"
}
Save your API key
The full API key is only shown once at creation time. Store it securely - you won't be able to retrieve it later.
Revoke API Key¶
Revoke an API key.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
key_id | string (UUID) | API key identifier |
Example Request¶
Response¶
Returns 204 No Content on success.
Rotate API Key¶
Create a new key and revoke the old one atomically.
Path Parameters¶
| Parameter | Type | Description |
|---|---|---|
key_id | string (UUID) | API key identifier |
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
grace_period_hours | integer | No | Hours to keep old key active (default: 0) |
Example Request¶
Response¶
{
"new_key": {
"id": "880e8400-e29b-41d4-a716-446655440023",
"name": "Production Integration",
"key": "cb_live_new123key456here789...",
"key_prefix": "cb_live_new123",
"permissions": ["workbooks:read", "calculations:execute", "compliance:read"],
"status": "active",
"created_at": "2026-01-25T10:30:00Z"
},
"old_key": {
"id": "880e8400-e29b-41d4-a716-446655440020",
"status": "deprecated",
"expires_at": "2026-01-26T10:30:00Z"
}
}
Audit Logs¶
List Audit Logs¶
Get audit logs for the tenant (Enterprise tier only).
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
page_size | integer | 50 | Items per page (max 100) |
user_id | string (UUID) | - | Filter by user |
action | string | - | Filter by action type |
resource_type | string | - | Filter by resource type |
start_date | string (ISO 8601) | - | Logs after this date |
end_date | string (ISO 8601) | - | Logs before this date |
Example Request¶
Response¶
{
"items": [
{
"id": "990e8400-e29b-41d4-a716-446655440030",
"timestamp": "2026-01-25T10:30:00Z",
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"user_email": "john@example.com",
"action": "delete",
"resource_type": "workbook",
"resource_id": "550e8400-e29b-41d4-a716-446655440099",
"resource_name": "Old Portfolio Q4 2025",
"ip_address": "192.168.1.100",
"user_agent": "Mozilla/5.0...",
"details": {
"reason": "Replaced with updated version"
}
}
],
"total": 150,
"page": 1,
"page_size": 50,
"total_pages": 3
}
User Roles¶
| Role | Description | Permissions |
|---|---|---|
owner | Tenant owner | Full access, can delete tenant |
admin | Administrator | Manage users, API keys, settings |
analyst | Power user | Full workbook and analysis access |
viewer | Read-only | View workbooks and reports only |
Role Permissions Matrix¶
| Permission | Owner | Admin | Analyst | Viewer |
|---|---|---|---|---|
| View workbooks | Yes | Yes | Yes | Yes |
| Upload workbooks | Yes | Yes | Yes | No |
| Delete workbooks | Yes | Yes | Yes | No |
| Run calculations | Yes | Yes | Yes | No |
| Run compliance tests | Yes | Yes | Yes | No |
| Create scenarios | Yes | Yes | Yes | No |
| Generate reports | Yes | Yes | Yes | No |
| Configure alerts | Yes | Yes | Yes | No |
| Manage mappings | Yes | Yes | Yes | No |
| Manage users | Yes | Yes | No | No |
| Manage API keys | Yes | Yes | No | No |
| Update tenant settings | Yes | Yes | No | No |
| Delete tenant | Yes | No | No | No |
Best Practices¶
- Use least privilege: Assign minimum necessary permissions to API keys
- Rotate keys regularly: Set expiration dates and rotate production keys periodically
- Monitor audit logs: Review access patterns and investigate anomalies
- Separate environments: Use different API keys for development/staging/production
- Document API key purposes: Use descriptive names for easy identification
- Review user access: Regularly audit user roles and remove inactive accounts