Developers

Build on Soom with your own app.

Read your company’s employees, departments, branches, shifts, holidays, attendance, overtime, attendance corrections, shift changes, leave, leave balances, work from home, permissions, assets, expenses, documents on file, onboarding, daily reports, announcements, projects, performance reviews and goals, job openings, clients and payroll totals from your own software, add new employees and job candidates, record clock-ins and apply for leave, and hear by webhook when people, leave or payroll change. One address, plain JSON, and every app approved by Soom before it reads anything. An app changes nothing unless Soom approved that change for it.

GET /employees
$ curl "https://api.soom.co.in/api/public/v1/employees?limit=1" \    -H "Authorization: Bearer $SOOM_TOKEN"{  "success": true,  "data": {    "items": [      {        "id": "665f1c2ab7e94d0012a3c4d5",        "employeeCode": "EMP0032",        "firstName": "Priya",        "lastName": "Sharma",        "email": "priya.sharma@yourcompany.com",        "jobTitle": "Senior Developer",        "employeeType": "fulltimesalaried",        "status": "active",        "department": {          "id": "665f0a11b7e94d0012a3b101",          "name": "Engineering"        },        "designation": {          "id": "665f0a11b7e94d0012a3b201",          "name": "Senior Developer"        },        "location": {          "id": "665f0a11b7e94d0012a3b301",          "name": "Hyderabad"        },        "reportingManager": {          "id": "665f1c2ab7e94d0012a3c4c1",          "employeeCode": "EMP0007",          "name": "Ravi Kumar"        },        "hireDate": "2024-06-03",        "exitDate": null,        "updatedAt": "2026-11-12T06:40:00.000Z"      }    ],    "total": 1,    "page": 1,    "limit": 1  }}
01

Register your app

A company or HR admin registers it in Settings ▸ Integrations ▸ Soom API: what it’s for, who builds it and what it needs.

02

Soom approves it

Soom reads why it needs what it asks for and approves it. The admin then makes its client secret, shown once.

03

Call the API

Swap the client id and secret for an access token and send it on every call. Answers are plain JSON, a page at a time.

Getting access

Every app gets in the same way: a company or HR admin registers it in Soom, Soom approves it, and the app swaps its client id and secret for an access token that works for an hour. An app reaches its own company only, and only what Soom approved it for.

  1. Register the app. A company or HR admin opens Settings ▸ Integrations ▸ Soom API ▸ Register an app and says what the app is called, why it needs Soom, who builds it and what it needs to read or change. The Soom API has to be on for the company: ask your Soom contact.
  2. Soom approves it. Soom reads why the app needs what it asks for, then approves it, maybe for less, or says what to change. The admin gets an email either way. For more later, they tick it on the same page and Soom approves that too, while the app keeps what it has.
  3. Make the client secret. Once approved, the admin makes the app’s client secret on the same page and gives it, with the client id, to whoever builds the app. The secret is shown once; a new one stops the old one and every token it got. Keep both on your server, never in a web page or an app people download.
  4. Get an access token. Swap the client id and secret for an access token and send it on every call. When it runs out, get another the same way.

Every call goes to https://api.soom.co.in/api/public/v1 and carries the access token in the Authorization header.

Authorization: Bearer soom_at_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The company can switch its app off in Soom, and Soom can stop it; either way it is refused on the next call.

Getting an access token

POST/oauth/token

Send your client id and secret as a form, like OAuth libraries do, or as JSON: this is OAuth 2.0’s client credentials grant. It answers in OAuth’s own shape, not the API’s data envelope. The access token works for 60 minutes, then calls answer 401 TOKEN_EXPIRED: get another the same way.

What to send

FieldWhat it is
grant_typeneededAlways client_credentials.client_credentials
client_idneededYour app’s client id. Or send it and the secret as HTTP Basic auth.
client_secretneededYour app’s client secret.
scopeoptionalFor a token that can do less than your app: tick names with spaces between, like employees leave. Everything the app is approved for when left out.

The answer has

FieldWhat it is
access_tokenstringSend it as Authorization: Bearer on every call. It works for an hour.
token_typestringAlways Bearer.Bearer
expires_innumberSeconds the access token works for.
scopestringWhat the token can do: tick names with spaces between.
company_idstringYour company’s id in Soom. Every webhook event carries it as company.id.
company_namestringYour company’s name in Soom.

Errors

ErrorWhat it means
invalid_clientHTTP 401The client id or secret is wrong. A new client secret stops the old one at once.
unauthorized_clientHTTP 400Soom hasn’t approved the app yet, didn’t approve it or stopped it; the company switched the app or the Soom API off; or the company’s Soom account isn’t active. error_description says which.
invalid_scopeHTTP 400scope asks for something the app isn’t approved for.
unsupported_grant_typeHTTP 400grant_type must be client_credentials.

To end a token before its hour is up, post it as token to /oauth/revoke with your client id and secret.

curl -X POST "https://api.soom.co.in/api/public/v1/oauth/token" \
  -d grant_type="client_credentials" \
  -d client_id="$SOOM_CLIENT_ID" \
  -d client_secret="$SOOM_CLIENT_SECRET"
200 Answer
{
  "access_token": "soom_at_Xo8k2mQ7vB1cR4tY9wE3nU6pL0aS5dF2gH8jK1zC4xV",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "employees leave",
  "company_id": "665f0a11b7e94d0012a3b001",
  "company_name": "Your Company Pvt Ltd"
}
curl -X POST "https://api.soom.co.in/api/public/v1/oauth/revoke" \
  -d token="$SOOM_TOKEN" \
  -d client_id="$SOOM_CLIENT_ID" \
  -d client_secret="$SOOM_CLIENT_SECRET"

Lists and pages

Every address that reads answers with a list, a page at a time. Send page (from 1) and limit (up to 200; 25 when left out). total counts every item that matches, on all pages together.

{
  "success": true,
  "data": {
    "items": [
      "…"
    ],
    "total": 132,
    "page": 1,
    "limit": 25
  }
}

Dates are written like 2026-11-30. Times are in UTC, like 2026-11-30T04:12:00.000Z.

Making changes

An address that changes something takes JSON, sent with Content-Type: application/json, and answers 201 with what it made. It goes through the same checks as Soom’s own screens. An app makes only the changes Soom approved for it; any other gets 403 SCOPE_MISSING.

Sending the same thing twice is safe: adding someone whose email is already in Soom answers 409 EMAIL_IN_USE and adds nobody, a clock-in sent again for the same day updates that day, and a leave sent again answers 409 LEAVE_OVERLAPS.

Errors

A call that fails answers with success: false and an error code you can act on.

{
  "success": false,
  "error": {
    "code": "SCOPE_MISSING",
    "message": "This app can’t read attendance. Ask for it in Settings ▸ Integrations ▸ Soom API."
  }
}
CodeWhat it means
VALIDATION_ERRORHTTP 400A filter, or a field you sent, is missing or isn’t valid. The message says which.
UNAUTHORIZEDHTTP 401No access token, or it isn’t in the Authorization header.
INVALID_TOKENHTTP 401The access token is wrong or was ended, or its app was switched off by the company or stopped by Soom.
TOKEN_EXPIREDHTTP 401The access token has run out: each works for an hour. Get a new one with your client id and secret.
SCOPE_MISSINGHTTP 403Your app isn’t approved for this. Ask for it in Settings ▸ Integrations ▸ Soom API, and Soom approves it.
API_ACCESS_OFFHTTP 403The Soom API is switched off for this company.
TENANT_UNAVAILABLEHTTP 403The company’s Soom account isn’t active.
FEATURE_OFFHTTP 403This part of Soom, like overtime, time off, shift changes, assets, expenses, documents, onboarding, daily reports, announcements, projects, performance, recruitment or clients, isn’t switched on for the company, so there is nothing to read or add.
NOT_FOUNDHTTP 404There is no such address.
BALANCES_IN_PAYROLLHTTP 409The company keeps its leave balances in Xero, so Soom has none to give. Its leave is still in List leave.
PAYROLL_IN_XEROHTTP 409The company runs its payroll in Xero, so Soom has no payroll runs or payslips to give.
EMAIL_IN_USEHTTP 409Someone in Soom already signs in with that email, so nobody was added. Sending the same person twice gets this too.
CODE_IN_USEHTTP 409Someone in the company already has that employee code, so nobody was added.
DAY_TAKENHTTP 409That day already has times from somewhere else in Soom (clocked in there, typed by HR, regularized or synced), so they weren’t changed. HR can change them in Soom.
ON_LEAVEHTTP 409The person is on approved leave all that day. That day’s leave has to be cancelled in Soom first.
PAYROLL_LOCKEDHTTP 409That month’s payroll is locked, so its days can’t change.
LEAVE_OVERLAPSHTTP 409The person already has leave, pending or approved, on some of those days. Sending the same leave twice gets this too.
TIMEOFF_OVERLAPSHTTP 409The person has a request to work from home, or a permission, on those days, and the company doesn’t allow leave on the same days.
DAY_WORKEDHTTP 409The person clocked in on a day the leave would cover in full. Ask for half that day, or apply in Soom, where the clock-in can be cancelled.
JOB_NOT_OPENHTTP 409The job is on hold, closed or filled, so nobody can be added to it.
CANDIDATE_EXISTSHTTP 409Someone with that email is already a candidate for that job, so nobody was added. Sending the same person twice gets this too.
LEAVE_NOT_ALLOWEDHTTP 422The leave type’s rules don’t allow this leave: who can take it, the notice it needs, the fewest or most days, a document it needs, or it is no longer in use. Or every day is a weekly off or holiday. The message says which, ready to show the person.
RATE_LIMITEDHTTP 429Too many calls this minute. Wait for the seconds in the Retry-After header, then try again.

Usage limits

Each app has its own calls a minute, set by your admin in Settings ▸ Integrations ▸ Soom API: 60 to start, up to 300. One address can make up to 300 calls a minute in all. Past either, the answer is 429 RATE_LIMITED with a Retry-After header saying how many seconds to wait. Ask for big pages rather than many small ones.

Employees

The people in the company: who they are, where they sit and who they report to. And adding someone new.

List employees

GET/employees

Everyone in the company, in employee code order. People who have left are only listed when you ask for them. Needs an app approved for Employees.

Filters

FilterWhat it does
statusoptionalcurrent (the default) is everyone still working, exited is people who have left, all is both.currentexitedall
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the person.
employeeCodestring or nullThe code your company gave them.
firstNamestringFirst name.
lastNamestringLast name.
emailstringThe email they sign in to Soom with.
jobTitlestring or nullJob title.
employeeTypestring or nullKind of employment.administratorconsultantcontractorfulltimehourlyfulltimeinternshipfulltimesalariedparttimehourlyparttimeinternshipparttimesalariedcasual
statusstringWhere they are in their time with the company. invited is someone who hasn’t signed in yet.invitedactiveprobationonNoticeexitedsuspended
departmentobject or nullTheir department.
department.idstringSoom’s id for it, the id in List departments.
department.namestringIts name.
designationobject or nullTheir designation.
designation.idstringSoom’s id for it, the id in List designations.
designation.namestringIts name.
locationobject or nullThe branch they work at. Their days and shift times run on its time zone.
location.idstringSoom’s id for it, the id in List locations.
location.namestringIts name.
reportingManagerobject or nullWho they report to.
reportingManager.idstringSoom’s id for the person, the id in List employees.
reportingManager.employeeCodestring or nullThe code your company gave them.
reportingManager.namestringFirst and last name.
hireDatedate or nullThe day they joined.
exitDatedate or nullThe day they left, when they have.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/employees?status=current&limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f1c2ab7e94d0012a3c4d5",
        "employeeCode": "EMP0032",
        "firstName": "Priya",
        "lastName": "Sharma",
        "email": "priya.sharma@yourcompany.com",
        "jobTitle": "Senior Developer",
        "employeeType": "fulltimesalaried",
        "status": "active",
        "department": {
          "id": "665f0a11b7e94d0012a3b101",
          "name": "Engineering"
        },
        "designation": {
          "id": "665f0a11b7e94d0012a3b201",
          "name": "Senior Developer"
        },
        "location": {
          "id": "665f0a11b7e94d0012a3b301",
          "name": "Hyderabad"
        },
        "reportingManager": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "hireDate": "2024-06-03",
        "exitDate": null,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

