> ## Documentation Index
> Fetch the complete documentation index at: https://ayakaleaf-pro.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Project Invites Collection

`projectInvites` holds the invitations that a project owner sends from the share dialog. One document is one invite to one email address. It is deleted when the invite is accepted or revoked, or by Mongo when it expires. The schema is `services/web/app/src/models/ProjectInvite.mjs`, and `services/web/app/src/Features/Collaborators/CollaboratorsInviteHandler.mjs` writes it.

An invite does not make anyone a member. Members are in the `*_refs` arrays of a project, see [Projects Collection](https://ayakaleaf-pro.mintlify.site/dev/data-model/projects-collection.md).

#### Fields

| Field | Type | Meaning | Source |
| - | - | - | - |
| `_id` | ObjectId | Invite id. The invite notification of an existing user uses the key `project-invite-<_id>` (`services/web/app/src/Features/Notifications/NotificationsBuilder.mjs:102`). | `ProjectInvite.mjs:14` |
| `email` | String | Address the invite was sent to. Revoking an invite for a user matches it against the user's confirmed emails (`CollaboratorsInviteHandler.mjs:103-116`). Not set on a sharing-link invite. | `ProjectInvite.mjs:16`, `CollaboratorsInviteHandler.mjs:84` |
| `tokenHmac` | String | HMAC-SHA256 (key `overleaf-token-invite`) of the token in the invite link. The plain token is only in the email and the notification; lookups hash the token from the URL and match this field. | `ProjectInvite.mjs:18`, `services/web/app/src/Features/Collaborators/CollaboratorsInviteHelper.mjs:11-15`, `CollaboratorsInviteHandler.mjs:82,256` |
| `encryptedToken` | String | Sharing-link invites only: the token, encrypted with `Settings.projectInviteEncryptorOptions`, so the owner can see the link again (`services/web/app/src/Features/Collaborators/CollaboratorsInviteController.mjs:606-611`). | `ProjectInvite.mjs:17`, `CollaboratorsInviteHelper.mjs:30-47`, `CollaboratorsInviteHandler.mjs:255-258` |
| `sendingUserId` | ObjectId | User who sent the invite. On accept it is passed on as the user who added the new member (`CollaboratorsInviteHandler.mjs:199-205`). Not set on a sharing-link invite. | `ProjectInvite.mjs:19`, `CollaboratorsInviteHandler.mjs:86` |
| `projectId` | ObjectId | Project the invite is for. | `ProjectInvite.mjs:20`, `CollaboratorsInviteHandler.mjs:87` |
| `privileges` | String or Boolean | Access the invitee gets: `readOnly`, `readAndWrite` or `review` (`services/web/app/src/Features/Authorization/PrivilegeLevels.mjs:6-12`). A sharing-link invite can also hold `false`, meaning no access. | `ProjectInvite.mjs:21-25`, `CollaboratorsInviteHandler.mjs:88` |
| `createdAt` | Date | Set by the schema default when the invite is saved. | `ProjectInvite.mjs:26` |
| `expires` | Date | Schema default: 30 days after creation. The TTL index removes the invite about 10 seconds after this time. `null` on a sharing-link invite, which never expires. | `ProjectInvite.mjs:6-12,27-31`, `CollaboratorsInviteHandler.mjs:264` |
| `reusable` | Boolean | `false` on an email invite, which is deleted when accepted (`CollaboratorsInviteHandler.mjs:207-220`). `true` on a sharing-link invite, which stays. | `ProjectInvite.mjs:32`, `CollaboratorsInviteHandler.mjs:89,263` |
| `subscriptionId` | ObjectId | Sharing-link invites only: the group subscription whose members may accept the link (`CollaboratorsInviteController.mjs:490-501`). | `ProjectInvite.mjs:33`, `CollaboratorsInviteHandler.mjs:262` |

#### Who Writes It

| Code | What it does |
| - | - |
| `CollaboratorsInviteHandler.mjs:76-101` (`inviteToProject`) | Creates an email invite, then sends the email and, for an existing user, a notification. Called by `POST /project/:Project_id/invite` (`services/web/app/src/Features/Collaborators/CollaboratorsRouter.mjs:114-125`). |
| `CollaboratorsInviteHandler.mjs:118-133` (`revokeInvite`) | Deletes one invite and marks its notification read. Called by `DELETE /project/:Project_id/invite/:invite_id` (`CollaboratorsRouter.mjs:154-159`). |
| `CollaboratorsInviteHandler.mjs:135-153` (`generateNewInvite`) | Resend: deletes the invite and creates a new one with a new token (`CollaboratorsRouter.mjs:161-169`). |
| `CollaboratorsInviteHandler.mjs:103-116` (`revokeInviteForUser`) | Deletes the invite sent to one of the user's confirmed emails, when the user joins through a link-sharing token (`services/web/app/src/Features/TokenAccess/TokenAccessController.mjs:396-401`) or is granted access after an access request (`services/web/app/src/Features/Collaborators/CollaboratorsController.mjs:503-513`). |
| `CollaboratorsInviteHandler.mjs:155-221` (`acceptInvite`) | Adds the user to the project, then deletes the invite unless it is `reusable`. |
| `CollaboratorsInviteHandler.mjs:249-269` (`createSharingLinkInvite`), `CollaboratorsInviteController.mjs:689-724` (`updateSharingLink`) | Create or update the one reusable sharing-link invite of a project. These routes require the split test `sharing-updates-new-link` (`CollaboratorsRouter.mjs:134-152`), which is not in `splitTestOverrides` (`services/web/config/settings.defaults.js:1371-1383`), so they answer 403 unless that split test is set up. |

#### Who Reads It

| Code | What it does |
| - | - |
| `services/web/app/src/Features/Collaborators/CollaboratorsInviteGetter.mjs:6-19` (`getAllInvites`) | Lists the pending email invites for the share dialog (`GET /project/:Project_id/invites`, `CollaboratorsRouter.mjs:127-132`). Skips reusable invites. |
| `CollaboratorsInviteGetter.mjs:21-29` (`getEditInviteCount`) | Counts pending email invites that are not `readOnly`. `services/web/app/src/Features/Subscription/LimitationsManager.mjs:61-70` adds them to the editors when it checks the collaborator limit. |
| `CollaboratorsInviteGetter.mjs:31-44` (`getInviteByToken`) | Finds an invite by the hash of the token in the URL, for the invite page and for accepting (`CollaboratorsInviteController.mjs:353,481`). |
| `CollaboratorsInviteGetter.mjs:46-70` | Finds the sharing-link invite of a project. |

#### Indexes

| Name | Keys | Options | Migration |
| - | - | - | - |
| `expires_1` | `{ expires: 1 }` | TTL, `expireAfterSeconds: 10` | `20190912145023_create_projectInvites_indexes.mjs` |
| `projectId_1` | `{ projectId: 1 }` | | `20190912145023_create_projectInvites_indexes.mjs` |
| `tokenHmac_1` | `{ tokenHmac: 1 }` | | `20240524135408_add_token_hmac_project_invite_tokens.mjs` |

Migrations are in `tools/migrations/`. The schema also declares the `expires` TTL index, but Mongoose `autoIndex` is off (`services/web/app/src/infrastructure/Mongoose.mjs:7`), so the migration creates it.

#### Notes

* If the project has no editor slot left when a `readAndWrite` or `review` invite is accepted, the user joins as read-only and is listed in `pendingEditor_refs` or `pendingReviewer_refs` of the project (`CollaboratorsInviteHandler.mjs:162-205`).
* Older invites stored the plain token in a `token` field. Migration `20240524135408_add_token_hmac_project_invite_tokens.mjs` added `tokenHmac` and `20240531082910_remove_project_invite_tokens.mjs` removed `token`.
* Nothing deletes the invites of a deleted project; they stay until the TTL index removes them.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.