grant_unit_role

Give access on a unit

act entity tenant-private since 2026-10-01

Gives a member of the org a role template on one unit. It reaches every unit beneath that unit and adds to the member's org role. A member holds one role per unit, so giving another replaces it. You must administer the unit (an org admin, or admin on it or a unit above it), you can give only a role whose every permission you hold there, and a role carrying an org-wide permission (role.manage, api_key.read) or a Counterparty role cannot be given on a unit. You cannot change your own access. Giving the role the member already holds there changes nothing.

Contract

shape
entity
authority
act
evidence
none
subscription
false
permission
any one of: org.admin (carried by the caller's role template in the org `org_id`, or by a role held on the unit `unit_id` or any unit above it by a member of that org; without `unit_id`, by the template); org.member (carried by the caller's role template in the org `org_id`, or by a role held on the unit `unit_id` or any unit above it by a member of that org; without `unit_id`, by the template); a deactivated caller is refused first
idempotency
natural
emits
unit_role.granted, unit_role.replaced
MCP tool
grant_unit_role
decision_set
approve, reject, edit
REST
POST https://api.containertradehq.com/v1/cap/grant_unit_role

Through MCP this call returns an approval request first; see Approvals.

Idempotency: keyed by the contract's own inputs: the same inputs replay the first call's result

Inputs

nametyperequiredvalidation
org_idorg_idrequiredtradehq-org-id; 1 to 20 characters
The org whose tree the unit is in.
unit_idunit_idrequiredtradehq-unit-id
The unit the role is given on.
user_iduser_idrequiredtradehq-user-id; 1 to 20 characters
The member who will hold it.
role_template_idtextrequired1 to 20 characters
The role template: a shipped id (admin, ops, ...) or one of the org's own (RT...).

Response

  • org_id · string · The org.
  • unit_id · string · The unit it is held on.
  • unit_name · string · The unit's name.
  • user_id · string · Who holds it.
  • name · value · Their name, or their address when they have none.
  • role_template_id · string · The role template it carries.
  • role_title · string · That template's name.
  • role · string · admin when the template carries org.admin, else user.
  • previous_role_template_id · stringnull · The template it replaced on this unit; null for a new grant.
  • created_by · string · Who gave it.
  • created_at · string · When.
Full JSON Schema
{
  "type": "object",
  "properties": {
    "org_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-org-id",
      "description": "The org.",
      "x-data-class": "operational"
    },
    "unit_id": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
      "format": "tradehq-unit-id",
      "description": "The unit it is held on.",
      "x-data-class": "operational"
    },
    "unit_name": {
      "type": "string",
      "description": "The unit's name.",
      "x-data-class": "operational"
    },
    "user_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-user-id",
      "description": "Who holds it.",
      "x-data-class": "operational"
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "const": "•••",
          "description": "Masked by the caller's data policy."
        },
        {
          "type": "null",
          "description": "Denied by the caller's data policy."
        }
      ],
      "description": "Their name, or their address when they have none.",
      "x-data-class": "personal"
    },
    "role_template_id": {
      "type": "string",
      "description": "The role template it carries.",
      "x-data-class": "operational"
    },
    "role_title": {
      "type": "string",
      "description": "That template's name.",
      "x-data-class": "operational"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "user"
      ],
      "description": "admin when the template carries org.admin, else user.",
      "x-data-class": "operational"
    },
    "previous_role_template_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "The template it replaced on this unit; null for a new grant.",
      "x-data-class": "operational"
    },
    "created_by": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-user-id",
      "description": "Who gave it.",
      "x-data-class": "operational"
    },
    "created_at": {
      "type": "string",
      "format": "date-time",
      "description": "When.",
      "x-data-class": "operational"
    }
  },
  "required": [
    "org_id",
    "unit_id",
    "unit_name",
    "user_id",
    "name",
    "role_template_id",
    "role_title",
    "role",
    "previous_role_template_id",
    "created_by",
    "created_at"
  ],
  "additionalProperties": false
}

Refusals

  • own_access · 422 · You can't change your own access. Another admin can.
  • unit_outside_caller · 422 · You administer {your unit} and the units beneath it. {unit} is outside it.
  • not_a_member · 422 · {Name} isn't a member of this organization. Invite them in Members first.
  • unknown_role_template · 422 · the role template is neither shipped nor one of this org's own
  • counterparty_role · 422 · Counterparty roles are given in Guests, not on a unit.
  • permission_not_unit_scoped · 422 · {Role} carries {permission}, which applies to the whole organization and can't be given on a unit.
  • permission_above_caller · 422 · {Role} carries {permission}, which you don't hold on {unit}.
  • holder_above_caller · 422 · {Name}'s role on {unit} carries {permission}, which you don't hold there.
  • tree_too_large · 422 · the org's unit tree holds more units than one call reads

The platform refusals apply to every call: version, authentication, authority, validation and rate limits, each in the same envelope.

Surfaces

DesktopF
MobileD5
APIF
MCPF*
VoiceD5
PushF
OneBoxN
OutboundN
  • Mobile: degraded: composition
  • MCP: approval required
  • Voice: degraded: composition
  • OneBox: no document originates a unit role
  • Outbound: crosses to no counterparty

Events

Related

create_invite_link, decide_access_review_item, impersonate_user, import_users, invite_user, list_sessions, list_unit_access, resolve_identity_finding, revoke_sessions, revoke_unit_role, set_member_role