Add an employee

POST/employees

Adds one person, as HR ▸ Employees ▸ Add employee does: Soom emails them an invite to sign in, and HR sees they were added through the Soom API. They are invited until they sign in. Needs an app approved for Add employees.

What to send

FieldWhat it is
firstNamestring, neededFirst name.
lastNamestring, neededLast name.
emailstring, neededThe email they will sign in with. No one in Soom can have it already.
employeeCodestring, optionalTheir employee code. The one after your latest code when left out.
jobTitlestring, optionalJob title.
employeeTypestring, optionalKind of employment.administratorconsultantcontractorfulltimehourlyfulltimeinternshipfulltimesalariedparttimehourlyparttimeinternshipparttimesalariedcasual
departmentIdstring, optionalTheir department, an id from List departments.
designationIdstring, optionalTheir designation, an id from List designations.
locationIdstring, optionalThe branch they work at, an id from List locations.
shiftIdstring, optionalA shift of their own, an id from List shifts. Without one they work the default shift.
reportingManagerIdstring, optionalWho they report to, the id of someone still working, from List employees.
hireDatedate, optionalThe day they join, like 2026-12-01.

The answer has

FieldWhat it is
employeeobjectThe new person, as List employees gives them.
employee.idstringSoom’s id for the person.
employee.employeeCodestring or nullThe code your company gave them.
employee.firstNamestringFirst name.
employee.lastNamestringLast name.
employee.emailstringThe email they sign in to Soom with.
employee.jobTitlestring or nullJob title.
employee.employeeTypestring or nullKind of employment.administratorconsultantcontractorfulltimehourlyfulltimeinternshipfulltimesalariedparttimehourlyparttimeinternshipparttimesalariedcasual
employee.statusstringWhere they are in their time with the company. invited is someone who hasn’t signed in yet.invitedactiveprobationonNoticeexitedsuspended
employee.departmentobject or nullTheir department.
employee.department.idstringSoom’s id for it, the id in List departments.
employee.department.namestringIts name.
employee.designationobject or nullTheir designation.
employee.designation.idstringSoom’s id for it, the id in List designations.
employee.designation.namestringIts name.
employee.locationobject or nullThe branch they work at. Their days and shift times run on its time zone.
employee.location.idstringSoom’s id for it, the id in List locations.
employee.location.namestringIts name.
employee.reportingManagerobject or nullWho they report to.
employee.reportingManager.idstringSoom’s id for the person, the id in List employees.
employee.reportingManager.employeeCodestring or nullThe code your company gave them.
employee.reportingManager.namestringFirst and last name.
employee.hireDatedate or nullThe day they joined.
employee.exitDatedate or nullThe day they left, when they have.
employee.updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
inviteEmailSentbooleanfalse when the invite email couldn’t go. HR can send it again from HR ▸ Employees.
curl -X POST "https://api.soom.co.in/api/public/v1/employees" \
  -H "Authorization: Bearer $SOOM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Anjali",
    "lastName": "Verma",
    "email": "anjali.verma@yourcompany.com",
    "jobTitle": "Senior Developer",
    "employeeType": "fulltimesalaried",
    "departmentId": "665f0a11b7e94d0012a3b101",
    "designationId": "665f0a11b7e94d0012a3b201",
    "locationId": "665f0a11b7e94d0012a3b301",
    "reportingManagerId": "665f1c2ab7e94d0012a3c4c1",
    "hireDate": "2026-12-01"
  }'
201 Answer
{
  "success": true,
  "data": {
    "employee": {
      "id": "665f1c2ab7e94d0012a3c4e9",
      "employeeCode": "EMP0033",
      "firstName": "Anjali",
      "lastName": "Verma",
      "email": "anjali.verma@yourcompany.com",
      "jobTitle": "Senior Developer",
      "employeeType": "fulltimesalaried",
      "status": "invited",
      "department": {
        "id": "665f0a11b7e94d0012a3b101",
        "name": "Engineering"
      },
      "designation": {
        "id": "665f0a11b7e94d0012a3b201",
        "name": "Senior Developer"
      },
      "location": {
        "id": "665f0a11b7e94d0012a3b301",
        "name": "Hyderabad"
      },
      "reportingManager": {
        "id": "665f1c2ab7e94d0012a3c4c1",
        "employeeCode": "EMP0007",
        "name": "Ravi Kumar"
      },
      "hireDate": "2026-12-01",
      "exitDate": null,
      "updatedAt": "2026-11-12T06:40:00.000Z"
    },
    "inviteEmailSent": true
  }
}

Company setup

How the company is set up in Soom: its departments, designations, branches, shifts and holidays, each with the id employees point at.

List departments

GET/departments

Every department, in the order Settings ▸ Departments shows them. Needs an app approved for Company setup.

Filters

FilterWhat it does
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the department, the id in an employee’s department.
namestringDepartment name.
descriptionstring or nullWhat it does, when written down.
headobject or nullWho heads it.
head.idstringSoom’s id for the person, the id in List employees.
head.employeeCodestring or nullThe code your company gave them.
head.namestringFirst and last name.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/departments?limit=100" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b101",
        "name": "Engineering",
        "description": "Builds and runs the product.",
        "head": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 100
  }
}

List designations

GET/designations

Every designation, in the order Settings ▸ Designations shows them. Needs an app approved for Company setup.

Filters

FilterWhat it does
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the designation, the id in an employee’s designation.
namestringDesignation name.
descriptionstring or nullWhat it means, when written down.
departmentobject or nullThe department it belongs to, when it has one.
department.idstringSoom’s id for it, the id in List departments.
department.namestringIts name.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/designations?limit=100" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b201",
        "name": "Senior Developer",
        "description": null,
        "department": {
          "id": "665f0a11b7e94d0012a3b101",
          "name": "Engineering"
        },
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 100
  }
}

List locations

GET/locations

Every branch, in the order Settings ▸ Locations shows them, with the time zone its people work on. Needs an app approved for Company setup.

Filters

FilterWhat it does
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the branch, the id in an employee’s location.
namestringBranch name.
citystring or nullCity.
statestring or nullState.
countrystring or nullCountry.
timezonestringTime zone, like Asia/Kolkata: the clock its people’s days and shift times run on. The company’s own when the branch has none set.
isHeadquartersbooleantrue for the head office.
managerobject or nullThe branch manager.
manager.idstringSoom’s id for the person, the id in List employees.
manager.employeeCodestring or nullThe code your company gave them.
manager.namestringFirst and last name.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/locations?limit=100" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b301",
        "name": "Hyderabad",
        "city": "Hyderabad",
        "state": "Telangana",
        "country": "India",
        "timezone": "Asia/Kolkata",
        "isHeadquarters": true,
        "manager": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 100
  }
}

List shifts

GET/shifts

Every shift, with its hours, break and working days, and the rules that make a day late or a half day. Needs an app approved for Company setup.

Filters

FilterWhat it does
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the shift.
namestringShift name.
startTimestringWhen it starts, like 09:30, on the clock of the branch each person works at.
endTimestringWhen it ends, like 18:30. Earlier than startTime when it ends the next morning.
isOvernightbooleantrue when it ends the next morning.
breakMinutesnumberMinutes of break in it.
workMinutesnumberA full day on it less the break: what a day of leave or a holiday counts as.
workingDayslist of stringsThe days it is worked. A person can have their own working days, and theirs win.MondayTuesdayWednesdayThursdayFridaySaturdaySunday
graceMinutesnumberMinutes after the start someone can clock in without being late. The company’s grace when the shift has none of its own.
halfDayHoursnumberA day worked shorter than this many hours is a half day. 0 means there is no such rule.
isDefaultbooleantrue for the shift everyone without a shift of their own works.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/shifts?limit=100" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b401",
        "name": "General",
        "startTime": "09:30",
        "endTime": "18:30",
        "isOvernight": false,
        "breakMinutes": 60,
        "workMinutes": 480,
        "workingDays": [
          "Monday",
          "Tuesday",
          "Wednesday",
          "Thursday",
          "Friday"
        ],
        "graceMinutes": 10,
        "halfDayHours": 4,
        "isDefault": true,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 100
  }
}

List holidays

GET/holidays

Every holiday in a year, by date, with the branches it is for. Needs an app approved for Company setup.

Filters

FilterWhat it does
yearoptionalHolidays in this year, like 2026. This year on the company’s clock when left out.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the holiday.
datedateThe day.
namestringHoliday name.
typestringpublic is a day off for the people it covers. optional keeps the office open: it is marked on calendars, and leave taken for it counts.publicoptional
descriptionstring or nullA note about it, when written down.
locationslistThe branches it is for. Empty means the whole company, and someone with no branch only gets those.
locations[].idstringSoom’s id for the branch, the id in List locations.
locations[].namestringBranch name.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/holidays?year=2026&limit=100" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b501",
        "date": "2026-06-02",
        "name": "Telangana Formation Day",
        "type": "public",
        "description": "Hyderabad office only.",
        "locations": [
          {
            "id": "665f0a11b7e94d0012a3b301",
            "name": "Hyderabad"
          }
        ],
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 100
  }
}

Attendance

Each person’s days: clock-in and clock-out times and how the day turned out, the overtime they claimed, and what they asked to change: a day’s times, or the shift they work. And recording clock-ins from a clock of your own.

List attendance days

GET/attendance

Every day with a record between two dates, newest first. A day nobody has a record for yet is not listed. Needs an app approved for Attendance.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Days on these dates are listed.
toneededLast day, on or after from.
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
employeeobject or nullWho it belongs to.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
datedateThe working day.
statusstringHow the day turned out. absent is a working day with no clock-in and no leave.presentlatehalf_dayabsentleavewfhpermissionholidayoff
clockIndate-time or nullWhen they clocked in, in UTC.
clockOutdate-time or nullWhen they clocked out, in UTC.
workedMinutesnumberMinutes worked, less the shift’s break.
lateMinutesnumberMinutes late, after the grace period.
earlyLeaveMinutesnumberMinutes left before the shift ended.
workFromstring or nullWhere they worked from.officewfh
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/attendance?from=2026-11-01&to=2026-11-30&limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "date": "2026-11-02",
        "status": "late",
        "clockIn": "2026-11-02T04:12:00.000Z",
        "clockOut": "2026-11-02T13:05:00.000Z",
        "workedMinutes": 473,
        "lateMinutes": 12,
        "earlyLeaveMinutes": 0,
        "workFrom": "office",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

Record a clock-in

POST/clock-ins

