Skip to content

Tenant Creation

Create and configure tenants in Mantis for multi-tenant deployments.

Tenants represent organizational units—customers, teams, or departments—that have isolated access to Mantis resources.

Terminal window
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"description": "Acme Corporation production tenant",
"contact_email": "ops@acme.com",
"logo_url": "https://acme.com/logo.png"
}' \
"https://api.mantis.local/api/v1/tenants"
FieldRequiredValidationDescription
nameYes1-255 chars, uniqueTenant display name
descriptionNoNoneTenant description
contact_emailNoValid emailPrimary contact
logo_urlNoNoneBranding logo URL
{
"id": "019b937d-4862-84ae-9c2a-6ac230cc4c7e",
"name": "Acme Corp",
"description": "Acme Corporation production tenant",
"contact_email": "ops@acme.com",
"logo_url": "https://acme.com/logo.png",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": null
}
  1. Navigate to Tenancy → Tenants
  2. Click New Tenant
  3. Fill in the tenant form:
    • Name: Unique identifier for the tenant
    • Description: Optional details about the tenant
    • Contact Email: Primary contact for notifications
    • Logo URL: Optional branding image
  4. Click Create Tenant

After creating a tenant, grant access to solutions by attaching them with specific environments.

Terminal window
# Attach solution to tenant for a specific environment
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"solution_id": "019b937d-4862-8ccc-ddee-3344556677bb",
"environment_id": "019b937d-fe4d-80c3-ac78-f9b11e725443"
}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/solutions"

A tenant can have different solution access per environment:

Terminal window
# Grant access to WebApp in Production
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"solution_id": "019b937d-5c9d-819f-a89c-41c299a3872e", "environment_id": "019b937d-806c-8867-a7a0-24c41b64c969"}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/solutions"
# Grant access to WebApp in Staging
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"solution_id": "019b937d-5c9d-819f-a89c-41c299a3872e", "environment_id": "019b937d-739d-8cd8-a8af-636d98857494"}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/solutions"
  1. Navigate to Admin → Tenants → select tenant
  2. Go to Solutions tab
  3. Click Attach Solution
  4. Select:
    • Solution: Choose from available solutions
    • Environment: Target environment for the entitlement
  5. Click Attach
Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/solutions"

Response:

[
{
"solution_id": "019b937d-5c9d-819f-a89c-41c299a3872e",
"solution_name": "WebApp",
"environment_id": "019b937d-806c-8867-a7a0-24c41b64c969",
"environment_name": "Production",
"created_at": "2026-01-15T09:12:03Z"
},
{
"solution_id": "019b937d-5c9d-819f-a89c-41c299a3872e",
"solution_name": "WebApp",
"environment_id": "019b937d-739d-8cd8-a8af-636d98857494",
"environment_name": "Staging",
"created_at": "2026-01-15T09:12:41Z"
}
]
Terminal window
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/solutions/$SOLUTION_ID/environments/$ENV_ID"

Bind targets to tenants for deployment scope.

Terminal window
# Assign target to tenant (all environments)
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_id": "019b937d-e1be-8b01-a285-68e757fa6139"
}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/targets"
# Assign target to tenant for specific environment
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_id": "019b937d-a975-8f41-ac94-8b81b3f7c813",
"environment_id": "019b937d-806c-8867-a7a0-24c41b64c969"
}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/targets"
ScopeConfigurationUse Case
All environmentsenvironment_id: nullShared infrastructure
Single environmentenvironment_id: <env-uuid>Dedicated production servers
  1. Navigate to Tenancy → Tenants → select tenant
  2. Go to Targets tab
  3. Click Attach Target
  4. Select:
    • Target: Choose from available targets
    • Environment: (Optional) Limit to specific environment
  5. Click Attach
Terminal window
# All targets
curl -H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/targets"
# Filter by environment
curl -H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/targets?environment_id=019b937d-806c-8867-a7a0-24c41b64c969"
Terminal window
# Remove from specific environment
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/targets/$TARGET_ID?environment_id=019b937d-806c-8867-a7a0-24c41b64c969"
# Remove from all environments
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/targets/$TARGET_ID"

Variables are not attached to the tenant record. They live in variable sets, and it is the set that carries the tenant: give POST /variable-sets a tenant_id and every variable in that set belongs to that tenant. A set created without a tenant_id is global.

Terminal window
# Create a variable set owned by this tenant
VARIABLE_SET_ID=$(curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "acme-defaults",
"description": "Defaults for Acme deployments",
"tenant_id": "'"$TENANT_ID"'"
}' \
"https://api.mantis.local/api/v1/variable-sets" | jq -r '.id')
# Add a variable to it
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "api_endpoint",
"value": "https://api.acme.local/v2",
"variable_type": "string"
}' \
"https://api.mantis.local/api/v1/variable-sets/$VARIABLE_SET_ID/variables"

variable_type is one of string, sensitive, certificate or execution_pool, and defaults to string. Use sensitive to store the value encrypted at rest; reads of that variable return "********" in value and "is_encrypted": true, so a set-but-withheld value is distinguishable from an unset one.

Add "scope_environment_id" to narrow a variable to one environment. It is the only scope this endpoint accepts, and the request body is declared deny_unknown_fields, so key, is_encrypted, scope_tenant_id and scope_solution_id are each rejected with 422 Unprocessable Entity rather than being ignored.

