> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wolfia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Invite Users via the Wolfia API

> Automate user onboarding with the Wolfia API: invite teammates by email and role, integrate with Okta, Azure AD, Google Workspace, or custom HR platforms.

## Overview

A single API call sends an invitation email and assigns the right role, so a teammate gains access the moment they join the corresponding group in your identity provider. Run it on a schedule to keep Wolfia membership in step with joiners and leavers, without manual seat management.

## The user invitation endpoint

**URL:** `POST https://api.wolfia.com/v1/organizations/invite`

**Authentication:** API key required (see [API overview](/how-to/api-overview) for setup)

### Request format

```bash theme={null}
curl -X POST https://api.wolfia.com/v1/organizations/invite \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newuser@yourcompany.com",
    "name": "Jane Smith",
    "role": "USER"
  }'
```

### Request parameters

| Parameter | Type   | Required | Description                                                                               |
| --------- | ------ | -------- | ----------------------------------------------------------------------------------------- |
| `email`   | string | Yes      | Email address of the person to invite. Must use an approved domain for your organization. |
| `name`    | string | Yes      | Full name of the person being invited                                                     |
| `role`    | string | Yes      | User role: `USER`, `EXPERT`, or `ADMIN`                                                   |

## Understanding user roles

Wolfia uses role-based access control to manage permissions:

### USER

Standard access for most team members.

* Use Wolfia's core features
* Ask questions and get expert answers
* Upload questionnaires and edit answers
* Access the trust center
* Cannot approve or unapprove answers

### EXPERT

Enhanced permissions for subject matter experts.

* Everything USER can do, plus:
* Approve and unapprove answers in questionnaires
* Manage and approve knowledge base content
* Review and validate AI-generated answers
* Train the AI with expert knowledge

### ADMIN

Full administrative access to your organization.

* Everything EXPERT can do, plus:
* Invite and manage users
* Configure integrations
* Manage organization settings
* Create and manage API keys

<Note>
  **Role hierarchy:** You can only invite users with roles equal to or lower than your own. For example, an EXPERT can invite USER and EXPERT roles, but not ADMIN.
</Note>

## Response handling

### Success response (200 OK)

```json theme={null}
{
  "message": "Invitation sent successfully to newuser@yourcompany.com"
}
```

When successful, Wolfia sends an invitation email to the user with instructions to set up their account.

### Error responses

| Status Code | Error        | What it means                                                   | How to fix                                                                       |
| ----------- | ------------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| 400         | Bad Request  | Email domain doesn't match your organization's approved domains | Verify the email domain is in your organization settings                         |
| 401         | Unauthorized | Invalid or expired API key                                      | Check that your API key is correct and hasn't been deactivated                   |
| 403         | Forbidden    | Insufficient permissions to invite this role                    | Your role doesn't allow inviting users with the specified role                   |
| 404         | Not Found    | Organization not found                                          | Contact support. This shouldn't happen with a valid API key                      |
| 409         | Conflict     | User already invited or exists                                  | This user is already part of your organization (not an error; handle gracefully) |
| 500         | Server Error | Something went wrong on our end                                 | Retry with exponential backoff                                                   |

## Integration examples

### Okta group synchronization

Automatically sync users from an Okta group to Wolfia:

```python theme={null}
import requests
from okta.client import Client as OktaClient
import os

WOLFIA_API_KEY = os.environ['WOLFIA_API_KEY']
OKTA_GROUP_ID = "00g1234567890abcdef"

async def sync_okta_group_to_wolfia():
    """
    Sync users from an Okta group to Wolfia.
    Run this as a scheduled job (e.g., daily cron).
    """
    okta_client = OktaClient()

    # Fetch all members from the Okta group
    group_members = await okta_client.list_group_users(OKTA_GROUP_ID)

    results = {
        'invited': [],
        'already_exists': [],
        'failed': []
    }

    for member in group_members:
        email = member.profile.email
        name = f"{member.profile.firstName} {member.profile.lastName}"

        try:
            response = requests.post(
                "https://api.wolfia.com/v1/organizations/invite",
                headers={
                    "X-API-Key": WOLFIA_API_KEY,
                    "Content-Type": "application/json"
                },
                json={
                    "email": email,
                    "name": name,
                    "role": "USER"
                },
                timeout=10
            )

            if response.status_code == 200:
                results['invited'].append(email)
                print(f"✓ Invited {email}")
            elif response.status_code == 409:
                results['already_exists'].append(email)
                print(f"→ {email} already in organization")
            else:
                results['failed'].append({
                    'email': email,
                    'status': response.status_code,
                    'error': response.text
                })
                print(f"✗ Failed to invite {email}: {response.text}")

        except Exception as e:
            results['failed'].append({
                'email': email,
                'error': str(e)
            })
            print(f"✗ Error inviting {email}: {str(e)}")

    # Summary
    print(f"\n📊 Summary:")
    print(f"   Invited: {len(results['invited'])}")
    print(f"   Already exists: {len(results['already_exists'])}")
    print(f"   Failed: {len(results['failed'])}")

    return results

# Run the sync
if __name__ == "__main__":
    import asyncio
    asyncio.run(sync_okta_group_to_wolfia())
```