Records when someone clocked in, and when they clocked out once they have, judged as their own clock-in in Soom is: the day on their branch’s clock, late and early against their shift, nothing late on a weekly off. Send the same day again with clockOut when they leave. It answers 201 when it starts a day and 200 when it updates one it started. A day with times from anywhere else in Soom is never changed. Needs an app approved for Record clock-ins.

What to send

FieldWhat it is
employeeCodestring, neededWho clocked in, by the employee code your company gave them.
clockIndate-time, neededWhen they clocked in, with its offset like 2026-11-09T09:28:00+05:30, or in UTC ending in Z.
clockOutdate-time, optionalWhen they clocked out. Leave it out until they have; a clock-out already recorded is kept when it is left out.
datedate, optionalThe working day it belongs to: the clock-in’s own day on their branch’s clock when left out. Send it for a night shift clocked in after midnight; it can only be that day or the day before.
workFromstring, optionalWhere they worked from. When left out: home on a day they have an approved request to work from home, or if they always work from home, else the office.officewfh

The answer has

FieldWhat it is
dayobjectThe day, as List attendance days gives it.
day.employeeobject or nullWho it belongs to.
day.employee.idstringSoom’s id for the person, the id in List employees.
day.employee.employeeCodestring or nullThe code your company gave them.
day.employee.namestringFirst and last name.
day.datedateThe working day.
day.statusstringHow the day turned out. absent is a working day with no clock-in and no leave.presentlatehalf_dayabsentleavewfhpermissionholidayoff
day.clockIndate-time or nullWhen they clocked in, in UTC.
day.clockOutdate-time or nullWhen they clocked out, in UTC.
day.workedMinutesnumberMinutes worked, less the shift’s break.
day.lateMinutesnumberMinutes late, after the grace period.
day.earlyLeaveMinutesnumberMinutes left before the shift ended.
day.workFromstring or nullWhere they worked from.officewfh
day.updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
createdbooleantrue when this started the day, false when it updated a day it started before.
curl -X POST "https://api.soom.co.in/api/public/v1/clock-ins" \
  -H "Authorization: Bearer $SOOM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "employeeCode": "EMP0032",
    "clockIn": "2026-11-09T09:28:00+05:30",
    "clockOut": "2026-11-09T18:41:00+05:30"
  }'
201 Answer
{
  "success": true,
  "data": {
    "day": {
      "employee": {
        "id": "665f1c2ab7e94d0012a3c4d5",
        "employeeCode": "EMP0032",
        "name": "Priya Sharma"
      },
      "date": "2026-11-09",
      "status": "present",
      "clockIn": "2026-11-09T03:58:00.000Z",
      "clockOut": "2026-11-09T13:11:00.000Z",
      "workedMinutes": 493,
      "lateMinutes": 0,
      "earlyLeaveMinutes": 0,
      "workFrom": "office",
      "updatedAt": "2026-11-12T06:40:00.000Z"
    },
    "created": true
  }
}

List overtime

GET/overtime

Overtime claimed for days between two dates, newest first. In Soom, time on the clock past the end of a shift is overtime only once someone claims it and the claim is approved, so only approved claims are listed unless you ask for others. Needs an app approved for Attendance.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Claims for days on these dates are listed.
toneededLast day, on or after from.
statusoptionalapproved (the default), pending, rejected, or all of them.approvedpendingrejectedall
kindoptionalOnly extra hours on working days, or only work on weekly offs.extra_hoursweekly_off
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the claim.
employeeobject or nullWho claimed it.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
datedateThe day worked.
kindstringextra_hours is time worked past a working day. weekly_off is working on a day off.extra_hoursweekly_off
minutesnumber or nullMinutes claimed, for extra hours. null for a weekly off, which is claimed as a day: its hours are that day’s workedMinutes in List attendance days.
enteredByHandbooleantrue when they typed the hours, as for work done after leaving. false when the hours come from their clock times.
statusstringWhere it stands. pending is still with an approver or HR.pendingapprovedrejected
decisionstring or nullHow approved overtime is given: paid, or comp_off as days of leave. null until it is approved.paidcomp_off
compOffDaysnumber or nullDays of leave given for it, when decision is comp_off.
appliedAtdate-time or nullWhen it was claimed, in UTC.
decidedAtdate-time or nullWhen it was approved or rejected, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/overtime?from=2026-11-01&to=2026-11-30" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6af1a2b3c4d5e6f708192a6e",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "date": "2026-11-05",
        "kind": "extra_hours",
        "minutes": 120,
        "enteredByHand": false,
        "status": "approved",
        "decision": "paid",
        "compOffDays": null,
        "appliedAt": "2026-11-06T04:20:00.000Z",
        "decidedAt": "2026-11-07T10:15:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List attendance corrections

GET/attendance-corrections

Requests to correct a day’s clock-in or clock-out time, for days between two dates, newest first. Soom calls them regularizations. Once one is approved, the day in List attendance days has the corrected times. Why it was asked for, and what approvers wrote, stay in Soom. Needs an app approved for Attendance.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Requests for days on these dates are listed.
toneededLast day, on or after from.
statusoptionalOnly requests with this status.pendingapprovedrejectedcancelled
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the request.
employeeobject or nullWho asked.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
datedateThe day to correct.
clockIndate-time or nullThe clock-in time asked for, in UTC. null when only the clock-out is to change.
clockOutdate-time or nullThe clock-out time asked for, in UTC. null when only the clock-in is to change.
statusstringWhere it stands.pendingapprovedrejectedcancelled
appliedAtdate-time or nullWhen it was asked for, in UTC.
decidedAtdate-time or nullWhen it was approved or rejected, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/attendance-corrections?from=2026-11-01&to=2026-11-30" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6af1a2b3c4d5e6f708192b80",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "date": "2026-11-03",
        "clockIn": "2026-11-03T03:55:00.000Z",
        "clockOut": "2026-11-03T12:40:00.000Z",
        "status": "approved",
        "appliedAt": "2026-11-04T05:20:00.000Z",
        "decidedAt": "2026-11-04T09:02:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List shift changes

GET/shift-changes

Requests to work another shift on a day, for days between two dates, newest first. Once one is approved, they work that shift that day, and Soom judges the day against it. Why it was asked for, and what approvers wrote, stay in Soom. Needs an app approved for Attendance.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Requests for days on these dates are listed.
toneededLast day, on or after from.
statusoptionalOnly requests with this status.pendingapprovedrejectedcancelled
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the request.
employeeobject or nullWho asked.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
datedateThe day.
fromShiftobject or nullThe shift they had that day when they asked. null when they had none.
fromShift.idstringSoom’s id for it, the id in List shifts.
fromShift.namestringIts name.
toShiftobject or nullThe shift they asked to work instead.
toShift.idstringSoom’s id for it, the id in List shifts.
toShift.namestringIts name.
statusstringWhere it stands.pendingapprovedrejectedcancelled
appliedAtdate-time or nullWhen it was asked for, in UTC.
decidedAtdate-time or nullWhen it was approved or rejected, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/shift-changes?from=2026-11-01&to=2026-11-30" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6af1a2b3c4d5e6f708192b91",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "date": "2026-11-20",
        "fromShift": {
          "id": "665f0a11b7e94d0012a3b401",
          "name": "General"
        },
        "toShift": {
          "id": "665f0a11b7e94d0012a3b402",
          "name": "Night"
        },
        "status": "approved",
        "appliedAt": "2026-11-17T06:10:00.000Z",
        "decidedAt": "2026-11-17T11:30:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Leave

Leave people applied for or HR added, the company’s leave types, and what each person has left. And applying for leave for someone.

List leave

GET/leave

Every leave that touches the dates you give, latest start first, whatever its status unless you pick one. Needs an app approved for Leave.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Leave that touches any day from here to to is listed.
toneededLast day, on or after from.
statusoptionalOnly leave with this status.pendingapprovedrejectedcancelled
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the leave.
employeeobject or nullWho it belongs to.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
leaveTypeobject or nullThe leave type.
leaveType.idstringSoom’s id for it, the id in List leave types.
leaveType.namestringIts name.
startDatedateFirst day of the leave.
endDatedateLast day of the leave.
daysnumberDays it takes, with half days as 0.5.
dayBreakdownlistEach day it covers, and which part of it.
dayBreakdown[].datedateThe day.
dayBreakdown[].portionstringThe whole day, or the first or second half.fullfirstHalfsecondHalf
statusstringWhere it stands.pendingapprovedrejectedcancelled
appliedAtdate-time or nullWhen it was applied for, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/leave?from=2026-11-01&to=2026-11-30&status=approved" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6af1a2b3c4d5e6f708192a3b",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "leaveType": {
          "id": "665f0a11b7e94d0012a3b601",
          "name": "Casual Leave"
        },
        "startDate": "2026-11-12",
        "endDate": "2026-11-13",
        "days": 1.5,
        "dayBreakdown": [
          {
            "date": "2026-11-12",
            "portion": "full"
          },
          {
            "date": "2026-11-13",
            "portion": "firstHalf"
          }
        ],
        "status": "approved",
        "appliedAt": "2026-11-05T06:30:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Apply for leave

POST/leave

Applies for leave for someone, as they would in Soom: weekly offs and holidays aren’t counted, the leave type’s rules hold (who can take it, the notice it needs, the fewest and most days), and it goes to their approvers, who are told as usual. It is pending until they decide, unless the person approves their own leave. A leave type that needs a document has to be applied for in Soom. Needs an app approved for Apply for leave.

What to send

FieldWhat it is
employeeCodestring, neededWho the leave is for, by the employee code your company gave them.
leaveTypeIdstring, neededThe leave type, an id from List leave types. It has to be in use.
startDatedate, neededFirst day of the leave, like 2026-11-26. It can be a day gone by, when the leave type needs no notice.
endDatedate, neededLast day, on or after startDate. The same day for a one-day leave.
startPortionstring, optionalHow much of startDate they are away: the whole day when left out, or only its first half (the morning) or second half.fullfirstHalfsecondHalf
endPortionstring, optionalHow much of endDate they are away, for a leave longer than one day: the whole day when left out.fullfirstHalfsecondHalf
reasonstring, neededWhy, in their words, up to 1000 characters. Their approvers read it.

The answer has

FieldWhat it is
leaveobjectThe leave, as List leave gives it.
leave.idstringSoom’s id for the leave.
leave.employeeobject or nullWho it belongs to.
leave.employee.idstringSoom’s id for the person, the id in List employees.
leave.employee.employeeCodestring or nullThe code your company gave them.
leave.employee.namestringFirst and last name.
leave.leaveTypeobject or nullThe leave type.
leave.leaveType.idstringSoom’s id for it, the id in List leave types.
leave.leaveType.namestringIts name.
leave.startDatedateFirst day of the leave.
leave.endDatedateLast day of the leave.
leave.daysnumberDays it takes, with half days as 0.5.
leave.dayBreakdownlistEach day it covers, and which part of it.
leave.dayBreakdown[].datedateThe day.
leave.dayBreakdown[].portionstringThe whole day, or the first or second half.fullfirstHalfsecondHalf
leave.statusstringWhere it stands.pendingapprovedrejectedcancelled
leave.appliedAtdate-time or nullWhen it was applied for, in UTC.
leave.updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
beyondBalancebooleantrue when it asks for more days than they had available. It still goes for approval, and their available in List leave balances drops below 0.
curl -X POST "https://api.soom.co.in/api/public/v1/leave" \
  -H "Authorization: Bearer $SOOM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "employeeCode": "EMP0032",
    "leaveTypeId": "665f0a11b7e94d0012a3b601",
    "startDate": "2026-11-26",
    "endDate": "2026-11-27",
    "endPortion": "firstHalf",
    "reason": "Family function in Vijayawada"
  }'
