Organization Settings
Comprehensive guide to managing organization configuration, preferences, security settings, and administrative options.
Overview
Organization Settings is the central hub for configuring your organization's profile, preferences, team management, resource quotas, and integrations. Access requires appropriate permissions based on the Permission Matrix system (Sprint 2E21).
Key Features:
- Organization profile and metadata management
- Resource quota monitoring and limits
- Default configurations for new resources
- Notification preferences and integrations
- Security and access control
- Billing and subscription management
- Audit logging and compliance tracking
Accessing Organization Settings
Navigation Path
- Click organization dropdown in top navigation
- Select Settings from the menu
- Or navigate directly to:
/dashboard/settings
Required Permissions
Different settings sections require specific permissions:
| Section | Required Permission | Scope |
|---|---|---|
| General Settings | org.settings.view | Organization |
| Organization Profile | org.view | Organization |
| Update Settings | org.settings.update | Organization |
| Members Management | org.members.list | Organization |
| Billing | org.billing.view | Organization |
| Storage Configs | org.storage.list | Organization |
| DNS Settings | org.dns.list | Organization |
| Git Connections | org.git.list | Organization |
| Delete Organization | org.delete | Organization |
Settings Page Sections
The settings page uses tab-based navigation with permission-based access control:
- General - Basic organization information
- Members - Team member management
- Permissions - Custom roles and permission matrix
- Storage - Backup storage provider configurations
- DNS - Domain management provider settings
- Git Connections - GitHub/GitLab authentication
- Addon Repos - Custom Odoo addon repositories
- Billing - Subscription and payment management
- Usage - Resource quotas and consumption tracking
General Settings
Organization Profile
Basic organization information displayed and editable by admins.
Editable Fields
Organization Name
- Display name shown throughout platform
- Validation: 1-255 characters required
- Updates reflected immediately in UI
- Used in emails and notifications
name: string (required, 1-255 chars)Organization Slug
- URL-friendly unique identifier
- Format: lowercase letters, numbers, and hyphens only
- Pattern:
^[a-z0-9-]+$ - Cannot be changed once environments are deployed
- Used in subdomain generation (if applicable)
slug: string (unique, 1-100 chars, lowercase alphanumeric + hyphens)Description
- Optional text description of organization
- Displayed on organization profile
- Supports plain text (no markdown)
description: string | nullWebsite URL
- Organization's public website
- Optional field for external reference
- Must be valid URL format
website: string | null (URL format)Logo URL
- Organization logo image URL
- Displayed in navigation and profile
- Recommended: 512x512px, PNG/JPG format
- Must be publicly accessible URL
logo_url: string | null (max 512 chars)Organization Metadata (Read-Only)
Organization ID
- UUID identifier for API operations
- Format:
UUID v4 - Used in all API endpoints as
{org_id}
Created Date
- Timestamp of organization creation
- Format: ISO 8601 with timezone
- Cannot be modified
Owner Information
- User who created the organization
- Displays owner's name and email
- Owner role cannot be removed without transfer
Status
is_active: Boolean flag- Active organizations can create resources
- Inactive organizations are soft-deleted
API: Update Organization
Endpoint: PATCH /api/v1/organizations/{org_id}
Required Permission: org.settings.update
Request Headers:
Authorization: Bearer {jwt_token}
Content-Type: application/jsonRequest Body:
{
"name": "Acme Corporation",
"description": "Leading provider of digital solutions",
"website": "https://acme.example.com",
"logo_url": "https://cdn.acme.com/logo.png",
"settings": {
"timezone": "America/New_York",
"default_storage_provider": "cloudflare_r2",
"notification_email": "ops@acme.com"
}
}Field Validation:
name: Required, 1-255 charactersdescription: Optional, textwebsite: Optional, valid URL formatlogo_url: Optional, valid URL, max 512 characterssettings: Optional, JSON object for flexible configuration
Response (200 OK):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corporation",
"slug": "acme-corp",
"description": "Leading provider of digital solutions",
"website": "https://acme.example.com",
"logo_url": "https://cdn.acme.com/logo.png",
"is_active": true,
"settings": {
"timezone": "America/New_York",
"default_storage_provider": "cloudflare_r2",
"notification_email": "ops@acme.com"
},
"billing_email": "billing@acme.com",
"member_count": 15,
"project_count": 8,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-12-10T14:22:00Z"
}Error Responses:
403 Forbidden:
{
"detail": "You don't have permission to update organization settings."
}404 Not Found:
{
"detail": "Organization not found"
}409 Conflict (Slug Already Exists):
{
"detail": "Organization with slug 'acme-corp' already exists"
}Organization Metadata
Display Information
Member Count
- Total active members in organization
- Calculated from
organization_memberstable - Filter:
is_active = true
Project Count
- Total projects owned by organization
- Calculated from
projectstable - Filter:
organization_id = {org_id}
Creation Date
- ISO 8601 timestamp with timezone
- Shown in organization profile
- Used for billing anniversary calculations
Organization Status
Active Status
is_active = true: Organization operationalis_active = false: Soft-deleted, marked for cleanup- Inactive organizations cannot create resources
Billing Email
- Email for invoices and billing notifications
- Defaults to owner's email on creation
- Can be updated separately from user emails
Default Settings Configuration
The settings JSONB field stores flexible organization preferences. Common configurations:
Timezone
Field: settings.timezone
Default timezone for deployments and scheduled tasks.
{
"settings": {
"timezone": "America/New_York"
}
}Supported Timezones: IANA timezone database (e.g., America/New_York, Europe/London, Asia/Tokyo)
Default Storage Provider
Field: settings.default_storage_provider
Default backup storage for new environments.
{
"settings": {
"default_storage_provider": "cloudflare_r2"
}
}Valid Values:
aws_s3- Amazon S3cloudflare_r2- Cloudflare R2backblaze_b2- Backblaze B2minio- MinIO (self-hosted)sftp- SFTP serverftp- FTP/FTPS server
References storage_configs table to fetch actual credentials.
Default Server
Field: settings.default_server_id
Default VM for new environment deployments.
{
"settings": {
"default_server_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
}Validation: Must reference valid vm_id in organization's servers.
Default Git Connection
Field: settings.default_git_connection_id
Default Git authentication for repository access.
{
"settings": {
"default_git_connection_id": "d9c73b2a-42f3-4c8e-9f3a-8b6e2c1d4e5f"
}
}Validation: Must reference valid OrganizationGitConnection ID.
Notification Email
Field: settings.notification_email
Email for operational notifications (deployments, alerts).
{
"settings": {
"notification_email": "ops@example.com"
}
}Separate from Billing Email: Allows different contacts for ops vs finance.
Notification Preferences
Organization-Level Notifications
Configure notification delivery channels and preferences for organization-wide events.
Notification Types
| Type | Default Channel | Description |
|---|---|---|
env_deployed | In-App | Environment deployment completed |
env_error | Both | Environment deployment failed |
task_completed | In-App | Background task finished |
task_failed | Both | Background task error |
system_maintenance | Scheduled maintenance window | |
billing_warning | Both | Approaching quota or payment due |
billing_overdue | Both | Payment failed or overdue |
Notification Channels
- In-App: Bell icon notifications in dashboard
- Email: Sent to user's email address or notification email
- Both: In-app + email delivery
User Notification Preferences
Individual users can override organization defaults via their profile settings.
Model: NotificationPreference
Configurable Settings:
interface NotificationPreference {
task_completed: 'in_app' | 'email' | 'both'
task_failed: 'in_app' | 'email' | 'both'
env_deployed: 'in_app' | 'email' | 'both'
env_error: 'in_app' | 'email' | 'both'
system_updates: 'in_app' | 'email' | 'both'
billing_alerts: 'in_app' | 'email' | 'both'
security_alerts: 'in_app' | 'email' | 'both'
email_digest: boolean // Receive daily digest vs real-time
}Webhook Notifications (Future Enhancement)
Planned support for webhook-based notifications to external systems (Slack, Discord, MS Teams).
Usage & Resource Quotas
Quota System Overview
Organizations have hard limits on entity counts and resource allocation based on their subscription plan.
Enforced By: QuotaService (backend/services/quota_service.py)
Entity Limits
Max Projects
- Maximum projects organization can create
- Default: 10 (Free plan)
- Field:
max_projects
Max Environments
- Maximum environments across all projects
- Default: 30 (Free plan)
- Field:
max_environments
Max Servers
- Maximum BYOS servers organization can add
- Default: 5 (Free plan)
- Field:
max_servers
Max Members
- Maximum team members (active memberships)
- Default: 20 (Free plan)
- Field:
max_members
Per-Environment Resource Limits
Max CPU Per Environment
- Maximum CPU cores for single environment
- Default: 2.0 cores
- Field:
max_cpu_per_env(NUMERIC(4,2))
Max RAM Per Environment
- Maximum RAM allocation for single environment
- Default: 4096 MB (4 GB)
- Field:
max_ram_per_env_mb(INTEGER)
Max Disk Per Environment
- Maximum disk allocation for single environment
- Default: 20 GB
- Field:
max_disk_per_env_gb(INTEGER)
Total Organization Quotas
Total CPU Quota
- Sum of all environment CPU allocations cannot exceed
- Default: 20.0 cores
- Field:
total_cpu_quota(NUMERIC(6,2))
Total RAM Quota
- Sum of all environment RAM cannot exceed
- Default: 40960 MB (40 GB)
- Field:
total_ram_quota_mb(INTEGER)
Total Disk Quota
- Sum of all environment disk cannot exceed
- Default: 200 GB
- Field:
total_disk_quota_gb(INTEGER)
Quota Calculation
Active Environments Only:
Quota service counts only is_active = true environments. When destroying environments, always set is_active = false to release quota.
Replica Resource Accounting (Sprint 2E40): PostgreSQL read replicas count as:
- 30% of primary CPU/RAM (e.g., primary=2 cores → replica=0.6 cores)
- 100% of primary disk (e.g., primary=20GB → replica=20GB)
API: GET /api/v1/organizations/{org_id}/quota
Response:
{
"projects_used": 8,
"projects_limit": 10,
"environments_used": 24,
"environments_limit": 30,
"servers_used": 3,
"servers_limit": 5,
"members_used": 15,
"members_limit": 20,
"cpu_used": 14.8,
"cpu_limit": 20.0,
"ram_used_mb": 32768,
"ram_limit_mb": 40960,
"disk_used_gb": 145,
"disk_limit_gb": 200
}Quota Warnings
Warning Thresholds:
- 80% utilization: Yellow warning indicator
- 95% utilization: Orange alert indicator
- 100% utilization: Red - creation blocked
Billing & Subscription
Current Plan Information
Subscription Model: Subscription (backend/models/billing.py)
Base Plans:
- Free: $0/month - 1 BYOS server or 1 small environment included
- Starter: $29/month - 3 servers or 3 environments
- Professional: $79/month - 10 servers or 10 environments
- Business: $199/month - 25 servers or 25 environments
- Enterprise: Custom pricing - unlimited
Billing Intervals:
- Monthly: Charged monthly
- Annual: 20% discount (charged annually)
Stripe Integration
Fields:
stripe_customer_id: Stripe customer referencestripe_subscription_id: Active subscription IDstripe_price_id: Base plan price ID
Subscription Status
Lifecycle States:
| Status | Description | Services |
|---|---|---|
trial | 14-day trial (Professional features) | Running |
active | Paid and operational | Running |
past_due | Payment failed, grace period | Running |
suspended | All payment retries failed | Stopped |
paused | User-initiated pause | Stopped |
cancelling | Scheduled to cancel at period end | Running |
expired | Cancelled, 30-day data retention | Stopped |
archived | Data deleted, account closed | N/A |
Billing Cycle
Current Period:
current_period_start: Period start timestampcurrent_period_end: Period end timestamp
Cancellation:
cancel_at_period_end: Flag for end-of-period cancellationcancelled_at: When cancellation was requestedcancel_at: Scheduled cancellation date
Viewing Billing Details
Navigation: Settings → Billing tab
Required Permission: org.billing.view
API: GET /api/v1/organizations/{org_id}/subscription
Response:
{
"id": "sub_1234567890",
"base_plan": "professional",
"status": "active",
"billing_interval": "monthly",
"monthly_price": 79.00,
"current_period_start": "2024-12-01T00:00:00Z",
"current_period_end": "2024-12-31T23:59:59Z",
"cancel_at_period_end": false,
"stripe_customer_id": "cus_ABC123XYZ",
"stripe_subscription_id": "sub_DEF456UVW"
}Upgrading/Downgrading Plans
Process:
- Navigate to Billing → Change Plan
- Select new plan
- Review pro-rated charges
- Confirm change
- Redirect to Stripe Checkout (for upgrades)
- Downgrade scheduled for period end
Immediate Upgrades: Applied instantly with pro-rated charges
Scheduled Downgrades: Applied at current period end to avoid refund complexity
Danger Zone
Transfer Organization Ownership
API: POST /api/v1/organizations/{org_id}/transfer-ownership
Required Permission: org.delete (OWNER role only)
Process:
- Select new owner from organization members
- Confirm with type-to-confirm pattern
- New owner receives email notification
- Original owner becomes ADMIN role
- New owner assumes OWNER role
Request Body:
{
"new_owner_user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"confirmation": "transfer ownership"
}Validation:
- New owner must be active organization member
- Cannot transfer to yourself
- Must type exact confirmation phrase
Delete Organization
API: DELETE /api/v1/organizations/{org_id}
Required Permission: org.delete (OWNER role only)
Safety Checks:
- Active environments prevent deletion
- Active subscriptions must be cancelled first
- Type-to-confirm: Organization slug
- Confirmation modal with impact summary
Soft Delete:
- Sets
is_active = false - Resources remain in database for 30 days
- Can be recovered by support within retention period
- After 30 days: Hard delete via cleanup job
Process:
- Check for active resources
- Display impact summary:
- X projects will be deleted
- Y environments will be destroyed
- Z members will lose access
- Type organization slug to confirm
- Execute deletion
Request (No body required):
DELETE /api/v1/organizations/{org_id}
Authorization: Bearer {jwt_token}Response (200 OK):
{
"message": "Organization deleted successfully"
}Error: Active Resources (400 Bad Request):
{
"detail": "Cannot delete organization with 5 active environments. Please destroy all environments first."
}Error: Not Owner (403 Forbidden):
{
"detail": "Only the organization owner can delete the organization."
}Archive Organization (Future)
Planned feature for temporarily disabling organization without deletion.
Organization API Keys (Future Enhancement)
Planned feature for automation and CI/CD integration.
Features:
- Generate API keys for programmatic access
- Scope keys to specific permissions
- Set expiration dates
- Rotate keys without downtime
- Revoke compromised keys instantly
- Audit log all API key usage
Use Cases:
- CI/CD pipelines (GitHub Actions, GitLab CI)
- Infrastructure as Code (Terraform, Ansible)
- Custom integrations and webhooks
- Monitoring and alerting systems
Audit Log
Organization Activity Tracking
All organization-level actions are logged to audit_logs table for compliance and debugging.
Model: AuditLog (backend/models/audit.py)
Logged Actions:
| Action Code | Description |
|---|---|
org_created | Organization created |
org_updated | Organization settings modified |
org_deleted | Organization deactivated |
org_member_added | Member invited/added |
org_member_removed | Member removed |
org_member_role_changed | Member role updated |
Audit Log Entry Structure
interface AuditLog {
id: string // UUID
timestamp: string // ISO 8601
user_id: string // Actor UUID
user_email: string // Actor email (denormalized)
action: AuditAction // Enum value
organization_id: string // Organization context
resource_type: string // 'organization', 'member', etc.
resource_id: string // Affected resource UUID
resource_name: string // Human-readable name
details: object // JSON with old/new values
ip_address: string // Client IP (IPv4/IPv6)
user_agent: string // Client user agent
request_id: string // Request trace ID
}Example Audit Entry
Action: Organization settings updated
{
"id": "log_abc123",
"timestamp": "2024-12-10T14:22:13Z",
"user_id": "user_xyz789",
"user_email": "admin@acme.com",
"action": "org_updated",
"organization_id": "org_acme_corp",
"resource_type": "organization",
"resource_id": "org_acme_corp",
"resource_name": "Acme Corporation",
"details": {
"changed_fields": ["name", "description"],
"old_values": {
"name": "Acme Corp",
"description": null
},
"new_values": {
"name": "Acme Corporation",
"description": "Leading provider of digital solutions"
}
},
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"request_id": "req_def456ghi"
}Viewing Audit Logs
API: GET /api/v1/audit-logs?organization_id={org_id}
Query Parameters:
organization_id: Filter by organizationuser_id: Filter by actoraction: Filter by action typeresource_type: Filter by resourcestart_date: Date range startend_date: Date range endlimit: Results per page (default: 50)offset: Pagination offset
Response:
{
"items": [
{...audit_log_entry...}
],
"total": 247,
"limit": 50,
"offset": 0
}Permissions
Permission Matrix System (Sprint 2E21)
Organization settings leverage the Permission Matrix for fine-grained access control.
Key Permissions:
| Permission Code | Resource | Action | Description |
|---|---|---|---|
org.view | Organization | View | View organization details |
org.settings.view | Settings | View | View organization settings |
org.settings.update | Settings | Update | Modify organization settings |
org.delete | Organization | Delete | Delete organization (OWNER only) |
org.members.list | Members | List | View member list |
org.members.invite | Members | Invite | Add new members |
org.members.remove | Members | Remove | Remove members |
org.members.update_role | Members | Update Role | Change member roles |
org.storage.list | Storage | List | View storage configs |
org.storage.create | Storage | Create | Add storage config |
org.dns.list | DNS | List | View DNS configs |
org.git.list | Git | List | View Git connections |
org.billing.view | Billing | View | View billing details |
System Roles
Pre-defined roles with permission sets:
| Role | Permission Level | Use Case |
|---|---|---|
org_owner | Full access | Organization creator |
org_admin | All except delete | Administrator |
org_member | Standard access | Regular developer |
project_admin | Project-scoped | Project manager |
project_member | Read + deploy | Project contributor |
Custom Roles
Organizations can create custom roles with specific permission combinations.
API: POST /api/v1/organizations/{org_id}/roles
Required Permission: org.permissions.manage
Best Practices
Organization Naming
Naming Conventions:
- Use full company/team name (e.g., "Acme Corporation" not "Acme")
- Avoid abbreviations unless universally recognized
- Match legal entity name for billing alignment
Slug Best Practices:
- Keep short and memorable (e.g., "acme" not "acme-corporation-inc")
- Use hyphens for multi-word organizations
- Cannot contain spaces or special characters
- Plan carefully - slug change impacts deployed environments
Security Recommendations
Access Control:
- Limit OWNER role to 1-2 trusted individuals
- Use ADMIN role for day-to-day administration
- Grant least privilege - use DEVELOPER for regular team members
- Review member list quarterly for stale accounts
Settings Protection:
- Enable 2FA for all OWNER and ADMIN users
- Use separate billing email from personal accounts
- Store logo images on CDN with proper access controls
- Audit settings changes via audit log
Quota Management
Monitoring:
- Set up alerts at 80% quota utilization
- Review usage monthly to right-size plan
- Destroy unused environments to free quota
- Plan replica deployments (count 30% CPU/RAM)
Optimization:
- Use BYOS servers for cost efficiency at scale
- Consolidate small environments when possible
- Archive inactive projects to free entity limits
- Upgrade plan before hitting hard limits
Backup Before Dangerous Operations
Pre-deletion Checklist:
- Export audit logs for compliance
- Backup project configurations
- Download critical environment data
- Document active integrations
- Notify team members of pending deletion
- Cancel subscriptions to avoid charges
- Verify no active deployments
Troubleshooting
Cannot Update Settings
Issue: "You don't have permission to update organization settings."
Causes:
- Missing
org.settings.updatepermission - Not a member of organization
- Organization is inactive
Solution:
- Check permission via
GET /api/v1/users/me/abilities?organization_id={org_id} - Verify organization membership
- Contact organization OWNER/ADMIN for permission grant
Organization Deletion Blocked
Issue: "Cannot delete organization with active resources."
Causes:
- Active environments still deployed
- Active subscription not cancelled
- Pending invoices unpaid
Solution:
- Navigate to Projects → Environments
- Destroy all active environments
- Wait 2-5 minutes for full cleanup
- Cancel subscription via Billing tab
- Retry deletion
Slug Already Taken
Issue: "Organization with slug 'acme' already exists"
Causes:
- Another organization using same slug
- Previously deleted organization in retention period
Solution:
- Choose different slug (e.g., "acme-corp", "acme-inc")
- Contact support if legitimate conflict
- Wait 30 days if you own deleted organization
Quota Warning Not Updating
Issue: Usage shows 80% but can still create resources
Causes:
- Quota calculation cron job runs every 5 minutes
- Recent environment destruction not reflected yet
- Cache not invalidated
Solution:
- Wait 5 minutes for quota recalculation
- Hard refresh browser (Ctrl+Shift+R)
- Check
is_active = falseon destroyed environments - Contact support if persistent
Missing Billing Tab
Issue: Billing tab not visible in settings
Causes:
- Missing
org.billing.viewpermission - Not authenticated properly
- Organization on legacy billing system
Solution:
- Verify JWT token not expired
- Check abilities:
useAbilities({ organizationId }) - Confirm organization has
stripe_customer_id - Contact support for legacy account migration
Settings Not Saving
Issue: Changes lost after page refresh
Causes:
- API request failing silently
- Browser localStorage corruption
- Concurrent updates from another session
Solution:
- Open browser DevTools → Network tab
- Check for 400/403/500 errors on PATCH request
- Clear browser cache and cookies
- Disable browser extensions (ad blockers)
- Check audit log for conflicting changes
Related Documentation
- Organization Overview - Organization concepts and architecture
- Team Management - Member roles and permissions
- Permission Matrix - Role-based access control
- Billing & Plans - Subscription management
- Quota System - Resource limits and tracking
- Audit Logging - Activity tracking and compliance
API Reference Summary
Organization Endpoints
| Method | Endpoint | Description | Permission |
|---|---|---|---|
GET | /api/v1/organizations | List organizations | org.view |
POST | /api/v1/organizations | Create organization | Authenticated |
GET | /api/v1/organizations/{org_id} | Get organization | org.view |
PATCH | /api/v1/organizations/{org_id} | Update settings | org.settings.update |
DELETE | /api/v1/organizations/{org_id} | Delete organization | org.delete |
GET | /api/v1/organizations/{org_id}/members | List members | org.members.list |
POST | /api/v1/organizations/{org_id}/members | Add member | org.members.invite |
PATCH | /api/v1/organizations/{org_id}/members/{user_id} | Update member | org.members.update_role |
DELETE | /api/v1/organizations/{org_id}/members/{user_id} | Remove member | org.members.remove |
GET | /api/v1/organizations/{org_id}/quota | Get quota usage | org.view |
POST | /api/v1/organizations/{org_id}/transfer-ownership | Transfer ownership | org.delete |
Request/Response Models
OrganizationUpdate Schema:
interface OrganizationUpdate {
name?: string // 1-255 chars
description?: string | null
website?: string | null // URL format
logo_url?: string | null // Max 512 chars
settings?: Record<string, any> // Flexible JSON
}OrganizationResponse Schema:
interface OrganizationResponse {
id: string // UUID
name: string
slug: string
description: string | null
website: string | null
logo_url: string | null
is_active: boolean
settings: Record<string, any>
billing_email: string | null
member_count: number | null
project_count: number | null
created_at: string // ISO 8601
updated_at: string // ISO 8601
}Last Updated: December 11, 2024 - Sprint 2E41 (Documentation Initiative)
Backend Reference Files:
/backend/models/organization.py- Organization model definition/backend/schemas/organization.py- Pydantic schemas/backend/api/v1/routes/organizations.py- API endpoints/backend/services/quota_service.py- Quota enforcement/backend/models/billing.py- Subscription and billing