WakaStart
Modules

Invitations API — Creation

Create an invitation: prerequisites, POST /api/invitations endpoint, body, response and error handling.

Version v1.05 min read

Invitations API — Creation

The Invitations API lets your Wakapp invite users to join a Customer. WakaStart handles email sending, Keycloak account creation, and rights provisioning.

Prerequisites

  1. invitation feature enabled in cascade (App ∧ Network ∧ Customer)
  2. invitations.create right on the token or API key — this right is an AppRight granted via the profile(s) assigned to the inviter, checked by RequireRight("invitations.create")
  3. adminLevel >= CUSTOMER_ADMIN for the caller — human (BFF) or technical (API key). For an API key, the check uses ApiKey.adminLevel (resolved as effectiveAdminLevel); a key without an explicit adminLevel (default NONE) is rejected like any insufficiently privileged caller
  4. Optional: App.acceptInvitationUrl configured to customize the URL in the email

The userLevel concept (and the userLevel >= INVITE rule) has been removed from invitation access control. Creation is now gated by the invitations.create application right and by the caller's adminLevel >= CUSTOMER_ADMIN. The request body no longer accepts a userLevel field: sending one now returns a 400 (strict DTO, whitelist/forbidNonWhitelisted).


Invitation lifecycle

Loading diagram…
StatusDescription
PENDINGCreated, awaiting acceptance
ACCEPTEDThe invitee has finalized their account
EXPIREDDeadline exceeded (10 days by default)
CANCELLEDRevoked by the inviter

Full invitation flow

Loading diagram…

Endpoint — Create an invitation

http
POST /api/invitations Authorization: Bearer <wakaToken> Content-Type: application/json { "customerId": "550e8400-e29b-41d4-a716-446655440000", "invitedEmail": "alice@acme.com", "invitedFirstName": "Alice", "invitedLastName": "Martin", "profileId": "22222222-2222-2222-2222-222222222222", "teamAssignments": [ { "teamId": "33333333-3333-3333-3333-333333333333", "teamLevel": "MEMBER" } ], "message": "Welcome to our application!", "language": "fr" }

Body fields:

FieldTypeRequiredDescription
customerIdUUIDYesTarget Customer
invitedEmailstringYesEmail (lowercase applied automatically)
invitedFirstNamestring (max 100)NoPre-fills the account
invitedLastNamestring (max 100)NoPre-fills the account
profileIdUUIDYesProfile to assign (determines the rights)
teamAssignmentsarrayNoTeams to join
messagestringNoCustom message in the email
languagestringNofr, en, es, de, nl, it, pt (default: fr)

teamLevel ∈ NONE | VIEWER | CONTRIBUTOR | MEMBER | MANAGER | ADMIN

WID alternative: customerId, profileId, and teamId (inside teamAssignments) can also be provided via their WID (customerWid, profileWid, teamWid) as an alternative to the UUID — both forms are accepted in XOR, never both at once for the same field.

201 Response:

json
{ "id": "uuid", "customerId": "uuid", "invitedEmail": "alice@acme.com", "status": "PENDING", "token": "a3f8c2d1e4b7...", "expiresAt": "2026-05-29T10:30:00.000Z", "createdAt": "2026-05-19T10:30:00.000Z", "inviter": { "firstName": "Bob", "lastName": "Smith", "email": "bob@..." }, "profile": { "name": "Participant", "app": { "name": "MyApp" } } }

Errors:

CodeCause
400Invalid or disposable email (yopmail, etc.)
400Team assignment references a team from another Customer
400The Network's realm is not provisioned on the target envType
400Unrecognized field in the body (e.g. userLevel, removed from the contract)
403Missing invitations.create right, adminLevel < CUSTOMER_ADMIN, or feature disabled
403App.invitationPerUserLimit reached
404Customer or Profile not found
409A user with this email already exists in this Customer
409A PENDING invitation already exists for this email + Customer

Management endpoints

List invitations

http
GET /api/invitations/sent # By the current user GET /api/invitations/sent/app/:appId # Filtered by App GET /api/invitations/by-customer/:customerId # All for a Customer

Query params: status, page (default 1), limit (default 20)

Resend an invitation

http
POST /api/invitations/:id/resend

Generates a new token, resets expiresAt to +10 days, resends the email. The old token is immediately invalidated.

Usable on PENDING or EXPIRED invitations.

Revoke an invitation

http
DELETE /api/invitations/:id

Sets the invitation to CANCELLED. Response 200 OK. Required right: invitations.revoke.


curl example

bash
# Create the invitation from your backend curl -X POST https://api.wakastart.app/api/invitations \ -H "x-api-key: sk_live_yourkey" \ -H "Content-Type: application/json" \ -d '{ "customerId": "11111111-1111-1111-1111-111111111111", "invitedEmail": "alice@acme.com", "invitedFirstName": "Alice", "profileId": "22222222-2222-2222-2222-222222222222", "language": "fr" }' # → 201: invitation created, email sent automatically

Best practices

  • Idempotency on creation: if a call fails after creation (network issue), retrying will raise a 409 Conflict — this is safe, the existing invitation is still valid.
  • Never log the invitation token — it's a secret that allows creating an account.
  • Pre-fill invitedFirstName/LastName when possible — better UX.
  • Handle the 4 error states with explicit, appropriate messages.

Go further