---
title: "Add a member to a vault"
method: POST
path: "/vaults/{id}/members"
tags: ["Contract Intelligence"]
---

# Add a member to a vault

`POST /vaults/{id}/members`

## Request

- Base URL: `https://app.gc.ai/api/external/v1`
- URL: `https://app.gc.ai/api/external/v1/vaults/{id}/members`
- Auth: API key in header `Authorization`
- TypeScript: <https://skmtc.dev/gc-ai/apis/gc-ai-external-api/docs/vaults/:id/members/post.ts> (a function with typed inputs; its response is checked against the schema)

Give someone a role in a vault. Name them by `user_id` or by `email`, exactly one of the two.

The person has to be a member of your organization already. An address that belongs to nobody in the organization returns `400` and nothing is sent, so this endpoint can never invite anyone or add a seat.

Adding someone who already holds a role returns their existing row unchanged, current role included. That keeps a repeated call from quietly demoting an owner to `editor`. To change a role, use `PATCH /vaults/{id}/members/{userId}`.

Requires the `owner` role in the vault and the organization-level permission to manage vaults.

Requires a user-scoped API key (`u:gcai_...`). A vault is shared with named people, so an organization-scoped key has no identity to resolve vault access with.

## Path parameters

- `id` string, uuid, required: The vault ID

## Request body

- AddVaultMemberRequest
  - `user_id` integer: GC AI user ID of the person to add, as `user_id` from `GET /vaults/{id}/members` or `id` from `GET /me`. Send this or `email`.
  - `email` string, email: Email address of the person to add. It has to belong to someone who is already a member of your organization. Send this or `user_id`.
  - `role` 'owner' | 'editor' | 'viewer': Role to grant. Defaults to `editor`.

## Response `201`

The vault member

- VaultMember
  - `user_id` integer, required: GC AI user ID of the member. This is the same identifier `GET /me` reports for the calling user, so a caller can pick their own row out of the list.
  - `email` string, nullable, required: Member email address, or null when the account has none on file.
  - `name` string, nullable, required: Member display name, preferring the name they chose for themselves. Null when the account has no name on file.
  - `role` 'owner' | 'editor' | 'viewer' | 'null', nullable, required: The member's role in this vault: `owner` (manage members and settings), `editor` (add documents and edit extracted data), or `viewer` (read and annotate). Null on the rare stored value GC AI cannot read as a role. Such a grant carries no permissions at all, so remove it and add the person again.
  - `added_at` string, nullable, required: ISO 8601 timestamp when the member was added, or null for grants written before GC AI recorded that.

## Other responses

- `400`: Invalid request body, or user is not a member of this organization
- `401`: Missing or malformed API key
- `403`: Contract Intelligence is not enabled for this account. Contact support to request access., or the caller is not a vault owner
- `404`: Vault not found or access denied
- `409`: The vault is archived. Restore it with `POST /vaults/{id}/restore` before changing it.
- `422`: Vault access is per-user and requires a user-scoped API key (u:gcai_...).
- `429`: Rate limit exceeded. See [Rate Limits](https://docs.gc.ai/api-reference/concepts/rate-limits) for the tiers, limits, and how to back off.
- `500`: Internal server error
- `503`: Service temporarily unavailable

---

[API](https://skmtc.dev/gc-ai/apis/gc-ai-external-api.md) - [All operations](https://skmtc.dev/gc-ai/apis/gc-ai-external-api/llms.txt) - [OpenAPI document](https://skmtc.dev/gc-ai/apis/gc-ai-external-api/revisions/2bbdfddf7107?raw)
