JUST-IN API
An official REST API for connecting external systems to JUST-IN — real-time read access to attendance and organizational data, delivered securely and with full auditability.
Core security principle: every API token belongs to the company itself — not to a user within it. The system identifies the company directly from the token, so there is no way for an external system to request (accidentally or intentionally) another company's data. Full details in Security & Privacy.
Quickstart
Three steps from your first login to your first JSON response:
1. Create an API Token
Log in to JUST-IN as a company admin → Company Settings → API tab → Create Token. Give it a descriptive name (e.g. the name of the connecting system) and select the required abilities. The full token is shown only once — store it somewhere safe.
2. Send your first request
curl -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/json" \ https://just-in.co.il/api/v1/employees
const res = await fetch('https://just-in.co.il/api/v1/employees', { headers: { 'Authorization': `Bearer ${TOKEN}`, 'Accept': 'application/json' } }); const json = await res.json(); console.log(json.data);
import requests res = requests.get( "https://just-in.co.il/api/v1/employees", headers={"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"} ) print(res.json()["data"])
3. Read the response
Every successful response is wrapped in a consistent envelope with data, and for list endpoints also meta and links for pagination. Full details further down this page.
Authentication
The API uses Bearer Tokens. Every request must include the following headers:
| Header | Value | Required |
|---|---|---|
Authorization | Bearer <token> | Required |
Accept | application/json | Required |
There is no endpoint for creating tokens via the API itself. Tokens are created and revoked exclusively through the admin interface (Company Settings → API) by an authenticated company admin — this is an intentional safeguard.
A request with no token, an invalid token, or a revoked token immediately returns 401 Unauthorized.
Abilities
Every token is granted specific abilities when it's created. A request to an endpoint the token isn't authorized for returns 403 Forbidden — even if the token itself is valid.
| Ability | Grants access to |
|---|---|
| attendance:read | GET /attendance |
| employees:read | GET /employees, GET /employees/:id, GET /departments, GET /branches |
Versioning
The API is versioned starting from v1 — it appears in the base URL itself (/api/v1/...). Backward-incompatible changes (removing a field, changing the meaning of a parameter) will appear under a new version (/api/v2) and will never break an existing integration against v1. Adding a new field to a response is not considered a breaking change.
Rate Limits
Every token is limited to 60 requests per minute. The limit applies per token, not per IP address — multiple external systems can operate from the same network address without affecting each other's quota.
| Response Header | Meaning |
|---|---|
X-RateLimit-Limit | Number of requests allowed in the current window (60) |
X-RateLimit-Remaining | Requests remaining in the current window |
Retry-After | Present only on a 429 response — seconds to wait before retrying |
Exceeding the quota returns 429 Too Many Requests. We recommend implementing exponential backoff rather than retrying immediately — see Best Practices.
Pagination
All list-returning endpoints (attendance, employees, departments, branches) are automatically paginated. The default is 50 records per page, controllable via the per_page parameter (maximum 200).
{
"data": [ /* array of records */ ],
"links": {
"first": "https://just-in.co.il/api/v1/employees?page=1",
"last": "https://just-in.co.il/api/v1/employees?page=3",
"prev": null,
"next": "https://just-in.co.il/api/v1/employees?page=2"
},
"meta": {
"current_page": 1,
"last_page": 3,
"per_page": 50,
"total": 142
}
}
Errors
Standard HTTP status codes. Errors always return in the shape {"message": "..."} (and sometimes a detailed errors object for 422s).
| Code | Name | Meaning |
|---|---|---|
| 200 | OK | The request succeeded |
| 401 | Unauthorized | Token missing, invalid, or revoked |
| 403 | Forbidden | The token is valid but lacks the ability required for this endpoint |
| 404 | Not Found | The record doesn't exist, or belongs to another company (intentionally indistinguishable) |
| 422 | Unprocessable Entity | An invalid parameter was supplied (e.g. a malformed date) |
| 429 | Too Many Requests | Rate limit exceeded |
{
"message": "The date from field must be a valid date.",
"errors": {
"date_from": ["The date from field must be a valid date."]
}
}
Attendance Records
Returns a list of attendance records (one working day per employee) with rich filtering options.
Query Parameters
| Name | Type | Description |
|---|---|---|
date_from | date | Start date, inclusive (YYYY-MM-DD) |
date_to | date | End date, inclusive (YYYY-MM-DD) |
employee_id | integer | Filter by a specific employee |
department_id | integer | Filter by the employee's department |
branch_id | integer | Filter by the employee's branch |
status | string | Filter by record status |
per_page | integer | Results per page — default 50, maximum 200 |
Example Request
curl -G https://just-in.co.il/api/v1/attendance \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/json" \ --data-urlencode "date_from=2026-08-01" \ --data-urlencode "date_to=2026-08-31"
const params = new URLSearchParams({ date_from: '2026-08-01', date_to: '2026-08-31' }); const res = await fetch(`https://just-in.co.il/api/v1/attendance?${params}`, { headers: { 'Authorization': `Bearer ${TOKEN}`, 'Accept': 'application/json' } });
res = requests.get( "https://just-in.co.il/api/v1/attendance", headers={"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"}, params={"date_from": "2026-08-01", "date_to": "2026-08-31"} )
Record Shape
Example Response — 200 OK
{
"data": [
{
"id": 4821,
"employee_id": 45,
"date": "2026-08-15",
"check_in": "2026-08-15T08:02:00+03:00",
"check_out": "2026-08-15T17:05:00+03:00",
"status": "approved",
"absence_type": null,
"is_approved": true
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": null },
"meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}
Employee List
Returns the company's employee list, with basic contact details and organizational structure. Does not include salary or national ID number data — see Security & Privacy.
Query Parameters
| Name | Type | Description |
|---|---|---|
department_id | integer | Filter by department |
branch_id | integer | Filter by branch |
status | string | Filter by employee status |
per_page | integer | Results per page — default 50, maximum 200 |
Example Request
curl https://just-in.co.il/api/v1/employees \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/json"
const res = await fetch('https://just-in.co.il/api/v1/employees', { headers: { 'Authorization': `Bearer ${TOKEN}`, 'Accept': 'application/json' } });
res = requests.get( "https://just-in.co.il/api/v1/employees", headers={"Authorization": f"Bearer {TOKEN}", "Accept": "application/json"} )
Record Shape
Salary fields, vacation/sick balances, and national ID number are never exposed by this endpoint, intentionally — even with full employees:read access.
Example Response — 200 OK
{
"data": [
{
"id": 45,
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "0501234567",
"role": "employee",
"status": "active",
"department": { "id": 3, "name": "Sales" },
"branch": { "id": 1, "name": "Tel Aviv Branch" }
}
],
"meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}
Single Employee
Returns a single employee by ID. Same data shape as a record in the /employees list, wrapped under data with no pagination.
curl https://just-in.co.il/api/v1/employees/45 \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: application/json"
Attempting to access an employee ID that belongs to another company returns 404 Not Found — not 403 — so as not to reveal even the existence of the record to anyone who shouldn't know about it.
Departments
List of all departments in the company, unpaginated (flat list).
{
"data": [
{ "id": 1, "name": "Sales" },
{ "id": 2, "name": "Development" }
]
}
Branches
List of all branches in the company, unpaginated.
{
"data": [
{ "id": 1, "name": "Tel Aviv Branch", "address": "3 HaBarzel St." }
]
}
Security & Privacy
The API was designed around one principle: complete isolation between companies. Here's how it's actually enforced, not just stated:
Best Practices
Handle 429s with backoff
If you receive a 429, wait according to the Retry-After header before retrying. Don't send repeated requests immediately — that only extends your effective block time.
Loop through all pages
Don't assume all results arrive on a single page. Track meta.current_page against meta.last_page, or simply keep following links.next until it's null.
Keep tokens secret
Don't embed tokens in visible client-side (frontend) code, and don't commit them to a code repository (git). Treat them like a password — if a token leaks, revoke it immediately through the interface and create a new one.
Use a separate token per system
Instead of one token for everything, create a dedicated token for each external system with only the minimum abilities it actually needs. That way revoking access for one system doesn't affect the others, and the audit log stays clear.
Changelog
Initial release of the public API.
Added /attendance, /employees, /employees/:id, /departments, /branches. Company-scoped token authentication, precise abilities, rate limiting, and audit logging.