201 Answer
{
  "success": true,
  "data": {
    "leave": {
      "id": "6af1a2b3c4d5e6f708192a7f",
      "employee": {
        "id": "665f1c2ab7e94d0012a3c4d5",
        "employeeCode": "EMP0032",
        "name": "Priya Sharma"
      },
      "leaveType": {
        "id": "665f0a11b7e94d0012a3b601",
        "name": "Casual Leave"
      },
      "startDate": "2026-11-26",
      "endDate": "2026-11-27",
      "days": 1.5,
      "dayBreakdown": [
        {
          "date": "2026-11-26",
          "portion": "full"
        },
        {
          "date": "2026-11-27",
          "portion": "firstHalf"
        }
      ],
      "status": "pending",
      "appliedAt": "2026-11-10T05:42:00.000Z",
      "updatedAt": "2026-11-12T06:40:00.000Z"
    },
    "beyondBalance": false
  }
}

List leave types

GET/leave-types

Every leave type, in use or not, by name as Settings ▸ Leave Types lists them, with how its days are given and the rules for asking for it. Needs an app approved for Leave.

Filters

FilterWhat it does
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the leave type, the id in a leave’s leaveType.
namestringLeave type name.
codestringIts short code, like CL.
descriptionstring or nullWhat it is for, when written down.
colorstringThe colour Soom shows it in, like #22C55E.
isPaidbooleanfalse for leave without pay.
accrualstringHow days are given. upfront gives the year’s days on 1 January, or on the 1st after someone joins. monthly adds daysPerMonth on the 1st of each month. none gives nothing on a schedule: days come only from comp-off or from HR.upfrontmonthlynone
daysPerYearnumberDays given over a year. Less than 12 × daysPerMonth when some months give none.
daysPerMonthnumberDays added each month, when accrual is monthly. 0 otherwise.
carriesForwardbooleantrue when days left at the end of the year move into the next. false when they lapse.
carryForwardMaxDaysnumberThe most days that move into the next year. 0 means no limit.
hasBalancebooleanfalse for leave kept off the balances, like maternity: it isn’t in List leave balances, and a request for it is never short of days.
minDaysPerRequestnumberThe fewest days one request can be for. 0.5 is half a day, 0 is any length.
maxDaysPerRequestnumberThe most days one request can be for. 0 means no limit.
noticeDaysnumberDays ahead it has to be asked for. 0 means no notice is needed.
attachmentRequiredbooleantrue when a request needs a document, like a doctor’s note.
eligibilityobjectWho can take it.
eligibility.genderstringall, or only people of this gender.allmalefemale
eligibility.employeeTypeslist of stringsOnly people with these kinds of employment, the employeeType in List employees. Empty means everyone.
isActivebooleanfalse when it can’t be asked for any more. Older leave can still be of this type.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/leave-types?limit=100" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b601",
        "name": "Casual Leave",
        "code": "CL",
        "description": "For personal errands and short breaks.",
        "color": "#22C55E",
        "isPaid": true,
        "accrual": "monthly",
        "daysPerYear": 12,
        "daysPerMonth": 1,
        "carriesForward": true,
        "carryForwardMaxDays": 5,
        "hasBalance": true,
        "minDaysPerRequest": 0.5,
        "maxDaysPerRequest": 3,
        "noticeDays": 1,
        "attachmentRequired": false,
        "eligibility": {
          "gender": "all",
          "employeeTypes": []
        },
        "isActive": true,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 100
  }
}

List leave balances

GET/leave-balances

What each person has of every leave type they can take, for one year, in employee code order: the figures HR ▸ Leave Balances shows. A company that keeps its leave balances in Xero has none in Soom, and gets BALANCES_IN_PAYROLL. Needs an app approved for Leave.

Filters

FilterWhat it does
yearoptionalBalances for this year, like 2026. This year on the company’s clock when left out.
statusoptionalcurrent (the default) is everyone still working, exited is people who have left, all is both.currentexitedall
employeeCodeoptionalOnly this person, by the employee code your company gave them.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
employeeobjectWhose balances they are.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
yearnumberThe year, January to December.
balanceslistOne for each leave type they can take, by name. Types kept off the balances, and types not in use, are left out.
balances[].leaveTypeobjectThe leave type.
balances[].leaveType.idstringSoom’s id for it, the id in List leave types.
balances[].leaveType.namestringIts name.
balances[].entitlednumberDays given for the year so far, with any carried forward and HR’s changes.
balances[].usednumberDays of approved leave.
balances[].pendingnumberDays asked for and waiting for approval.
balances[].carriedForwardnumberDays brought from last year, already counted in entitled.
balances[].encashednumberDays paid out, or set aside to be paid out, instead of taken.
balances[].availablenumberDays they can still ask for: entitled less used, pending and encashed. Below 0 when more was approved than they had.
curl "https://api.soom.co.in/api/public/v1/leave-balances?year=2026&limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "year": 2026,
        "balances": [
          {
            "leaveType": {
              "id": "665f0a11b7e94d0012a3b601",
              "name": "Casual Leave"
            },
            "entitled": 12,
            "used": 4.5,
            "pending": 1,
            "carriedForward": 2,
            "encashed": 0,
            "available": 6.5
          }
        ]
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

Time off

Requests to work from home, and permissions: a few hours away during a working day.

List work from home

GET/work-from-home

Every request to work from home that touches the dates you give, latest start first. Someone who picked work from home when clocking in, with no request, is in List attendance days with workFrom wfh. Needs an app approved for Time off.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Requests that touch any day from here to to are listed.
toneededLast day, on or after from.
statusoptionalOnly requests with this status.pendingapprovedrejectedcancelled
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the request.
employeeobject or nullWho it belongs to.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
startDatedateFirst day.
endDatedateLast day.
daysnumberDays from startDate to endDate, both counted.
statusstringWhere it stands.pendingapprovedrejectedcancelled
appliedAtdate-time or nullWhen it was asked for, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/work-from-home?from=2026-11-01&to=2026-11-30" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6af1a2b3c4d5e6f708192a4c",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "startDate": "2026-11-18",
        "endDate": "2026-11-19",
        "days": 2,
        "status": "approved",
        "appliedAt": "2026-11-16T05:10:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List permissions

GET/permissions

Every permission on the dates you give, latest first. A permission is a few hours away from work within a working day, like leaving at 2 for an appointment. Needs an app approved for Time off.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Permissions on these dates are listed.
toneededLast day, on or after from.
statusoptionalOnly permissions with this status.pendingapprovedrejectedcancelled
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the permission.
employeeobject or nullWho it belongs to.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
datedateThe day.
fromTimestring or nullWhen it starts, like 14:00, on the clock of the branch they work at.
toTimestring or nullWhen it ends, like 16:00.
hoursnumberHours away, to 2 decimal places.
statusstringWhere it stands.pendingapprovedrejectedcancelled
appliedAtdate-time or nullWhen it was asked for, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/permissions?from=2026-11-01&to=2026-11-30" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6af1a2b3c4d5e6f708192a5d",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "date": "2026-11-24",
        "fromTime": "14:00",
        "toTime": "16:00",
        "hours": 2,
        "status": "approved",
        "appliedAt": "2026-11-23T09:45:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Assets

The company’s equipment: what it owns, who has each item now and who had it before, and what people have asked for.

List assets

GET/assets

Everything the company owns, in asset code order, with who has it now. Needs an app approved for Assets.

Filters

FilterWhat it does
statusoptionalOnly assets in this state.in_stockassignedunder_repairlostretired
categoryoptionalOnly this kind, a code from List asset categories.
employeeCodeoptionalOnly what this person has now, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the asset.
assetCodestringThe code on its label, like LT-0042.
namestringIts name.
categoryobjectThe kind of asset.
category.codestringIts code, from List asset categories, or other.
category.namestringIts name.
brandstring or nullWho made it.
modelstring or nullIts model.
serialNumberstring or nullIts serial number.
statusstringWhere it stands. in_stock is free to hand out. Something under_repair can still be with someone.in_stockassignedunder_repairlostretired
locationobject or nullThe branch it belongs to.
location.idstringSoom’s id for it, the id in List locations.
location.namestringIts name.
heldByobject or nullWho has it now. null when nobody does.
heldBy.idstringSoom’s id for the person, the id in List employees.
heldBy.employeeCodestring or nullThe code your company gave them.
heldBy.namestringFirst and last name.
heldSincedate-time or nullWhen they were given it, in UTC.
expectedReturnDatedate or nullWhen they are due to give it back, if a day was set.
purchaseDatedate or nullWhen the company bought it.
warrantyEndDatedate or nullThe last day of its warranty.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/assets?status=assigned&limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b02c3d4e5f6a7b8c9d0e1f2",
        "assetCode": "LT-0042",
        "name": "Dell Latitude 5440",
        "category": {
          "code": "laptop",
          "name": "Laptop"
        },
        "brand": "Dell",
        "model": "Latitude 5440",
        "serialNumber": "7HX2QK3",
        "status": "assigned",
        "location": {
          "id": "665f0a11b7e94d0012a3b301",
          "name": "Hyderabad"
        },
        "heldBy": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "heldSince": "2025-01-06T05:30:00.000Z",
        "expectedReturnDate": null,
        "purchaseDate": "2024-12-18",
        "warrantyEndDate": "2027-12-17",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

List asset categories

GET/asset-categories

The kinds of asset the company keeps, in the order Settings ▸ Asset Categories lists them. A company that never changed them has Soom’s own. Needs an app approved for Assets.

Filters

FilterWhat it does
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
codestringIts code, the code in an asset’s category.
namestringIts name.
isActivebooleanfalse when nothing new can be added to it. Older assets can still be of this kind.
curl "https://api.soom.co.in/api/public/v1/asset-categories?limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "code": "laptop",
        "name": "Laptop",
        "isActive": true
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

List asset assignments

GET/asset-assignments

Every time an asset was handed to someone, newest first: who had it, from when, and when they gave it back. Needs an app approved for Assets.

Filters

FilterWhat it does
statusoptionalopen is what people still have, returned is what came back, all (the default) is both.openreturnedall
employeeCodeoptionalOnly this person, by the employee code your company gave them.
assetCodeoptionalOnly this asset, by the code on its label.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the hand-over.
assetobject or nullThe asset.
asset.idstringSoom’s id for the asset, the id in List assets.
asset.assetCodestringThe code on its label.
asset.namestringIts name.
employeeobject or nullWho was given it.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
assignedAtdate-timeWhen they were given it, in UTC.
acknowledgedAtdate-time or nullWhen they confirmed in Soom that they have it.
expectedReturnDatedate or nullWhen they are due to give it back, if a day was set.
returnedAtdate-time or nullWhen it came back, in UTC. null while they still have it.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/asset-assignments?status=open" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b02c3d4e5f6a7b8c9d0e2a3",
        "asset": {
          "id": "6b02c3d4e5f6a7b8c9d0e1f2",
          "assetCode": "LT-0042",
          "name": "Dell Latitude 5440"
        },
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "assignedAt": "2025-01-06T05:30:00.000Z",
        "acknowledgedAt": "2025-01-06T07:02:00.000Z",
        "expectedReturnDate": null,
        "returnedAt": null,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List asset requests

