Register a new user account by redeeming an invitation token
POST/api/auth/register
Register a new user account by redeeming an invitation token. Registration is invitation-only: the token identifies the tenant, the organisation role and, for a project-scoped invitation, the project the new account is granted access to.
Request
Responses
- 200
- 400
- 409
- 500
Successful response
Bad Request — a body that is not valid JSON, a body that fails schema validation, or an invitation that cannot be redeemed. The redemption cases are checked BEFORE any account is created, so in the ordinary case a 400 here leaves no orphaned auth user. That is not an absolute guarantee: the preflight reads are not held across to the grant, so membership or organisation state changing in between can still produce a 400 from the grant itself, after the external auth write. The cases: an invalid, expired or already-consumed token; an invitation whose organisation role or project role does not belong to its tenant; a project outside that tenant; a tenant that is not ACTIVE; a tenant with no organisation to join; a membership already bound to another tenant's organisation; or — only where the invitee has no membership yet — a tenant whose target organisation cannot be determined unambiguously.
Conflict. Two distinct causes, and they differ in whether anything can be done about them. (1) The invitation stopped being redeemable between the preflight and the grant (accepted, revoked or expired concurrently) — retryable only if the invitation is reissued. (2) A conflicting collaborator record for this user and project exists outside the invitation's tenant, so the grant refuses to write it. Reissuing the invitation does NOT clear that one: the obstruction is a row in another tenant, which this caller cannot see and an operator must resolve. The two carry DIFFERENT messages — "Invitation is no longer pending" for (1), and for (2) a deliberately non-specific one that names neither the tenant nor the conflicting row, so no response discloses cross-tenant state. Both can land AFTER the external auth user and the identity user have been created. The grant itself is atomic, so nothing it does survives: the membership, the invitation role, any collaborator row and the audit entry all roll back together. What remains is the auth user and the identity user — an account that exists with no access granted.
Internal Server Error — always the generic "Registration failed". This route is unauthenticated and failures reaching it can originate inside the identity boundary, whose messages carry function names and tenant/user identifiers. The genericness is enforced by status, not by error class: a sub-500 AppError is returned verbatim because its message is written for the caller, but a 500-status one (UNEXPECTED_ROW_SHAPE, naming an internal query; IDENTITY_GRANT_FAILED, from the boundary) is collapsed to this message and its detail kept in the server log. Sibling AUTHENTICATED routes do serve those codes to the client — the difference is deliberate and follows from who is calling.