# Add a collaborator

> Give someone a role on a repository, by username or email address.

<div class="g1t-endpoint"><span class="g1t-method" data-method="post">POST</span><code>/repos/{owner}/{name}/collaborators</code></div>

A member of its workspace gets the role at once (`result` is `granted`, with the `collaborator`). Anyone else becomes an outside collaborator once they accept an invitation, which is emailed to them and waits 7 days (`result` is `invited`, with the `invitation`); an address with no g1t account is sent an invite that makes the account and accepts in one step. The role is read, triage, write, maintain or admin. Needs the Admin role on the repository, signed in as a person with a confirmed email address; agents' and workspaces' tokens are refused.

`invitee` is a username or an email address. A member of the workspace answers `{"result": "granted", "collaborator": {…}}` with the role already given, shown as [`list_collaborators`](/reference/api/access/list-collaborators/) shows a person. Anyone else answers `{"result": "invited", "invitation": {…}}`: the invitation is emailed and waits 7 days, and the role is theirs once they accept it. An email address without an account gets an invitation with `email` set and `invitee` null, and an invite that makes the account and accepts in one step. Refused with `404` when no account has that username, `409` when they already have a role of their own or a pending invitation (change it with [`update_collaborator`](/reference/api/access/update-collaborator/)), and `403` without the Admin role, from an agent's or a workspace's token, or when the person does not meet what the workspace asks of everyone with access. The `repo.collaborator_added` webhook event is sent once they have the role. See [Access and roles](/guides/access-and-roles/).

- **Authentication:** Required. Send an [access token](/reference/api/#authentication) as `Authorization: Bearer`.
- **MCP tool:** [`access`](/reference/mcp/#access) with `action` `add_collaborator`, and the same inputs
- **Scope:** An access token needs [`access:admin`](/guides/authentication/#scopes).

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `owner` | string | Yes | The workspace that owns the repository. |
| `name` | string | Yes | The repository's name. |

## Body parameters

Send a JSON object. Names are `snake_case`, as in responses; the `camelCase` spelling is accepted too.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `invitee` | string | Yes | A username, or an email address. An address confirmed on an account invites that account; any other address is sent an invite that makes the account. |
| `role` | string | Yes | read: read and comment. triage: also label, assign and close. write: also push, merge and put agents to work. maintain: also settings and branch protection. admin: everything, including who has access. One of `read`, `triage`, `write`, `maintain`, `admin`. |

## Example request

```sh
curl -X POST https://api.g1t.sh/repos/flagon-io/hello/collaborators \
  -H "Authorization: Bearer $G1T_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "invitee": "ada",
    "role": "triage"
  }'
```

## Example response

A successful request answers `200` with:

```json
{
  "result": "invited",
  "invitation": {
    "id": "rin_01kp3e9f0g1h2j3k4m5n6p7q8r",
    "repo": "flagon-io/hello",
    "repo_id": "rep_01m3m5q6p0e2qaw6mmjahk0qrr",
    "invitee": "ada",
    "email": null,
    "role": "triage",
    "invited_by": "syntaqx",
    "inviter_avatar": null,
    "status": "pending",
    "created_at": "2026-10-05T17:00:00.000Z",
    "expires_at": "2026-10-12T17:00:00.000Z"
  }
}
```

## Errors

A failed request answers with one of these statuses and a body like `{"error": {"code": "not_found", "message": "Repository not found."}}`. See [errors](/reference/api/#errors).

| Status | Code | When |
| --- | --- | --- |
| 401 | `unauthenticated` | A token is required, or the one sent is not valid. |
| 403 | `forbidden` | The token is valid but not allowed to do this, such as a member-only change or an agent token outside its repository. |
| 404 | `not_found` | It does not exist, or you cannot see it. |
| 409 | `conflict` | The request conflicts with the current state. |
| 422 | `invalid` | The input is not valid. `message` says which field and why. |
