> The full uetr.ai documentation index is at https://uetr.ai/llms.txt, and every page under https://uetr.ai/docs/api is also Markdown at its address plus `.md`.

# Invite a teammate

Ask to invite someone to the team. The account owner must confirm the invitation on the website before anything happens: until then no invitation is sent and no seat is used. The owner sees the request on the website and is normally notified by email. Asking again for the same address returns the open request. Only the account owner's API keys and connected apps can ask, while the plan includes team seats.

`POST https://uetr.ai/api/v1/team/members`

- MCP tool: `invite_team_member`
- Access: a key or connection with read and write access
- Only the account owner's keys and connections

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `email` | string (email, at most 255 characters) | Yes | The address to invite. |

## Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string | Yes | Nothing has taken effect yet: the account owner must confirm the request. |
| `request` | object | Yes |  |
| `request.id` | string (uuid) | Yes |  |
| `request.kind` | `team_invite`, `record_share`, `webhook_endpoint` | Yes | team_invite: an invitation to the team. record_share: a payment record link. webhook_endpoint: a webhook endpoint that receives status changes. |
| `request.status` | `pending`, `confirmed`, `declined`, `expired` | Yes | pending: waiting for the account owner. confirmed: the owner approved it and it took effect. declined: the owner refused it. expired: nobody answered within seven days, or a later request for the same transfer with different settings replaced it. |
| `request.created_at` | string | Yes |  |
| `request.expires_at` | string | Yes |  |
| `request.decided_at` | string or null | Yes |  |
| `request.invite_email` | string or null | Yes | For a team invite: the address to invite. |
| `request.uetr` | string or null | Yes | For a record share: the transfer's UETR. |
| `request.record_url` | string or null | Yes | For a confirmed record share: the payment record link, while the record still has that link. Null otherwise. |
| `request.endpoint_url` | string or null | Yes | For a webhook endpoint: its host and a masked path. Null otherwise. |
| `detail` | string | Yes | Plain English to pass on to the person behind the request. |

## Example

```sh
curl -X POST "https://uetr.ai/api/v1/team/members" \
  -H "Authorization: Bearer $UETR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"teammate@example.com"}'
```

The answer:

```json
{
  "status": "pending_owner_confirmation",
  "request": {
    "id": "2e5a8d1b-6c9f-4d2a-b4e7-0f3c6d9a2b5e",
    "kind": "team_invite",
    "status": "pending",
    "created_at": "2026-10-14T09:30:00.000Z",
    "expires_at": "2026-10-21T09:30:00.000Z",
    "decided_at": null,
    "invite_email": "teammate@example.com",
    "uetr": null,
    "record_url": null,
    "endpoint_url": null
  },
  "detail": "The account owner must confirm the invitation to teammate@example.com on the website before it takes effect. They have been notified by email."
}
```

## Refusals

Besides the [access refusals](/docs/api/errors#access-refusals) every call can meet, this call can answer:

- [`read_only_credential`](/docs/api/errors#read_only_credential) (403): Read-only key or connection.
- [`machine_writes_disabled`](/docs/api/errors#machine_writes_disabled) (503): Changes are switched off.
- [`request_too_large`](/docs/api/errors#request_too_large) (413): Request too large.
- [`not_permitted`](/docs/api/errors#not_permitted) (403): Not permitted.
- [`already_owner`](/docs/api/errors#already_owner) (409): Already the account owner.