approve_grant

Decide a staff access request

act entity tenant-private since 2026-10-01

An admin of the org approves or rejects a Trade HQ staff member's request for access to it. Approving makes the access active now, for the hours asked or for `hours` when given (the card's Edit duration), and it ends on its own when they pass; reject refuses it. Either resolves the request's decision card. A decided request answers the same decision again and refuses another. A staff member who is in the org through a grant can never decide one.

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`); a deactivated caller is refused first
idempotency
natural
emits
grant.approved, grant.rejected
MCP tool
approve_grant
decision_set
approve, reject
REST
POST https://api.containertradehq.com/v1/cap/approve_grant

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 the request is to.
grant_iduuidrequiredUUID
The request, as list_org_grants or the decision card names it.
decisionone_ofrequiredone of approve, reject
approve or reject.
hoursintegeroptional1 to 720
For an approval: how many hours to approve, when not the hours asked (an edit).

Response

  • grant_id · string · The grant's id.
  • org_id · string · The customer org it is a grant to.
  • org_name · stringnull · That org's name.
  • grantee_user_id · string · The staff member it lets in.
  • grantee_name · stringnull · Their name, or their address when they have none.
  • grantee_email · value · Their address.
  • level · string · read: what the Read-only template carries. act: what the Admin template carries.
  • kind · string · request: asked of the org's admins. break_glass: entered at once by Sage or Sonja, the admins told.
  • reason · string · Why, in the requester's words.
  • requested_hours · integer · How long it was asked for.
  • requested_at · string · When it was asked for.
  • exception_id · stringnull · The decision card the org's admins decide a request on.
  • decision · stringnull · What the admin decided; null while it waits.
  • decided_by · stringnull · Who decided it.
  • decided_at · stringnull · When.
  • approved_at · stringnull · When it became active.
  • expires_at · stringnull · When it stops being active.
  • revoked_at · stringnull · When it was revoked, if it was.
  • revoked_by · stringnull · Who revoked it.
  • status · string · pending, active, rejected, revoked or expired, as of now.
  • notified · integer · How many of the org's admins a break-glass notice was written to.
Full JSON Schema
{
  "type": "object",
  "properties": {
    "grant_id": {
      "type": "string",
      "pattern": "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$",
      "format": "uuid",
      "description": "The grant's id.",
      "x-data-class": "operational"
    },
    "org_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-org-id",
      "description": "The customer org it is a grant to.",
      "x-data-class": "operational"
    },
    "org_name": {
      "type": [
        "string",
        "null"
      ],
      "description": "That org's name.",
      "x-data-class": "operational"
    },
    "grantee_user_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-user-id",
      "description": "The staff member it lets in.",
      "x-data-class": "operational"
    },
    "grantee_name": {
      "type": [
        "string",
        "null"
      ],
      "description": "Their name, or their address when they have none.",
      "x-data-class": "operational"
    },
    "grantee_email": {
      "anyOf": [
        {
          "type": [
            "string",
            "null"
          ]
        },
        {
          "const": "•••",
          "description": "Masked by the caller's data policy."
        },
        {
          "type": "null",
          "description": "Denied by the caller's data policy."
        }
      ],
      "description": "Their address.",
      "x-data-class": "personal"
    },
    "level": {
      "type": "string",
      "enum": [
        "read",
        "act"
      ],
      "description": "read: what the Read-only template carries. act: what the Admin template carries.",
      "x-data-class": "operational"
    },
    "kind": {
      "type": "string",
      "enum": [
        "request",
        "break_glass"
      ],
      "description": "request: asked of the org's admins. break_glass: entered at once by Sage or Sonja, the admins told.",
      "x-data-class": "operational"
    },
    "reason": {
      "type": "string",
      "description": "Why, in the requester's words.",
      "x-data-class": "operational"
    },
    "requested_hours": {
      "type": "integer",
      "description": "How long it was asked for.",
      "x-data-class": "operational"
    },
    "requested_at": {
      "type": "string",
      "format": "date-time",
      "description": "When it was asked for.",
      "x-data-class": "operational"
    },
    "exception_id": {
      "type": [
        "string",
        "null"
      ],
      "description": "The decision card the org's admins decide a request on.",
      "x-data-class": "operational"
    },
    "decision": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "approved",
        "rejected",
        null
      ],
      "description": "What the admin decided; null while it waits.",
      "x-data-class": "operational"
    },
    "decided_by": {
      "type": [
        "string",
        "null"
      ],
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-user-id",
      "description": "Who decided it.",
      "x-data-class": "operational"
    },
    "decided_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When.",
      "x-data-class": "operational"
    },
    "approved_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When it became active.",
      "x-data-class": "operational"
    },
    "expires_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When it stops being active.",
      "x-data-class": "operational"
    },
    "revoked_at": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "When it was revoked, if it was.",
      "x-data-class": "operational"
    },
    "revoked_by": {
      "type": [
        "string",
        "null"
      ],
      "minLength": 1,
      "maxLength": 20,
      "pattern": "^[A-Za-z0-9_-]{1,20}$",
      "format": "tradehq-user-id",
      "description": "Who revoked it.",
      "x-data-class": "operational"
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "active",
        "rejected",
        "revoked",
        "expired"
      ],
      "description": "pending, active, rejected, revoked or expired, as of now.",
      "x-data-class": "operational"
    },
    "notified": {
      "type": "integer",
      "description": "How many of the org's admins a break-glass notice was written to.",
      "x-data-class": "operational"
    }
  },
  "required": [
    "grant_id",
    "org_id",
    "org_name",
    "grantee_user_id",
    "grantee_name",
    "grantee_email",
    "level",
    "kind",
    "reason",
    "requested_hours",
    "requested_at",
    "exception_id",
    "decision",
    "decided_by",
    "decided_at",
    "approved_at",
    "expires_at",
    "revoked_at",
    "revoked_by",
    "status",
    "notified"
  ],
  "additionalProperties": false
}

Refusals

  • no_such_grant · 422 · the org has no such grant
  • grant_not_pending · 409 · the request was already decided, withdrawn or revoked
  • granted_staff_cannot_decide · 422 · a grant is decided by the org's own admin, never by staff in the org through a grant or by its grantee
  • hours_only_on_approve · 422 · hours is an approval's duration; a rejection takes none
  • org_untyped · 422 · the org has no tradehq_org row, so the decision plane can record nothing against it

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

Surfaces

DesktopF
MobileF
APIF
MCPF*
VoiceD2
PushF
OneBoxN
OutboundN
  • MCP: approval required
  • Voice: degraded: confirmation
  • OneBox: no document originates a staff grant
  • Outbound: crosses to no counterparty

Events

Related

break_glass_access, decide_access_review_item, request_staff_grant, resolve_identity_finding, revoke_grant