Invitations Module
The invitations module (sso.invitations) manages organization team invitations. It allows admins to invite users, list pending invitations, and enables users to accept or decline invitations.
Creating Invitations
sso.invitations.create()
Signature:
create(orgSlug: string, payload: CreateInvitationPayload): Promise<Invitation>
Description: Create and send an invitation to join an organization. Requires ‘owner’ or ‘admin’ role in the organization.
Parameters:
| Name | Type | Description |
|---|---|---|
| orgSlug | string | Organization slug |
| payload | CreateInvitationPayload | Invitation details |
| payload.invitee_email | string | Email address of the person to invite |
| payload.role | ‘owner’ | ‘admin’ | ‘member’ | Role to assign to the invited user |
Returns: Promise<Invitation> - The created invitation object with the following fields:
id(string) - Unique invitation IDorganization_id(string) - Organization IDinviter_id(string) - ID of the user who sent the invitationinvitee_email(string) - Email of the invited userrole(string) - Role assigned to the invitationstatus(‘pending’ | ‘accepted’ | ‘declined’ | ’expired’) - Current statustoken(string) - Invitation token (used for accepting/declining)expires_at(string) - ISO 8601 timestamp when invitation expirescreated_at(string) - ISO 8601 timestamp when invitation was created
Example:
const invitation = await sso.invitations.create('acme-corp', {
invitee_email: 'newuser@example.com',
role: 'member'
});
console.log(`Invitation sent to ${invitation.invitee_email}`);
console.log(`Expires at: ${invitation.expires_at}`);
Throws:
403 Forbidden- User doesn’t have admin/owner role400 Bad Request- Invalid email or user already a member404 Not Found- Organization doesn’t exist
Related:
Listing Invitations
sso.invitations.listForOrg()
Signature:
listForOrg(orgSlug: string): Promise<Invitation[]>
Description: List all invitations for an organization. Requires ‘owner’ or ‘admin’ role.
Parameters:
| Name | Type | Description |
|---|---|---|
| orgSlug | string | Organization slug |
Returns: Promise<Invitation[]> - Array of invitation objects. Each invitation includes:
id(string) - Unique invitation IDorganization_id(string) - Organization IDinviter_id(string) - ID of the user who sent the invitationinvitee_email(string) - Email of the invited userrole(string) - Role assigned to the invitationstatus(‘pending’ | ‘accepted’ | ‘declined’ | ’expired’) - Current statusexpires_at(string) - ISO 8601 timestamp when invitation expirescreated_at(string) - ISO 8601 timestamp when invitation was created
Example:
const invitations = await sso.invitations.listForOrg('acme-corp');
invitations.forEach(inv => {
console.log(`${inv.invitee_email} - ${inv.status} - Expires: ${inv.expires_at}`);
});
// Filter pending invitations
const pending = invitations.filter(inv => inv.status === 'pending');
console.log(`${pending.length} pending invitations`);
Throws:
403 Forbidden- User doesn’t have admin/owner role404 Not Found- Organization doesn’t exist
Related:
sso.invitations.listForUser()
Signature:
listForUser(): Promise<InvitationWithOrg[]>
Description: List invitations received by the current authenticated user across all organizations.
Parameters: None
Returns: Promise<InvitationWithOrg[]> - Array of invitations with organization details. Each invitation includes:
id(string) - Unique invitation IDorganization_id(string) - Organization IDorganization_name(string) - Organization display nameorganization_slug(string) - Organization sluginviter_id(string) - ID of the user who sent the invitationinviter_email(string) - Email of the user who sent the invitationrole(string) - Role assigned to the invitationstatus(‘pending’ | ‘accepted’ | ‘declined’ | ’expired’) - Current statusexpires_at(string) - ISO 8601 timestamp when invitation expirescreated_at(string) - ISO 8601 timestamp when invitation was created
Example:
const myInvitations = await sso.invitations.listForUser();
myInvitations.forEach(inv => {
console.log(`Invited to ${inv.organization_name} as ${inv.role}`);
console.log(`From: ${inv.inviter_email}`);
console.log(`Status: ${inv.status}`);
});
// Show only pending invitations
const pending = myInvitations.filter(inv => inv.status === 'pending');
if (pending.length > 0) {
console.log(`You have ${pending.length} pending invitation(s)`);
}
Throws:
401 Unauthorized- User is not authenticated
Related:
Managing Invitations
sso.invitations.cancel()
Signature:
cancel(orgSlug: string, invitationId: string): Promise<void>
Description: Cancel a pending invitation. Requires ‘owner’ or ‘admin’ role. The invitation token will no longer be valid.
Parameters:
| Name | Type | Description |
|---|---|---|
| orgSlug | string | Organization slug |
| invitationId | string | Invitation ID to cancel |
Returns: Promise<void> - No return value on success
Example:
// Cancel a specific invitation
await sso.invitations.cancel('acme-corp', 'invitation-id-123');
console.log('Invitation cancelled successfully');
// Example: Cancel all pending invitations for a specific email
const invitations = await sso.invitations.listForOrg('acme-corp');
const userInvites = invitations.filter(
inv => inv.invitee_email === 'user@example.com' && inv.status === 'pending'
);
for (const inv of userInvites) {
await sso.invitations.cancel('acme-corp', inv.id);
}
Throws:
403 Forbidden- User doesn’t have admin/owner role404 Not Found- Invitation or organization doesn’t exist400 Bad Request- Invitation is not in pending status
Related:
sso.invitations.accept()
Signature:
accept(token: string): Promise<void>
Description: Accept an invitation using its token. The token is typically sent to the user via email. After accepting, the user becomes a member of the organization with the role specified in the invitation.
Parameters:
| Name | Type | Description |
|---|---|---|
| token | string | Invitation token (from email link) |
Returns: Promise<void> - No return value on success
Example:
// Extract token from email link or URL parameter
const urlParams = new URLSearchParams(window.location.search);
const invitationToken = urlParams.get('invitation_token');
if (invitationToken) {
try {
await sso.invitations.accept(invitationToken);
console.log('Successfully joined the organization!');
// Redirect to organization dashboard
window.location.href = '/dashboard';
} catch (error) {
console.error('Failed to accept invitation:', error);
}
}
// Alternative: Accept invitation from user's invitation list
const invitations = await sso.invitations.listForUser();
const pendingInvite = invitations.find(
inv => inv.organization_slug === 'acme-corp' && inv.status === 'pending'
);
if (pendingInvite) {
await sso.invitations.accept(pendingInvite.token);
}
Throws:
401 Unauthorized- User is not authenticated400 Bad Request- Invalid or expired token404 Not Found- Invitation doesn’t exist409 Conflict- User is already a member of the organization
Related:
sso.invitations.decline()
Signature:
decline(token: string): Promise<void>
Description: Decline an invitation using its token. The invitation will be marked as declined and cannot be used again.
Parameters:
| Name | Type | Description |
|---|---|---|
| token | string | Invitation token (from email link) |
Returns: Promise<void> - No return value on success
Example:
// Extract token from email link or URL parameter
const urlParams = new URLSearchParams(window.location.search);
const invitationToken = urlParams.get('invitation_token');
if (invitationToken) {
try {
await sso.invitations.decline(invitationToken);
console.log('Invitation declined');
} catch (error) {
console.error('Failed to decline invitation:', error);
}
}
// Alternative: Decline invitation from user's invitation list
const invitations = await sso.invitations.listForUser();
const unwantedInvite = invitations.find(
inv => inv.organization_slug === 'spam-org' && inv.status === 'pending'
);
if (unwantedInvite) {
await sso.invitations.decline(unwantedInvite.token);
console.log('Declined invitation from spam-org');
}
Throws:
401 Unauthorized- User is not authenticated400 Bad Request- Invalid or expired token404 Not Found- Invitation doesn’t exist
Related:
Complete Example: Invitation Workflow
Here’s a complete example showing the full invitation workflow from both admin and user perspectives:
import { SsoClient } from '@drmhse/sso-sdk';
const sso = new SsoClient({
baseURL: 'https://authos.example.com',
token: localStorage.getItem('sso_access_token')
});
// === ADMIN PERSPECTIVE ===
// 1. Admin invites a new team member
async function inviteTeamMember(orgSlug: string, email: string, role: 'member' | 'admin') {
try {
const invitation = await sso.invitations.create(orgSlug, {
invitee_email: email,
role: role
});
console.log(` Invitation sent to ${email}`);
console.log(` Role: ${role}`);
console.log(` Expires: ${new Date(invitation.expires_at).toLocaleDateString()}`);
return invitation;
} catch (error) {
console.error('Failed to send invitation:', error);
throw error;
}
}
// 2. Admin views all pending invitations
async function viewPendingInvitations(orgSlug: string) {
const invitations = await sso.invitations.listForOrg(orgSlug);
const pending = invitations.filter(inv => inv.status === 'pending');
console.log(`\nPending invitations (${pending.length}):`);
pending.forEach(inv => {
const daysUntilExpiry = Math.ceil(
(new Date(inv.expires_at).getTime() - Date.now()) / (1000 * 60 * 60 * 24)
);
console.log(` - ${inv.invitee_email} (${inv.role}) - Expires in ${daysUntilExpiry} days`);
});
}
// 3. Admin cancels an invitation
async function cancelInvitation(orgSlug: string, invitationId: string) {
await sso.invitations.cancel(orgSlug, invitationId);
console.log(' Invitation cancelled');
}
// === USER PERSPECTIVE ===
// 4. User views their invitations
async function viewMyInvitations() {
const invitations = await sso.invitations.listForUser();
const pending = invitations.filter(inv => inv.status === 'pending');
if (pending.length === 0) {
console.log('No pending invitations');
return;
}
console.log(`\nYou have ${pending.length} pending invitation(s):\n`);
pending.forEach((inv, index) => {
console.log(`${index + 1}. ${inv.organization_name}`);
console.log(` Role: ${inv.role}`);
console.log(` From: ${inv.inviter_email}`);
console.log(` Expires: ${new Date(inv.expires_at).toLocaleDateString()}`);
});
}
// 5. User accepts an invitation
async function acceptInvitation(token: string) {
try {
await sso.invitations.accept(token);
console.log(' Successfully joined the organization!');
} catch (error) {
console.error('Failed to accept invitation:', error);
throw error;
}
}
// 6. User declines an invitation
async function declineInvitation(token: string) {
try {
await sso.invitations.decline(token);
console.log(' Invitation declined');
} catch (error) {
console.error('Failed to decline invitation:', error);
throw error;
}
}
// === EXAMPLE USAGE ===
async function main() {
// Admin invites a new member
await inviteTeamMember('acme-corp', 'newdev@example.com', 'member');
// Admin checks pending invitations
await viewPendingInvitations('acme-corp');
// User checks their invitations
await viewMyInvitations();
// User accepts the invitation
const invitationToken = 'token-from-email-link';
await acceptInvitation(invitationToken);
}
main();
Best Practices
-
Invitation Expiry: Invitations expire after a set period (typically 7 days). Monitor expiration dates and resend if needed.
-
Role Assignment: Be mindful of role assignments:
member- Can view organization details and servicesadmin- Can manage services, plans, and invite membersowner- Full administrative access including billing
-
Error Handling: Always handle invitation errors gracefully:
- User might already be a member
- Invitation might have expired
- Email might be invalid
-
User Experience: When accepting invitations:
- Show organization details before accepting
- Provide clear success/error messages
- Redirect to organization dashboard after acceptance
-
Security: Never expose invitation tokens in client-side logs or URLs that might be cached.
Type Definitions
Invitation
interface Invitation {
id: string;
org_id: string;
inviter_user_id: string;
invitee_email: string;
role: MemberRole;
token: string;
status: InvitationStatus;
expires_at: string;
created_at: string;
}
InvitationWithOrg
interface InvitationWithOrg extends Invitation {
organization_name: string;
organization_slug: string;
inviter_email: string;
}
CreateInvitationPayload
interface CreateInvitationPayload {
invitee_email: string;
role: MemberRole;
}
AcceptInvitationPayload
interface AcceptInvitationPayload {
token: string;
}
DeclineInvitationPayload
interface DeclineInvitationPayload {
token: string;
}
MemberRole
type MemberRole = 'owner' | 'admin' | 'member';
InvitationStatus
type InvitationStatus = 'pending' | 'accepted' | 'declined' | 'cancelled';