### Azure AD automation

Sync users based on Azure AD group membership:

```javascript theme={null}
const axios = require('axios');
const { Client } = require('@microsoft/microsoft-graph-client');

const WOLFIA_API_KEY = process.env.WOLFIA_API_KEY;
const AZURE_GROUP_ID = 'your-azure-group-id';

async function syncAzureGroupToWolfia() {
  // Initialize Microsoft Graph client
  const graphClient = Client.initWithMiddleware({
    authProvider: /* your auth provider */
  });

  // Get all members from the Azure AD group
  const response = await graphClient
    .api(`/groups/${AZURE_GROUP_ID}/members`)
    .get();

  const results = {
    invited: [],
    alreadyExists: [],
    failed: []
  };

  for (const member of response.value) {
    const email = member.mail || member.userPrincipalName;
    const name = member.displayName;

    try {
      const apiResponse = await axios.post(
        'https://api.wolfia.com/v1/organizations/invite',
        {
          email: email,
          name: name,
          role: 'USER'
        },
        {
          headers: {
            'X-API-Key': WOLFIA_API_KEY,
            'Content-Type': 'application/json'
          },
          timeout: 10000
        }
      );

      results.invited.push(email);
      console.log(`✓ Invited ${name}`);

    } catch (error) {
      if (error.response?.status === 409) {
        results.alreadyExists.push(email);
        console.log(`→ ${name} already exists`);
      } else {
        results.failed.push({
          email,
          error: error.message
        });
        console.error(`✗ Failed to invite ${name}:`, error.message);
      }
    }
  }

  // Print summary
  console.log('\n📊 Summary:');
  console.log(`   Invited: ${results.invited.length}`);
  console.log(`   Already exists: ${results.alreadyExists.length}`);
  console.log(`   Failed: ${results.failed.length}`);

  return results;
}

// Run the sync
syncAzureGroupToWolfia()
  .then(results => {
    console.log('\n✅ Sync completed');
    process.exit(0);
  })
  .catch(error => {
    console.error('❌ Sync failed:', error);
    process.exit(1);
  });
```

### Bulk CSV import

Invite multiple users from a CSV file:

```python theme={null}
import requests
import csv
import os
from time import sleep

WOLFIA_API_KEY = os.environ['WOLFIA_API_KEY']
CSV_FILE_PATH = 'users_to_invite.csv'

def bulk_invite_from_csv(csv_path):
    """
    Bulk invite users from a CSV file.
    CSV format: email,name,role
    Example: jane.doe@company.com,Jane Doe,USER
    """
    results = {
        'invited': [],
        'already_exists': [],
        'failed': []
    }

    with open(csv_path, 'r') as csvfile:
        reader = csv.DictReader(csvfile)

        for row in reader:
            email = row['email'].strip()
            name = row['name'].strip()
            role = row.get('role', 'USER').strip().upper()

            # Validate role
            if role not in ['USER', 'EXPERT', 'ADMIN']:
                print(f"⚠️  Invalid role '{role}' for {email}, using USER instead")
                role = 'USER'

            try:
                response = requests.post(
                    'https://api.wolfia.com/v1/organizations/invite',
                    headers={
                        'X-API-Key': WOLFIA_API_KEY,
                        'Content-Type': 'application/json'
                    },
                    json={
                        'email': email,
                        'name': name,
                        'role': role
                    },
                    timeout=10
                )

                if response.status_code == 200:
                    results['invited'].append(email)
                    print(f"✓ Invited {name} ({email}) as {role}")
                elif response.status_code == 409:
                    results['already_exists'].append(email)
                    print(f"→ {email} already exists")
                else:
                    results['failed'].append({
                        'email': email,
                        'status': response.status_code,
                        'error': response.text
                    })
                    print(f"✗ Failed to invite {email}: {response.text}")

                # Rate limiting: be nice to the API
                sleep(0.1)

            except Exception as e:
                results['failed'].append({
                    'email': email,
                    'error': str(e)
                })
                print(f"✗ Error inviting {email}: {str(e)}")

    # Print summary
    print(f"\n📊 Summary:")
    print(f"   Total processed: {len(results['invited']) + len(results['already_exists']) + len(results['failed'])}")
    print(f"   ✓ Invited: {len(results['invited'])}")
    print(f"   → Already exists: {len(results['already_exists'])}")
    print(f"   ✗ Failed: {len(results['failed'])}")

    # Save failed invites for retry
    if results['failed']:
        with open('failed_invites.csv', 'w', newline='') as f:
            writer = csv.DictWriter(f, fieldnames=['email', 'error'])
            writer.writeheader()
            writer.writerows(results['failed'])
        print(f"\n💾 Failed invitations saved to failed_invites.csv")

    return results

if __name__ == '__main__':
    bulk_invite_from_csv(CSV_FILE_PATH)
```

