Organizations in Zitadel provide multi-tenancy and isolation for users and resources. This guide covers how to create and manage organizations, configure domains, and handle organization settings.
Prerequisites
- Zitadel Ruby SDK installed and configured
- Authentication configured with appropriate permissions
- Required permissions:
org.read, org.write, org.create, org.delete
Creating Organizations
Create a New Organization
Organizations are created with at least one administrative user who receives the ORG_OWNER role.
require 'zitadel-client'
client = Zitadel::Client::Zitadel.with_access_token(
"https://example.zitadel.cloud",
"your_access_token"
)
request = Zitadel::Client::OrganizationServiceAddOrganizationRequest.new(
name: "Acme Corporation",
admins: [
{
user_id: "admin_user_id_here",
roles: ["ORG_OWNER"]
}
]
)
begin
response = client.organizations.add_organization(request)
puts "Organization created with ID: #{response.organization_id}"
puts "Details: #{response.details}"
rescue Zitadel::Client::ApiError => e
puts "Error creating organization: #{e.message}"
end
If no roles are specified for the admin users, they will automatically be granted the ORG_OWNER role.
Create Organization with Multiple Admins
request = Zitadel::Client::OrganizationServiceAddOrganizationRequest.new(
name: "Enterprise Inc",
admins: [
{
user_id: "user_1_id",
roles: ["ORG_OWNER"]
},
{
user_id: "user_2_id",
roles: ["ORG_OWNER_VIEWER"]
},
{
user_id: "user_3_id",
roles: ["ORG_USER_MANAGER"]
}
]
)
response = client.organizations.add_organization(request)
puts "Organization created: #{response.organization_id}"
List Organizations
request = Zitadel::Client::OrganizationServiceListOrganizationsRequest.new(
queries: [],
sorting_column: "FIELD_NAME_CREATION_DATE",
asc: false
)
response = client.organizations.list_organizations(request)
response.result.each do |org|
puts "Organization: #{org.name}"
puts " ID: #{org.id}"
puts " State: #{org.state}"
puts " Created: #{org.details.creation_date}"
end
Search Organizations by Name
request = Zitadel::Client::OrganizationServiceListOrganizationsRequest.new(
queries: [
{
name_query: {
name: "Acme",
method: "TEXT_QUERY_METHOD_CONTAINS"
}
}
]
)
response = client.organizations.list_organizations(request)
puts "Found #{response.result.length} organizations"
Updating Organizations
Update Organization Name
request = Zitadel::Client::OrganizationServiceUpdateOrganizationRequest.new(
organization_id: "org_id_here",
name: "Acme Corporation - Updated"
)
response = client.organizations.update_organization(request)
puts "Organization updated"
Managing Organization Domains
Domains are used to identify which organization a user belongs to based on their email address.
Add a Domain
request = Zitadel::Client::OrganizationServiceAddOrganizationDomainRequest.new(
organization_id: "org_id_here",
domain: "acme.com"
)
response = client.organizations.add_organization_domain(request)
puts "Domain added: #{response.domain}"
Generate Domain Validation
After adding a domain, you need to verify ownership using DNS or HTTP validation.
request = Zitadel::Client::OrganizationServiceGenerateOrganizationDomainValidationRequest.new(
organization_id: "org_id_here",
domain: "acme.com",
type: "DOMAIN_VALIDATION_TYPE_DNS" # or DOMAIN_VALIDATION_TYPE_HTTP
)
response = client.organizations.generate_organization_domain_validation(request)
if response.validation_type == "DOMAIN_VALIDATION_TYPE_DNS"
puts "Add this DNS TXT record:"
puts " Token: #{response.token}"
puts " URL: #{response.url}"
elsif response.validation_type == "DOMAIN_VALIDATION_TYPE_HTTP"
puts "Place this file at the URL:"
puts " URL: #{response.url}"
puts " Token: #{response.token}"
end
Use add_organization_domain to register the domain with your organization.
Generate validation token
Call generate_organization_domain_validation to get DNS or HTTP validation details.
Add DNS record or HTTP file
For DNS: Add a TXT record with the provided token.
For HTTP: Host a file at the provided URL containing the token.
Call verify_organization_domain to complete the verification process.
Verify Domain
request = Zitadel::Client::OrganizationServiceVerifyOrganizationDomainRequest.new(
organization_id: "org_id_here",
domain: "acme.com"
)
begin
response = client.organizations.verify_organization_domain(request)
puts "Domain verified successfully"
rescue Zitadel::Client::ApiError => e
puts "Verification failed: #{e.message}"
end
List Organization Domains
request = Zitadel::Client::OrganizationServiceListOrganizationDomainsRequest.new(
organization_id: "org_id_here"
)
response = client.organizations.list_organization_domains(request)
response.result.each do |domain|
puts "Domain: #{domain.domain_name}"
puts " Verified: #{domain.is_verified}"
puts " Primary: #{domain.is_primary}"
end
Remove a Domain
request = Zitadel::Client::OrganizationServiceDeleteOrganizationDomainRequest.new(
organization_id: "org_id_here",
domain: "acme.com"
)
response = client.organizations.delete_organization_domain(request)
puts "Domain removed"
Removing a domain will prevent users with email addresses from that domain from logging in using the domain-based login.
Metadata allows you to store custom key-value data associated with an organization.
require 'base64'
metadata_value = {
subscription_tier: "enterprise",
max_users: 1000,
features: ["sso", "audit_logs", "custom_branding"]
}.to_json
request = Zitadel::Client::OrganizationServiceSetOrganizationMetadataRequest.new(
organization_id: "org_id_here",
key: "subscription",
value: Base64.strict_encode64(metadata_value)
)
response = client.organizations.set_organization_metadata(request)
puts "Metadata updated"
Metadata values must be base64 encoded before sending to the API.
request = Zitadel::Client::OrganizationServiceListOrganizationMetadataRequest.new(
organization_id: "org_id_here",
queries: []
)
response = client.organizations.list_organization_metadata(request)
response.result.each do |metadata|
decoded_value = Base64.strict_decode64(metadata.value)
puts "Key: #{metadata.key}"
puts "Value: #{decoded_value}"
end
request = Zitadel::Client::OrganizationServiceDeleteOrganizationMetadataRequest.new(
organization_id: "org_id_here",
key: "subscription"
)
response = client.organizations.delete_organization_metadata(request)
puts "Metadata deleted"
Organization Lifecycle
Deactivate Organization
Deactivating an organization prevents all users in that organization from logging in.
request = Zitadel::Client::OrganizationServiceDeactivateOrganizationRequest.new(
organization_id: "org_id_here"
)
response = client.organizations.deactivate_organization(request)
puts "Organization deactivated"
Activate Organization
request = Zitadel::Client::OrganizationServiceActivateOrganizationRequest.new(
organization_id: "org_id_here"
)
response = client.organizations.activate_organization(request)
puts "Organization activated"
Delete Organization
Deleting an organization removes it and all associated resources (users, projects, grants).
request = Zitadel::Client::OrganizationServiceDeleteOrganizationRequest.new(
organization_id: "org_id_here"
)
response = client.organizations.delete_organization(request)
puts "Organization deleted"
Deleting an organization is permanent and will remove all resources including users, projects, and grants. Users will not be able to log in.
Complete Workflow Example
Here’s a complete example of creating and configuring an organization:
require 'zitadel-client'
require 'base64'
client = Zitadel::Client::Zitadel.with_private_key(
"https://example.zitadel.cloud",
"path/to/service-account.json"
)
begin
# 1. Create organization
org_request = Zitadel::Client::OrganizationServiceAddOrganizationRequest.new(
name: "Tech Startup Inc",
admins: [
{ user_id: "admin_user_id" }
]
)
org_response = client.organizations.add_organization(org_request)
org_id = org_response.organization_id
puts "Created organization: #{org_id}"
# 2. Add domain
domain_request = Zitadel::Client::OrganizationServiceAddOrganizationDomainRequest.new(
organization_id: org_id,
domain: "techstartup.com"
)
client.organizations.add_organization_domain(domain_request)
puts "Added domain: techstartup.com"
# 3. Generate DNS validation
validation_request = Zitadel::Client::OrganizationServiceGenerateOrganizationDomainValidationRequest.new(
organization_id: org_id,
domain: "techstartup.com",
type: "DOMAIN_VALIDATION_TYPE_DNS"
)
validation_response = client.organizations.generate_organization_domain_validation(validation_request)
puts "DNS Token: #{validation_response.token}"
# 4. Set metadata
metadata = { tier: "starter", created_by: "api" }.to_json
metadata_request = Zitadel::Client::OrganizationServiceSetOrganizationMetadataRequest.new(
organization_id: org_id,
key: "config",
value: Base64.strict_encode64(metadata)
)
client.organizations.set_organization_metadata(metadata_request)
puts "Metadata configured"
puts "Organization setup complete!"
rescue Zitadel::Client::ApiError => e
puts "Error: #{e.message}"
end
Best Practices
Always verify organization domains to ensure secure user identification and prevent domain spoofing.
Implement proper access control
Assign appropriate organization roles (ORG_OWNER, ORG_USER_MANAGER, etc.) based on user responsibilities.
Use organization metadata to store application-specific configuration and settings.
Handle deactivation gracefully
Before deactivating an organization, notify users and provide alternatives if needed.
Monitor organization lifecycle
Track organization creation, modification, and deletion events for audit purposes.
Next Steps