API reference
Setup and import
Set up a whole company in one call, or import people from a CSV.
4 actions · base URL https://api.bizisy.com/v1 · generated from the same definitions as the API.
Initialise the company (idempotent)#
POST/v1/org/setup.initWrite
Creates whatever is missing of: the legal entity (company name and country), a "Head office" location, the company root unit, the org settings, and your own person record (job title "Founder" unless owner_title is given, starting today) linked to your login. Only owners and admins can link their own login; for anyone else the person is created and warnings asks an owner or admin to link it with platform_members_link_person. Safe to call any number of times; call it first on a new company. Returns the setup checklist plus warnings.
- Who can call it
- Manage key owner admin hr
- MCP tool
org_setup_initon HR Assistant- Preview
?dry_run=trueruns every check and saves nothing- Retries
- An Idempotency-Key replays the first result
Input
owner_titlestring1–120 characters.
Returns
8 fields
entitybooleanrequiredlocationbooleanrequiredunitsbooleanrequiredpeoplebooleanrequiredinvitedbooleanrequiredai_connectedbooleanrequiredcompletedbooleanrequiredwarningsstring[]required
Errors
validation_failed unauthenticated forbidden not_found conflict rate_limited internal
curl https://api.bizisy.com/v1/org/setup.init \
-H "Authorization: Bearer $BIZISY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"owner_title":"CEO"}'const res = await fetch('https://api.bizisy.com/v1/org/setup.init', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BIZISY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"owner_title": "CEO"
}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data);import os, uuid
import requests
res = requests.post(
"https://api.bizisy.com/v1/org/setup.init",
headers={"Authorization": f"Bearer {os.environ['BIZISY_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
json={
"owner_title": "CEO",
},
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
print(body["data"]){
"data": {
"entity": true,
"location": true,
"units": true,
"people": false,
"invited": false,
"ai_connected": false,
"completed": false,
"warnings": [
"Ask an owner or admin to link your login to your person record."
]
}
}Setup checklist#
POST/v1/org/setup.statusRead
Returns which setup steps are done: entity, location, units (more than the root), people (more than the owner), invited (at least one invite), ai_connected (a manage API key exists) and completed (all but ai_connected). Use it to decide what to do next.
- Who can call it
- Manage key any Manage role
- MCP tool
org_setup_statuson HR Assistant- Method
POSTwith a JSON body, orGETwith the input as query parameters
Input
No input: send {}.
Returns
7 fields
entitybooleanrequiredlocationbooleanrequiredunitsbooleanrequiredpeoplebooleanrequiredinvitedbooleanrequiredai_connectedbooleanrequiredcompletedbooleanrequired
Errors
validation_failed unauthenticated forbidden not_found rate_limited internal
curl https://api.bizisy.com/v1/org/setup.status \
-H "Authorization: Bearer $BIZISY_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const res = await fetch('https://api.bizisy.com/v1/org/setup.status', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BIZISY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data);import os
import requests
res = requests.post(
"https://api.bizisy.com/v1/org/setup.status",
headers={"Authorization": f"Bearer {os.environ['BIZISY_API_KEY']}"},
json={},
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
print(body["data"]){
"data": {
"entity": true,
"location": true,
"units": true,
"people": false,
"invited": false,
"ai_connected": false,
"completed": false
}
}Set up or update the company in bulk#
POST/v1/org/setup.applyWrite
Creates or updates locations, units and people in one transaction (the fast path for setting up a whole company). At most 250 people per call: split bigger lists into batches. Matching: locations and units by name (case-insensitive), people by work_email; existing records are updated, missing ones created. People may also carry contract terms (contract_type, contract_end_date, probation_end_date, weekly_hours, notice_period_days), legal_entity (legal name), payroll details (tax_id, social_security_number, iban: refused over an API key or MCP; a change notifies the person and HR) and custom: { <field key>: value } (a file field takes no value here: upload files with documents_field_file_start; null removes one); pay is never set here (org_pay_add). A person's work_email is never changed here. units[].parent and people[].unit/location/manager_email may refer to items in the same payload, in any order. manager_email null says no manager (no default; an existing manager is cleared). A person joining or moving into a unit with no manager_email gets that unit's team lead as manager (team_lead_managers counts them; never a lead the same payload puts below them); people who stay in their unit keep their manager. Job changes of existing people take effect today (people hired today or not started yet are corrected in place); start dates of people already employed are not changed (a warning says so), nor are leavers. Everything is validated first; on validation_failed, details lists every problem with a path such as people[3].manager_email and nothing is written. Always call with dry_run=true first, show the counts and warnings to the user, and commit only after approval.
- Who can call it
- Manage key owner admin hr
- Keys and apps
- Payroll details are refused.
- MCP tool
org_setup_applyon HR Assistant- Preview
?dry_run=trueruns every check and saves nothing- Retries
- An Idempotency-Key prevents a second run, but the result is never stored (it holds a secret or personal data): a retry gets a 409 that points to Activity
Input
locationsobject[]Up to 50 items.
4 fields
namestringrequired1–100 characters.
countryanyrequiredcitystring | nullUp to 100 characters.
timezonestring
unitsobject[]Up to 200 items.
3 fields
namestringrequired1–100 characters.
kind"division" | "department" | "team"requiredparentstringParent unit name; defaults to the company root for new units.
1–100 characters.
peopleobject[]At most 250 people per call.
19 fields
given_namestringrequired1–100 characters.
family_namestringrequired1–100 characters.
work_emailanyrequiredtitlestringrequired1–120 characters.
unitstring1–100 characters.
manager_emailany | nullThe manager's work email; null for no manager (no team-lead default); absent: the team lead for someone joining or moving teams, else unchanged.
locationstring1–100 characters.
start_datestringemployment_type"full_time" | "part_time" | "contractor" | "intern"contract_type"permanent" | "fixed_term" | "temporary" | "internship" | "freelance"contract_end_datestringprobation_end_datestringweekly_hoursnumberFrom 0 to 80.
notice_period_daysintegerFrom 0 to 365.
legal_entitystringLegal name of an existing legal entity (case-insensitive).
1–200 characters.
tax_idstringPattern
^[A-Za-z0-9 ./-]{1,40}$.social_security_numberstringPattern
^[A-Za-z0-9 ./-]{1,40}$.ibananycustomobjectCustom field values by key (
org_fields_list); null clears one.
Returns
createdobjectrequired3 fields
locationsintegerrequiredunitsintegerrequiredpeopleintegerrequired
updatedobjectrequired3 fields
locationsintegerrequiredunitsintegerrequiredpeopleintegerrequired
warningsstring[]requiredteam_lead_managersintegerrequiredPeople who got their team lead as manager because no manager was given (new people, and people moving teams).
Errors
validation_failed unauthenticated forbidden not_found conflict rate_limited internal
curl https://api.bizisy.com/v1/org/setup.apply \
-H "Authorization: Bearer $BIZISY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"locations": [
{
"name": "Porto",
"country": "PT",
"city": "Porto",
"timezone": "Europe/Lisbon"
},
{
"name": "Remote",
"country": "PT",
"city": null
}
],
"units": [
{
"name": "Engineering",
"kind": "department"
},
{
"name": "Platform",
"kind": "team",
"parent": "Engineering"
}
],
"people": [
{
"given_name": "Inês",
"family_name": "Martins",
"work_email": "ines@acme.test",
"title": "AE",
"unit": "Platform",
"manager_email": "ana@acme.test",
"location": "Porto",
"start_date": "2026-11-02",
"employment_type": "part_time"
}
]
}'const res = await fetch('https://api.bizisy.com/v1/org/setup.apply', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BIZISY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"locations": [
{
"name": "Porto",
"country": "PT",
"city": "Porto",
"timezone": "Europe/Lisbon"
},
{
"name": "Remote",
"country": "PT",
"city": null
}
],
"units": [
{
"name": "Engineering",
"kind": "department"
},
{
"name": "Platform",
"kind": "team",
"parent": "Engineering"
}
],
"people": [
{
"given_name": "Inês",
"family_name": "Martins",
"work_email": "ines@acme.test",
"title": "AE",
"unit": "Platform",
"manager_email": "ana@acme.test",
"location": "Porto",
"start_date": "2026-11-02",
"employment_type": "part_time"
}
]
}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data);import os, uuid
import requests
res = requests.post(
"https://api.bizisy.com/v1/org/setup.apply",
headers={"Authorization": f"Bearer {os.environ['BIZISY_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
json={
"locations": [
{
"name": "Porto",
"country": "PT",
"city": "Porto",
"timezone": "Europe/Lisbon",
},
{
"name": "Remote",
"country": "PT",
"city": None,
},
],
"units": [
{
"name": "Engineering",
"kind": "department",
},
{
"name": "Platform",
"kind": "team",
"parent": "Engineering",
},
],
"people": [
{
"given_name": "Inês",
"family_name": "Martins",
"work_email": "ines@acme.test",
"title": "AE",
"unit": "Platform",
"manager_email": "ana@acme.test",
"location": "Porto",
"start_date": "2026-11-02",
"employment_type": "part_time",
},
],
},
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
print(body["data"]){
"data": {
"created": {
"locations": 0,
"units": 0,
"people": 1
},
"updated": {
"locations": 0,
"units": 0,
"people": 0
},
"warnings": [],
"team_lead_managers": 1
}
}Import people from a CSV#
POST/v1/org/people.import_csvWrite
Imports people from CSV text (comma or semicolon separated, first row = header, at most 1 MB and 250 people; split bigger files into batches). Columns, any case: given_name, family_name, work_email, title (required), team, manager_email, location, start_date (YYYY-MM-DD), employment_type (full_time, part_time, contractor, intern), contract_type (permanent, fixed_term, temporary, internship, freelance), contract_end_date and probation_end_date (YYYY-MM-DD), weekly_hours (e.g. 40 or 37,5), notice_period_days, legal_entity (legal name of an existing entity), tax_id, social_security_number, iban (payroll details: only when signed in to Bizisy, refused over an API key or MCP), and custom fields by their key (org_fields_list; values typed per field, select values must be one of its options; private fields are refused over an API key or MCP). Empty cells leave values unchanged, except an empty manager_email for someone new or moving to another team: the team lead of that team becomes the manager (team_lead_managers counts them); write none (or -) in manager_email for no manager. Pay is never imported (record it with org_pay_add). Teams that do not exist yet become teams under the company root; locations and legal entities must already exist. Managers may appear anywhere in the file. Rows are matched by work_email like org_setup_apply. If row_errors is not empty, nothing was applied: fix those rows (row numbers as in a spreadsheet, the header is row 1; row 0 means the whole file) and retry. Unknown columns are ignored with a warning. Use dry_run first.
- Who can call it
- Manage key owner admin hr
- Keys and apps
- Payroll details and private custom fields are refused.
- MCP tool
org_people_import_csvon HR Assistant- Preview
?dry_run=trueruns every check and saves nothing- Retries
- An Idempotency-Key prevents a second run, but the result is never stored (it holds a secret or personal data): a retry gets a 409 that points to Activity
Input
csvstringrequired1–1000000 characters.
Returns
createdobjectrequired3 fields
locationsintegerrequiredunitsintegerrequiredpeopleintegerrequired
updatedobjectrequired3 fields
locationsintegerrequiredunitsintegerrequiredpeopleintegerrequired
warningsstring[]requiredteam_lead_managersintegerrequiredPeople who got their team lead as manager because no manager was given (new people, and people moving teams).
row_errorsobject[]required2 fields
rowintegerrequiredmessagestringrequired
Errors
validation_failed unauthenticated forbidden not_found conflict rate_limited internal
curl https://api.bizisy.com/v1/org/people.import_csv \
-H "Authorization: Bearer $BIZISY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"csv": "given_name,family_name,work_email,title\nInês,Martins,ines@acme.test,AE\n"
}'const res = await fetch('https://api.bizisy.com/v1/org/people.import_csv', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BIZISY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
"csv": "given_name,family_name,work_email,title\nInês,Martins,ines@acme.test,AE\n"
}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
console.log(body.data);import os, uuid
import requests
res = requests.post(
"https://api.bizisy.com/v1/org/people.import_csv",
headers={"Authorization": f"Bearer {os.environ['BIZISY_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
json={
"csv": "given_name,family_name,work_email,title\nInês,Martins,ines@acme.test,AE\n",
},
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
print(body["data"]){
"data": {
"created": {
"locations": 0,
"units": 0,
"people": 0
},
"updated": {
"locations": 0,
"units": 0,
"people": 0
},
"warnings": [],
"team_lead_managers": 0,
"row_errors": [
{
"row": 3,
"message": "work_email: Use a valid email address."
}
]
}
}Something missing or wrong on this page? Write to hello@bizisy.com.