P
PUGUH

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

MethodEndpointDescription
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

MethodEndpointDescription
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)

MethodEndpointDescription
GET /api/v1/organizations/{id}/policies Get organization policies
PUT /api/v1/organizations/{id}/policies Update organization policies

Branding (Business+ Plans)

MethodEndpointDescription
GET /api/v1/organizations/{id}/branding Get branding configuration
PUT /api/v1/organizations/{id}/branding Update branding configuration

SSO (Business+ Plans)

MethodEndpointDescription
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.

http
GET /api/v1/organizations

Query Parameters

ParameterTypeDefaultDescription
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)

json
{
  "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.

http
POST /api/v1/organizations
Content-Type: application/json

Request Body

json
{
  "name": "Acme Corp",
  "slug": "acme-corp",
  "type": "team"
}

Request Fields

FieldTypeRequiredDescription
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)

json
{
  "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.

http
GET /api/v1/organizations/{id}

Response (200 OK)

json
{
  "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.

http
PATCH /api/v1/organizations/{id}
Content-Type: application/json

Request Body

json
{
  "name": "Acme Corporation",
  "settings": {
    "default_role": "viewer",
    "allow_member_invite": true
  }
}

Request Fields

FieldTypeDescription
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)

json
{
  "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.

http
DELETE /api/v1/organizations/{id}

Response (200 OK)

json
{
  "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.

http
GET /api/v1/organizations/{id}/members

Query Parameters

ParameterTypeDefaultDescription
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)

json
{
  "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.

http
POST /api/v1/organizations/{id}/members
Content-Type: application/json

Request Body

json
{
  "email": "new@acme.com",
  "role": "member"
}

Request Fields

FieldTypeRequiredDescription
email string Yes Email address of the invitee
role string Yes Role to assign: admin, member, or viewer

Response (201 Created)

json
{
  "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.

http
PATCH /api/v1/organizations/{id}/members/{user_id}
Content-Type: application/json

Request Body

json
{
  "role": "admin"
}

Response (200 OK)

json
{
  "user_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "email": "bob@acme.com",
  "role": "admin",
  "updated_at": "2026-02-20T11:00:00Z"
}

Role Hierarchy

RolePermissions
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.

http
DELETE /api/v1/organizations/{id}/members/{user_id}

Response (200 OK)

json
{
  "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

http
GET /api/v1/organizations/{id}/policies

Response (200 OK)

json
{
  "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.

http
PUT /api/v1/organizations/{id}/policies
Content-Type: application/json

Request Body

json
{
  "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

SectionFieldTypeDescription
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

http
GET /api/v1/organizations/{id}/branding

Response (200 OK)

json
{
  "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

http
PUT /api/v1/organizations/{id}/branding
Content-Type: application/json

Request Body

json
{
  "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

FieldTypeDescription
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

http
POST /api/v1/sso/configurations
Content-Type: application/json

Request Body

json
{
  "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

FieldTypeRequiredDescription
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)

json
{
  "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

http
GET /api/v1/sso/configurations/{org_id}

Response (200 OK)

json
{
  "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

http
PUT /api/v1/sso/configurations/{org_id}
Content-Type: application/json

Request Body

json
{
  "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:

json
{
  "detail": "Validation error: name must be between 2 and 100 characters"
}

401 Unauthorized

Missing or invalid authentication:

json
{
  "detail": "Invalid or expired token"
}

403 Forbidden

Insufficient permissions or plan restriction:

json
{
  "detail": "Organization policies require Business plan or higher"
}

Or insufficient role:

json
{
  "detail": "Only organization owner or admin can invite members"
}

404 Not Found

Organization or member not found:

json
{
  "detail": "Organization not found"
}

409 Conflict

Duplicate resource:

json
{
  "detail": "Organization with slug 'acme-corp' already exists"
}

Or duplicate invitation:

json
{
  "detail": "User bob@acme.com is already a member of this organization"
}

422 Unprocessable Entity

Semantically invalid request:

json
{
  "detail": "Cannot remove the last owner from the organization"
}

429 Too Many Requests

Rate limit exceeded:

json
{
  "detail": "Rate limit exceeded. Retry after 60 seconds"
}

Related