Skip to main content

Teams

Teams group your application's users together and allow roles to be assigned at the team level. Every member of a team automatically inherits the roles assigned to that team, in addition to any roles assigned to them directly.

How Teams Work

text
Team "Engineering"
  → Role: developer (permissions: code:push, code:review)
  → Members: user-101, user-102, user-103

user-101 checks "code:push"
  → Direct roles: none with code:push
  → Team roles: developer → code:push ✓
  → Result: allowed

Team-inherited permissions are evaluated alongside direct user permissions in every authorization check. The permission set loaded from cache includes both sources.

Team Fields

FieldTypeDescription
idUUIDUnique team identifier
application_idUUIDApplication this team belongs to
namestringTeam name
descriptionstringOptional description
scopestringOptional scope to associate the team with a namespace
metadataobjectArbitrary key-value metadata
member_countintegerNumber of members
created_atISO 8601Creation timestamp

List Teams

text
GET /api/v1/applications/{applicationId}/teams

Requires JWT scope: teams:read

Query parameters:

ParameterDescription
searchFilter teams by name (case-insensitive)
per_pageResults per page (max 100, default 15)

Response 200 OK:

json
{
  "data": [
    {
      "id": "018e5f3a-bbbb-7000-8000-000000000010",
      "name": "Engineering",
      "description": "Core engineering team",
      "scope": null,
      "metadata": null,
      "member_count": 8,
      "created_at": "2026-02-01T10:00:00+00:00",
      "updated_at": "2026-02-01T10:00:00+00:00"
    }
  ],
  "meta": { "current_page": 1, "last_page": 1, "per_page": 15, "total": 1 }
}

Get a Team

text
GET /api/v1/applications/{applicationId}/teams/{teamId}

Requires JWT scope: teams:read

Returns the team with its full member list and all assigned roles.

Response 200 OK:

json
{
  "data": {
    "id": "018e5f3a-bbbb-7000-8000-000000000010",
    "name": "Engineering",
    "description": "Core engineering team",
    "scope": null,
    "metadata": null,
    "member_count": 2,
    "members": [
      {
        "id": "018e5f3a-0001-7000-8000-000000000020",
        "user_id": "user-101",
        "added_by": "user-admin",
        "created_at": "2026-02-01T10:00:00+00:00"
      }
    ],
    "roles": [
      {
        "id": "018e5f3a-0001-7000-8000-000000000030",
        "role": {
          "id": "018e5f3a-abcd-7000-8000-000000000001",
          "name": "developer",
          "display_name": "Developer"
        },
        "scope": null,
        "granted_at": "2026-02-01T10:00:00+00:00",
        "expires_at": null
      }
    ],
    "created_at": "2026-02-01T10:00:00+00:00",
    "updated_at": "2026-02-01T10:00:00+00:00"
  }
}

Create a Team

text
POST /api/v1/applications/{applicationId}/teams

Requires JWT scope: teams:manage

Request body:

FieldRequiredDescription
nameYesTeam name (max 255 chars)
descriptionNoOptional description (max 1000 chars)
scopeNoOptional scope string (max 255 chars)
metadataNoOptional JSON object
bash
curl -X POST \
  https://api.yorauth.com/api/v1/applications/your-application-id/teams \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Engineering",
    "description": "Core engineering team",
    "scope": null,
    "metadata": { "slack_channel": "#engineering" }
  }'
javascript
const response = await fetch(
  'https://api.yorauth.com/api/v1/applications/your-application-id/teams',
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer <jwt>',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Engineering',
      description: 'Core engineering team',
      metadata: { slack_channel: '#engineering' }
    })
  }
);
const { data } = await response.json();

Response 201 Created: Returns the created team object.

Update a Team

text
PUT /api/v1/applications/{applicationId}/teams/{teamId}

Requires JWT scope: teams:manage

All fields are optional.

Request body:

json
{
  "name": "Platform Engineering",
  "description": "Updated description"
}

Response 200 OK: Returns the updated team object.

Delete a Team

text
DELETE /api/v1/applications/{applicationId}/teams/{teamId}

Requires JWT scope: teams:manage

Response 204 No Content on success.

Team Members

List Members

text
GET /api/v1/applications/{applicationId}/teams/{teamId}/members

Requires JWT scope: teams:read

Response 200 OK:

