Documentation
DocsAPI reference

Teams

Groups of members. A team can be an escalation target, and its members inherit the team's permissions.

Updated Sep 22, 2026

A team's members come from two sources: people added by hand, and people added by your directory. Replace a team's members changes only the first. A team managed by your directory cannot be renamed or archived here. See Team administration for the app's membership, permissions and archiving features.

Permissions granted to a team are inherited by every member, and follow the roster as it changes.

List teams

GET/api/v1/teamsSession or API keyRequires team.read

Returns the teams you can read. With an organization-wide grant, that is every team. With a grant scoped to a team, only that team.

Request

Parameters

ParameterTypeDescription
statusquery · enum

Select live teams (the default), archived teams, or all teams.

One of live, archived, all
limitquery · integer

How many teams to return. Defaults to 200.

Response

Response codes

StatusBodyDescription
200object

The teams

400ErrorResponse

The status filter is invalid

401ErrorResponse

You are not signed in

403ErrorResponse

You cannot read teams

Response body200

teamsarray<Team>required

Example

GET /api/v1/teams
curl -X GET "$WITHHUMAN_URL/api/v1/teams" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "teams": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "name": "Payments approvers",
      "directory_managed": true,
      "member_count": 1,
      "assignments": [
        {
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "principal_kind": "membership",
          "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "scope_kind": "organization",
          "scope_id": "string",
          "scope_name": "string",
          "source": "manual",
          "source_key": "string",
          "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "via_team_name": "string",
          "created_at": "2026-09-08T12:02:11Z",
          "permissions": [
            "string"
          ],
          "is_owner": true,
          "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalation_policy": {
        "active_revision": 1,
        "latest_revision": 1
      },
      "archived_at": "2026-09-08T12:02:11Z",
      "created_at": "2026-09-08T12:02:11Z"
    }
  ]
}

Create a team

POST/api/v1/teamsSession or API keyRequires team.write

Creates an empty team. Add members and roles afterwards. Names are unique among live teams in the organization.

Request

Request body

namestringrequired

The team's name. Unique among live teams in the organization.

Response

Response codes

StatusBodyDescription
201object

The new team

400ErrorResponse

The name is empty

403ErrorResponse

You cannot create teams

409ErrorResponse

A team with this name already exists

Response body201

teamTeamrequired

A group of members. A team can be an escalation target, and its members inherit the roles it holds. A team may also own an escalation policy that runs when a path level names it.

Example

POST /api/v1/teams
curl -X POST "$WITHHUMAN_URL/api/v1/teams" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Payments approvers"
}'
201 response
{
  "team": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "name": "Payments approvers",
    "directory_managed": true,
    "member_count": 1,
    "assignments": [
      {
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "principal_kind": "membership",
        "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "scope_kind": "organization",
        "scope_id": "string",
        "scope_name": "string",
        "source": "manual",
        "source_key": "string",
        "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "via_team_name": "string",
        "created_at": "2026-09-08T12:02:11Z",
        "permissions": [
          "string"
        ],
        "is_owner": true,
        "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
      }
    ],
    "escalation_policy": {
      "active_revision": 1,
      "latest_revision": 1
    },
    "archived_at": "2026-09-08T12:02:11Z",
    "created_at": "2026-09-08T12:02:11Z"
  }
}

Retrieve a team

GET/api/v1/teams/{id}Session or API keyRequires team.read

Returns one team with its members. The list includes members added by hand and members added by your directory, and says which for each.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

Response

Response codes

StatusBodyDescription
200object

The team and its members

401ErrorResponse

You are not signed in

404ErrorResponse

No such team

Response body200

teamTeamrequired

A group of members. A team can be an escalation target, and its members inherit the roles it holds. A team may also own an escalation policy that runs when a path level names it.

membersarray<TeamMember>required

Example

