# Invitations

Invite people to the organization by email.

An invitation is addressed to one email and expires after seven days by default. The
link is returned once, when the invitation is [created](#createInvitation) or
[resent](#resendInvitation). Resending replaces the previous link and renews
the expiry. Acceptance requires the invited email and creates a membership
with the invitation's selected permissions.

See [Membership management](/docs/web-app/membership-management) for
invitations, joining and managing access in the app.

## List invitations

`GET /api/v1/invitations`

Auth: Reviewer session cookie (`withhuman_session`) or personal API key (`Authorization: Bearer $WITHHUMAN_API_KEY`)

Requires: `membership.invite`

Returns the organization's invitations in every status, newest first.

### Request

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | no | How many invitations to return. Defaults to 100. |

### Response

| Status | Body | Description |
| --- | --- | --- |
| 200 | object | The invitations |
| 401 | ErrorResponse | You are not signed in |
| 403 | ErrorResponse | You cannot invite people |

Response body (200):

- `invitations` · array<Invitation> · required
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `email` · string · required: The address the invitation was sent to.
  - `grants` · array<InvitationGrant> · required: The roles the person receives when they accept.
    - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
    - `permissions` · array<string> · required
  - `invited_by_membership_id` · uuid · required: The member who sent it.
  - `status` · enum · required: `pending` can still be accepted. `accepted` and `revoked` are final. `expired` passed its expiry without being accepted. One of `pending`, `accepted`, `revoked`, `expired`.
  - `expires_at` · date-time · required: When the link stops working. Seven days from creation or the last resend.
  - `accepted_membership_id` · uuid: The membership created on acceptance. Present for `accepted` only.
  - `created_at` · date-time · required
  - `updated_at` · date-time · required

### Example

```bash
curl -X GET "$WITHHUMAN_URL/api/v1/invitations" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
```

200 response

```json
{
  "invitations": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "email": "ada@example.com",
      "grants": [
        {
          "scope_kind": "organization",
          "scope_id": "string",
          "permissions": [
            "string"
          ]
        }
      ],
      "invited_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "status": "pending",
      "expires_at": "2026-09-08T12:02:11Z",
      "accepted_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "created_at": "2026-09-08T12:02:11Z",
      "updated_at": "2026-09-08T12:02:11Z"
    }
  ]
}
```

## Invite a person

`POST /api/v1/invitations`

Auth: Reviewer session cookie (`withhuman_session`), browser only; a personal API key is refused

Requires: `membership.invite`

Invite a member with explicit permission policies. Every permission must be held by the inviter at the selected scope or wider.

### Request

Request body:

- `email` · email · required: Where to send the invitation. The person must accept with this address.
- `permission_policies` · array<PermissionPolicySpec> · required
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `resource_ids` · array<string> · required: Empty for organization scope; otherwise the selected team IDs or agent slugs.
  - `permissions` · array<string> · required

### Response

| Status | Body | Description |
| --- | --- | --- |
| 201 | InvitationWithURL | The invitation and its link. The link appears only in this response |
| 403 | ErrorResponse | You cannot invite people, or a grant exceeds what you hold or needs a fresher sign-in |
| 409 | ErrorResponse | A member or a pending invitation with this email already exists, or the organization is at its seat limit |

Response body (201):

- `invitation` · Invitation · required: An invitation to join the organization.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `email` · string · required: The address the invitation was sent to.
  - `grants` · array<InvitationGrant> · required: The roles the person receives when they accept.
    - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
    - `permissions` · array<string> · required
  - `invited_by_membership_id` · uuid · required: The member who sent it.
  - `status` · enum · required: `pending` can still be accepted. `accepted` and `revoked` are final. `expired` passed its expiry without being accepted. One of `pending`, `accepted`, `revoked`, `expired`.
  - `expires_at` · date-time · required: When the link stops working. Seven days from creation or the last resend.
  - `accepted_membership_id` · uuid: The membership created on acceptance. Present for `accepted` only.
  - `created_at` · date-time · required
  - `updated_at` · date-time · required
- `invite_url` · uri: The link to send to the person. It contains the invitation's secret token, appears only in this response, and only on a deployment with no email delivery configured.

### Example

```bash
curl -X POST "$WITHHUMAN_URL/api/v1/invitations" \
  -b "withhuman_session=$WITHHUMAN_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "reviewer@example.com",
  "permission_policies": [
    {
      "scope_kind": "organization",
      "resource_ids": [
        "string"
      ],
      "permissions": [
        "string"
      ]
    }
  ]
}'
```

201 response

```json
{
  "invitation": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "email": "ada@example.com",
    "grants": [
      {
        "scope_kind": "organization",
        "scope_id": "string",
        "permissions": [
          "string"
        ]
      }
    ],
    "invited_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "status": "pending",
    "expires_at": "2026-09-08T12:02:11Z",
    "accepted_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "created_at": "2026-09-08T12:02:11Z",
    "updated_at": "2026-09-08T12:02:11Z"
  },
  "invite_url": "https://example.com/webhooks/withhuman"
}
```

## Resend an invitation

`POST /api/v1/invitations/{id}/resend`

Auth: Reviewer session cookie (`withhuman_session`), browser only; a personal API key is refused

Requires: `membership.invite`

Issues a new link for a pending invitation and extends its expiry by seven days. The previous link stops working. The new link appears only in this response.

### Request

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | uuid | yes | The invitation's id. |

### Response

| Status | Body | Description |
| --- | --- | --- |
| 200 | InvitationWithURL | The invitation and its new link. The link appears only in this response |
| 403 | ErrorResponse | You cannot invite people |
| 404 | ErrorResponse | No such invitation, or it is no longer pending |

Response body (200):

- `invitation` · Invitation · required: An invitation to join the organization.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `email` · string · required: The address the invitation was sent to.
  - `grants` · array<InvitationGrant> · required: The roles the person receives when they accept.
    - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
    - `permissions` · array<string> · required
  - `invited_by_membership_id` · uuid · required: The member who sent it.
  - `status` · enum · required: `pending` can still be accepted. `accepted` and `revoked` are final. `expired` passed its expiry without being accepted. One of `pending`, `accepted`, `revoked`, `expired`.
  - `expires_at` · date-time · required: When the link stops working. Seven days from creation or the last resend.
  - `accepted_membership_id` · uuid: The membership created on acceptance. Present for `accepted` only.
  - `created_at` · date-time · required
  - `updated_at` · date-time · required
- `invite_url` · uri: The link to send to the person. It contains the invitation's secret token, appears only in this response, and only on a deployment with no email delivery configured.

### Example

```bash
curl -X POST "$WITHHUMAN_URL/api/v1/invitations/{id}/resend" \
  -b "withhuman_session=$WITHHUMAN_SESSION"
```

200 response

```json
{
  "invitation": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "email": "ada@example.com",
    "grants": [
      {
        "scope_kind": "organization",
        "scope_id": "string",
        "permissions": [
          "string"
        ]
      }
    ],
    "invited_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "status": "pending",
    "expires_at": "2026-09-08T12:02:11Z",
    "accepted_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "created_at": "2026-09-08T12:02:11Z",
    "updated_at": "2026-09-08T12:02:11Z"
  },
  "invite_url": "https://example.com/webhooks/withhuman"
}
```

## Revoke an invitation

`DELETE /api/v1/invitations/{id}`

Auth: Reviewer session cookie (`withhuman_session`) or personal API key (`Authorization: Bearer $WITHHUMAN_API_KEY`)

Requires: `membership.invite`

Revokes a pending invitation. Its link stops working. A revoked invitation cannot be resent. Invite the person again instead.

### Request

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | uuid | yes | The invitation's id. |

### Response

| Status | Body | Description |
| --- | --- | --- |
| 204 |  | The invitation is revoked |
| 403 | ErrorResponse | You cannot invite people |
| 404 | ErrorResponse | No such invitation, or it is no longer pending |

### Example

```bash
curl -X DELETE "$WITHHUMAN_URL/api/v1/invitations/{id}" \
  -H "Authorization: Bearer $WITHHUMAN_API_KEY"
```