GET/asset-requests

What people have asked for, newest first. An approved request is waiting to be handed over; a fulfilled one has been, with the asset they were given. Why they asked is not shared. Needs an app approved for Assets.

Filters

FilterWhat it does
statusoptionalOnly requests with this status.pendingapprovedrejectedfulfilledcancelled
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the request.
employeeobject or nullWho asked.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
categoryobjectThe kind of asset.
category.codestringIts code, from List asset categories, or other.
category.namestringIts name.
descriptionstringWhat they asked for, in their words.
urgencystringHow soon they need it.lownormalhigh
statusstringWhere it stands.pendingapprovedrejectedfulfilledcancelled
appliedAtdate-time or nullWhen they asked, in UTC.
decidedAtdate-time or nullWhen it was approved or rejected, in UTC.
fulfilledAtdate-time or nullWhen it was handed over, in UTC.
assetobject or nullThe asset they were given, once it was handed over.
asset.idstringSoom’s id for the asset, the id in List assets.
asset.assetCodestringThe code on its label.
asset.namestringIts name.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/asset-requests?status=pending" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b02c3d4e5f6a7b8c9d0e3b4",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "category": {
          "code": "monitor",
          "name": "Monitor"
        },
        "description": "A second monitor for design reviews",
        "urgency": "normal",
        "status": "pending",
        "appliedAt": "2026-11-03T08:15:00.000Z",
        "decidedAt": null,
        "fulfilledAt": null,
        "asset": null,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Expenses

Expense claims people made and payment vouchers the company recorded: what was spent, where each stands and how it was paid. Receipts, bank details and approvers are never shared.

List expenses

GET/expenses

Every claim and voucher spent on the days you give, newest first, or paid on them with by=paid. A claim with several bills lists each bill too. Needs an app approved for Expenses.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Days are the company’s, and are the day on the bill unless by is paid.
toneededLast day, on or after from.
byoptionalspent (the default) picks expenses by the day on the bill; paid picks them by the day they were paid, and lists only paid ones.spentpaid
statusoptionalOnly expenses at this stage.in_reviewto_be_paidpaidrejected
kindoptionalclaim is someone’s own spending, paid back to them; voucher is a payment the company made to a vendor.claimvoucher
categoryoptionalOnly this category, a code from List expense categories. A claim with several bills is listed when any bill is in it.
employeeCodeoptionalOnly this person’s: what they claimed, or are paid back for.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the expense.
expenseNostring or nullIts number in Soom, like EXP-00042.
kindstringclaim or voucher.claimvoucher
employeeobject or nullWho made the claim, or who recorded the voucher.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
reimburseToobject or nullWho the company pays back: usually who made the claim, or the person they claimed for. null for a voucher.
reimburseTo.idstringSoom’s id for the person, the id in List employees.
reimburseTo.employeeCodestring or nullThe code your company gave them.
reimburseTo.namestringFirst and last name.
vendorstring or nullWho the money went to, when written down.
categoryobjectIts category.
category.codestringIts code, from List expense categories.
category.namestringThe company’s name for it.
otherCategorystring or nullWhat it was, typed when the category is other. Several categories for a claim whose bills are in more than one.
descriptionstringWhat it was for.
expenseDatedateThe day on the bill; the earliest bill’s for a claim with several.
amountnumberHow much, in the company’s currency; the bills added up for a claim with several.
statusstringWhere it stands: in_review with its approvers, to_be_paid once they accepted it, then paid, or rejected.in_reviewto_be_paidpaidrejected
submittedAtdate-time or nullWhen it was made in Soom, in UTC.
acceptedAtdate-time or nullWhen its approvers accepted it to be paid, in UTC.
paidAtdate-time or nullWhen it was paid, in UTC. A payment recorded for an earlier day reads noon that day on the company’s clock.
paymentMethodstring or nullHow it was paid.cashbank_transferupichequecredit_cardother
paymentNotesstring or nullWhat was noted when it was paid, like the bank reference.
paidTogetherIdstring or nullThe same on every expense paid as one payment. null for one paid on its own. Linking payments together later doesn’t change updatedAt.
rejectedAtdate-time or nullWhen it was rejected, in UTC.
billslistEach bill of a claim that has several. Empty for an expense with one bill, which is the expense itself.
bills[].categoryobjectIts category.
bills[].category.codestringIts code, from List expense categories.
bills[].category.namestringThe company’s name for it.
bills[].otherCategorystring or nullWhat it was, typed when the category is other.
bills[].expenseDatedateThe day on the bill.
bills[].amountnumberHow much.
bills[].vendorstring or nullWho it was paid to.
bills[].notestring or nullWhat the bill was for.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/expenses?from=2026-11-01&to=2026-11-30&status=paid" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b13d4e5f6a7b8c9d0e1f203",
        "expenseNo": "EXP-00042",
        "kind": "claim",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "reimburseTo": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "vendor": null,
        "category": {
          "code": "other",
          "name": "Other"
        },
        "otherCategory": "Several categories",
        "description": "Client visit to Pune",
        "expenseDate": "2026-11-04",
        "amount": 3850,
        "status": "paid",
        "submittedAt": "2026-11-05T06:12:00.000Z",
        "acceptedAt": "2026-11-06T09:40:00.000Z",
        "paidAt": "2026-11-08T06:30:00.000Z",
        "paymentMethod": "bank_transfer",
        "paymentNotes": "UTR 452188901132",
        "paidTogetherId": null,
        "rejectedAt": null,
        "bills": [
          {
            "category": {
              "code": "travel",
              "name": "Travel"
            },
            "otherCategory": null,
            "expenseDate": "2026-11-04",
            "amount": 3200,
            "vendor": "IndiGo",
            "note": "Flight to Pune"
          },
          {
            "category": {
              "code": "meals",
              "name": "Meals"
            },
            "otherCategory": null,
            "expenseDate": "2026-11-04",
            "amount": 650,
            "vendor": null,
            "note": null
          }
        ],
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List expense categories

GET/expense-categories

The categories people pick from, in the company’s order: Soom’s own, renamed or turned off as the company chose, and any the company added. Needs an app approved for Expenses.

Filters

FilterWhat it does
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
codestringIts code, the code in an expense’s category. Soom’s own are words like travel; the company’s own start with c_.
namestringIts name.
isActivebooleanfalse when it can’t be picked for a new expense. Older expenses can still be in it.
curl "https://api.soom.co.in/api/public/v1/expense-categories?limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "code": "travel",
        "name": "Travel",
        "isActive": true
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

Documents

Which documents each person has on file, by the company’s document categories, and the required ones they haven’t given yet. The files, their names and the numbers on them never leave Soom.

List employee documents

GET/employee-documents

Everyone’s documents on file, a person to a row in employee code order: the category and when each was added, and the required categories they still miss. Never the files themselves. Needs an app approved for Documents.

Filters

FilterWhat it does
statusoptionalcurrent (the default) is everyone still working, exited is people who have left, all is both.currentexitedall
employeeCodeoptionalOnly this person, by the employee code your company gave them.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
employeeobjectWhose documents they are.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
documentslistEach document they have, by category in the company’s order, then oldest first.
documents[].categoryobjectThe category it is filed under.
documents[].category.idstring or nullSoom’s id for the category, the id in List document categories. null when that category has since been removed.
documents[].category.namestringIts name.
documents[].uploadedAtdate-time or nullWhen it was added, in UTC.
documents[].uploadedBystring or nullself when they added it themselves, hr when HR did.selfhr
missinglistThe required categories they have nothing in yet.
missing[].idstringSoom’s id for the category, the id in List document categories.
missing[].namestringIts name.
curl "https://api.soom.co.in/api/public/v1/employee-documents?status=current&limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "documents": [
          {
            "category": {
              "id": "665f0a11b7e94d0012a3b701",
              "name": "PAN card"
            },
            "uploadedAt": "2024-06-04T06:20:00.000Z",
            "uploadedBy": "self"
          },
          {
            "category": {
              "id": "665f0a11b7e94d0012a3b702",
              "name": "Offer letter"
            },
            "uploadedAt": "2024-06-03T10:05:00.000Z",
            "uploadedBy": "hr"
          }
        ],
        "missing": [
          {
            "id": "665f0a11b7e94d0012a3b703",
            "name": "Address proof"
          }
        ]
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

List document categories

GET/document-categories

The kinds of document the company keeps on its people, in the order Settings ▸ Document Categories lists them. Needs an app approved for Documents.

Filters

FilterWhat it does
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the category.
namestringIts name.
descriptionstring or nullWhat goes in it, when written down.
isRequiredbooleantrue when everyone should have one. List employee documents names who doesn’t yet.
allowsMultiplebooleantrue when a person can keep several files in it; false keeps one.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/document-categories?limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b701",
        "name": "PAN card",
        "description": "A copy of the PAN card, for TDS.",
        "isRequired": true,
        "allowsMultiple": false,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

Onboarding

Each joiner’s onboarding: its steps and its checklist, and which are done. What people signed, uploaded or filled in stays in Soom.

List onboarding

GET/onboarding

Every onboarding, latest start first, with its steps and checklist in the order its template has them. Needs an app approved for Onboarding.

Filters

FilterWhat it does
statusoptionalOnly onboardings with this status.inProgresscompletedcancelled
employeeCodeoptionalOnly this joiner’s, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the onboarding.
employeeobject or nullWho is joining.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
templatestring or nullThe template it was started from.
statusstringWhere it stands.inProgresscompletedcancelled
startDatedateThe day it started, usually the day they join.
completedAtdate-time or nullWhen it was finished, by the joiner or by HR, in UTC.
stepslistWhat the joiner goes through, in order.
steps[].titlestringWhat it is.
steps[].typestringconsent is signing a form, document is uploading one, profileFields is filling in their details, policy is reading a policy, video is watching one, task is something to do.consentdocumentprofileFieldspolicyvideotask
steps[].requiredbooleantrue when it has to be done to finish.
steps[].dueDatedate or nullWhen it is due.
steps[].donebooleantrue once it is done.
steps[].doneAtdate-time or nullWhen it was done, in UTC.
taskslistThe checklist around it, like ordering a laptop.
tasks[].titlestringWhat it is.
tasks[].assigneestringWho does it: HR, or the joiner.hremployee
tasks[].dueDatedate or nullWhen it is due.
tasks[].donebooleantrue once it is done.
tasks[].completedAtdate-time or nullWhen it was done, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/onboarding?status=inProgress" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b79d0e1f20314253647586a",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4e9",
          "employeeCode": "EMP0033",
          "name": "Anjali Verma"
        },
        "template": "Engineering joiners",
        "status": "inProgress",
        "startDate": "2026-12-01",
        "completedAt": null,
        "steps": [
          {
            "title": "Sign the code of conduct",
            "type": "consent",
            "required": true,
            "dueDate": "2026-12-01",
            "done": true,
            "doneAt": "2026-11-26T10:12:00.000Z"
          },
          {
            "title": "Upload your PAN card",
            "type": "document",
            "required": true,
            "dueDate": "2026-12-03",
            "done": false,
            "doneAt": null
          }
        ],
        "tasks": [
          {
            "title": "Order a laptop",
            "assignee": "hr",
            "dueDate": "2026-11-28",
            "done": true,
            "completedAt": "2026-11-25T07:30:00.000Z"
          },
          {
            "title": "Add to payroll",
            "assignee": "hr",
            "dueDate": "2026-12-01",
            "done": false,
            "completedAt": null
          }
        ],
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Daily reports

