Summary and recommendation
Lightspeed Retail (R-Series) exposes a REST API at https://api.lightspeedapp.com/API/V3/Account/{accountID} authenticated via OAuth 2.0. Access tokens expire after 30 minutes; proactive refresh_token rotation is required to avoid mid-operation failures. The accountID is not the OAuth client_id - it must be resolved separately via GET /API/V3/Account.json immediately after token exchange, and is required for every subsequent call.
This documentation covers R-Series only. Retail X-Series uses a different base URL (https://api.lightspeedhq.com) with a distinct endpoint structure and less publicly documented employee management surface. Restaurant and eCom APIs are separate systems entirely.
Any identity graph built on this API must treat each product line as an independent identity domain with no cross-product linkage.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0
Setup steps
- Register an application at https://cloud.lightspeedapp.com/oauth/register.php to obtain a client_id and client_secret.
- Redirect the merchant to the authorization URL: https://cloud.lightspeedapp.com/oauth/authorize.php?response_type=code&client_id={client_id}&scope={scopes}
- Exchange the returned authorization code for an access_token and refresh_token via POST to https://cloud.lightspeedapp.com/oauth/access_token.php.
- Include the access token in all API requests as an Authorization header: 'Authorization: Bearer {access_token}'.
- Use the refresh_token to obtain a new access_token before expiry (tokens expire in 30 minutes).
Required scopes
| Scope | Description | Required for |
|---|---|---|
| employee:all | Full read/write access to employee (user) records. | Creating, reading, updating, and deleting employee records. |
| employee:read | Read-only access to employee records. | Listing and retrieving employee details. |
| systemuserroles:all | Access to system user roles. | Reading and assigning roles to employees. |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| employeeID | integer | Unique identifier for the employee. | auto-assigned | read-only | Used as path parameter for individual employee operations. |
| firstName | string | Employee's first name. | required | optional | |
| lastName | string | Employee's last name. | required | optional | |
| username | string | Login username for the employee. | required | optional | Must be unique within the account. |
| password | string | Employee login password. | optional | optional | Write-only; never returned in responses. |
| pin | string | POS PIN for the employee. | optional | optional | Write-only; used for POS terminal login. |
| string | Employee's email address. | optional | optional | ||
| role | object | Role assigned to the employee (e.g., Manager, Cashier). | optional | optional | Contains roleID and roleName. Roles are managed separately via /SystemUserRole endpoints. |
| employeeRoleID | integer | ID of the role assigned to the employee. | optional | optional | |
| archived | boolean | Whether the employee record is archived (soft-deleted). | optional | optional | Set to true to deactivate without deleting. |
| timeStamp | datetime | Last modification timestamp (ISO 8601). | auto-assigned | auto-assigned | Useful for polling-based sync. |
| contactID | integer | Associated contact record ID. | optional | optional | Links employee to a Contact object for additional details. |
| hourlyWage | decimal | Employee's hourly wage. | optional | optional | |
| doNotExport | boolean | Flag to exclude employee from exports. | optional | optional |
Core endpoints
List all employees
- Method: GET
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/Employee.json - Watch out for: Returns up to 100 records. Use ?offset=100 to paginate. Archived employees are included unless filtered with ?archived=false.
Request example
GET /API/V3/Account/12345/Employee.json
Authorization: Bearer {access_token}
Response example
{
"@attributes": {"count":"2","offset":"0"},
"Employee": [
{"employeeID":"1","firstName":"Jane","lastName":"Doe","username":"jdoe","archived":"false"}
]
}
Get single employee
- Method: GET
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/Employee/{employeeID}.json - Watch out for: Password and PIN fields are never returned in GET responses.
Request example
GET /API/V3/Account/12345/Employee/1.json
Authorization: Bearer {access_token}
Response example
{
"Employee": {
"employeeID":"1",
"firstName":"Jane",
"lastName":"Doe",
"username":"jdoe",
"email":"jane@example.com",
"archived":"false"
}
}
Create employee
- Method: POST
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/Employee.json - Watch out for: Username must be unique. Duplicate usernames return a 409 conflict. Password is write-only and not echoed back.
Request example
POST /API/V3/Account/12345/Employee.json
Content-Type: application/json
{"Employee":{"firstName":"John","lastName":"Smith","username":"jsmith","password":"secret"}}
Response example
{
"Employee": {
"employeeID":"42",
"firstName":"John",
"lastName":"Smith",
"username":"jsmith",
"archived":"false"
}
}
Update employee
- Method: PUT
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/Employee/{employeeID}.json - Watch out for: Uses full PUT semantics on the Lightspeed side; omitting fields may reset them. Send all desired field values.
Request example
PUT /API/V3/Account/12345/Employee/42.json
Content-Type: application/json
{"Employee":{"email":"john@example.com","employeeRoleID":"3"}}
Response example
{
"Employee": {
"employeeID":"42",
"firstName":"John",
"email":"john@example.com",
"archived":"false"
}
}
Archive (deactivate) employee
- Method: PUT
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/Employee/{employeeID}.json - Watch out for: Lightspeed does not expose a DELETE endpoint for employees. Archiving (archived=true) is the only supported deactivation method.
Request example
PUT /API/V3/Account/12345/Employee/42.json
Content-Type: application/json
{"Employee":{"archived":"true"}}
Response example
{
"Employee": {
"employeeID":"42",
"archived":"true"
}
}
List employee roles
- Method: GET
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/EmployeeRole.json - Watch out for: Role IDs are account-specific; do not assume consistent IDs across different Lightspeed accounts.
Request example
GET /API/V3/Account/12345/EmployeeRole.json
Authorization: Bearer {access_token}
Response example
{
"EmployeeRole": [
{"employeeRoleID":"1","name":"Manager"},
{"employeeRoleID":"2","name":"Cashier"}
]
}
Search employees by modified timestamp
- Method: GET
- URL:
https://api.lightspeedapp.com/API/V3/Account/{accountID}/Employee.json?timeStamp=%3E%2C{ISO8601_datetime} - Watch out for: The timeStamp filter uses URL-encoded operators (e.g., %3E%2C for >,). This is the recommended approach for incremental sync polling.
Request example
GET /API/V3/Account/12345/Employee.json?timeStamp=%3E%2C2024-01-01T00:00:00+00:00
Authorization: Bearer {access_token}
Response example
{
"@attributes": {"count":"3"},
"Employee": [
{"employeeID":"5","firstName":"Alice","timeStamp":"2024-06-01T10:00:00+00:00"}
]
}
Get account info (resolve accountID)
- Method: GET
- URL:
https://api.lightspeedapp.com/API/V3/Account.json - Watch out for: The accountID is required for all other API calls. Retrieve it first after OAuth token exchange.
Request example
GET /API/V3/Account.json
Authorization: Bearer {access_token}
Response example
{
"Account": {
"accountID":"12345",
"name":"My Store",
"timeZone":"America/New_York"
}
}
Rate limits, pagination, and events
- Rate limits: Lightspeed Retail API uses a leaky-bucket rate limiting model. Each account has a bucket with a maximum capacity of 60 units. Each API request costs 1 unit. The bucket refills at 1 unit per second. If the bucket is full, requests return HTTP 429.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: Response headers include X-LS-API-Bucket-Level (current bucket usage) and X-LS-API-Drip-Rate (refill rate). When HTTP 429 is returned, back off and retry after bucket drains. No explicit Retry-After header is documented.
- Pagination method: offset
- Default page size: 100
- Max page size: 100
- Pagination pointer: offset
| Plan | Limit | Concurrent |
|---|---|---|
| All plans (Retail R-Series) | 60 requests burst; refills at 1 req/sec | 1 |
- Webhooks available: No
- Webhook notes: Lightspeed Retail (R-Series) does not offer native webhooks for employee/user events. The Lightspeed eCom (C-Series) API has webhooks but they cover commerce events (orders, products), not user management.
- Alternative event strategy: Poll the Employee endpoint using the timeStamp filter (timeStamp>={last_sync_time}) to detect changes incrementally.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Enterprise
- Endpoint: Not documented
Limitations:
- No SCIM 2.0 endpoint is documented in official Lightspeed developer documentation for any product line (Retail, Restaurant, or eCom).
- SSO is available on Retail X-Series plans but SCIM provisioning is not documented as a supported feature.
- Enterprise customers should contact Lightspeed sales directly to inquire about any unpublished SCIM or directory sync capabilities.
Common scenarios
Three primary automation scenarios are supported by the Employee API. For provisioning, POST to /API/V3/Account/{accountID}/Employee.
json with firstName, lastName, username, password, email, and employeeRoleID - role IDs must be fetched first from /EmployeeRole. json and are account-specific, not portable across accounts.
Username uniqueness is enforced at the account level; handle 409 conflicts explicitly.
For incremental sync, poll /Employee.json with a timeStamp filter (timeStamp>={last_sync_ts}) to retrieve only modified records. Normalize timestamps to UTC before comparison - the API is account-timezone-aware and DST offsets can cause missed records. For offboarding, there is no DELETE endpoint; set archived=true via PUT. Note that archiving does not revoke active POS sessions or OAuth tokens - terminal logout must be coordinated separately with POS administrators.
All API responses serialize numeric IDs as strings (e.g., employeeID returns as '42', not 42). Cast types explicitly in consuming code. Pagination is offset-based with a hard maximum of 100 records per page; cursor-based pagination is not available.
Provision a new employee after HR onboarding
- Authenticate via OAuth 2.0 and retrieve the accountID from GET /API/V3/Account.json.
- Fetch available roles via GET /API/V3/Account/{accountID}/EmployeeRole.json to map the correct employeeRoleID.
- POST to /API/V3/Account/{accountID}/Employee.json with firstName, lastName, username, password, email, and employeeRoleID.
- Store the returned employeeID in your HR system for future updates.
Watch out for: Username uniqueness is enforced at the account level. Pre-check or handle 409 conflicts gracefully.
Incremental sync of employee changes
- Store the timestamp of the last successful sync.
- Poll GET /API/V3/Account/{accountID}/Employee.json?timeStamp=%3E%2C{last_sync_ts} to retrieve only modified records.
- Process each returned employee: update your directory if changed, flag archived=true records as deactivated.
- Update the stored last_sync_ts to the current time after successful processing.
- Respect the rate limit bucket; add a 1-second delay between paginated requests if bucket level is high.
Watch out for: Timestamps are account-timezone-aware. Normalize to UTC before comparison to avoid DST-related missed records.
Deactivate an employee upon offboarding
- Look up the employee by username using GET /API/V3/Account/{accountID}/Employee.json?username={username} to retrieve their employeeID.
- Send PUT to /API/V3/Account/{accountID}/Employee/{employeeID}.json with body {"Employee":{"archived":"true"}} to deactivate.
- Optionally clear the password field by sending a PUT with a new random password to prevent any residual access.
Watch out for: Archiving does not revoke active OAuth tokens or POS sessions. Coordinate with POS administrators to ensure the employee is logged out of all terminals.
Why building this yourself is a trap
The rate limit model is a leaky-bucket with a 60-unit burst capacity and a 1 req/sec refill rate, scoped per account - not per token. Multiple integrations or services authenticating against the same Lightspeed account share the same 60-unit bucket and will contend with each other under load.
Response headers X-LS-API-Bucket-Level and X-LS-API-Drip-Rate expose current state; there is no Retry-After header on 429 responses, so back-off logic must be implemented manually.
No SCIM 2.0 endpoint is documented for any Lightspeed product line. SSO is available on Retail X-Series but SCIM provisioning is not documented as a supported feature. Any identity graph integration must rely entirely on the REST Employee API with polling-based sync, as native webhooks for employee events are not available in R-Series.
The eCom webhook system covers commerce events only and cannot substitute for user lifecycle signals.
Automate Lightspeed workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.