Namespace & Tenant Administration
The namespace overview covers what
a namespace is, the profiles
and linkages pages cover how
namespaces relate, and the
configuration pages cover what’s
inside one. This page is about the objects themselves: creating tenants,
navigating the tree, and — carefully — removing them. Conventions as
usual: $VSS / $TOKEN / $NSID on the curl side, an active profile on
the vsscli side.
The namespace object
Section titled “The namespace object”Every namespace is identified by a UUID and carries a small set of head metadata:
| Field | Meaning |
|---|---|
nsid |
The namespace UUID — its identity everywhere in the API |
parent_id |
The parent namespace (empty for a root namespace) |
partition_id |
The partition (customer account) that owns it |
running_version / latest_version |
The two configuration pointers (lifecycle) |
owner |
The user who created it |
last_modified |
Last head change |
Three of these are immutable for the namespace’s lifetime: the parent (a namespace is never re-grafted elsewhere in the tree), the partition, and the resource-authority mode (below). If any of them needs to be different, the path is a new namespace.
Permissions
Section titled “Permissions”| Operation | Requires | Held by |
|---|---|---|
| Read head, parent, children, tree | namespace:read on the namespace |
Every switch role, down to viewer and noc-viewer |
| List a partition’s namespaces | namespace:list on the partition |
Partition-scoped grants only — partition-admin, tenant-provisioner, and partition-scoped ops/read roles |
| Create a root namespace | namespace:create on the partition |
partition-admin, tenant-provisioner (partition-scoped) |
| Create a child namespace | namespace:create-child on the parent — no partition authority involved |
partition-admin, namespace-admin, provisioner |
| Delete, flush call status | namespace:delete on the namespace |
partition-admin only |
Two boundaries worth internalizing: a namespace-scoped admin cannot
enumerate the partition (use tree on your own subtree instead), and
deletion is deliberately withheld from namespace-admin — a tenant
admin can do anything inside their subtree but cannot destroy it; that
stays with the partition.
Navigating
Section titled “Navigating”# Head metadata — running vs latest tells you if a change is stagedcurl -s "$VSS/v1/ns/$NSID" -H "Authorization: Bearer $TOKEN"
# Structurecurl -s "$VSS/v1/ns/$NSID/parent" -H "Authorization: Bearer $TOKEN"curl -s "$VSS/v1/ns/$NSID/children" -H "Authorization: Bearer $TOKEN"curl -s "$VSS/v1/ns/$NSID/tree" -H "Authorization: Bearer $TOKEN" # nested subtree
# Partition-wide inventory (200/page; pass ?cursor= from next_cursor)curl -s "$VSS/v1/pt/$PTID/ns" -H "Authorization: Bearer $TOKEN"
# Immutable resource-authority modecurl -s "$VSS/v1/ns/$NSID/resource-authority" -H "Authorization: Bearer $TOKEN"vsscli ns show # active namespace (or: ns show <nsid-or-alias>)vsscli ns childrenvsscli ns tree # indented subtree; --quiet = flat nsid listvsscli ns list # whole partition, auto-paginatedvsscli ns list --partition <ptid>vsscli resources authority # document | managedvsscli working context: use and aliases
Section titled “vsscli working context: use and aliases”Most vsscli commands target the profile’s active namespace. vsscli ns use <nsid> switches it, and profile-local aliases save you the UUID
gymnastics:
vsscli ns alias add acme-corp 0197f9d2-…vsscli ns use acme-corpvsscli ns tree acme-corpvsscli ns alias listCreating a namespace
Section titled “Creating a namespace”A create request carries the namespace’s initial configuration
document as its body — the same YAML you’d config push, validated by
the same compiler (a child’s document compiles against its parent’s
running config, so inherited references resolve). The response returns the
new nsid and the initial version’s ULID.
The initial version is stored, not running: like any other configuration version it processes no calls until someone applies it. That two-step is useful — provisioning automation can create the tenant, let a human review, then promote.
# Root namespace (a new tenant at the top of your partition)curl -s -X POST "$VSS/v1/ns" \ -H "Authorization: Bearer $TOKEN" --data-binary @tenant.yaml# → 201 {"success":true,"nsid":"…","new_version":"01JD…"}
# Root namespace, naming the partition explicitly (required when your# token holds grants on more than one partition)curl -s -X POST "$VSS/v1/ns?partition=$PTID" \ -H "Authorization: Bearer $TOKEN" --data-binary @tenant.yaml
# Child namespace — the partition comes from the parent; only# create-child authority on the parent is checkedcurl -s -X POST "$VSS/v1/ns?parent=$PARENT_NSID" \ -H "Authorization: Bearer $TOKEN" --data-binary @customer.yaml
# Managed resource authority (immutable — decide now)curl -s -X POST "$VSS/v1/ns?authority=managed" \ -H "Authorization: Bearer $TOKEN" --data-binary @tenant.yaml
# Then promote the initial version when readycurl -s -X POST "$VSS/v1/ns/$NEW_NSID/config/latest/apply" \ -H "Authorization: Bearer $TOKEN"?authority= is document (default — resources authored in the YAML) or
managed (lines/trunks/ring-groups managed through the resources API; see
the overview). It cannot be
changed later — switching modes means a new namespace.
vsscli ns create -f tenant.yaml # rootvsscli ns create -f tenant.yaml --partition <ptid> # root, multi-partition tokenvsscli ns create -f customer.yaml --parent <nsid> # childvsscli ns create -f tenant.yaml --authority managed # immutable — decide nowvsscli ns create -f tenant.yaml --apply # create + promotevsscli ns create -f tenant.yaml --apply --use # …and make it activecat tenant.yaml | vsscli ns create --stdin--apply promotes the initial version immediately; --use points your
profile at the new namespace so follow-up commands land there.
--authority accepts document or managed and is rejected client-side
on anything else — worth the strictness, since a mis-keyed authority mode
can’t be fixed after creation.
Where the new namespace lands depends on the create shape. A child
inherits its parent’s partition — the request is authorized purely by
create-child on the parent, which is how a provisioner or
namespace-admin onboards customers without holding any partition-level
rights (naming both ?partition= and ?parent= together is rejected as
contradictory). A root create resolves the partition from your token’s
partition-scoped grant; if the token holds grants on several partitions,
name the target with ?partition= / --partition — the parameter is a
disambiguator, not an escalation, so naming a partition you hold no grant
on is simply denied.
Deleting a namespace
Section titled “Deleting a namespace”Deletion is a recursive cascade with no undo: the namespace, every
descendant namespace, all their configuration history, users, voicemail
boxes (including stored media), devices, and call records go. Because of
that, the API refuses a bare DELETE and demands an explicit handshake:
- DELETE without a confirmation body → 412 with an impact preview:
how many descendants, users, mailboxes, and devices would be destroyed,
plus an
example_body. - Re-send with the confirmation — a JSON body naming the exact
nsid(confirm_id) and a current unix timestamp (confirm_timestamp, accepted within one hour of server time). This forces the client to have seen the target, not just matched a URL pattern. - Live calls anywhere in the subtree → 409, listing the namespaces with calls up. Wait for them to end — or see the force path below.
# Step 1: request the preview (this is safe — nothing is deleted)curl -s -X DELETE "$VSS/v1/ns/$NSID" -H "Authorization: Bearer $TOKEN"# → 412 {"title":"Destructive action requires confirmation",# "descendants":3,"users":12,"vm_boxes":40,"peers":55,# "example_body":{"confirm_id":"…","confirm_timestamp":…}}
# Step 2: confirmcurl -s -X DELETE "$VSS/v1/ns/$NSID" \ -H "Authorization: Bearer $TOKEN" \ -d "{\"confirm_id\":\"$NSID\",\"confirm_timestamp\":$(date +%s)}"# → 204vsscli ns delete <nsid-or-alias>The CLI drives the handshake for you: it fetches the preview, shows the
impact counts, and asks you to type the nsid to confirm. --yes
skips the prompt for automation (the server-side handshake still runs
underneath).
When a stuck call blocks deletion
Section titled “When a stuck call blocks deletion”The live-call guard exists to stop you deleting a tenant mid-call. But
call-tracking rows can occasionally wedge (they normally expire on their
own within their TTL), leaving a namespace that refuses deletion with a
409 for a call that ended long ago. Two escape hatches, both gated on the
same namespace:delete authority:
# Clear stuck tracking rows — one call by ULID, or all of themcurl -s -X POST "$VSS/v1/ns/$NSID/flush-call-status" \ -H "Authorization: Bearer $TOKEN" -d '{"call":"all"}'
# Or let the cascade flush them itself: force bypasses ONLY the# live-call gate — the confirmation body is still requiredcurl -s -X DELETE "$VSS/v1/ns/$NSID?force=true" \ -H "Authorization: Bearer $TOKEN" \ -d "{\"confirm_id\":\"$NSID\",\"confirm_timestamp\":$(date +%s)}"vsscli ns flush-call-status all # or a single call ULIDvsscli ns delete <nsid> --forceTroubleshooting
Section titled “Troubleshooting”| Response | Meaning | Do |
|---|---|---|
412 on DELETE |
Missing/stale confirmation — the body carries the impact preview | Review the counts, re-send with confirm_id + fresh confirm_timestamp |
409 active calls on DELETE |
Live calls somewhere in the subtree (listed in the body) | Wait for them to end; if provably stuck, flush-call-status or --force |
401 on a root create |
Token has no partition-scoped grant | Root creation needs partition-level authority; child creation only needs create-child on the parent |
400 ambiguous partition on a root create |
Token holds grants on several partitions | Name the target: ?partition= / --partition |
400 partition + parent together |
The two parameters contradict — a child inherits its parent’s partition | Drop ?partition= from child creates |
400 validation errors on create |
The initial configuration failed compilation | Same fix loop as a config push |
404 on any read |
The nsid doesn’t exist | Check the id (ns list / ns tree) |
401 on any read |
The namespace exists but your grants don’t reach it | See how requests are authorized |