Organizations API
Manage tenant organizations. Each organization represents an isolated tenant with its own users, clients, and configuration.
Base path: /api/admin/organizations
Create Organization
POST /api/admin/organizationsRequest body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✅ | Organization display name |
slug | string | URL slug (auto-generated from name if omitted) | |
defaultLocale | string | Default locale (e.g., en) | |
defaultLoginMethods | string[] | Non-empty selection of password and magic_link | |
branding | object | Optional initial branding settings |
{
"name": "Acme Corp",
"defaultLocale": "en",
"defaultLoginMethods": ["password", "magic_link"]
}Response: 201 Created
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corp",
"slug": "acme-corp",
"status": "active",
"isSuperAdmin": false,
"defaultLocale": "en",
"defaultLoginMethods": ["password", "magic_link"],
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}Validate Slug
Check whether a slug can be assigned before creating an organization.
GET /api/admin/organizations/validate-slug?slug=acme-corp| Input | Result |
|---|---|
| Well-formed and available | 200 { "isValid": true } |
| Well-formed but taken | 200 { "isValid": false, "error": "Slug already in use" } |
| Malformed or reserved | 400 with the standard validation error |
new is reserved as a create-action segment and is rejected with 400. Only the exact word is reserved, so slugs such as new-york and renew remain valid.
An organization that already uses a reserved word as its slug keeps its data and remains reachable by UUID. The Admin API does not rename slugs; an operator can change the slug directly in the database when needed.
List Organizations
GET /api/admin/organizationsQuery parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
pageSize | integer | Items per page (default: 20) |
search | string | Search by name or slug |
status | string | Filter by status |
sortBy | string | Sort field (name, created_at) |
sortOrder | string | Sort direction (asc, desc) |
Response: 200 OK
{
"data": [
{
"id": "...",
"name": "Acme Corp",
"slug": "acme-corp",
"status": "active",
"isSuperAdmin": false,
"createdAt": "2024-01-15T10:30:00.000Z"
}
],
"pagination": {
"total": 5,
"page": 1,
"pageSize": 20,
"totalPages": 1
}
}Get Organization
GET /api/admin/organizations/:idResponse: 200 OK — Full organization object including branding fields.
Update Organization
PUT /api/admin/organizations/:idRequest body: Any subset of mutable fields:
| Field | Type | Description |
|---|---|---|
name | string | Display name |
defaultLocale | string | Default locale |
defaultLoginMethods | string[] | Non-empty selection of password and magic_link |
branding | object | Nested branding fields; use null to clear an optional field |
The optional branding object accepts logoUrl, faviconUrl, primaryColor, companyName, and customCss. Image URLs are fallbacks for organizations without an uploaded asset. Porta accepts HTTPS URLs. In non-production environments it also accepts HTTP URLs for exact loopback hosts only. Credentials in image URLs and other URL schemes are rejected.
Response: 200 OK — { "data": <updated organization> }.
Suspend Organization
POST /api/admin/organizations/:id/suspendSuspends the organization. All authentication requests will be rejected.
Response: 204 No Content
Activate Organization
POST /api/admin/organizations/:id/activateReactivates a suspended organization.
Response: 204 No Content
Update Branding
PUT /api/admin/organizations/:id/brandingRequest body:
| Field | Type | Description |
|---|---|---|
logoUrl | string or null | External fallback logo URL, or null to clear it |
faviconUrl | string or null | External fallback favicon URL, or null to clear it |
primaryColor | string or null | Six-digit hex color such as #0078d4, or null |
companyName | string or null | Company display name, or null to use the organization name |
customCss | string or null | Existing custom template CSS, up to 10 KB |
Uploaded logo and favicon assets take precedence over logoUrl and faviconUrl. The URLs remain configured fallbacks and are used again if the matching uploaded asset is deleted.
Permission: admin:org:update
Response: 200 OK — { "data": <updated organization> }.
The SDK exposes the same operation as porta.branding.updateSettings(organizationId, input) and returns the complete updated Organization. Branding settings are otherwise read from the organization resource; there is no separate branding-settings read endpoint. GET /:id/branding belongs to the branding-assets API and returns asset metadata.
Delete Organization
DELETE /api/admin/organizations/:idOrSlugPermanently deletes an organization and its owned users, clients, assignments, credentials, and security data in one database transaction. Affected sessions are revoked. The super-admin organization is protected and cannot be deleted.
Permission: admin:org:delete
Response: 204 No Content
The retained audit event follows the configured audit retention policy. It is not a surviving organization record.
Error responses:
| Status | Condition |
|---|---|
400 | Attempting to delete the super-admin organization |
404 | Organization not found |