JUST-IN API
v1
Base URL: https://just-in.co.il/api/v1

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.

v1
Current API version
READ ONLY
All endpoints are read-only
60/min
Rate limit per token
JSON
All responses
🔒

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:

HeaderValueRequired
AuthorizationBearer <token>Required
Acceptapplication/jsonRequired
⚠️

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.

AbilityGrants access to
attendance:readGET /attendance
employees:readGET /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 HeaderMeaning
X-RateLimit-LimitNumber of requests allowed in the current window (60)
X-RateLimit-RemainingRequests remaining in the current window
Retry-AfterPresent 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).

Response shape — pagination
{
  "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).

CodeNameMeaning
200OKThe request succeeded
401UnauthorizedToken missing, invalid, or revoked
403ForbiddenThe token is valid but lacks the ability required for this endpoint
404Not FoundThe record doesn't exist, or belongs to another company (intentionally indistinguishable)
422Unprocessable EntityAn invalid parameter was supplied (e.g. a malformed date)
429Too Many RequestsRate limit exceeded
Example — error response
{
  "message": "The date from field must be a valid date.",
  "errors": {
    "date_from": ["The date from field must be a valid date."]
  }
}

Attendance Records

GET /attendance attendance:read

Returns a list of attendance records (one working day per employee) with rich filtering options.

Query Parameters

NameTypeDescription
date_fromdateStart date, inclusive (YYYY-MM-DD)
date_todateEnd date, inclusive (YYYY-MM-DD)
employee_idintegerFilter by a specific employee
department_idintegerFilter by the employee's department
branch_idintegerFilter by the employee's branch
statusstringFilter by record status
per_pageintegerResults 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

id
integer
Unique identifier of the attendance record
employee_id
integer
Employee identifier (matches the id returned from /employees)
date
string (date)
Work day date, YYYY-MM-DD
check_in
string (ISO 8601) | null
Check-in time, with timezone
check_out
string (ISO 8601) | null
Check-out time, with timezone
status
string
Record status
absence_type
string | null
Absence type, if applicable
is_approved
boolean
Whether the record has been approved by a manager

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

GET /employees employees:read

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

NameTypeDescription
department_idintegerFilter by department
branch_idintegerFilter by branch
statusstringFilter by employee status
per_pageintegerResults 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

id
integer
name
string
email
string
phone
string | null
role
string
status
string
department
object | null
{ id, name }
branch
object | null
{ id, name }

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

GET /employees/{id} employees:read

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

GET /departments employees:read

List of all departments in the company, unpaginated (flat list).

id
integer
name
string
{
  "data": [
    { "id": 1, "name": "Sales" },
    { "id": 2, "name": "Development" }
  ]
}

Branches

GET /branches employees:read

List of all branches in the company, unpaginated.

id
integer
name
string
address
string | null
{
  "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:

Token-based isolation
Every token belongs to the company itself, not to a user within it. The company's identity is derived directly from the token on every request — there is no company_id parameter accepted from the client at all.
Read-only
Every endpoint in the current API is GET only. There is no way to modify data through the API.
Precise abilities
Every token is limited to exactly the data it was approved for (attendance:read / employees:read) — there is no "all access" ability.
No salary data
National ID number, salary, and vacation/sick balances are never exposed by the API, on any endpoint, under any ability.
Audit log
Every API call is logged — who, when, which endpoint, from which IP address, and which status code was returned.
Instant revocation
Revoking a token through the interface blocks access immediately — no delay or caching.

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

v1.14.0

Initial release of the public API.

Added /attendance, /employees, /employees/:id, /departments, /branches. Company-scoped token authentication, precise abilities, rate limiting, and audit logging.