GET /api/v1/teams/{id}
curl -X GET "$WITHHUMAN_URL/api/v1/teams/{id}" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "team": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "name": "Payments approvers",
    "directory_managed": true,
    "member_count": 1,
    "assignments": [
      {
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "principal_kind": "membership",
        "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "scope_kind": "organization",
        "scope_id": "string",
        "scope_name": "string",
        "source": "manual",
        "source_key": "string",
        "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "via_team_name": "string",
        "created_at": "2026-09-08T12:02:11Z",
        "permissions": [
          "string"
        ],
        "is_owner": true,
        "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
      }
    ],
    "escalation_policy": {
      "active_revision": 1,
      "latest_revision": 1
    },
    "archived_at": "2026-09-08T12:02:11Z",
    "created_at": "2026-09-08T12:02:11Z"
  },
  "members": [
    {
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "Ada Lovelace",
      "email": "[email protected]",
      "status": "active",
      "sources": [
        "manual"
      ]
    }
  ]
}

Rename a team

PATCH/api/v1/teams/{id}Session or API keyRequires team.write

Changes a team's name. Teams managed by your directory cannot be renamed here, because the directory owns their names.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

Request body

namestringrequired

The new name. Unique among live teams in the organization.

Response

Response codes

StatusBodyDescription
200object

The renamed team

400ErrorResponse

The name is empty

403ErrorResponse

You cannot change teams

404ErrorResponse

No such team

409ErrorResponse

The team is archived or managed by your directory, or a live team with this name already exists

Response body200

teamTeamrequired

A group of members. A team can be an escalation target, and its members inherit the roles it holds. A team may also own an escalation policy that runs when a path level names it.

Example

PATCH /api/v1/teams/{id}
curl -X PATCH "$WITHHUMAN_URL/api/v1/teams/{id}" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Payments approvers"
}'
200 response
{
  "team": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "name": "Payments approvers",
    "directory_managed": true,
    "member_count": 1,
    "assignments": [
      {
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "principal_kind": "membership",
        "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "scope_kind": "organization",
        "scope_id": "string",
        "scope_name": "string",
        "source": "manual",
        "source_key": "string",
        "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "via_team_name": "string",
        "created_at": "2026-09-08T12:02:11Z",
        "permissions": [
          "string"
        ],
        "is_owner": true,
        "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
      }
    ],
    "escalation_policy": {
      "active_revision": 1,
      "latest_revision": 1
    },
    "archived_at": "2026-09-08T12:02:11Z",
    "created_at": "2026-09-08T12:02:11Z"
  }
}

Archive a team

DELETE/api/v1/teams/{id}Session or API keyRequires team.write

Archives a team permanently. Its identity, roster, and policy history stay readable. Its members lose inherited roles and the active policy is deactivated. Archived teams cannot be edited or targeted by routing. Repeating this operation succeeds without changing the archive date.

Archiving is refused while the team is managed by your directory, is targeted by an active escalation path, is targeted by the path revision of a request still waiting for review.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

Response

Response codes

StatusBodyDescription
204

The team is archived

403ErrorResponse

You cannot archive teams

404ErrorResponse

No such team

409ErrorResponse

The team is managed by your directory, is targeted by an escalation path or a pending request

Example

DELETE /api/v1/teams/{id}
curl -X DELETE "$WITHHUMAN_URL/api/v1/teams/{id}" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"

Replace a team's members

PUT/api/v1/teams/{id}/membersSession or API keyRequires team.member.write

Replaces the members added by hand with the given set. Send an empty list to remove them all. Members added by your directory are not touched. A team's members are the union of both.

Removing a member that the team's active escalation policy still names is refused with team_member_in_escalation_policy; details carries team_id, revision, and the membership_ids being removed. Pass force to remove them anyway. The level naming them then skips them until the policy is edited.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

Request body

membership_idsarray<uuid>required

The membership ids of the people to add by hand.

forceboolean

Remove members the active escalation policy still names.

Response

Response codes

StatusBodyDescription
200object

The team's members after the change

400ErrorResponse

A membership id does not belong to this organization

403ErrorResponse

You cannot change team members

404ErrorResponse

No such team

409ErrorResponse

The team is archived, or its active escalation policy still names a member being removed: error.code is team_member_in_escalation_policy

Response body200

membersarray<TeamMember>required

Example

PUT /api/v1/teams/{id}/members
curl -X PUT "$WITHHUMAN_URL/api/v1/teams/{id}/members" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "membership_ids": [
    "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
  ],
  "force": true
}'
200 response
{
  "members": [
    {
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "Ada Lovelace",
      "email": "[email protected]",
      "status": "active",
      "sources": [
        "manual"
      ]
    }
  ]
}

