Skip to content

Invites

Endpoints for resolving invite tokens and accepting or declining organization invitations. These endpoints support the invite-first onboarding flow.

Base path: /api/invites


Endpoints

Method Path Auth Description
GET /api/invites/:token Public Resolve an invite token
POST /api/invites/accept Public Create account and accept invite (legacy flow)
GET /api/invites/my-pending Required List caller's pending invites
POST /api/invites/:id/accept Required Accept a pending invite
POST /api/invites/:id/decline Required Decline a pending invite

Onboarding Flows

New user (no existing account)

  1. User clicks invite link in email — token is embedded in the URL.
  2. Frontend calls GET /api/invites/:token to resolve invite metadata.
  3. User fills in name and password.
  4. Frontend calls POST /api/invites/accept with the token, credentials, and profile fields.
  5. Account is created, org membership is assigned, and invite is marked accepted.

Existing user

  1. User clicks invite link — hasAccount: true in the GET /api/invites/:token response.
  2. Frontend shows sign-in CTA.
  3. User signs in normally.
  4. After sign-in, if GET /api/invites/my-pending returns pending invites, the app shows the pending invites screen.
  5. User accepts or declines each invite manually.

Invite gate

When an authenticated user has pending invites, most protected endpoints return 403 until all invites are accepted or declined. Invite endpoints remain accessible so the user can complete onboarding.


GET /api/invites/:token

Resolve an invite token and return its metadata. Public — no authentication required.

Path parameters:

Parameter Type Description
token string The raw invite token from the URL

Response 200:

{
  "data": {
    "email": "newcoach@example.com",
    "roleInOrganization": "Coach",
    "organizationName": "Praxia Academy",
    "status": "pending",
    "expiresAt": "2025-04-15T12:00:00.000Z",
    "isAvailable": true,
    "hasAccount": false
  }
}

Response fields:

Field Type Description
email string Email address the invite was sent to
roleInOrganization string Role to be assigned on acceptance
organizationName string Name of the inviting organization
status string pending, accepted, revoked, or expired
expiresAt string ISO 8601 expiry timestamp
isAvailable boolean true only when status = 'pending' and not expired
hasAccount boolean Whether the invited email already has a user account

Error responses:

Code Condition
404 Token not found

POST /api/invites/accept

Create a new user account and accept the invite in a single request. Use this for the new-user flow only — existing users should sign in first and then call POST /api/invites/:id/accept.

Authentication: Not required.

Request body:

{
  "token": "<raw-invite-token>",
  "firstName": "Jane",
  "lastName": "Smith",
  "password": "Secret1234!",
  "email": "newcoach@example.com",
  "preferredLanguage": "es"
}
Field Type Required Constraints
token string Yes The raw token from the invite URL
firstName string Yes 1–100 characters
lastName string Yes 1–100 characters
password string Yes Minimum 8 characters
email string No If provided, must match the invite's email exactly
preferredLanguage string No en or es (fallback: Accept-Language, then en)

Response 200:

{
  "data": {
    "userId": "user-cuid",
    "email": "newcoach@example.com",
    "organizationId": "org-uuid",
    "roleInOrganization": "Coach"
  }
}

Error responses:

Code Condition
400 Invalid token, expired invite, or email mismatch
409 A user with this email already exists
500 Account creation or org assignment failed

GET /api/invites/my-pending

Returns all pending, non-expired invitations addressed to the current user's email.

Authentication: Required.

Response 200:

{
  "data": [
    {
      "id": "invite-uuid",
      "email": "jane@example.com",
      "organizationId": "org-uuid",
      "organizationName": "Praxia Academy",
      "roleInOrganization": "Coach",
      "status": "pending",
      "expiresAt": "2025-04-15T12:00:00.000Z",
      "createdAt": "2025-04-08T12:00:00.000Z"
    }
  ]
}

POST /api/invites/:id/accept

Accept a specific pending invitation. The caller's email must match the invite's email.

Authentication: Required.

Path parameters:

Parameter Type Description
id string Invite UUID

Request body: None.

Response 200:

{
  "data": {
    "id": "invite-uuid",
    "organizationId": "org-uuid",
    "roleInOrganization": "Coach",
    "status": "accepted"
  }
}

Error responses:

Code Condition
400 Invite is no longer in pending status, or has expired
403 Caller's email does not match the invite's email
404 Invite not found

POST /api/invites/:id/decline

Decline a specific pending invitation. Sets the invite status to revoked.

Authentication: Required.

Path parameters:

Parameter Type Description
id string Invite UUID

Request body: None.

Response 200:

{
  "data": {
    "id": "invite-uuid",
    "status": "revoked"
  }
}

Error responses:

Code Condition
400 Invite is no longer in pending status
403 Caller's email does not match the invite's email
404 Invite not found