Organizations API
Complete API reference for managing organizations, members, policies, branding, and SSO in ARSAKA PUGUH.
API Convention
All PUGUH API responses are returned directly as JSON (no {success, data} wrapper). HTTP status codes indicate success or failure. Paginated endpoints return {items, total, page, page_size, has_next, has_prev}.
Endpoints Overview
Organization CRUD
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/organizations | List organizations |
POST | /api/v1/organizations | Create organization |
GET | /api/v1/organizations/{id} | Get organization details |
PATCH | /api/v1/organizations/{id} | Update organization |
DELETE | /api/v1/organizations/{id} | Soft delete organization |
Members
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/organizations/{id}/members | List members |
POST | /api/v1/organizations/{id}/members | Invite member |
PATCH | /api/v1/organizations/{id}/members/{user_id} | Update member role |
DELETE | /api/v1/organizations/{id}/members/{user_id} | Remove member |
Policies (Business+ Plans)
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/organizations/{id}/policies | Get organization policies |
PUT | /api/v1/organizations/{id}/policies | Update organization policies |
Branding (Business+ Plans)
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/organizations/{id}/branding | Get branding configuration |
PUT | /api/v1/organizations/{id}/branding | Update branding configuration |
SSO (Business+ Plans)
| Method | Endpoint | Description |
|---|---|---|
POST | /api/v1/sso/configurations | Create SSO configuration |
GET | /api/v1/sso/configurations/{org_id} | Get SSO configuration |
PUT | /api/v1/sso/configurations/{org_id} | Update SSO configuration |
List Organizations
Returns a paginated list of organizations the authenticated user belongs to.
GET /api/v1/organizations Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number |
page_size | int | 20 | Items per page (max 100) |
type | string | — | Filter by type: personal or team |
search | string | — | Search by organization name |
Response (200 OK)
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corp",
"slug": "acme-corp",
"type": "team",
"plan": "business",
"member_count": 24,
"is_active": true,
"created_at": "2026-01-15T08:00:00Z",
"updated_at": "2026-02-10T14:30:00Z"
},
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Jane's Workspace",
"slug": "janes-workspace",
"type": "personal",
"plan": "free",
"member_count": 1,
"is_active": true,
"created_at": "2026-01-10T06:00:00Z",
"updated_at": "2026-01-10T06:00:00Z"
}
],
"total": 2,
"page": 1,
"page_size": 20,
"has_next": false,
"has_prev": false
} Create Organization
Creates a new team organization. Each user automatically receives a personal workspace on signup; this endpoint is for creating additional team organizations.
POST /api/v1/organizations
Content-Type: application/json Request Body
{
"name": "Acme Corp",
"slug": "acme-corp",
"type": "team"
} Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name (2-100 characters) |
slug | string | No | URL-friendly identifier (auto-generated from name if omitted) |
type | string | No | team (default) or personal |
Response (201 Created)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corp",
"slug": "acme-corp",
"type": "team",
"plan": "free",
"is_active": true,
"created_at": "2026-02-20T10:00:00Z",
"updated_at": "2026-02-20T10:00:00Z"
} Info
The creating user is automatically assigned the owner role in the new organization.
Get Organization
Returns detailed information about a specific organization.
GET /api/v1/organizations/{id} Response (200 OK)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corp",
"slug": "acme-corp",
"type": "team",
"plan": "business",
"member_count": 24,
"application_count": 3,
"is_active": true,
"settings": {
"default_role": "member",
"allow_member_invite": false
},
"created_at": "2026-01-15T08:00:00Z",
"updated_at": "2026-02-10T14:30:00Z"
} Update Organization
Updates organization details. Only fields included in the request body are modified.
PATCH /api/v1/organizations/{id}
Content-Type: application/json Request Body
{
"name": "Acme Corporation",
"settings": {
"default_role": "viewer",
"allow_member_invite": true
}
} Request Fields
| Field | Type | Description |
|---|---|---|
name | string | Organization display name |
slug | string | URL-friendly identifier (must be unique) |
settings.default_role | string | Default role for new members: member or viewer |
settings.allow_member_invite | boolean | Whether non-admin members can invite others |
Response (200 OK)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corporation",
"slug": "acme-corp",
"type": "team",
"plan": "business",
"is_active": true,
"settings": {
"default_role": "viewer",
"allow_member_invite": true
},
"updated_at": "2026-02-20T11:00:00Z"
} Delete Organization
Soft-deletes an organization. The organization is deactivated and marked as deleted but data is retained for 30 days before permanent removal.
DELETE /api/v1/organizations/{id} Response (200 OK)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"is_active": false,
"deleted_at": "2026-02-20T12:00:00Z"
} Warning
Only the organization owner can delete an organization. Deleting an organization deactivates all associated applications and revokes member access immediately.
List Members
Returns a paginated list of members in an organization.
GET /api/v1/organizations/{id}/members Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number |
page_size | int | 20 | Items per page (max 100) |
role | string | — | Filter by role: owner, admin, member, viewer |
search | string | — | Search by name or email |
Response (200 OK)
{
"items": [
{
"user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "jane@acme.com",
"full_name": "Jane Smith",
"role": "owner",
"status": "active",
"accepted_at": "2026-01-15T08:00:00Z"
},
{
"user_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"email": "bob@acme.com",
"full_name": "Bob Johnson",
"role": "admin",
"status": "active",
"accepted_at": "2026-01-20T09:30:00Z"
},
{
"user_id": null,
"email": "new@acme.com",
"full_name": null,
"role": "member",
"status": "pending",
"accepted_at": null
}
],
"total": 3,
"page": 1,
"page_size": 20,
"has_next": false,
"has_prev": false
} Invite Member
Sends an invitation email to join the organization. If the email does not yet have a PUGUH account, the user will be prompted to sign up first.
POST /api/v1/organizations/{id}/members
Content-Type: application/json Request Body
{
"email": "new@acme.com",
"role": "member"
} Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address of the invitee |
role | string | Yes | Role to assign: admin, member, or viewer |
Response (201 Created)
{
"email": "new@acme.com",
"role": "member",
"status": "pending",
"invited_at": "2026-02-20T10:00:00Z",
"invited_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
} Info
You cannot invite someone as owner. Ownership must be transferred explicitly by the current owner.
Update Member Role
Changes the role of an existing organization member.
PATCH /api/v1/organizations/{id}/members/{user_id}
Content-Type: application/json Request Body
{
"role": "admin"
} Response (200 OK)
{
"user_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"email": "bob@acme.com",
"role": "admin",
"updated_at": "2026-02-20T11:00:00Z"
} Role Hierarchy
| Role | Permissions |
|---|---|
owner | Full control, billing, delete org, transfer ownership |
admin | Manage members, applications, settings, policies |
member | Access applications, create resources within applications |
viewer | Read-only access to applications and resources |
Remove Member
Removes a member from the organization. The user's account is not deleted, only their membership.
DELETE /api/v1/organizations/{id}/members/{user_id} Response (200 OK)
{
"user_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"removed_at": "2026-02-20T12:00:00Z"
} Warning
You cannot remove the organization owner. Transfer ownership first using a separate ownership transfer flow.
Organization Policies
Policies let Business+ organizations enforce security requirements across all members. These control password strength, MFA, session behavior, and IP restrictions.
Business+ Feature
Organization policies are available on Business and Enterprise plans. Free and Starter plans receive a 403 response.
Get Policies
GET /api/v1/organizations/{id}/policies Response (200 OK)
{
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"password_policy": {
"min_length": 12,
"require_uppercase": true,
"require_lowercase": true,
"require_numbers": true,
"require_special_chars": true,
"max_age_days": 90,
"prevent_reuse_count": 5
},
"mfa_policy": {
"required": true,
"allowed_methods": ["totp", "webauthn"],
"grace_period_hours": 48
},
"session_policy": {
"max_session_duration_hours": 24,
"idle_timeout_minutes": 30,
"max_concurrent_sessions": 5
},
"ip_allowlist": {
"enabled": false,
"allowed_ips": []
},
"updated_at": "2026-02-15T09:00:00Z"
} Update Policies
Replaces the entire policy configuration. Include all policy sections in the request.
PUT /api/v1/organizations/{id}/policies
Content-Type: application/json Request Body
{
"password_policy": {
"min_length": 14,
"require_uppercase": true,
"require_lowercase": true,
"require_numbers": true,
"require_special_chars": true,
"max_age_days": 60,
"prevent_reuse_count": 10
},
"mfa_policy": {
"required": true,
"allowed_methods": ["totp", "webauthn"],
"grace_period_hours": 24
},
"session_policy": {
"max_session_duration_hours": 12,
"idle_timeout_minutes": 15,
"max_concurrent_sessions": 3
},
"ip_allowlist": {
"enabled": true,
"allowed_ips": ["203.0.113.0/24", "198.51.100.42"]
}
} Policy Fields
| Section | Field | Type | Description |
|---|---|---|---|
password_policy | min_length | int | Minimum password length (8-128) |
require_uppercase | boolean | Require at least one uppercase letter | |
require_lowercase | boolean | Require at least one lowercase letter | |
require_numbers | boolean | Require at least one digit | |
require_special_chars | boolean | Require at least one special character | |
max_age_days | int | Force password reset after N days (0 = disabled) | |
prevent_reuse_count | int | Number of previous passwords to block (0-24) | |
mfa_policy | required | boolean | Enforce MFA for all members |
allowed_methods | string[] | Allowed MFA methods: totp, webauthn | |
grace_period_hours | int | Hours before MFA is enforced after policy change | |
session_policy | max_session_duration_hours | int | Absolute session lifetime (1-720) |
idle_timeout_minutes | int | Inactivity timeout (5-1440) | |
max_concurrent_sessions | int | Maximum active sessions per user (1-100) | |
ip_allowlist | enabled | boolean | Enable IP restriction |
allowed_ips | string[] | Allowed IPs or CIDR ranges |
Response (200 OK)
Returns the full updated policy object (same shape as GET response).
Organization Branding
Customize the look and feel of authentication pages and emails for your organization (white-label). Available on Business+ plans.
Business+ Feature
Branding customization is available on Business and Enterprise plans.
Get Branding
GET /api/v1/organizations/{id}/branding Response (200 OK)
{
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"logo_url": "https://cdn.arsaka.io/orgs/acme/logo.png",
"favicon_url": "https://cdn.arsaka.io/orgs/acme/favicon.ico",
"primary_color": "#2563EB",
"accent_color": "#7C3AED",
"custom_login": {
"title": "Acme Corp Portal",
"subtitle": "Sign in to your workspace",
"background_url": "https://cdn.arsaka.io/orgs/acme/bg.jpg"
},
"email_branding": {
"from_name": "Acme Corp",
"reply_to": "support@acme.com",
"footer_text": "Acme Corp, 123 Main St, Jakarta"
},
"updated_at": "2026-02-18T16:00:00Z"
} Update Branding
PUT /api/v1/organizations/{id}/branding
Content-Type: application/json Request Body
{
"logo_url": "https://cdn.arsaka.io/orgs/acme/logo-v2.png",
"primary_color": "#1D4ED8",
"accent_color": "#6D28D9",
"custom_login": {
"title": "Welcome to Acme",
"subtitle": "Enterprise workspace login",
"background_url": null
},
"email_branding": {
"from_name": "Acme Corporation",
"reply_to": "noreply@acme.com",
"footer_text": "Acme Corporation, Jakarta, Indonesia"
}
} Branding Fields
| Field | Type | Description |
|---|---|---|
logo_url | string | URL to organization logo (PNG/SVG, max 2 MB) |
favicon_url | string | URL to favicon (ICO/PNG, max 256 KB) |
primary_color | string | Primary brand color (hex, e.g. #2563EB) |
accent_color | string | Accent brand color (hex) |
custom_login.title | string | Login page heading |
custom_login.subtitle | string | Login page subheading |
custom_login.background_url | string|null | Login page background image URL (null to remove) |
email_branding.from_name | string | Display name in outgoing emails |
email_branding.reply_to | string | Reply-to email address |
email_branding.footer_text | string | Footer text in transactional emails |
Response (200 OK)
Returns the full updated branding object (same shape as GET response).
SSO Configuration
Configure SAML-based Single Sign-On for your organization. When SSO is enabled, members authenticate through your identity provider (IdP) instead of PUGUH credentials.
Business+ Feature
SSO is available on Business and Enterprise plans. Supports SAML 2.0 with any compliant IdP (Okta, Azure AD, Google Workspace, etc.).
Create SSO Configuration
POST /api/v1/sso/configurations
Content-Type: application/json Request Body
{
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"provider": "saml",
"idp_entity_id": "https://idp.acme.com/metadata",
"idp_sso_url": "https://idp.acme.com/sso/saml",
"idp_certificate": "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
"domain_whitelist": ["acme.com", "acme.co.id"],
"auto_provision": true,
"default_role": "member"
} Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
organization_id | uuid | Yes | Organization to configure SSO for |
provider | string | Yes | SSO provider type: saml |
idp_entity_id | string | Yes | Identity Provider entity ID (from IdP metadata) |
idp_sso_url | string | Yes | IdP Single Sign-On URL |
idp_certificate | string | Yes | IdP X.509 certificate (PEM format) |
domain_whitelist | string[] | No | Email domains that must use SSO |
auto_provision | boolean | No | Auto-create user accounts on first SSO login (default false) |
default_role | string | No | Role for auto-provisioned users (default member) |
Response (201 Created)
{
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"provider": "saml",
"idp_entity_id": "https://idp.acme.com/metadata",
"idp_sso_url": "https://idp.acme.com/sso/saml",
"sp_entity_id": "https://api-puguh.arsaka.io/sso/saml/550e8400",
"sp_acs_url": "https://api-puguh.arsaka.io/sso/saml/550e8400/acs",
"domain_whitelist": ["acme.com", "acme.co.id"],
"auto_provision": true,
"default_role": "member",
"is_active": true,
"created_at": "2026-02-20T10:00:00Z"
} Tip
After creating the SSO configuration, use the returned sp_entity_id and sp_acs_url to configure the Service Provider in your IdP.
Get SSO Configuration
GET /api/v1/sso/configurations/{org_id} Response (200 OK)
{
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"provider": "saml",
"idp_entity_id": "https://idp.acme.com/metadata",
"idp_sso_url": "https://idp.acme.com/sso/saml",
"sp_entity_id": "https://api-puguh.arsaka.io/sso/saml/550e8400",
"sp_acs_url": "https://api-puguh.arsaka.io/sso/saml/550e8400/acs",
"domain_whitelist": ["acme.com", "acme.co.id"],
"auto_provision": true,
"default_role": "member",
"is_active": true,
"created_at": "2026-02-20T10:00:00Z",
"updated_at": "2026-02-20T10:00:00Z"
} Update SSO Configuration
PUT /api/v1/sso/configurations/{org_id}
Content-Type: application/json Request Body
{
"idp_sso_url": "https://idp.acme.com/sso/saml/v2",
"idp_certificate": "-----BEGIN CERTIFICATE-----\nNEWC...\n-----END CERTIFICATE-----",
"domain_whitelist": ["acme.com"],
"auto_provision": false
} Response (200 OK)
Returns the full updated SSO configuration object (same shape as GET response).
Error Responses
All errors return the appropriate HTTP status code with a JSON body containing a detail field. There is no {success, error} wrapper.
400 Bad Request
Invalid request body or parameters:
{
"detail": "Validation error: name must be between 2 and 100 characters"
} 401 Unauthorized
Missing or invalid authentication:
{
"detail": "Invalid or expired token"
} 403 Forbidden
Insufficient permissions or plan restriction:
{
"detail": "Organization policies require Business plan or higher"
} Or insufficient role:
{
"detail": "Only organization owner or admin can invite members"
} 404 Not Found
Organization or member not found:
{
"detail": "Organization not found"
} 409 Conflict
Duplicate resource:
{
"detail": "Organization with slug 'acme-corp' already exists"
} Or duplicate invitation:
{
"detail": "User bob@acme.com is already a member of this organization"
} 422 Unprocessable Entity
Semantically invalid request:
{
"detail": "Cannot remove the last owner from the organization"
} 429 Too Many Requests
Rate limit exceeded:
{
"detail": "Rate limit exceeded. Retry after 60 seconds"
} Related
- Authentication API — API keys, JWT tokens, and OAuth
- Webhooks API — Configure webhook endpoints and event delivery
- Python SDK —
OrganizationClientwith 17 methods - TypeScript SDK —
OrganizationClientfor frontend and Node.js