What people wrote they worked on each day: each piece of work, how long it took, where it stands, and the client it was for.

List daily reports

GET/daily-reports

Every daily report for days between two dates, newest first. A person has one a day at most. Needs an app approved for Daily reports.

Filters

FilterWhat it does
fromneededFirst day, like 2026-11-01. Reports for days on these dates are listed.
toneededLast day, on or after from.
clientIdoptionalOnly reports with work for this client, an id from List clients.
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the report.
employeeobject or nullWhose report it is.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
datedateThe day it is about.
taskslistWhat they worked on.
tasks[].descriptionstringWhat they did.
tasks[].minutesnumberHow long it took, in minutes.
tasks[].statusstringWhere it stands.completedin_progressblocked
tasks[].clientobject or nullThe client it was for, when it was for one of the company’s clients.
tasks[].client.idstringSoom’s id for it, the id in List clients.
tasks[].client.namestringIts name.
tasks[].companystring or nullWho it was for, as they wrote it, when it wasn’t one of the company’s clients.
totalMinutesnumberAll its minutes added up.
submittedAtdate-time or nullWhen it was sent, in UTC.
editedAtdate-time or nullWhen it was last changed after that, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/daily-reports?from=2026-11-01&to=2026-11-30" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b79d0e1f20314253647597b",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "date": "2026-11-10",
        "tasks": [
          {
            "description": "Built the store-wise sales export",
            "minutes": 270,
            "status": "completed",
            "client": {
              "id": "665f0a11b7e94d0012a3b801",
              "name": "Northwind Retail"
            },
            "company": null
          },
          {
            "description": "Reviewed pull requests for the payroll team",
            "minutes": 90,
            "status": "in_progress",
            "client": null,
            "company": "Internal"
          }
        ],
        "totalMinutes": 360,
        "submittedAt": "2026-11-10T12:45:00.000Z",
        "editedAt": null,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Announcements

What the company announced to everyone in Soom.

List announcements

GET/announcements

Every announcement, pinned ones first and then newest first, as Soom shows them. Needs an app approved for Announcements.

Filters

FilterWhat it does
typeoptionalOnly notices, or only celebrations.noticecelebration
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the announcement.
titlestringIts title.
bodystringWhat it says, as HTML with only these tags: p, br, strong, em, u, s, ul, ol, li, a, h1 to h3, blockquote, code, pre and img.
typestringnotice is news for everyone, like a deadline or an office closing; celebration is a greeting, like for a festival.noticecelebration
pinnedbooleantrue when it is pinned to the top.
publishedByobject or nullWho posted it.
publishedBy.idstringSoom’s id for the person, the id in List employees.
publishedBy.employeeCodestring or nullThe code your company gave them.
publishedBy.namestringFirst and last name.
publishedAtdate-time or nullWhen it was posted, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/announcements?limit=20" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b79d0e1f2031425364760ac",
        "title": "Office closed on 25 December",
        "body": "<p>The office is closed on <strong>Friday 25 December</strong> for Christmas.</p>",
        "type": "notice",
        "pinned": true,
        "publishedBy": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "publishedAt": "2026-11-11T05:30:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}

Projects

Projects and their tasks: who owns and works on each, where every task stands, who has it and when it is due. Comments and files on tasks are never shared.

List projects

GET/projects

Every project, by name, with its people and how many of its tasks are at each stage. Needs an app approved for Projects.

Filters

FilterWhat it does
statusoptionalOnly projects with this status.activeon_holdcompletedarchived
employeeCodeoptionalOnly projects this person owns or is a member of.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the project, the projectId List tasks takes.
namestringIts name.
descriptionstring or nullWhat it is about, when written down.
statusstringWhere it stands.activeon_holdcompletedarchived
ownerobject or nullWho runs it.
owner.idstringSoom’s id for the person, the id in List employees.
owner.employeeCodestring or nullThe code your company gave them.
owner.namestringFirst and last name.
memberslistWho works on it, besides its owner.
members[].idstringSoom’s id for the person, the id in List employees.
members[].employeeCodestring or nullThe code your company gave them.
members[].namestringFirst and last name.
clientobject or nullThe client it is for, when it is work done for a client.
client.idstringSoom’s id for the client, the id in List clients.
client.namestringIts name.
startDatedate or nullWhen it starts.
dueDatedate or nullWhen it is due.
taskCountsobjectHow many of its tasks are at each stage.
taskCounts.todonumberStill to do.
taskCounts.in_progressnumberBeing worked on.
taskCounts.in_reviewnumberWaiting to be checked.
taskCounts.donenumberFinished.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/projects?status=active&limit=50" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b24e5f6a7b8c9d0e1f20314",
        "name": "Payroll go-live",
        "description": "Move October payroll from spreadsheets to Soom.",
        "status": "active",
        "owner": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "members": [
          {
            "id": "665f1c2ab7e94d0012a3c4d5",
            "employeeCode": "EMP0032",
            "name": "Priya Sharma"
          }
        ],
        "client": null,
        "startDate": "2026-10-01",
        "dueDate": "2026-11-30",
        "taskCounts": {
          "todo": 4,
          "in_progress": 2,
          "in_review": 1,
          "done": 9
        },
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 50
  }
}

List tasks

GET/tasks

Every task in every project, newest first. Needs an app approved for Projects.

Filters

FilterWhat it does
projectIdoptionalOnly this project’s tasks, an id from List projects.
statusoptionalOnly tasks at this stage.todoin_progressin_reviewdone
employeeCodeoptionalOnly tasks assigned to this person.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the task.
projectobject or nullThe project it belongs to.
project.idstringSoom’s id for it, the id in List projects.
project.namestringIts name.
titlestringWhat it is.
descriptionstring or nullMore about it, when written down.
statusstringIts column on the board.todoin_progressin_reviewdone
prioritystringHow urgent it is.lowmediumhighurgent
assigneeobject or nullWho has it. null when nobody does yet.
assignee.idstringSoom’s id for the person, the id in List employees.
assignee.employeeCodestring or nullThe code your company gave them.
assignee.namestringFirst and last name.
dueDatedate or nullWhen it is due.
createdAtdate-time or nullWhen it was added, in UTC.
completedAtdate-time or nullWhen it was moved to done, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/tasks?status=in_progress" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b24e5f6a7b8c9d0e1f20425",
        "project": {
          "id": "6b24e5f6a7b8c9d0e1f20314",
          "name": "Payroll go-live"
        },
        "title": "Check October PF figures against last year",
        "description": null,
        "status": "in_progress",
        "priority": "high",
        "assignee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "dueDate": "2026-11-14",
        "createdAt": "2026-11-02T07:45:00.000Z",
        "completedAt": null,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Performance

Review cycles, where each person’s review stands, and their goals. A final rating goes out only once the review is released to the person. What anyone wrote in a review, and goals still in draft, stay in Soom.

List review cycles

GET/review-cycles

Every review cycle, the latest period first, with how many reviews are in. Needs an app approved for Performance.

Filters

FilterWhat it does
statusoptionalOnly cycles at this step.draftself_reviewmanager_reviewcalibrationclosed
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the cycle, the cycleId List reviews and List goals take.
namestringIts name.
descriptionstring or nullWhat it is for, when written down.
periodFromdateFirst day of the time it reviews.
periodTodateLast day of the time it reviews.
statusstringIts step: draft, people reviewing themselves, managers reviewing, HR settling the ratings, or closed.draftself_reviewmanager_reviewcalibrationclosed
selfReviewDeadlinedate or nullWhen self-reviews are due, when set.
managerReviewDeadlinedate or nullWhen managers’ reviews are due, when set.
peoplenumberHow many people it reviews.
selfReviewsDonenumberHow many sent their self-review.
managerReviewsDonenumberHow many had their manager’s review sent.
openedAtdate-time or nullWhen it was opened, in UTC.
closedAtdate-time or nullWhen it was closed, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/review-cycles?status=closed" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b68c9d0e1f2031425364758",
        "name": "April to September 2026",
        "description": "How the first half of the financial year went.",
        "periodFrom": "2026-04-01",
        "periodTo": "2026-09-30",
        "status": "closed",
        "selfReviewDeadline": "2026-10-15",
        "managerReviewDeadline": "2026-10-31",
        "people": 42,
        "selfReviewsDone": 41,
        "managerReviewsDone": 42,
        "openedAt": "2026-10-01T04:30:00.000Z",
        "closedAt": "2026-11-06T09:30:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List reviews

GET/reviews

Each person’s review in each cycle, newest first: who reviews them, where it stands, and the final rating once it is released to them. Never the answers or comments. Needs an app approved for Performance.

Filters

FilterWhat it does
cycleIdoptionalOnly this review cycle’s, an id from List review cycles.
statusoptionalOnly reviews where this stands.pendingself_submittedmanager_submittedclosed
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the review.
cycleobject or nullThe review cycle it is in.
cycle.idstringSoom’s id for it, the id in List review cycles.
cycle.namestringIts name.
employeeobject or nullWhose review it is.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
reviewerobject or nullWho reviews them, usually their manager, when someone does.
reviewer.idstringSoom’s id for the person, the id in List employees.
reviewer.employeeCodestring or nullThe code your company gave them.
reviewer.namestringFirst and last name.
statusstringWhere it stands: waiting on them, self-review sent, manager’s review sent, or closed.pendingself_submittedmanager_submittedclosed
selfSubmittedAtdate-time or nullWhen they sent their self-review, in UTC.
managerSubmittedAtdate-time or nullWhen the reviewer sent theirs, in UTC.
closedAtdate-time or nullWhen it was closed, in UTC.
finalRatingnumber or nullThe final rating, once the review is released to them: it or its cycle closed. null before.
finalRatingLabelstring or nullWhat that rating means on the company’s scale.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/reviews?status=closed" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b68c9d0e1f2031425364869",
        "cycle": {
          "id": "6b68c9d0e1f2031425364758",
          "name": "April to September 2026"
        },
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "reviewer": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "status": "closed",
        "selfSubmittedAt": "2026-10-12T08:20:00.000Z",
        "managerSubmittedAt": "2026-10-28T11:05:00.000Z",
        "closedAt": "2026-11-06T09:30:00.000Z",
        "finalRating": 4,
        "finalRatingLabel": "Exceeds expectations",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List goals

GET/goals

Everyone’s goals, newest first. A goal its owner still has in draft isn’t listed. Needs an app approved for Performance.

Filters

FilterWhat it does
cycleIdoptionalOnly this review cycle’s, an id from List review cycles.
statusoptionalOnly goals where this stands.activeon_trackat_riskdonemissed
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the goal.
employeeobject or nullWhose goal it is.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
cycleobject or nullThe review cycle it counts towards, when it is tied to one.
cycle.idstringSoom’s id for it, the id in List review cycles.
cycle.namestringIts name.
titlestringThe goal.
descriptionstring or nullMore about it, when written down.
metricstring or nullHow it is measured, when written down.
statusstringWhere it stands.activeon_trackat_riskdonemissed
weightnumber or nullHow much it counts, out of 100, when weighted.
startDatedate or nullWhen it starts, when set.
dueDatedate or nullWhen it is due, when set.
createdAtdate-time or nullWhen it was set in Soom, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/goals?status=on_track" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b68c9d0e1f203142536497a",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "cycle": {
          "id": "6b68c9d0e1f2031425364758",
          "name": "April to September 2026"
        },
        "title": "Move every branch’s payroll to Soom",
        "description": "Every branch paid from Soom, with no spreadsheet step.",
        "metric": "Branches paid from Soom",
        "status": "on_track",
        "weight": 40,
        "startDate": "2026-04-01",
        "dueDate": "2026-09-30",
        "createdAt": "2026-04-03T06:15:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Recruitment