Read the team's active escalation policy revision

GET/api/v1/teams/{id}/escalation-policySession or API keyRequires team.read

The revision a path level that targets the team runs. Warnings name user targets who are no longer active members and members who cannot decide. 404 while no revision is active, in which case a level that targets the team notifies every member at once.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

Response

Response codes

StatusBodyDescription
200TeamEscalationPolicyRevisionSummary

Active immutable revision with its full document

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body200

iduuidrequired
team_iduuidrequired
revisionint64required
is_activebooleanrequired
created_atdate-timerequired

Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.

document_versionintegerrequired

Advisory findings about the document against the team's roster and grants. Never a refusal: the policy narrows who is asked within the team, it never grants. Creation and activation report members who cannot decide; reads of the active revision report that and every target who left the team; reads of a historical revision report departed targets only.

Example

GET /api/v1/teams/{id}/escalation-policy
curl -X GET "$WITHHUMAN_URL/api/v1/teams/{id}/escalation-policy" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "is_active": true,
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  },
  "document_version": 1,
  "warnings": [
    {
      "code": "team_members_cannot_decide",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "count": 1,
      "member_count": 1
    }
  ]
}

List immutable team escalation policy revisions

GET/api/v1/teams/{id}/escalation-policy/revisionsSession or API keyRequires team.read

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

limitquery · integer

How many revisions to return. Defaults to 50.

Response

Response codes

StatusBodyDescription
200object

Revision history ordered newest first

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body200

Example

GET /api/v1/teams/{id}/escalation-policy/revisions
curl -X GET "$WITHHUMAN_URL/api/v1/teams/{id}/escalation-policy/revisions" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "revisions": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "revision": 1,
      "is_active": true,
      "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "created_at": "2026-09-08T12:02:11Z"
    }
  ]
}

Create an inactive immutable team escalation policy revision

POST/api/v1/teams/{id}/escalation-policy/revisionsSession or API keyRequires team.escalation.write

Validates the complete document before creating the next inactive revision. Levels name members of the team by membership id or broadcast to the whole team; a user target who is not an active member of the team is refused, and team targets belong to organization escalation paths.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

Request body

Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.

Response

Response codes

StatusBodyDescription
201TeamEscalationPolicyRevisionSummary

Created inactive immutable revision

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

The team is archived

Response body201

iduuidrequired
team_iduuidrequired
revisionint64required
is_activebooleanrequired
created_atdate-timerequired

Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.

document_versionintegerrequired

Advisory findings about the document against the team's roster and grants. Never a refusal: the policy narrows who is asked within the team, it never grants. Creation and activation report members who cannot decide; reads of the active revision report that and every target who left the team; reads of a historical revision report departed targets only.

Example

POST /api/v1/teams/{id}/escalation-policy/revisions
curl -X POST "$WITHHUMAN_URL/api/v1/teams/{id}/escalation-policy/revisions" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  }
}'
201 response
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "is_active": true,
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  },
  "document_version": 1,
  "warnings": [
    {
      "code": "team_members_cannot_decide",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "count": 1,
      "member_count": 1
    }
  ]
}

Read one immutable team escalation policy revision

GET/api/v1/teams/{id}/escalation-policy/revisions/{revision}Session or API keyRequires team.read

Warnings name user targets who are no longer active members, so a rollback candidate can be judged before it is activated.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

revisionrequiredpath · int64

The revision number. Revisions count up from 1.

Response

Response codes

StatusBodyDescription
200TeamEscalationPolicyRevisionSummary

Historical revision with its full document

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body200

iduuidrequired
team_iduuidrequired
revisionint64required
is_activebooleanrequired
created_atdate-timerequired

Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.

document_versionintegerrequired

Advisory findings about the document against the team's roster and grants. Never a refusal: the policy narrows who is asked within the team, it never grants. Creation and activation report members who cannot decide; reads of the active revision report that and every target who left the team; reads of a historical revision report departed targets only.

Example

GET /api/v1/teams/{id}/escalation-policy/revisions/{revision}
curl -X GET "$WITHHUMAN_URL/api/v1/teams/{id}/escalation-policy/revisions/{revision}" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "is_active": true,
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  },
  "document_version": 1,
  "warnings": [
    {
      "code": "team_members_cannot_decide",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "count": 1,
      "member_count": 1
    }
  ]
}

