Roles & permissions
Roles control what each user and API key can do inside an organization. A role is a named bundle of permissions, and every permission grants a single scope on a single resource. OpusDNS ships a set of built-in roles that cover the most common needs, and you can define your own custom roles when you need a more specific set of permissions.
Roles are scoped to an organization. A user or API key in a suborganization can only ever hold a role defined in — and limited to — that suborganization.
Permissions
A permission is written as a resource:scope string, for example
domains:read or dns:manage. The scope describes what level of access the
permission grants:
| Scope | Grants |
|---|---|
read |
View resources of this type (list and get). |
manage |
Create and update resources of this type. |
delete |
Delete resources of this type. |
Permissions exist for each resource area of the API:
organization, domains, contacts, dns, hosts, email_forwards,
domain_forwards, parking, events, jobs, billing, users, api_keys,
registrar_credentials, tags, audit_logs
To retrieve the exact catalog of permissions a custom role may grant, call:
curl "$OPUSDNS_API_BASE/v1/organizations/role-permissions" \
--header "X-Api-Key: $OPUSDNS_API_KEY"
{
"permissions": [
"audit_logs:read",
"billing:manage",
"billing:read",
"contacts:delete",
"contacts:manage",
"contacts:read",
"dns:delete",
"dns:manage",
"dns:read",
"domains:delete",
"domains:manage",
"domains:read"
]
}
Built-in roles
Built-in roles are maintained by OpusDNS and are available in every organization. They are immutable — you cannot change or delete them.
| Role | What it grants |
|---|---|
owner |
Full access. The organization's first user / super-admin. Report-only — see below. |
admin |
Full access to every resource in the organization. |
viewer |
Read-only access across domains, DNS, contacts, hosts, forwarding, parking, events, jobs, and users. |
domain_manager |
Full access (read/manage/delete) to domains, DNS, contacts, hosts, email & domain forwarding, parking, events, and jobs. |
dns_manager |
Full access to DNS zones, email forwarding, and domain forwarding. |
billing_manager |
Read and manage billing. |
owner is report-only. It identifies the organization's first user and is never assigned through the role API — it is established when the organization is created. The remaining built-in roles are freely assignable.
Custom roles
When the built-in roles don't fit, create a custom role with exactly the permissions you need. Custom roles are owned by your organization.
Each custom role has a label — a snake_case identifier derived from its
display name (for example, the name Support Staff produces the label
support_staff). The label is unique within the organization and is used as the
URL path parameter for the role.
Custom roles cannot grant the escalation-bearing admin or owner permissions. Use the built-in admin role when full access is required.
Listing roles
List every role assignable in the organization — the built-in roles plus your custom roles:
curl "$OPUSDNS_API_BASE/v1/organizations/roles" \
--header "X-Api-Key: $OPUSDNS_API_KEY"
[
{
"label": "admin",
"name": "Admin",
"description": "Full access to the organization.",
"built_in": true,
"permissions": ["organization:manage", "domains:manage", "dns:manage"]
},
{
"label": "support_staff",
"name": "Support Staff",
"description": "Read domains and manage DNS.",
"built_in": false,
"permissions": ["domains:read", "dns:manage"],
"created_on": "2026-06-16T10:00:00Z",
"updated_on": "2026-06-16T10:00:00Z"
}
]
Creating a custom role
curl "$OPUSDNS_API_BASE/v1/organizations/roles" \
--request POST \
--header "X-Api-Key: $OPUSDNS_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"name": "Support Staff",
"description": "Read domains and manage DNS.",
"permissions": ["domains:read", "dns:manage"]
}'
| Field | Required | Description |
|---|---|---|
name |
Yes | Display name (1–64 chars). The label is derived from this. |
description |
No | Free-text description of the role. |
permissions |
Yes | List of resource:scope strings the role grants. |
The response is the created role, including its generated label:
{
"label": "support_staff",
"name": "Support Staff",
"description": "Read domains and manage DNS.",
"built_in": false,
"permissions": ["dns:manage", "domains:read"],
"created_on": "2026-06-16T10:00:00Z",
"updated_on": "2026-06-16T10:00:00Z"
}
A name that resolves to a built-in role label, or a duplicate name within the
organization, returns 409 Conflict.
Getting a single role
Retrieve any role — built-in or custom — by its label:
curl "$OPUSDNS_API_BASE/v1/organizations/roles/support_staff" \
--header "X-Api-Key: $OPUSDNS_API_KEY"
Updating a custom role
Update a custom role's name, description, and/or permissions. Provide only the
fields you want to change. When permissions is included it is a full
replacement of the role's permission set.
curl "$OPUSDNS_API_BASE/v1/organizations/roles/support_staff" \
--request PATCH \
--header "X-Api-Key: $OPUSDNS_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"permissions": ["domains:read", "dns:manage", "dns:delete"]
}'
Permission changes take effect immediately for every user and API key that holds the role. Built-in roles are immutable and return 409 Conflict if you attempt to update them.
Deleting a custom role
curl "$OPUSDNS_API_BASE/v1/organizations/roles/support_staff" \
--request DELETE \
--header "X-Api-Key: $OPUSDNS_API_KEY"
A role cannot be deleted while it is still assigned to any user or API key — reassign those subjects first, or the request returns 409 Conflict. Built-in roles cannot be deleted.
Assigning roles
Roles are granted per subject:
- Users — assign a role with the User management role endpoints.
-
API keys — every API key carries its own role, granted when the key is
issued (defaulting to
admin, full access). A key's role governs its permissions exactly like a user's role, and applies independently of the user who created the key. Choose or change a key's role from the API Credentials page of the OpusDNS Dashboard. Grant each key the narrowest role it needs.
You can assign either a built-in role (by name, for example viewer) or a
custom role (by its label, for example support_staff).

