Upload the employee directory
Adds directory contacts from a one-sheet .xlsx file.
Accepts one .xlsx file of up to 20 MB with a single sheet.
Required columns:
name(orfull_name), orfirst_nameroledepartmentphone_number(orphone)isd_code(orisdcode/isd)
Optional columns: last_name, extension, email.
Headers are matched after lower-casing and replacing non-letters with _. A + is added to isd_code if missing. Phones must be 7 to 15 digits.
If any row fails, nothing is saved. The call instead stores an error copy of the file with an errors column, and returns a download link valid for 24 hours. If all rows pass, the contacts are sent to the directory by department name. Missing departments are created, and exact duplicates are skipped. The call does not update any checklist task.
Before you call it
-
You need
enterpriseIdandteamId. Send them as multipart text fields. -
Use the template file if you have it. A 400 for missing columns returns the template link in
errorFileUrl. -
Put the text fields before the file part. The UI sends the file first. To be confirmed: whether text fields sent after the file reach the endpoint. If they do not, the call returns 400
enterpriseId and teamId are required for directory creation. -
Authentication: Bearer token of a signed-in console user. The token, and any cookie on the request, are forwarded to the directory write.
-
Rate limit: Not documented
-
Processing: Synchronous. Parses the file, then waits for the directory write (30 second timeout).
Request
- Build
multipart/form-datawithenterpriseId,teamId,teamName, thenfile. - Make sure the workbook has exactly one sheet and at least one data row.
- On 400 with
errorFileUrl, download the file, fix the rows listed in itserrorscolumn, and upload again. - Note that the UI does not mark the Employee Directory task after an upload. To mark it, call Mark a checklist task done with
{teamId, taskName: "Employee Directory", isCompleted: true}.
| Parameter | Type | Required | Description |
|---|---|---|---|
| Headers | — | — | Sent as HTTP headers. |
Authorization | string | Required | Bearer YOUR_TOKEN |
Content-Type | string | Required | multipart/form-data (set by your HTTP client) |
| Body (multipart form fields) | — | — | Sent as multipart/form-data fields. |
enterpriseId | text | Required | Dealer group ID. |
teamId | text | Required | Rooftop ID. |
teamName | text | Optional | Sent by the UI. Not used. |
file | file | Required | One .xlsx file, up to 20 MB, one sheet. |
Sample request
curl -X POST "https://api.spyne.ai/console/v1/product-onboarding/vini/directory/file" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "enterpriseId=<enterpriseId>" \
-F "teamId=<teamId>" \
-F "teamName=Example Motors" \
-F "[email protected];type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"import requests
with open("employee-directory.xlsx", "rb") as fh:
res = requests.post(
"https://api.spyne.ai/console/v1/product-onboarding/vini/directory/file",
headers={"Authorization": "Bearer YOUR_TOKEN"},
data={"enterpriseId": "<enterpriseId>", "teamId": "<teamId>", "teamName": "Example Motors"},
files={"file": ("employee-directory.xlsx", fh,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
)
print(res.status_code, res.json())import { readFile } from "node:fs/promises";
const form = new FormData();
form.append("enterpriseId", "<enterpriseId>");
form.append("teamId", "<teamId>");
form.append("teamName", "Example Motors");
form.append(
"file",
new Blob([await readFile("employee-directory.xlsx")], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}),
"employee-directory.xlsx"
);
const res = await fetch("https://api.spyne.ai/console/v1/product-onboarding/vini/directory/file", {
method: "POST",
headers: { Authorization: "Bearer YOUR_TOKEN" },
body: form,
});
console.log(res.status, await res.json());Sample response
{
"success": true,
"error": false,
"message": "Users uploaded and processed successfully.",
"data": {
"totalUsers": 12,
"invitedUsers": 0,
"directoryUsers": 12,
"inviteUsersApiCalled": false,
"directoryApiCalled": true,
"inviteUsersStatusCode": null,
"directoryStatusCode": 200
}
}{
"error": true,
"message": "Errors found in uploaded Excel file.",
"errorFileUrl": "https://<signed-download-url>",
"errorFileKey": "product-onboarding/users-directory/errors/<timestamp>-<id>-employee-directory.xlsx"
}{
"error": true,
"message": "Missing required columns in .xlsx file.",
"missingColumns": ["name_or_first_name", "isd_code"],
"errorFileUrl": "https://<template-file-url>"
}{ "message": "Invalid Token", "error": true, "code": "ERROR_AUTH_401", "details": "", "data": null }{ "error": true, "message": "Unexpected error while processing users file." }400 Bad Request (missing columns). Other 400 messages from this endpoint:
No file uploaded. Provide one .xlsx file in `file` field.Only one file is allowed.Invalid file format. Only .xlsx is supported.Found multiple sheets. Only one sheet is allowed.Uploaded .xlsx file does not contain any sheets.Uploaded .xlsx file is empty.enterpriseId and teamId are required for directory creation.
The directory write's 400 errors (for example an invalid phone) are passed through with the shape shown under Add directory contacts.
Response fields
| Parameter | Type | Description |
|---|---|---|
success | boolean | true on 200. |
data.totalUsers / directoryUsers | number | Rows sent to the directory. Exact duplicates are skipped by the directory and still counted here. |
data.directoryStatusCode | number or null | Status of the directory write. |
errorFileUrl | string | On 400: a 24-hour download link to the error copy, or the template link when columns are missing. |
errorFileKey | string | On 400 row errors: the stored error file key. |
missingColumns | string[] | On 400 missing columns. |
Errors
| Status | Reason | When it happens |
|---|---|---|
400 | File checks | No file, more than one file, not .xlsx, more than one sheet, no sheet, or no rows. |
400 | Missing required columns in .xlsx file. | A required column is missing. |
400 | Errors found in uploaded Excel file. | One or more rows failed. Nothing is saved. |
400 | enterpriseId and teamId are required for directory creation. | IDs missing from the form fields. |
400 | Passed through from the directory write | e.g. a phone number that is not valid with its country code. |
401 | ERROR_AUTH_401 | Missing or invalid token. |
500 | Unexpected error | Parse failure, a file over 20 MB, or a failure storing the error file. |
Next
Refresh with List the employee directory. The UI does not mark the Employee Directory task after an upload. If the upload is the only way the rooftop adds contacts, call Mark a checklist task done with {teamId: "<teamId>", taskName: "Employee Directory", isCompleted: true}.
- Called before: List the employee directory.
- Called after: Mark a checklist task done (not called by the UI after an upload), Submit or save the brand registration.
Updated about 2 hours ago
