Invites
Anchorify uses signed invite links (no DB rows, no separate invite queue) to bring collaborators into an org or project. An admin generates a link; Anchorify emails the recipient automatically and the admin can also DM the link from the dashboard's "Copy link" button.
Two kinds of invites
- Org invite (admin role). Adds the invitee as an admin of your org. They can edit and publish anywhere in the org. Each user can only be admin of one org at a time, so accepting an org invite when you already have a home org triggers a merge (see below).
- Project invite (viewer role). Adds the invitee as a viewer of a single project — they can read shares (including
members-visibility shares) but cannot edit. They keep their own home org.
Sending an invite
The fastest path is the Invite button on any share page in your org. Admins see it in the top-right of the share chrome. The modal lets you pick:
- "Invite to this project (
<project-name>)" — viewer role. - "Invite to this org (
<org-slug>)" — admin role.
Submit with an email + optional message. On success you get a toast and a "Copy link" button so you can DM the accept URL too in case the email lands in spam.
Programmatically: POST /api/v1/orgs/<slug>/invites with body:
{
"email": "[email protected]",
"target": "project",
"project_slug": "q1-reports",
"message": "Q1 numbers, take a look."
}
The response is { "token": "...", "accept_url": "...", "expires_at": <ms> }. Tokens are HMAC-signed and last 7 days.
What gets emailed
Each invite enqueues a plain-text email (via Resend) with the inviter's username, target description ("the <org> org" or "the <org> / <project> project"), the accept URL, the optional message text, and a "This invite expires in 7 days" line. The email is sent via the send_email pg-boss queue so the response stays fast and pg-boss handles retries on transient failures.
In dev (no RESEND_API_KEY), the email body is logged to stdout instead. The accept URL is still returned inline so you can copy it from the API response.
Accepting an invite
The invitee opens the accept URL. Anchorify routes them through one of four state branches:
- Not signed in, no Anchorify account. Server creates the account inline using the token's email and runs the pending-username flow. (This branch lights up once magic-link sign-in ships; until then the link asks them to sign in with Google first.)
- Not signed in, account exists. Magic-link sign-in (or Google for now), then resume accept.
- Signed in, email matches the token. Accept happens directly.
- Signed in but the session email doesn't match. Error page: "this invite is for
<token-email>; sign out and sign in as that."
What happens after accept depends on the target:
Project invite
The server inserts a project_members row and redirects the user to the project page. Idempotent — re-accepting an invite they've already used is a no-op.
Org invite — three sub-cases
- You have no current org. The server inserts your
org_membersrow in the inviting org and lands you on the dashboard. - You're already a member of the target org. No-op; the dashboard loads.
- You're sole admin of another org. The server shows a merge confirmation page. You pick which org slug to keep (yours or the inviter's), rename any conflicting project slugs, and confirm. Your projects + shares move under the destination org; the discarded slug is permanently retired (tombstoned).
- You're not the sole admin of your current org. The server blocks the accept: "your current org has other admins; ask them to remove you before accepting." This protects orgs from one of their admins silently quitting via an invite.
Expired or stale invites
- Expired (past 7 days): error page suggesting the invitee ask the inviter for a fresh link.
- Deleted org or project: error page "this invite is no longer valid."
Tokens are stateless — there's no revoke endpoint. To "revoke" an active invite before the 7-day window, rename the target org/project; the token's target_id still resolves, but a fresh invite is needed to land on the renamed target.
The merge asymmetry
Once you've merged your org into someone else's, the projects you brought belong to the destination org. If you're later removed from that org (by another admin), the projects stay — they don't follow you out. You get a fresh empty home org on next sign-in via the auto-create hook.
This is intentional. Anchorify doesn't track "who brought what"; projects are org property, not user property. Make sure you're OK with that before accepting an org-level invite that triggers a merge.