> For the complete documentation index, see [llms.txt](https://help.swarmia.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.swarmia.com/settings/integrations/swarmia-apis/team-management.md).

# Team management

## Overview

The team management API allows you to programmatically manage teams and team members in Swarmia, enabling you to sync team data from external systems like HR platforms or identity providers. The API provides two operations:

* List organization's teams `GET /api/v0/teams`
* Update organization's teams `PUT /api/v0/teams`

## Authentication

Authentication works based on a token which you can generate [here](https://app.swarmia.com/settings/api-tokens).

Two options are currently available:

* Include an HTTP header like `Authorization: Bearer {TOKEN}` header in the request. Example request with the `curl` command line tool: `curl https://app.swarmia.com/api/v0/teams -H "Authorization: Bearer {TOKEN}"`
* *(Only GET requests)* Pass the token as a value for the `token` query parameter. Example request line: `GET https://app.swarmia.com/api/v0/teams?token={TOKEN}`
  * **Security note**: even though Swarmia redacts secret tokens from query logs, please bear in mind that some other proxies might log query parameters before reaching Swarmia.

## List organization's teams

`GET /api/v0/teams`

Lists the current teams and their settings.

> **Note:** If your teams contain members attached via GitHub teams, those won't be included in the response. Additionally, when using API-based team management, GitHub teams cannot be used as team members.

> **Note:** Teams created through the Swarmia UI will have `externalId` set to `null` until you assign an `externalId` via the API. This is expected and normal for teams that haven't been migrated to API-based management yet.

> **Note:** `jiraProjectKeys` and `linearTeamKeys` reflect the Jira projects and Linear teams a team owns. Each field is `null` when the team has no mapping for that issue tracker. Other issue-ownership conditions configurable in the Swarmia UI — labels, issue types, custom fields, etc. — are not represented in these fields.

> **Note:** `teamAdmins` lists the email addresses of the team's admins, sorted alphabetically. It only includes admins assigned directly to the team, not admins inherited from a parent team. The field is left out for a team when one of its admins can't be identified by a single email address: they have no email in Swarmia, or their email also belongs to another user. Sending such a response back with `PUT` keeps those admins unchanged.

### **Example query**

```bash
curl -H "Accept: application/json" -H "Authorization: Bearer TOKEN" \
https://app.swarmia.com/api/v0/teams
```

### Parameters

#### Query parameters

**token:** string

The authentication token, for accessing the organization's endpoint

### Response

Response codes

| **Code** | **Description** |
| -------- | --------------- |
| 200      | Ok              |

#### **Example response**

```json
{
    "teams": [
        {
            "id": "3eaa7913-cf0a-4ecb-a9b1-953444d3fdf4",
            "parentId": null,
            "name": "Engineering",
            "externalId": "engineering",
            "parentExternalId": null,
            "jiraProjectKeys": null,
            "linearTeamKeys": null,
            "teamAdmins": [],
            "members": []
        },
        {
            "id": "7e14ac2c-5090-4426-b371-4bee7e9ba2bb",
            "parentId": "3eaa7913-cf0a-4ecb-a9b1-953444d3fdf4",
            "name": "Platform team",
            "externalId": "platform-team",
            "parentExternalId": "engineering",
            "jiraProjectKeys": [
                "PLA"
            ],
            "linearTeamKeys": [
                "ENG"
            ],
            "teamAdmins": [
                "aimo@example.com"
            ],
            "members": [
                {
                    "id": "87197b8b-20f8-4a8d-98c8-d4ccfc64c0b9",
                    "email": "aimo@example.com",
                    "githubUsername": "aimo",
                    "country": "NL",
                    "name": "Aimo K."
                },
                {
                    "id": "d7770e04-7155-40c9-ba24-fef7aef9b58f",
                    "email": "bee@example.com",
                    "name": "Bumble Bee"
                }
            ]
        }
    ]
}
```

#### **Response schema**

```json
{
  "title": "Teams",
  "type": "object",
  "properties": {
    "teams": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "externalId": {
            "type": ["null", "string"]
          },
          "parentId": {
            "type": [
              "null",
              "string"
            ]
          },
          "parentExternalId": {
            "type": [
              "null",
              "string"
            ]
          },
          "jiraProjectKeys": {
            "type": ["null", "array"],
            "items": {
              "type": "string"
            }
          },
          "linearTeamKeys": {
            "type": ["null", "array"],
            "items": {
              "type": "string"
            }
          },
          "teamAdmins": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "members": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "email": {
                  "type": ["null", "string"]
                },
                "githubUsername": {
                  "type": "string"
                },
                "country": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "name"
              ]
            }
          }
        },
        "required": [
          "id",
          "name",
          "members"
        ]
      }
    }
  }
}
```

## Update organization's teams

`PUT /api/v0/teams`

Update teams of the organization. After the operation is complete all of the data is recalculated and the updates in the application can take a while to be visible.

> ⚠️ This is a destructive operation. We recommend storing the current state of teams with the GET request before doing any changes. All teams not present in the query will be removed.

#### Protection against accidental mass deletion

Because teams missing from the payload are removed, a payload produced by a broken script (for example, failed pagination returning only one team) could wipe out your organization's teams. Two guardrails reject such requests with a 400 error before anything is changed:

* An empty `teams` array is never accepted. At least one team is required.
* A request that would remove **at least 10 teams** and **more than 90%** of your organization's current teams is rejected. Smaller organizations and smaller removals are not affected, because for them an intentional full replacement looks the same as an accident.

If you do intend to remove most of your teams, do it in smaller batches across several requests.

### **Example query**

```bash
curl -H "Content-Type: application/json" -H "Authorization: Bearer TOKEN" \
-X PUT https://app.swarmia.com/api/v0/teams \
-d '
{
	"teams": [{
		"name": "Engineering",
		"externalId": "engineering",
		"parentExternalId": null,
		"jiraProjectKeys": null,
		"linearTeamKeys": null,
		"teamAdmins": null,
		"members": []
	}]
}'
```

### Parameters

#### Query parameters

`token` *string*

The authentication token, for accessing the organization's endpoint

#### Body parameters

`teams` *Array*

***Team*****&#x20;parameters**

`id` *uuid* optional

Swarmia identifier of the team

***

`externalId` *string* required

Identifier for the team used in the external system. This should not change between the updates. Otherwise a new team will be created and the returned Swarmia identifier will change.

> **Note:** The `externalId` is your stable identifier for matching teams across API calls. It's different from Swarmia's internal `id` - you provide `externalId`, while Swarmia generates `id`.

***

`name` *string* required

Descriptive name of the team

***

`members` *Array* required

Array of team members. To remove all members from the team set this to `[]`

***

`parentExternalId` *string* optional

If the team is a child of another team, this is the reference to that team's `externalId`

> 📢 A parent cannot have Jira projects or Linear teams mapped. The parent teams will inherit the issues from the child teams. When a team becomes a parent, these mappings are removed for that team.

***

`jiraProjectKeys` Array<*string> optional*

The Jira projects whose issues this team owns.

This field is the source of truth for a team's **Jira project** ownership *only*. A team's issue ownership can also include other conditions — labels, issue types, custom fields, and so on — configured in the Swarmia UI. `jiraProjectKeys` never touches those: it merges the Jira project mapping into whatever else is already configured rather than replacing the whole issue ownership. For teams whose ownership is just a Jira project mapping, which is the common case, this simply sets or clears that mapping.

* **Non-empty array:** sets the Jira projects this team owns, leaving any other conditions intact. If a project mapping already exists, it is replaced with the given keys.
* **`null` or `[]`:** removes the Jira project mapping only, leaving any other conditions intact.
* **Omitted / `undefined`:** leaves the team's issue ownership untouched.

All new Jira projects listed in the array will be automatically synced to Swarmia.

> 📢 Only leaf-teams in the hierarchy can have Jira projects assigned. Omit this field if the team is a parent team

`linearTeamKeys` Array<*string> optional*

The Linear teams whose issues this team owns. Use each Linear team's key, for example, `ENG`.

This field is the source of truth for a team's **Linear team** ownership only. It follows the same update behavior as `jiraProjectKeys`:

* **Non-empty array:** sets the Linear teams this team owns, leaving any other conditions intact. If a Linear team mapping already exists, it is replaced with the given keys.
* **`null` or `[]`:** removes the Linear team mapping only, leaving any other conditions intact.
* **Omitted / `undefined`:** leaves the team's issue ownership untouched.

You can set `jiraProjectKeys` and `linearTeamKeys` together. Swarmia assigns the team issues that match either mapping while retaining any additional issue-ownership conditions configured in the UI.

> 📢 Only leaf teams in the hierarchy can have Linear teams assigned. Omit this field if the team is a parent team.

> **Note:** If a team's issue ownership was configured in the UI with rules the API can't safely merge into — a hand-crafted filter using conditions that aren't editable in the team issue ownership UI, or a "Jira project is **not**" exclusion rule — the request is rejected with a 400 error and the team's existing issue ownership is left unchanged. Adjust or clear that team's issue ownership in Swarmia first.

`teamAdmins` *string | Array\<string> | null* optional

The email address(es) of the team's administrator(s) who will be granted `Manage Team Configuration` permissions. This field controls who can manage team settings and configurations.

* **When string:** Assigns the specified user as the team administrator.
* **When array:** Assigns multiple users as team administrators. Duplicate emails are automatically deduplicated.
* **When null or empty array:** Removes all existing team administrator permissions for this team.
* **Omitted / `undefined`:** Leaves the team's current administrators untouched.

**Validation:**

* All emails must be in a valid email address format
* Users must exist in the organization with the specified email
* If an email is invalid, the API returns a 400 error
* If a user doesn't exist for a given email, that email is silently ignored (warning logged server-side)
* If an email matches more than one user, the API returns a 422 error and no teams are changed. [Merge the duplicate authors](/settings/organization/contributors.md#merge-authors-manually) or use an email that matches only one user, then retry

> **Note:** The deprecated `teamAdmin` field is still supported for backwards compatibility but accepts the same values. Do not specify both `teamAdmin` and `teamAdmins` in the same request—this will return a 400 error.

**Member** parameters

`name` *string* required

Name of the team member

***

`email` *string* required

Email of the team member. This field is required when creating or updating teams via the API.

***

`githubUsername` *string* optional

Member's Github username

***

`country` *string* optional

Member's country using ISO 3166-1 alpha-2 format (e.g., `US`, `NL`, `FI`). Must be exactly 2 uppercase characters. See the [full list of valid codes](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes)

***

If separate authors are found for the `email` and `githubUsername` they will get merged automatically, and only one author is retained.

### Response

Response codes

| **Code** | **Description**                                                                                     |
| -------- | --------------------------------------------------------------------------------------------------- |
| 200      | Ok                                                                                                  |
| 400      | Validation error                                                                                    |
| 409      | Teams are being modified concurrently                                                               |
| 422      | Referenced Jira project or Linear team does not exist, or a team admin email matches multiple users |
| 429      | Too many requests                                                                                   |

400 validation errors include:

* The request contains no teams (removing all teams is not allowed — at least one team is required)
* The request would remove at least 10 teams and more than 90% of the organization's current teams (see [Protection against accidental mass deletion](#protection-against-accidental-mass-deletion))
* Parent team has `jiraProjectKeys` or `linearTeamKeys` defined. Only leaf teams can have issue ownership definitions.
* Multiple teams found with same `externalId`
* Found team with incorrect `parentExternalId`
* Cyclic dependency between parents and child teams
* Both `teamAdmin` and `teamAdmins` specified in the same request
* A team admin email is not in a valid email address format
* A team's existing issue ownership can't be merged with a Jira project or Linear team mapping (a hand-crafted filter, or a "Jira project is not" exclusion rule)

A 409 is returned when your organization's teams are being modified at the same time by another request. Wait a moment and retry.

A 422 is returned when `jiraProjectKeys` references a Jira project or `linearTeamKeys` references a Linear team that doesn't exist in Swarmia, or when a `teamAdmins` email matches more than one user.

#### Rate limits

Team updates are rate limited per organization: you can send a few `PUT` requests in quick succession, but sustained usage is limited to one update every 15 minutes. When you exceed the limit, the API returns 429 with a `Retry-After` header indicating how many seconds to wait before retrying.

Rejected requests — validation errors and concurrent-modification conflicts — don't count toward the limit.

#### Example response

Same as in `GET` request when the operation is successful

#### Response schema

Same as in `GET` request

## Taking the API into use

> ⚠️ **Important:** Once you start using the API to manage teams, the team management UI in Swarmia becomes read-only. This prevents conflicts between API updates and manual UI changes. You can still view teams in the UI, but all modifications must be made through the API.

### With existing teams in Swarmia

If you have existing teams created through the Swarmia UI, they will have `externalId` set to `null`. To migrate to API-based team management, you need to link these teams with your external system by assigning `externalId` values.

**To not remove any existing team configuration**, send the existing Swarmia `id` along the new `externalId` with the first `PUT` request.

* Take the current state using `GET /api/v0/teams`. This state includes our internal `id` for the teams. Teams created via the UI will have `externalId: null`. Example result:

```json
{
    "teams": [
        {
            "id": "3eaa7913-cf0a-4ecb-a9b1-953444d3fdf4",
            "name": "Engineering",
            "externalId": null,
            "parentExternalId": null,
            "members": []
        }
    ]
}
```

* Add `externalId` along with the `id` for all of the existing teams.

```json
{
    "teams": [
        {
            "id": "3eaa7913-cf0a-4ecb-a9b1-953444d3fdf4",
            "name": "Engineering",
            "externalId": "engineers",
            "parentExternalId": null,
            "members": []
        }
    ]
}
```

* Decorate that state with any new teams or memberships.

```json
{
    "teams": [
        {
            "id": "3eaa7913-cf0a-4ecb-a9b1-953444d3fdf4",
            "name": "Engineering",
            "externalId": "engineers",
            "parentExternalId": null,
            "members": []
        },
        {
            "name": "Platform",
            "externalId": "platform",
            "parentExternalId": "engineers",
            "members": [{
	            "name": "Aimo",
	            "email": "aimo@swarmia.com"
            }]
        }
    ]
}
```

* Send that state to Swarmia via the `PUT /api/v0/teams`. This request will retain the "Engineers" team and link it via the `externalId` and create a new team "Platform" as a sub-team of "Engineers".

After this Swarmia will have linked the `externalId` to the correct teams. After the initial setup, we are able to match the teams with the `externalId`. So the `id` parameter is redundant in consecutive requests.

> **Note:** After your first successful `PUT` request, Swarmia's team management UI becomes read-only. All future team updates must be made through the API to maintain consistency.

### Without existing teams

No migration actions needed if you do not have any existing teams in Swarmia.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.swarmia.com/settings/integrations/swarmia-apis/team-management.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