**Example CSV format:**

```csv theme={null}
email,name,role
jane.smith@company.com,Jane Smith,USER
john.doe@company.com,John Doe,EXPERT
admin@company.com,Sarah Admin,ADMIN
```

## Best practices

### Email domain validation

Before sending invitations, verify that email domains match your organization's approved domains:

* View approved domains in your [organization settings](https://wolfia.com/settings/organization)
* Pre-validate emails in your integration code to avoid 400 errors
* Contact support if you need to add new domains

### Error handling strategies

<AccordionGroup>
  <Accordion title="Handle 409 gracefully">
    A 409 status means the user already exists or has been invited. This is normal and shouldn't be treated as an error.

    ```python theme={null}
    if response.status_code == 409:
        print(f"User {email} already in organization")
        # Continue processing other users
        continue
    ```
  </Accordion>

  <Accordion title="Implement retry logic for transient errors">
    Use exponential backoff for 5xx errors:

    ```python theme={null}
    import time

    max_retries = 3
    for attempt in range(max_retries):
        try:
            response = requests.post(...)
            if response.status_code < 500:
                break
        except Exception:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt  # 1s, 2s, 4s
                time.sleep(wait_time)
            else:
                raise
    ```
  </Accordion>

  <Accordion title="Log all failures for review">
    Keep detailed logs of failed invitations:

    ```python theme={null}
    import logging

    logging.basicConfig(
        filename='wolfia_invites.log',
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s'
    )

    if response.status_code != 200:
        logging.error(
            f"Failed to invite {email}: "
            f"status={response.status_code}, "
            f"response={response.text}"
        )
    ```
  </Accordion>

  <Accordion title="Monitor API usage">
    Track your integration's health:

    * Log successful invitations
    * Monitor error rates
    * Set up alerts for unusual patterns
    * Review the "Last Used" timestamp in your API settings
  </Accordion>
</AccordionGroup>

### Role mapping strategies

When syncing from identity providers, map their roles to Wolfia roles appropriately:

```python theme={null}
# Example: Map Okta roles to Wolfia roles
ROLE_MAPPING = {
    'Security Admin': 'ADMIN',
    'Security Team': 'EXPERT',
    'Engineering': 'USER',
    'Sales': 'USER'
}

okta_role = member.profile.customAttribute.get('department')
wolfia_role = ROLE_MAPPING.get(okta_role, 'USER')  # Default to USER
```

## Scheduled automation

Set up automated syncs using your preferred scheduler:

### Linux/Mac (cron)

```bash theme={null}
# Run daily at 2 AM
0 2 * * * /usr/bin/python3 /path/to/sync_users.py >> /var/log/wolfia_sync.log 2>&1
```

### Windows (Task Scheduler)

1. Open Task Scheduler
2. Create a new task
3. Set trigger (e.g., daily at 2 AM)
4. Set action: Run Python script
5. Configure logging and error handling

### Cloud platforms

* **AWS Lambda:** Schedule with EventBridge
* **Azure Functions:** Use Timer trigger
* **Google Cloud Functions:** Use Cloud Scheduler

## Monitoring and alerts

Track your integration's performance:

* **Success rate:** Monitor the ratio of successful to failed invitations
* **API errors:** Alert on 5xx errors or high failure rates
* **Domain mismatches:** Track 400 errors to identify configuration issues
* **Duplicate attempts:** Monitor 409 responses; high rates might indicate sync logic issues

## Getting help

If you encounter issues inviting users via the API:

* **Check email domains:** Verify invited emails use approved domains in [organization settings](https://wolfia.com/settings/organization)
* **Verify API key:** Confirm your key is active in [API settings](https://wolfia.com/settings/api)
* **Review error logs:** Check the full error response for specific guidance
* **Contact support:** Email [support@wolfia.com](mailto:support@wolfia.com) with your integration details

## Next steps

<CardGroup cols={2}>
  <Card title="API overview" icon="code" href="/how-to/api-overview">
    Learn about API authentication, rate limits, and best practices
  </Card>

  <Card title="Organization settings" icon="building" href="https://wolfia.com/settings/organization">
    Manage approved email domains and other settings
  </Card>
</CardGroup>