Resolution across the ten scopes is described in Multi-Tenancy Overview. There is no resolved-variables endpoint — resolution happens during deployment planning.

Organize tenants with tags for filtering and grouping.

Terminal window
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tag_ids": ["019b937d-887b-86e6-ae7f-5aa6214f0833", "019b937d-946c-8d41-a642-2b58fe4e3ec2"]
}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/tags"
Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/tags"
Terminal window
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tag_ids": ["019b937d-887b-86e6-ae7f-5aa6214f0833"]
}' \
"https://api.mantis.local/api/v1/tenants/$TENANT_ID/tags"

After tenant creation, assign users to the tenant.

Terminal window
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "alice",
"email": "alice@acme.com",
"tenant_id": "019b937d-4862-84ae-9c2a-6ac230cc4c7e",
"role_ids": ["019b93a2-1f04-8c7e-b6d1-2a5f0c83e714"]
}' \
"https://api.mantis.local/api/v1/admin/users"

The body rejects any field it does not declare, and password is not one of them: the account is created in an invited state with no usable password, and Mandible emails an activation link the user completes themselves. role_ids are role UUIDs — list them with GET /api/v1/admin/roles.

To set a password directly instead — for an account that will not go through the emailed invitation — post to the user returned above:

Terminal window
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"new_password": "correct-horse-battery-staple"
}' \
"https://api.mantis.local/api/v1/admin/users/$USER_ID/password"

The password must meet the platform complexity rules, and the reset also revokes the user’s OAuth2 tokens, API keys and active sessions.

Configure identity provider with tenant mapping:

  1. Set up OIDC provider for the tenant
  2. Configure role mappings
  3. Users created on first SSO login are automatically tenant-scoped

See Identity Providers for SSO configuration.

Full tenant onboarding script:

#!/bin/bash
set -e
API_URL="https://api.mantis.local/api/v1"
TOKEN="your-admin-token"
# 1. Create tenant
TENANT=$(curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"description": "Enterprise customer",
"contact_email": "ops@acme.com"
}' \
"$API_URL/tenants")
TENANT_ID=$(echo $TENANT | jq -r '.id')
echo "Created tenant: $TENANT_ID"
# The solution, environments and targets to hand to the tenant, by UUID
SOLUTION_ID="019b937d-5c9d-819f-a89c-41c299a3872e"
ENVIRONMENT_IDS=("019b937d-806c-8867-a7a0-24c41b64c969" "019b937d-739d-8cd8-a8af-636d98857494")
TARGET_IDS=("019b937d-e1be-8b01-a285-68e757fa6139" "019b937d-a975-8f41-ac94-8b81b3f7c813")
# 2. Attach solutions to environments
for ENV in "${ENVIRONMENT_IDS[@]}"; do
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"solution_id\": \"$SOLUTION_ID\", \"environment_id\": \"$ENV\"}" \
"$API_URL/tenants/$TENANT_ID/solutions"
done
echo "Attached solutions"
# 3. Assign targets
for TARGET in "${TARGET_IDS[@]}"; do
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"target_id\": \"$TARGET\"}" \
"$API_URL/tenants/$TENANT_ID/targets"
done
echo "Assigned targets"
# 4. Set variables — the set carries the tenant, the variables live inside it
VARIABLE_SET_ID=$(curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"acme-defaults\",
\"tenant_id\": \"$TENANT_ID\"
}" \
"$API_URL/variable-sets" | jq -r '.id')
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"database_url\",
\"value\": \"postgresql://db.acme.local:5432/webapp\",
\"variable_type\": \"string\"
}" \
"$API_URL/variable-sets/$VARIABLE_SET_ID/variables"
echo "Set variables"
# 5. Add tags
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"tag_ids": ["019b937d-4d29-86bf-a4f8-f9a4bbf364e1", "019b937d-887b-86e6-ae7f-5aa6214f0833"]}' \
"$API_URL/tenants/$TENANT_ID/tags"
echo "Added tags"
echo "Tenant setup complete: $TENANT_ID"

Tenant creation and modifications are logged:

{
"event_type": "tenant.created",
"event_category": "tenant_management",
"severity": "info",
"actor_type": "user",
"actor_id": "019b937d-4862-8e07-ac3a-1b8589d1b807",
"actor_name": "admin",
"resource_type": "tenant",
"resource_id": "019b937d-4862-84ae-9c2a-6ac230cc4c7e",
"resource_name": "Acme Corp",
"tenant_id": null,
"action": "create_tenant",
"outcome": "success",
"occurred_at": "2024-02-01T10:00:00Z"
}

The entry’s action is create_tenant (the operation label) and its tenant_id is null — the builder does not stamp the tenant scope for this event, so the new tenant is identified only by resource_id / resource_name.

Error: Tenant with name 'Acme Corp' already exists

Tenant names must be unique. Use a different name or include a suffix.

Error: Solution 019b937d-4862-8ccc-ddee-3344556677bb not found

Verify the solution exists before attaching to tenant.

Error: Insufficient permissions

The response body contains error code INSUFFICIENT_PERMISSIONS. The required permission (tenants:create) appears in server-side audit logs. Assign the appropriate role to the user.