Activate or roll back to a team escalation policy revision

POST/api/v1/teams/{id}/escalation-policy/revisions/{revision}/activateSession or API keyRequires team.escalation.activate

Atomically switches the team's active revision. If-Match must be "0" when no revision is active. The stored document is checked against the roster again, so a revision naming someone who has since left the team is refused. Requests already routed keep the revision they pinned.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

revisionrequiredpath · int64

The revision number. Revisions count up from 1.

If-Matchrequiredheader · string

The revision you expect to be active, quoted, for example "2". Take it from the ETag of your last read. If the active revision changed in the meantime, the call fails with 412 and nothing changes. Escalation paths and webhook endpoints accept "0" when no revision is active. Pipelines always have an active revision.

Response

Response codes

StatusBodyDescription
200TeamEscalationPolicyRevisionSummary

Activated revision

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

The team is archived

412ErrorResponse

If-Match no longer equals the active revision

428ErrorResponse

The required If-Match header was omitted

Response body200

iduuidrequired
team_iduuidrequired
revisionint64required
is_activebooleanrequired
created_atdate-timerequired

Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.

document_versionintegerrequired

Advisory findings about the document against the team's roster and grants. Never a refusal: the policy narrows who is asked within the team, it never grants. Creation and activation report members who cannot decide; reads of the active revision report that and every target who left the team; reads of a historical revision report departed targets only.

Example

POST /api/v1/teams/{id}/escalation-policy/revisions/{revision}/activate
curl -X POST "$WITHHUMAN_URL/api/v1/teams/{id}/escalation-policy/revisions/{revision}/activate" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY" \
  -H "If-Match: "2""
200 response
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "is_active": true,
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  },
  "document_version": 1,
  "warnings": [
    {
      "code": "team_members_cannot_decide",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "count": 1,
      "member_count": 1
    }
  ]
}

Archive the team's escalation policy by deactivating its current revision

DELETE/api/v1/teams/{id}/escalation-policy/activeSession or API keyRequires team.escalation.activate

Refused when the team is archived. Otherwise, a path level that targets the team falls back to notifying every member at once; requests already routed keep their pinned revision.

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

The team's id.

If-Matchrequiredheader · string

The revision you expect to be active, quoted, for example "2". Take it from the ETag of your last read. If the active revision changed in the meantime, the call fails with 412 and nothing changes. Escalation paths and webhook endpoints accept "0" when no revision is active. Pipelines always have an active revision.

Response

Response codes

StatusBodyDescription
204

Current revision deactivated

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

The team is archived

412ErrorResponse

If-Match no longer equals the active revision

428ErrorResponse

The required If-Match header was omitted

Example

DELETE /api/v1/teams/{id}/escalation-policy/active
curl -X DELETE "$WITHHUMAN_URL/api/v1/teams/{id}/escalation-policy/active" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY" \
  -H "If-Match: "2""

Get teams permission policies

GET/api/v1/teams/{id}/permission_policiesSession or API keySelf-service

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid

Response

Response codes

StatusBodyDescription
200PermissionPoliciesPage

Policy operation completed

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body200

namestringrequired
can_createbooleanrequired
is_ownerbooleanrequired

Example

GET /api/v1/teams/{id}/permission_policies
curl -X GET "$WITHHUMAN_URL/api/v1/teams/{id}/permission_policies" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "name": "string",
  "policies": [
    {
      "scope_kind": "organization",
      "resource_ids": [
        "string"
      ],
      "permissions": [
        "string"
      ],
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "revision": 1,
      "resources": [
        {
          "id": "string",
          "name": "string",
          "kind": "organization"
        }
      ],
      "source": "manual",
      "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_name": "string",
      "can_edit": true,
      "read_only_reason": "string"
    }
  ],
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "permissions": [
    {
      "key": "request.decide",
      "area": "request",
      "description": "Approve or deny requests routed to you",
      "holders": [
        "human"
      ],
      "resource": "organization",
      "dangerous": true
    }
  ],
  "can_create": true,
  "is_owner": true,
  "read_only_reason": "string"
}

Post teams permission policies

POST/api/v1/teams/{id}/permission_policiesSession cookieRequires permission_policy.write

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid
Idempotency-Keyrequiredheader · string

