Invitations Module

Complete API reference for sso.invitations module - invite users to organizations, manage invitations, accept and decline invitations

Updated Dec 16, 2025
Edit on GitHub

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 ID
  • organization_id (string) - Organization ID
  • inviter_id (string) - ID of the user who sent the invitation
  • invitee_email (string) - Email of the invited user
  • role (string) - Role assigned to the invitation
  • status (‘pending’ | ‘accepted’ | ‘declined’ | ’expired’) - Current status
  • token (string) - Invitation token (used for accepting/declining)
  • expires_at (string) - ISO 8601 timestamp when invitation expires
  • created_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 role
  • 400 Bad Request - Invalid email or user already a member
  • 404 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 ID
  • organization_id (string) - Organization ID
  • inviter_id (string) - ID of the user who sent the invitation
  • invitee_email (string) - Email of the invited user
  • role (string) - Role assigned to the invitation
  • status (‘pending’ | ‘accepted’ | ‘declined’ | ’expired’) - Current status
  • expires_at (string) - ISO 8601 timestamp when invitation expires
  • created_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 role
  • 404 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 ID
  • organization_id (string) - Organization ID
  • organization_name (string) - Organization display name
  • organization_slug (string) - Organization slug
  • inviter_id (string) - ID of the user who sent the invitation
  • inviter_email (string) - Email of the user who sent the invitation
  • role (string) - Role assigned to the invitation
  • status (‘pending’ | ‘accepted’ | ‘declined’ | ’expired’) - Current status
  • expires_at (string) - ISO 8601 timestamp when invitation expires
  • created_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 role
  • 404 Not Found - Invitation or organization doesn’t exist
  • 400 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 authenticated
  • 400 Bad Request - Invalid or expired token
  • 404 Not Found - Invitation doesn’t exist
  • 409 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 authenticated
  • 400 Bad Request - Invalid or expired token
  • 404 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

  1. Invitation Expiry: Invitations expire after a set period (typically 7 days). Monitor expiration dates and resend if needed.

  2. Role Assignment: Be mindful of role assignments:

    • member - Can view organization details and services
    • admin - Can manage services, plans, and invite members
    • owner - Full administrative access including billing
  3. Error Handling: Always handle invitation errors gracefully:

    • User might already be a member
    • Invitation might have expired
    • Email might be invalid
  4. User Experience: When accepting invitations:

    • Show organization details before accepting
    • Provide clear success/error messages
    • Redirect to organization dashboard after acceptance
  5. 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';