Overview
Inviting users via the Wolfia API enables you to automate onboarding workflows and keep your team synchronized with your identity provider. This is especially useful when integrating with systems like Okta, Azure AD, Google Workspace, or custom HR platforms.
The user invitation endpoint
URL: POST https://wolfia.com/api/organizations/invite
Authentication: API key required (see API overview for setup)
curl -X POST https://wolfia.com/api/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:
Standard access for most team members.
Use Wolfiaâs core features
Ask questions and get expert answers
Complete assigned questionnaires
Access the trust center
Enhanced permissions for subject matter experts.
Everything USER can do, plus:
Manage and approve knowledge base content
Review and validate AI-generated answers
Train the AI with expert knowledge
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
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.
Response handling
Success response (200 OK)
{
"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:
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://wolfia.com/api/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:
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://wolfia.com/api/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:
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://wolfia.com/api/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:
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
Pre-validate emails in your integration code to avoid 400 errors
Contact support if you need to add new domains
Error handling strategies
A 409 status means the user already exists or has been invited. This is normal and shouldnât be treated as an error. if response.status_code == 409 :
print ( f "User { email } already in organization" )
# Continue processing other users
continue
Implement retry logic for transient errors
Use exponential backoff for 5xx errors: 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
Log all failures for review
Keep detailed logs of failed invitations: 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 } "
)
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
Role mapping strategies
When syncing from identity providers, map their roles to Wolfia roles appropriately:
# 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)
# 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)
Open Task Scheduler
Create a new task
Set trigger (e.g., daily at 2 AM)
Set action: Run Python script
Configure logging and error handling
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
Verify API key: Confirm your key is active in API settings
Review error logs: Check the full error response for specific guidance
Contact support: Email support@wolfia.com with your integration details
Next steps