Request body

scope_kindenumrequired

One of organization, team, agent

resource_idsarray<string>required

Empty for organization scope; otherwise the selected team IDs or agent slugs.

permissionsarray<string>required

Response

Response codes

StatusBodyDescription
201PermissionPolicy

Policy operation completed

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body201

scope_kindenumrequired

One of organization, team, agent

resource_idsarray<string>required

Empty for organization scope; otherwise the selected team IDs or agent slugs.

permissionsarray<string>required
iduuidrequired
revisionint64required
sourceenumrequired

One of manual, directory

can_editbooleanrequired

Example

POST /api/v1/teams/{id}/permission_policies
curl -X POST "$WITHHUMAN_URL/api/v1/teams/{id}/permission_policies" \
  -b "withhuman_session=$WITHHUMAN_SESSION" \
  -H "Idempotency-Key: string" \
  -H "Content-Type: application/json" \
  -d '{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ]
}'
201 response
{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ],
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "source": "manual",
  "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "via_team_name": "string",
  "can_edit": true,
  "read_only_reason": "string"
}

Get teams permission policy

GET/api/v1/teams/{id}/permission_policies/{policy_id}Session or API keySelf-service

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid
policy_idrequiredpath · uuid

Response

Response codes

StatusBodyDescription
200PermissionPolicy

Policy operation completed

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body200

scope_kindenumrequired

One of organization, team, agent

resource_idsarray<string>required

Empty for organization scope; otherwise the selected team IDs or agent slugs.

permissionsarray<string>required
iduuidrequired
revisionint64required
sourceenumrequired

One of manual, directory

can_editbooleanrequired

Example

GET /api/v1/teams/{id}/permission_policies/{policy_id}
curl -X GET "$WITHHUMAN_URL/api/v1/teams/{id}/permission_policies/{policy_id}" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
200 response
{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ],
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "source": "manual",
  "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "via_team_name": "string",
  "can_edit": true,
  "read_only_reason": "string"
}

Put teams permission policy

PUT/api/v1/teams/{id}/permission_policies/{policy_id}Session cookieRequires permission_policy.write

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid
policy_idrequiredpath · uuid
Idempotency-Keyrequiredheader · string

Request body

scope_kindenumrequired

One of organization, team, agent

resource_idsarray<string>required

Empty for organization scope; otherwise the selected team IDs or agent slugs.

permissionsarray<string>required
revisionint64required

Required when updating an existing policy.

Response

Response codes

StatusBodyDescription
200PermissionPolicy

Policy operation completed

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Response body200

scope_kindenumrequired

One of organization, team, agent

resource_idsarray<string>required

Empty for organization scope; otherwise the selected team IDs or agent slugs.

permissionsarray<string>required
iduuidrequired
revisionint64required
sourceenumrequired

One of manual, directory

can_editbooleanrequired

Example

PUT /api/v1/teams/{id}/permission_policies/{policy_id}
curl -X PUT "$WITHHUMAN_URL/api/v1/teams/{id}/permission_policies/{policy_id}" \
  -b "withhuman_session=$WITHHUMAN_SESSION" \
  -H "Idempotency-Key: string" \
  -H "Content-Type: application/json" \
  -d '{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ],
  "revision": 1
}'
200 response
{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ],
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "source": "manual",
  "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "via_team_name": "string",
  "can_edit": true,
  "read_only_reason": "string"
}

Delete teams permission policy

DELETE/api/v1/teams/{id}/permission_policies/{policy_id}Session cookieRequires permission_policy.write

Request

Parameters

ParameterTypeDescription
idrequiredpath · uuid
policy_idrequiredpath · uuid
Idempotency-Keyrequiredheader · string
revisionrequiredquery · integer

Response

Response codes

StatusBodyDescription
204

Policy operation completed

400ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

401ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

403ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

404ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

409ErrorResponse

Error response. A 403 from a permission check carries ForbiddenDetails in error.details.

Example

DELETE /api/v1/teams/{id}/permission_policies/{policy_id}
curl -X DELETE "$WITHHUMAN_URL/api/v1/teams/{id}/permission_policies/{policy_id}?revision=1" \
  -b "withhuman_session=$WITHHUMAN_SESSION" \
  -H "Idempotency-Key: string"