The company’s job openings and the candidates for them: each opening’s pipeline, and where every candidate stands. And adding the people who apply, with their resume. Salary ranges, and candidates’ phones, resumes, notes, ratings and answers, stay in Soom.

List jobs

GET/jobs

Every job opening, newest first, with how many candidates are at each stage. Ask for status=open to show what you are hiring for. Needs an app approved for Recruitment.

Filters

FilterWhat it does
statusoptionalOnly openings with this status.openon_holdclosedfilled
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the opening, the jobId List candidates takes.
titlestringThe role.
departmentobject or nullThe department it is in.
department.idstringSoom’s id for it, the id in List departments.
department.namestringIts name.
locationstring or nullWhere the role is, as written on the opening.
employmentTypestringKind of role.full_timepart_timecontractintern
descriptionstring or nullWhat the role is, when written down.
openingsnumberHow many people it is for.
statusstringWhere it stands. filled is when everyone was hired.openon_holdclosedfilled
hiringManagerobject or nullWho the role reports to, when set.
hiringManager.idstringSoom’s id for the person, the id in List employees.
hiringManager.employeeCodestring or nullThe code your company gave them.
hiringManager.namestringFirst and last name.
candidateCountsobjectHow many candidates are at each stage.
candidateCounts.appliednumberApplied, not looked at yet.
candidateCounts.screeningnumberBeing screened.
candidateCounts.interviewnumberIn interviews.
candidateCounts.offernumberOffered the job.
candidateCounts.hirednumberHired.
candidateCounts.rejectednumberNot taken forward.
createdAtdate-time or nullWhen it was opened in Soom, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/jobs?status=open" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b35f6a7b8c9d0e1f2031425",
        "title": "Senior Developer",
        "department": {
          "id": "665f0a11b7e94d0012a3b101",
          "name": "Engineering"
        },
        "location": "Hyderabad",
        "employmentType": "full_time",
        "description": "Build and run the payroll engine.",
        "openings": 2,
        "status": "open",
        "hiringManager": {
          "id": "665f1c2ab7e94d0012a3c4c1",
          "employeeCode": "EMP0007",
          "name": "Ravi Kumar"
        },
        "candidateCounts": {
          "applied": 14,
          "screening": 5,
          "interview": 3,
          "offer": 1,
          "hired": 0,
          "rejected": 6
        },
        "createdAt": "2026-10-20T05:00:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List candidates

GET/candidates

Every candidate, newest first, and where they stand. A hired candidate names the employee they became. Needs an app approved for Recruitment.

Filters

FilterWhat it does
jobIdoptionalOnly this opening’s candidates, an id from List jobs.
stageoptionalOnly candidates at this stage.appliedscreeninginterviewofferhiredrejected
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the candidate.
jobobject or nullThe opening they are a candidate for.
job.idstringSoom’s id for it, the id in List jobs.
job.titlestringThe role.
namestringTheir name.
emailstringTheir email.
sourcestringWhere they came from.referrallinkedinnaukriwalk_inagencywebsiteother
stagestringWhere they stand.appliedscreeninginterviewofferhiredrejected
offerStatusstring or nullWhere their offer stands, once one was sent.sentaccepteddeclinedwithdrawn
hiredEmployeeobject or nullThe employee they became, once hired. Their id is the one in List employees.
hiredEmployee.idstringSoom’s id for the person, the id in List employees.
hiredEmployee.employeeCodestring or nullThe code your company gave them.
hiredEmployee.namestringFirst and last name.
addedAtdate-time or nullWhen they were added in Soom, in UTC.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/candidates?stage=interview" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b35f6a7b8c9d0e1f2031536",
        "job": {
          "id": "6b35f6a7b8c9d0e1f2031425",
          "title": "Senior Developer"
        },
        "name": "Kiran Rao",
        "email": "kiran.rao@example.com",
        "source": "referral",
        "stage": "interview",
        "offerStatus": null,
        "hiredEmployee": null,
        "addedAt": "2026-10-28T09:15:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Upload a resume

POST/resume-uploads

Gives a link to upload one candidate’s resume to, before Add a candidate. PUT the file to uploadUrl within expiresIn seconds, with a Content-Type header the same as contentType, then send resumeKey with the candidate. Resumes stay private in Soom: HR opens them there, and no list gives them back. Needs an app approved for Add candidates.

What to send

FieldWhat it is
fileNamestring, neededThe file’s name, as HR will see it.
contentTypestring, neededWhat kind of file it is: a PDF, a Word file, or a photo of the resume.application/pdfapplication/mswordapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentimage/jpegimage/png
sizenumber, neededIts size in bytes, up to 15 MB.

The answer has

FieldWhat it is
uploadUrlstringPUT the file’s bytes here, with the same Content-Type, before expiresIn runs out.
resumeKeystringSend this as resumeKey in Add a candidate, once the file is uploaded.
expiresInnumberSeconds uploadUrl works for.
maxBytesnumberThe largest file it takes, in bytes.
curl -X POST "https://api.soom.co.in/api/public/v1/resume-uploads" \
  -H "Authorization: Bearer $SOOM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "meera-iyer-cv.pdf",
    "contentType": "application/pdf",
    "size": 248000
  }'
201 Answer
{
  "success": true,
  "data": {
    "uploadUrl": "https://example-bucket.s3.ap-south-1.amazonaws.com/v2/tenants/665f0a11b7e94d0012a3b001/recruitment/resumes/1762929600000_k3j9x2_meera-iyer-cv.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=300&X-Amz-Signature=9f2c7e1b",
    "resumeKey": "v2/tenants/665f0a11b7e94d0012a3b001/recruitment/resumes/1762929600000_k3j9x2_meera-iyer-cv.pdf",
    "expiresIn": 300,
    "maxBytes": 15728640
  }
}

Add a candidate

POST/candidates

Adds someone who applied for an open job, as HR does in Recruitment: they start at applied, and HR sees they came through the Soom API. Sending the same email for the same job again is refused, so a retry never adds anyone twice. Needs an app approved for Add candidates.

What to send

FieldWhat it is
jobIdstring, neededThe job they applied for, the id of an open job from List jobs.
namestring, neededTheir full name.
emailstring, neededTheir email.
phonestring, optionalTheir phone number, for HR. No list gives it back.
sourcestring, optionalWhere they came from. website when left out.referrallinkedinnaukriwalk_inagencywebsiteother
resumeKeystring, optionalTheir resume: the resumeKey from Upload a resume, once the file is uploaded.

The answer has

FieldWhat it is
candidateobjectThe new candidate, as List candidates gives them.
candidate.idstringSoom’s id for the candidate.
candidate.jobobject or nullThe opening they are a candidate for.
candidate.job.idstringSoom’s id for it, the id in List jobs.
candidate.job.titlestringThe role.
candidate.namestringTheir name.
candidate.emailstringTheir email.
candidate.sourcestringWhere they came from.referrallinkedinnaukriwalk_inagencywebsiteother
candidate.stagestringWhere they stand.appliedscreeninginterviewofferhiredrejected
candidate.offerStatusstring or nullWhere their offer stands, once one was sent.sentaccepteddeclinedwithdrawn
candidate.hiredEmployeeobject or nullThe employee they became, once hired. Their id is the one in List employees.
candidate.hiredEmployee.idstringSoom’s id for the person, the id in List employees.
candidate.hiredEmployee.employeeCodestring or nullThe code your company gave them.
candidate.hiredEmployee.namestringFirst and last name.
candidate.addedAtdate-time or nullWhen they were added in Soom, in UTC.
candidate.updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
resumeAttachedbooleantrue when their resume was attached.
curl -X POST "https://api.soom.co.in/api/public/v1/candidates" \
  -H "Authorization: Bearer $SOOM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jobId": "6b35f6a7b8c9d0e1f2031425",
    "name": "Meera Iyer",
    "email": "meera.iyer@example.com",
    "phone": "+91 98480 22338",
    "source": "website",
    "resumeKey": "v2/tenants/665f0a11b7e94d0012a3b001/recruitment/resumes/1762929600000_k3j9x2_meera-iyer-cv.pdf"
  }'
201 Answer
{
  "success": true,
  "data": {
    "candidate": {
      "id": "6b35f6a7b8c9d0e1f2031647",
      "job": {
        "id": "6b35f6a7b8c9d0e1f2031425",
        "title": "Senior Developer"
      },
      "name": "Meera Iyer",
      "email": "meera.iyer@example.com",
      "source": "website",
      "stage": "applied",
      "offerStatus": null,
      "hiredEmployee": null,
      "addedAt": "2026-11-12T06:40:00.000Z",
      "updatedAt": "2026-11-12T06:40:00.000Z"
    },
    "resumeAttached": true
  }
}

Clients

The companies your people work for: each client, who of yours works for which, and what you invoiced them. Contacts, addresses, tax numbers and the rate for each person stay in Soom.

List clients

GET/clients

Every client, by name, with how many of your people work for it now. Needs an app approved for Clients.

Filters

FilterWhat it does
statusoptionalOnly clients with this status.activeinactive
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the client, the clientId the other client addresses take.
namestringIts name.
statusstringinactive when you no longer work for it.activeinactive
currencystringWhat it is invoiced in, like INR or AUD.
invoiceCyclestringHow often it is invoiced.monthlyfortnightlyweekly
timezonestring or nullIts time zone, like Australia/Sydney, when set.
activeEmployeesnumberHow many of your people work for it now.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/clients?status=active" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "665f0a11b7e94d0012a3b801",
        "name": "Northwind Retail",
        "status": "active",
        "currency": "AUD",
        "invoiceCycle": "monthly",
        "timezone": "Australia/Sydney",
        "activeEmployees": 6,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List client assignments

GET/client-assignments

Who of yours works for which client, newest first: their role there and from when to when. Needs an app approved for Clients.

Filters

FilterWhat it does
statusoptionalactive (the default) is who works for a client now, ended is who did, all is both.activeendedall
clientIdoptionalOnly this client’s, an id from List clients.
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the assignment.
clientobject or nullThe client.
client.idstringSoom’s id for it, the id in List clients.
client.namestringIts name.
employeeobject or nullWho works for it.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
rolestring or nullTheir role at the client, like Backend Developer.
engagementTypestring or nulldedicated is full time for that client; contract is for a piece of work.dedicatedcontract
startDatedate or nullWhen they started for the client.
endDatedate or nullWhen they stopped, or are due to.
statusstringactive while they work for it.activeended
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/client-assignments?status=active" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b46a7b8c9d0e1f203142536",
        "client": {
          "id": "665f0a11b7e94d0012a3b801",
          "name": "Northwind Retail"
        },
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "role": "Backend Developer",
        "engagementType": "dedicated",
        "startDate": "2025-04-01",
        "endDate": null,
        "status": "active",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List client invoices

GET/client-invoices

What you invoiced your clients, latest period first, with the totals. Each person’s line stays in Soom, since it shows their rate. Needs an app approved for Clients.

Filters

