Organization Onboarding¶
Complete guide to creating organizations in Fermi.
Overview¶
Organizations are the core entities in Fermi that contain users, service actors, and configuration. Partners create organizations with an API key. Fermi app signup uses a user JWT on a different path (POST /api/v1/organisations) — do not use that for API-key provisioning.
Prerequisites¶
- An API key with
provision:org:createscope
Creating an Organization¶
Create organizations using API keys for programmatic provisioning.
curl -X POST https://api.fermi.dev/public/v1/identity/organisations/api-key \
-H "Authorization: Bearer fmk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "My Organization",
"domain": "https://org.example.com"
}'
Request Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Name of the organization |
domain | string | Yes | Must be a URL (for example https://org.example.com). The API stores the root domain |
theme | object | No | Theme configuration object |
isSignupAllowed | boolean | No | Allow user signup (default: false when omitted) |
domainSlug | string | No | 2–63 lowercase alphanumeric plus hyphens |
industry | string | No | Industry identifier |
companySize | number | No | Number of employees |
companyUrls | string[] | No | Array of company URLs |
documentUploads | string[] | No | Array of document upload paths |
operatingRegions | string | No | Operating regions (comma-separated) |
brief | string | No | Company description |
Response:
{
"organisation": {
"id": "507f1f77bcf86cd799439011",
"name": "My Organization",
"domain": "org.example.com",
"parentOrganisationId": "507f1f77bcf86cd799439012",
"createdAt": "2025-01-01T00:00:00Z"
}
}
Create Organization with API Key¶
Python Example
import requests
import os
api_key = os.getenv("FERMI_API_KEY")
def create_organization_with_api_key(name: str, domain: str):
response = requests.post(
"https://api.fermi.dev/public/v1/identity/organisations/api-key",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json={
"name": name,
"domain": domain,
"industry": "Technology",
"companySize": 50
}
)
if response.status_code == 201:
org = response.json()
print(f"Created organization: {org['organisation']['id']}")
return org['organisation']['id']
else:
print(f"Error: {response.status_code} - {response.text}")
return None
const apiKey = process.env.FERMI_API_KEY;
async function createOrganizationWithApiKey(name, domain) {
try {
const response = await fetch('https://api.fermi.dev/public/v1/identity/organisations/api-key', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: name,
domain: domain,
industry: 'Technology',
companySize: 50
})
});
if (response.ok) {
const result = await response.json();
console.log(`Created organization: ${result.organisation.id}`);
return result.organisation.id;
} else {
console.error(`Error: ${response.status} - ${await response.text()}`);
return null;
}
} catch (error) {
console.error('Error creating organization:', error);
return null;
}
}
// Usage
createOrganizationWithApiKey('My Organization', 'https://myorg.example.com');
Organization Onboarding Questions & Answers¶
After creating an organization, complete onboarding with a service actor token.
Partner Chat API (this page): POST /public/v1/analytics/chat with "action": "onboarding". Live step values are 1 through 5. step: 0 returns "Invalid step number: 0. Must be between 1 and 5."
Product UI: the Fermi app uses a 5-chapter REST flow (Business, Processes, Operations, Performance, Technology), each with baseline / follow-up / review. That surface is also callable with a service actor token:
GET https://api.fermi.dev/core-analytics/onboarding/api/v2/tenant/{tenantId}/user/{userId}/status
GET https://api.fermi.dev/core-analytics/onboarding/api/v2/tenant/{tenantId}/user/{userId}/steps/{step}/sub-steps/{subStep}/questions
POST https://api.fermi.dev/core-analytics/onboarding/api/v2/tenant/{tenantId}/user/{userId}/steps/{step}/sub-steps/{subStep}/answers
step is 1–5. subStep is 1 (baseline), 2 (follow-up), or 3 (review). Keep using the Chat API below if you want the single-endpoint partner flow.
Conversation Flow¶
Step-by-Step Workflow¶
- Initial request (step 1): send
"action": "onboarding","step": 1, and"conversation_items": []. The API returns two questions. - Steps 2–5: send answers in
conversation_itemswith the nextstep. The API returns two new questions (orcompleted: truewhen finished).
Chat API for Onboarding¶
Process onboarding conversations and get contextual follow-up questions.
Initial Call (Step 1)¶
Start with step 1 and an empty conversation_items array:
curl -X POST https://api.fermi.dev/public/v1/analytics/chat \
-H "Authorization: Bearer your_service_actor_token" \
-H "Content-Type: application/json" \
-d '{
"action": "onboarding",
"conversation_items": [],
"step": 1
}'
Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | Must be "onboarding" |
conversation_items | array | No | Previous Q&A pairs |
step | integer | Yes for a new conversation | 1–5. Do not send 0 |
Response (Step 1):
{
"success": "true",
"error": "No Errors",
"questions": [
{
"question": "Describe your core business offering and key services or products.",
"options": [
"B2B Enterprise",
"Small and Medium Businesses",
"Individual Consumers",
"Government/Public Sector"
]
},
{
"question": "What outcomes or metrics are you looking to improve?",
"options": [
"Reduce response time",
"Increase accuracy of results",
"Lower operational or infrastructure cost",
"Improve scalability and system reliability"
]
}
],
"step": 1,
"completed": false,
"total_conversations_used": 0
}
Response Fields:
| Field | Type | Description |
|---|---|---|
success | string | "true" for successful requests, "false" for errors |
error | string | Error message if success is "false", otherwise "No Errors" |
questions | array | List of follow-up questions with suggested options |
step | integer | The next step number in the conversation flow |
completed | boolean | true when onboarding is complete, false otherwise |
total_conversations_used | integer | Total number of Q&A pairs used for generating context |
Subsequent Calls (Step 2+)¶
Continue with the next step and the Q&A pairs from the previous response:
curl -X POST https://api.fermi.dev/public/v1/analytics/chat \
-H "Authorization: Bearer your_service_actor_token" \
-H "Content-Type: application/json" \
-d '{
"action": "onboarding",
"conversation_items": [
{
"question": "Describe your core business offering and key services or products.",
"answer": "We provide cloud-based analytics solutions for e-commerce businesses."
},
{
"question": "What outcomes or metrics are you looking to improve?",
"answer": "We want to improve customer retention rate by 20% and reduce churn."
}
],
"step": 2
}'
Response (Step 2+):
{
"success": "true",
"error": "No Errors",
"questions": [
{
"question": "What specific market segments within e-commerce do you serve?",
"options": [
"Fashion and Apparel",
"Electronics and Technology",
"Food and Beverage",
"Health and Beauty"
]
}
],
"step": 2,
"completed": false,
"total_conversations_used": 2
}
Completion (After Step 6)¶
Request (Step 6):
curl -X POST https://api.fermi.dev/public/v1/analytics/chat \
-H "Authorization: Bearer your_service_actor_token" \
-H "Content-Type: application/json" \
-d '{
"action": "onboarding",
"step": 6,
"conversation_items": [
{
"question": "What is your timeline for implementing these improvements?",
"answer": "We'\''re aiming for Q2 2025 launch, with beta testing starting in April."
},
{
"question": "What resources do you have available for this initiative?",
"answer": "We have a dedicated team of 5 engineers, 2 data scientists, and $500K budget allocated."
}
]
}'
Response:
{
"success": "true",
"error": "No Errors",
"questions": [],
"conversation_id": "507f1f77bcf86cd799439016",
"step": 7,
"completed": true,
"total_conversations_used": 12
}
Organization Onboarding Data Management¶
Retrieving Stored Conversations¶
Get stored onboarding conversations for analysis or review:
curl -X GET "https://api.fermi.dev/public/v1/analytics/retrieve/association/conversation?step=4" \
-H "Authorization: Bearer your_service_actor_token"
Response:
{
"success": true,
"conversations": [
{
"tenant_id": "507f1f77bcf86cd799439011",
"user_id": "user123",
"step": 1,
"convo": [
{
"question": "Describe your core business offering and key services or products.",
"answer": "We provide cloud-based analytics solutions for e-commerce businesses."
},
{
"question": "What specific market segments within e-commerce do you serve?",
"answer": "We focus on Fashion and Apparel, particularly direct-to-consumer brands."
}
],
"created_at": "2025-01-01T00:00:00Z"
}
],
"total_count": 1,
"error": null
}
Example Usage¶
Python Example¶
import requests
def start_onboarding_conversation(token):
"""Start onboarding with step 1."""
response = requests.post(
"https://api.fermi.dev/public/v1/analytics/chat",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={
"action": "onboarding",
"conversation_items": [],
"step": 1
}
)
if response.status_code == 200:
result = response.json()
if result.get("success") == "true":
return result
return None
def continue_onboarding_conversation(token, conversation_items, step):
"""Continue onboarding with step 2–5 (include conversation_items)"""
response = requests.post(
"https://api.fermi.dev/public/v1/analytics/chat",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={
"action": "onboarding",
"conversation_items": conversation_items,
"step": step
}
)
if response.status_code == 200:
result = response.json()
if result.get("success") == "true":
return result
return None
token = "your_service_actor_token"
# Start onboarding
initial_response = start_onboarding_conversation(token)
while True:
if not response:
print("No response from server")
break
questions = response.get("questions", [])
conversation_items = [
{"question": q["question"], "answer": "My answer"}
for q in questions
]
if response.get("completed") is True:
print("Onboarding completed")
break
step += 1
response = continue_onboarding_conversation(
token,
conversation_items,
step
)
JavaScript Example¶
async function startOnboardingConversation(token) {
const response = await fetch('https://api.fermi.dev/public/v1/analytics/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'onboarding',
conversation_items: [],
step: 1
})
});
if (response.ok) {
const result = await response.json();
if (result.success === "true") {
return result;
}
}
return null;
}
async function continueOnboardingConversation(token, conversationItems, step) {
const response = await fetch('https://api.fermi.dev/public/v1/analytics/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action: 'onboarding',
conversation_items: conversationItems,
step: step
})
});
if (response.ok) {
const result = await response.json();
if (result.success === "true") {
return result;
}
}
return null;
}
(async () => {
const token = 'your_service_actor_token';
let step = 1;
let response = await startOnboardingConversation(token);
while (true) { // do-while equivalent
if (!response) {
console.log('No response from server');
break;
}
const questions = response.questions || [];
const conversationItems = questions.map(q => ({
question: q.question,
answer: 'My answer'
}));
if (response.completed === true) {
console.log('Onboarding completed');
break;
}
step += 1;
response = await continueOnboardingConversation(
token,
conversationItems,
step
);
}
})();
Error Handling¶
Authentication Errors¶
Missing or Invalid Token¶
Solution: Ensure you're using a valid authentication token in the Authorization header.
Missing API Key Scope¶
Solution: Ensure your API key has the provision:org:create scope for organization creation.
Validation Errors¶
Invalid Domain¶
Solution: Use a unique, valid domain name that follows URL format (e.g., company.example.com).
Missing Required Fields¶
Solution: Ensure all required fields are provided in your request.
Invalid Organization ID¶
Solution: Verify the organization ID exists and you have access to it.
Permission Errors¶
Access Denied¶
Solution: Ensure your authentication token has permission to access the requested organization.
Rate Limiting¶
Solution: Implement exponential backoff and respect rate limits.
Server Errors¶
Internal Server Error¶
Solution: Retry the request with exponential backoff, or contact support if the issue persists.
Answer delegated onboarding questions¶
Teammates receive a tokenized link.
GET https://app.fermi.dev/api/onboarding/delegation/public?token=TOKEN
Minimum token length is 8. Response fields: organization_name, assigner_name, questions[] (assignment_id, question_id, question, question_type, options, status, due_date), batch_status.
POST https://app.fermi.dev/api/onboarding/delegation/public/answer
{
"token": "TOKEN",
"answers": [
{
"assignment_id": "ASSIGNMENT_ID",
"answer": ["The process owner is Finance"]
}
]
}
Response: { "answered_count": 1, "batch_status": "..." }
Organization public details (no partner token): GET https://api.fermi.dev/backend/api/v1/organisations/details?domain=example.com. domain (or an organization id) is required. Omitting it returns an error (domainOrIdRequired).
Delegation GET requires token (minimum length 8). Delegation POST requires token and answers.
Related Documentation¶
- API Keys - Manage API keys
- Service Actors - Create service actors in organizations
- Token Exchange - Exchange for tokens