# Invitations tools

> MCP tools for Invitations: invitations_accept, invitations_create, invitations_list, invitations_lookup, invitations_revoke.

- `invitations_accept`: Accept an invitation
- `invitations_create`: Invite someone to the organization
- `invitations_list`: List invitations
- `invitations_lookup`: Look up an invitation
- `invitations_revoke`: Revoke an invitation

## `invitations_accept`

**Accept an invitation.** Operation [`invitations.accept`](https://ownrecord.io/docs/reference/invitations-accept.md) · Platform level · permission `account:write` · risk tier `write` · since M1

Accepts an invitation: you become a member of the organization with the invitation's role and scope.
You must be signed in with the address the invitation was sent to, and the invitation must be pending
(not accepted, revoked or older than 7 days). Live-mode access follows the role (developers start
without it). Returns the new membership and the organization, whose console is `/console/{slug}`.

**Who can call it:** any signed-in user holding the token from the invitation link.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type    | Required | Description                                                                                                                                                                                              |
| ----------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`           | string  | yes      | –                                                                                                                                                                                                        |
| `idempotency_key` | string  | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The token is malformed.
- [`invitation_invalid`](https://ownrecord.io/docs/reference/errors.md#invitation_invalid): No invitation has this token, or what it grants no longer exists.
- [`invitation_expired`](https://ownrecord.io/docs/reference/errors.md#invitation_expired): The invitation is older than 7 days.
- [`invitation_revoked`](https://ownrecord.io/docs/reference/errors.md#invitation_revoked): The invitation was revoked.
- [`invitation_already_accepted`](https://ownrecord.io/docs/reference/errors.md#invitation_already_accepted): The invitation was already accepted.
- [`invitation_email_mismatch`](https://ownrecord.io/docs/reference/errors.md#invitation_email_mismatch): You're signed in with another address than the invited one.
- [`membership_exists`](https://ownrecord.io/docs/reference/errors.md#membership_exists): You already have a membership at this scope.
- [`entitlement_required`](https://ownrecord.io/docs/reference/errors.md#entitlement_required): The organization's `members.max` is reached.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The invitation changed at the same time (for example, it was revoked).
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): The caller isn't a signed-in user.

### Example

**Accept an invitation to a brand**

Arguments:

```json
{
  "token": "ExampleOnlyNotARealInvitationToken000000000",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "membership": {
    "id": "mem_01m4tx5nj8emwbyf7y9vcf47x6",
    "object": "membership",
    "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
    "userId": "usr_01m4tx5nj8emwbyf7y9vcf47xb",
    "role": "developer",
    "scope": {
      "type": "brand",
      "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4"
    },
    "livemodeAccess": false,
    "invitedBy": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
    "user": {
      "email": "dev@acme.example",
      "name": "Dana Developer"
    },
    "createdAt": "2026-10-13T12:00:00.000Z",
    "updatedAt": "2026-10-13T12:00:00.000Z"
  },
  "organization": {
    "id": "org_01m4tx5nj7ekd958x53w4bntcg",
    "slug": "acme",
    "name": "Acme"
  }
}
```

## `invitations_create`

**Invite someone to the organization.** Operation [`invitations.create`](https://ownrecord.io/docs/reference/invitations-create.md) · Organization level · permission `members:admin` · risk tier `security` · since M1

Invites a person by email with a role and a scope (the whole organization, one legal entity or one brand).
They accept by signing in with that address and opening the link; the invitation expires after 7 days.
Live-mode access follows the role (developers start without it; change it after they join).

Ownrecord emails the invitation link to the address right after the change is saved. The response also
contains the invitation `token` (link: `/console/invitations/{token}`), **once**: only a hash is stored.

You can't invite anyone with more access than you have. A pending invitation reserves a seat: members plus
pending invitations count against the plan's `members.max`. Needs `members:admin` and a signed-in member
(the invitation says who invited; API keys can't invite). A security-tier change.

Annotations: `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type                                                                                                         | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------------------------------------------------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`           | string                                                                                                       | yes      | (at most 254 characters)                                                                                                                                                                                 |
| `role`            | one of `org_owner`, `org_admin`, `billing_admin`, `finance`, `tax_manager`, `support`, `developer`, `viewer` | yes      | –                                                                                                                                                                                                        |
| `scope`           | object or object or object                                                                                   | yes      | –                                                                                                                                                                                                        |
| `idempotency_key` | string                                                                                                       | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean                                                                                                      | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The email, role or scope is malformed, or the role isn't allowed at the scope.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): The scope's entity or brand doesn't exist in the organization.
- [`role_not_grantable`](https://ownrecord.io/docs/reference/errors.md#role_not_grantable): The role and scope give more access than you have.
- [`membership_exists`](https://ownrecord.io/docs/reference/errors.md#membership_exists): The person is already a member at this scope.
- [`invitation_exists`](https://ownrecord.io/docs/reference/errors.md#invitation_exists): The person already has a pending invitation at this scope.
- [`entitlement_required`](https://ownrecord.io/docs/reference/errors.md#entitlement_required): Members and pending invitations already fill `members.max`.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage members of this organization, or the caller is an API key.

### Example

**Invite a finance colleague for one entity**

Arguments:

```json
{
  "email": "finance@acme.example",
  "role": "finance",
  "scope": {
    "type": "entity",
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2"
  },
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "invt_01m4tx5nj8emwbyf7y9vcf47x8",
  "object": "invitation",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "email": "finance@acme.example",
  "role": "finance",
  "scope": {
    "type": "entity",
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2"
  },
  "status": "pending",
  "expiresAt": "2026-10-20T12:00:00.000Z",
  "invitedBy": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
  "acceptedAt": null,
  "revokedAt": null,
  "createdAt": "2026-10-13T12:00:00.000Z",
  "token": "ExampleOnlyNotARealInvitationToken000000000"
}
```

## `invitations_list`

**List invitations.** Operation [`invitations.list`](https://ownrecord.io/docs/reference/invitations-list.md) · Organization level · permission `members:read` · risk tier `read` · since M1

Lists the organization's invitations, newest first, with their status: `pending`, `accepted`, `revoked`
or `expired` (invitations work for 7 days). Filter with `status`, e.g. `pending` for the invitations still
waiting for an answer. Tokens are never returned. Paginate with `limit` and `starting_after`.

Needs `members:read` (organization owners and admins, or an org management key).

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument         | Type                                               | Required | Description |
| ---------------- | -------------------------------------------------- | -------- | ----------- |
| `status`         | one of `pending`, `accepted`, `revoked`, `expired` | no       | –           |
| `limit`          | integer                                            | no       | (1–100)     |
| `starting_after` | ID (`invt_…`)                                      | no       | –           |

- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A filter or pagination value is malformed.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't see this organization's members.

### Examples

**Pending invitations**

Arguments:

```json
{
  "status": "pending"
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "invt_01m4tx5nj8emwbyf7y9vcf47x8",
      "object": "invitation",
      "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
      "email": "finance@acme.example",
      "role": "finance",
      "scope": {
        "type": "entity",
        "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2"
      },
      "status": "pending",
      "expiresAt": "2026-10-20T12:00:00.000Z",
      "invitedBy": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
      "acceptedAt": null,
      "revokedAt": null,
      "createdAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

**Everything, two per page**

Arguments:

```json
{
  "limit": 2
}
```

Result `output`:

```json
{
  "object": "list",
  "data": [
    {
      "id": "invt_01m4tx5nj8emwbyf7y9vcf47x8",
      "object": "invitation",
      "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
      "email": "finance@acme.example",
      "role": "finance",
      "scope": {
        "type": "entity",
        "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2"
      },
      "status": "pending",
      "expiresAt": "2026-10-20T12:00:00.000Z",
      "invitedBy": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
      "acceptedAt": null,
      "revokedAt": null,
      "createdAt": "2026-10-13T12:00:00.000Z"
    },
    {
      "id": "invt_01m4tx5nj8emwbyf7y9vcf47x9",
      "object": "invitation",
      "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
      "email": "dev@acme.example",
      "role": "developer",
      "scope": {
        "type": "brand",
        "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4"
      },
      "status": "accepted",
      "expiresAt": "2026-10-20T12:00:00.000Z",
      "invitedBy": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
      "acceptedAt": "2026-10-14T09:30:00.000Z",
      "revokedAt": null,
      "createdAt": "2026-10-13T12:00:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "invt_01m4tx5nj8emwbyf7y9vcf47x9"
}
```

## `invitations_lookup`

**Look up an invitation.** Operation [`invitations.lookup`](https://ownrecord.io/docs/reference/invitations-lookup.md) · Platform level · permission `account:read` · risk tier `read` · since M1

Shows what an invitation link offers: the organization, the role and scope, who invited, when it expires
and its status. The invited address is shown in full only when you're signed in with it.

**Who can call it:** any signed-in user holding the token from the invitation link.

Annotations: `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Arguments

| Argument | Type   | Required | Description |
| -------- | ------ | -------- | ----------- |
| `token`  | string | yes      | –           |

- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The token is malformed.
- [`invitation_invalid`](https://ownrecord.io/docs/reference/errors.md#invitation_invalid): No invitation has this token.
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): The caller isn't a signed-in user.

### Example

**A pending invitation for the signed-in address**

Arguments:

```json
{
  "token": "ExampleOnlyNotARealInvitationToken000000000"
}
```

Result `output`:

```json
{
  "object": "invitation_preview",
  "status": "pending",
  "organization": {
    "name": "Acme",
    "slug": "acme"
  },
  "role": "finance",
  "scope": {
    "type": "entity",
    "name": "Acme Digital d.o.o."
  },
  "email": "finance@acme.example",
  "emailMatches": true,
  "invitedBy": "Olivia Owner",
  "expiresAt": "2026-10-20T12:00:00.000Z"
}
```

## `invitations_revoke`

**Revoke an invitation.** Operation [`invitations.revoke`](https://ownrecord.io/docs/reference/invitations-revoke.md) · Organization level · permission `members:admin` · risk tier `security` · since M1

Revokes a pending invitation: its link stops working at once. Accepted, revoked and expired invitations
can't be revoked (`invitation_not_pending`). Give a `reason`: it goes to the audit log.

Like members, you can only revoke invitations you could have sent (an admin can't revoke an invitation
to become an owner). Needs `members:admin`; a security-tier change.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Arguments

| Argument          | Type          | Required | Description                                                                                                                                                                                              |
| ----------------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | ID (`invt_…`) | yes      | –                                                                                                                                                                                                        |
| `reason`          | string        | no       | (1–500 characters)                                                                                                                                                                                       |
| `idempotency_key` | string        | no       | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's `idempotency_key`. (1–255 characters) |
| `dry_run`         | boolean       | no       | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default `false`)                                                                                 |

- **Idempotency:** the result returns the `idempotency_key` used, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours).
- **Dry run:** supported. `dry_run: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such invitation in this organization.
- [`invitation_not_pending`](https://ownrecord.io/docs/reference/errors.md#invitation_not_pending): The invitation was already accepted, revoked or has expired.
- [`role_not_grantable`](https://ownrecord.io/docs/reference/errors.md#role_not_grantable): The invitation grants more access than you have.
- [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict): The invitation changed at the same time (for example, it was accepted).
- [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied): You can't manage members of this organization.

### Example

**Revoke an invitation sent to the wrong address**

Arguments:

```json
{
  "id": "invt_01m4tx5nj8emwbyf7y9vcf47x8",
  "reason": "Sent to the wrong address",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "id": "invt_01m4tx5nj8emwbyf7y9vcf47x8",
  "object": "invitation",
  "orgId": "org_01m4tx5nj7ekd958x53w4bntcg",
  "email": "finance@acme.example",
  "role": "finance",
  "scope": {
    "type": "entity",
    "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2"
  },
  "status": "revoked",
  "expiresAt": "2026-10-20T12:00:00.000Z",
  "invitedBy": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
  "acceptedAt": null,
  "revokedAt": "2026-10-14T09:30:00.000Z",
  "createdAt": "2026-10-13T12:00:00.000Z"
}
```