FilterWhat it does
statusoptionalOnly invoices with this status.draftapprovedsentpaidvoid
clientIdoptionalOnly this client’s, an id from List clients.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the invoice.
invoiceNostringIts number.
clientobject or nullWho it is for.
client.idstringSoom’s id for it, the id in List clients.
client.namestringIts name.
periodStartdateFirst day it bills for.
periodEnddateLast day it bills for.
statusstringWhere it stands. void is cancelled.draftapprovedsentpaidvoid
currencystringWhat it is in, like AUD.
subtotalnumberBefore tax.
taxPercentnumberThe tax rate, like 18.
taxAmountnumberThe tax.
totalnumberWhat the client pays.
issueDatedate or nullWhen it was sent.
dueDatedate or nullWhen it is to be paid by.
paidDatedate or nullWhen it was paid.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/client-invoices?status=sent" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b46a7b8c9d0e1f203142647",
        "invoiceNo": "INV-2026-0031",
        "client": {
          "id": "665f0a11b7e94d0012a3b801",
          "name": "Northwind Retail"
        },
        "periodStart": "2026-10-01",
        "periodEnd": "2026-10-31",
        "status": "sent",
        "currency": "AUD",
        "subtotal": 42000,
        "taxPercent": 10,
        "taxAmount": 4200,
        "total": 46200,
        "issueDate": "2026-11-01",
        "dueDate": "2026-11-15",
        "paidDate": null,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Payroll

Each month’s payroll and each person’s payslip totals: gross pay, deductions and net pay. An app reads them only when Soom approved Payroll for it. Salaries, the lines of a payslip, bank details and PAN, UAN, PF and ESI numbers stay in Soom. A company that runs its payroll in Xero gets PAYROLL_IN_XERO.

List payroll runs

GET/payroll-runs

Each month’s payroll, latest first, with its totals and how far it has gone. Its figures are final once it is locked. Needs an app approved for Payroll.

Filters

FilterWhat it does
statusoptionalOnly runs with this status.draftcomputingcomputedlockedfailed
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the run.
monthstringThe month it pays, like 2026-10.
statusstringdraft is made but not worked out yet, computing is being worked out now, computed is worked out but can still change, locked is final, and failed is a work-out that stopped part-way and has to be done again in Soom.draftcomputingcomputedlockedfailed
employeeCountnumberHow many people it pays, leavers’ final settlements included.
grossPaynumberEveryone’s pay before anything is taken off.
deductionsnumberEverything taken off it: PF, ESI, professional tax, TDS, loan instalments and the like.
employerContributionsnumberWhat the company pays on top, like its share of PF and ESI.
netPaynumberWhat is taken home: gross pay less deductions.
paidCountnumberHow many of its payslips are paid.
sentCountnumberHow many people can see their payslip in Soom.
computedAtdate-time or nullWhen its figures were last worked out.
lockedAtdate-time or nullWhen it was locked.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/payroll-runs?status=locked" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b57b8c9d0e1f20314253647",
        "month": "2026-10",
        "status": "locked",
        "employeeCount": 42,
        "grossPay": 2184500,
        "deductions": 196230,
        "employerContributions": 98750,
        "netPay": 1988270,
        "paidCount": 42,
        "sentCount": 42,
        "computedAt": "2026-10-29T10:12:00.000Z",
        "lockedAt": "2026-10-30T06:45:00.000Z",
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

List payslips

GET/payslips

Each person’s payslip totals, latest month first, then by employee code. A payslip is here once it is final: when its month is locked, or a leaver’s final settlement once it is settled. Until then its figures can change. Needs an app approved for Payroll.

Filters

FilterWhat it does
monthoptionalOnly this month’s, like 2026-10.
employeeCodeoptionalOnly this person, by the employee code your company gave them.
updatedSinceoptionalOnly what was added or changed at or after this moment, like 2026-11-12T09:30:00Z. The list then comes oldest change first: keep the last updatedAt you got and send it as updatedSince next time. Leave it out the first time to get everything.
pageoptionalWhich page of the list, from 1.
limitoptionalHow many items a page, up to 200. 25 when left out.

Each item has

FieldWhat it is
idstringSoom’s id for the payslip.
monthstringThe month it pays, the month of a run in List payroll runs.
employeeobject or nullWhose it is.
employee.idstringSoom’s id for the person, the id in List employees.
employee.employeeCodestring or nullThe code your company gave them.
employee.namestringFirst and last name.
kindstringfinal_settlement is a leaver’s last payslip, settled on its own.monthlyfinal_settlement
paidDaysnumberThe days it pays for: the days their salary covered, less loss of pay.
lopDaysnumberDays of loss of pay.
grossPaynumberTheir pay before anything is taken off.
deductionsnumberEverything taken off it: PF, ESI, professional tax, TDS, loan instalments and the like.
employerContributionsnumberWhat the company pays on top, like its share of PF and ESI.
netPaynumberWhat is taken home: gross pay less deductions.
paidOndate or nullThe day it was paid.
sentToEmployeebooleantrue once they can see it in Soom: when it is sent to them, or for a final settlement once it is paid.
updatedAtdate-time or nullWhen it was last added or changed in Soom, in UTC. Send it back as updatedSince.
curl "https://api.soom.co.in/api/public/v1/payslips?month=2026-10" \
  -H "Authorization: Bearer $SOOM_TOKEN"
200 Answer
{
  "success": true,
  "data": {
    "items": [
      {
        "id": "6b57b8c9d0e1f20314253758",
        "month": "2026-10",
        "employee": {
          "id": "665f1c2ab7e94d0012a3c4d5",
          "employeeCode": "EMP0032",
          "name": "Priya Sharma"
        },
        "kind": "monthly",
        "paidDays": 30,
        "lopDays": 1,
        "grossPay": 82258,
        "deductions": 8466,
        "employerContributions": 1800,
        "netPay": 73792,
        "paidOn": "2026-10-31",
        "sentToEmployee": true,
        "updatedAt": "2026-11-12T06:40:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}

Webhooks

Soom can tell your app when something happens, so it needn’t keep asking. Once Soom has approved your app, give it a webhook address in Settings ▸ Integrations ▸ Soom API, which must start with https://, and tick the events it wants. Each event goes out only while your app can read what it is about: employee events need Employees, leave events need Leave, payroll events need Payroll. Soom posts it as JSON, signed with the webhook’s secret, which is shown once, when the address is first saved.

How webhooks work

Each event is a POST to your address with a JSON body. Answer with any status from 200 to 299 within 10 seconds. Anything else, or no answer, and Soom tries again after 5 and 30 minutes, then 2, 6, 12 and 24 hours: 7 tries in all. Soom never follows a redirect.

Events can arrive out of order, and the same event can arrive twice. Use its id to do the work once, and read the list again when the order matters.

Headers

HeaderWhat it is
Soom-Signaturet is when it was signed, in seconds since 1970. v1 is the HMAC-SHA256, in hex, of t, a full stop and the body exactly as it arrived, keyed with your signing secret. Check it, and that t is recent, before trusting the body.
Soom-EventThe event’s type, as in the body.
Soom-DeliveryThis delivery’s id, the same on every try. Settings ▸ Integrations ▸ Soom API lists every delivery and how it went.

The body has

FieldWhat it is
idstringThe event’s id, the same on every try. Do the work for an id once.
typestringWhat happened.employee.addedemployee.updatedemployee.exitedemployee.rejoinedleave.appliedleave.changedleave.approvedleave.rejectedleave.cancelledpayroll.lockedpayroll.unlockedping
createdAtdate-timeWhen it happened.
companyobjectThe company it is about: the one your app belongs to.
company.idstringThe company’s id in Soom, the company_id your app got with its access token.
company.namestringThe company’s name in Soom.
dataobjectWhat it is about, shaped like its item in List employees, List leave or List payroll runs, as it stands when the event goes out.
POST What your app receives
{
  "id": "evt_6b68c9d0e1f2031425364758",
  "type": "leave.approved",
  "createdAt": "2026-11-06T09:41:12.000Z",
  "company": {
    "id": "665f0a11b7e94d0012a3b001",
    "name": "Your Company Pvt Ltd"
  },
  "data": {
    "id": "6af1a2b3c4d5e6f708192a3b",
    "employee": {
      "id": "665f1c2ab7e94d0012a3c4d5",
      "employeeCode": "EMP0032",
      "name": "Priya Sharma"
    },
    "leaveType": {
      "id": "665f0a11b7e94d0012a3b601",
      "name": "Casual Leave"
    },
    "startDate": "2026-11-12",
    "endDate": "2026-11-13",
    "days": 1.5,
    "dayBreakdown": [
      {
        "date": "2026-11-12",
        "portion": "full"
      },
      {
        "date": "2026-11-13",
        "portion": "firstHalf"
      }
    ],
    "status": "approved",
    "appliedAt": "2026-11-05T06:30:00.000Z",
    "updatedAt": "2026-11-12T06:40:00.000Z"
  }
}

Checking the signature

Anyone can post to your address, so check every event before you act on it. Work out the HMAC-SHA256 of t, a full stop and the body exactly as it arrived, before any parsing, keyed with the webhook’s signing secret. It must equal v1, and t must be within five minutes of now.

Soom-Signature: t=1762422072,v1=6c3f0e…

The signing secret is shown once, when the webhook’s address is first saved. If it’s lost, make a new one in Settings ▸ Integrations ▸ Soom API; the old one stops working at once.

import crypto from "node:crypto";
import express from "express";

function isFromSoom(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

const app = express();
app.post("/soom-events", express.raw({ type: "application/json" }), (req, res) => {
  const ok = isFromSoom(req.body.toString("utf8"), req.get("Soom-Signature") || "", process.env.SOOM_WEBHOOK_SECRET);
  if (!ok) return res.sendStatus(400);
  const event = JSON.parse(req.body);
  console.log(event.type, event.id);
  res.sendStatus(200);
});

Events

EventWhen it is sent
employee.addedneeds EmployeesSomeone joined the company in Soom: added by HR or through the API, hired in Recruitment, or brought in from Xero. Soom has emailed them their invite. Its data is an item of List employees.
employee.updatedneeds EmployeesSomething List employees shows about someone changed: their name, email, code, job title, kind of employment, status, department, designation, branch, manager, or the day they joined or left. Joining, leaving and rejoining also send their own event. Its data is an item of List employees.
employee.exitedneeds EmployeesSomeone’s leaving was recorded; exitDate is their last day. Its data is an item of List employees.
employee.rejoinedneeds EmployeesSomeone who had left is back, on the same profile, with a new hireDate. Its data is an item of List employees.
leave.appliedneeds LeaveLeave was applied for and waits on its approvers. Someone who approves their own leave may already show approved. Its data is an item of List leave.
leave.changedneeds LeaveA leave’s dates or days changed: by the person while it waited, or by HR. Its data is an item of List leave.
leave.approvedneeds LeaveA leave was approved by its last approver or by HR, or HR added it already approved. Its data is an item of List leave.
leave.rejectedneeds LeaveA leave was turned down. Its data is an item of List leave.
leave.cancelledneeds LeaveA leave was cancelled, by the person or by HR, or put aside for another approved on the same days. Its data is an item of List leave.
payroll.lockedneeds PayrollA month’s payroll was locked: its figures are final, and its payslips are in List payslips. Its data is an item of List payroll runs.
payroll.unlockedneeds PayrollA locked month was opened again, so its figures can change until it is locked once more. Its data is an item of List payroll runs.
pinga testThe test Settings ▸ Integrations ▸ Soom API sends when you press Send a test event. Its data is empty.