json
{
  "data": [
    {
      "id": "018e5f3a-0001-7000-8000-000000000020",
      "user_id": "user-101",
      "added_by": "user-admin",
      "created_at": "2026-02-01T10:00:00+00:00"
    }
  ]
}

Add a Member

text
POST /api/v1/applications/{applicationId}/teams/{teamId}/members

Requires JWT scope: teams:manage

Request body:

json
{ "user_id": "user-101" }

Response 201 Created:

json
{
  "data": {
    "id": "018e5f3a-0001-7000-8000-000000000020",
    "user_id": "user-101",
    "created_at": "2026-02-25T14:30:00+00:00"
  }
}

Remove a Member

text
DELETE /api/v1/applications/{applicationId}/teams/{teamId}/members/{userId}

Requires JWT scope: teams:manage

Response 204 No Content on success.

Error responses:

  • 404 Not Found — User is not a member of this team
    json
    { "error": { "code": "TEAM_MEMBER_NOT_FOUND", "message": "..." } }
    

Team Roles

Roles assigned to a team are inherited by all current and future team members.

List Team Roles

text
GET /api/v1/applications/{applicationId}/teams/{teamId}/roles

Requires JWT scope: teams:read

Response 200 OK:

json
{
  "data": [
    {
      "id": "018e5f3a-0001-7000-8000-000000000030",
      "role": {
        "id": "018e5f3a-abcd-7000-8000-000000000001",
        "name": "developer",
        "display_name": "Developer"
      },
      "scope": null,
      "granted_at": "2026-02-01T10:00:00+00:00",
      "expires_at": null
    }
  ]
}

Assign a Role to a Team

text
POST /api/v1/applications/{applicationId}/teams/{teamId}/roles

Requires JWT scope: teams:manage

Request body:

FieldRequiredDescription
role_idYesUUID of the role to assign
scopeNoOptional scope for this role assignment
expires_atNoISO 8601 expiration datetime
bash
curl -X POST \
  https://api.yorauth.com/api/v1/applications/your-application-id/teams/018e5f3a-bbbb-7000-8000-000000000010/roles \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "role_id": "018e5f3a-abcd-7000-8000-000000000001",
    "scope": null
  }'

Response 201 Created:

json
{
  "data": {
    "id": "018e5f3a-0001-7000-8000-000000000030",
    "role_id": "018e5f3a-abcd-7000-8000-000000000001",
    "scope": null,
    "granted_at": "2026-02-25T14:30:00+00:00",
    "expires_at": null
  }
}

Revoke a Role from a Team

text
DELETE /api/v1/applications/{applicationId}/teams/{teamId}/roles/{roleId}

Requires JWT scope: teams:manage

Query parameters:

ParameterDescription
scopeRequired if the role was assigned with a scope

Response 204 No Content on success.

Error responses:

  • 404 Not Found — Role assignment not found
    json
    { "error": { "code": "TEAM_ROLE_ASSIGNMENT_NOT_FOUND", "message": "..." } }
    

Get a User's Teams

text
GET /api/v1/applications/{applicationId}/users/{userId}/teams

Requires JWT scope: teams:read

Returns all teams the user belongs to, along with the roles each team holds.

Response 200 OK:

json
{
  "data": [
    {
      "id": "018e5f3a-bbbb-7000-8000-000000000010",
      "name": "Engineering",
      "description": "Core engineering team",
      "scope": null,
      "roles": [
        {
          "role_id": "018e5f3a-abcd-7000-8000-000000000001",
          "role_name": "developer",
          "scope": null
        }
      ]
    }
  ]
}

Permission Inheritance

When a user is added to a team that has roles assigned, those roles are factored into every subsequent permission check automatically. Specifically:

  • The permission cache for the user's application is invalidated (version incremented)
  • On the next permission check, both direct user roles and team-inherited roles are loaded into the user's cached permission set
  • There is no distinction in the permission check result between direct and team-inherited permissions

Team role assignments support the same scope and expires_at fields as user role assignments. An expired team role is ignored during permission evaluation just like an expired direct role.

Organizations

Organizations are a platform-level concept (for YorAuth dashboard users managing multiple applications collaboratively). They are distinct from the team system described on this page, which operates at the application-user level.

If you are looking for organization-level access control within your application's user base, use teams with scoped role assignments (e.g., scope: "org:acme-corp").

See the Authorization Overview for scoped role examples.