API

POS / Station integration

Connect a shared cash register using employee selection and existing Curio Station PINs.

Use this flow when several employees share a cash register or other POS terminal. It reuses Curio Station’s existing company access, employee PINs and punch endpoints. You do not need a separate PIN system.

The terminal connects to the company, displays employee names, verifies the selected employee’s PIN with Curio, then sends an explicit clock-in or clock-out action. Employee GUIDs are handled by your integration; employees select their name and never have to type a GUID.

API base URL: https://pro.curiotime.com/api/v1

Choose the right flow

Integration Authentication Punch endpoint
Shared POS terminal with employee PINs Registered device token + PIN-authenticated employee token POST /kiosk/punch
Trusted server-side integration acting for the company Company owner/manager JWT POST /companies/{companyId}/punches

Do not mix these tokens. A device token is not a JWT access token, and an employee token is not a refresh token. The company punch endpoint does not accept a PIN. This guide covers the first flow only.

Before you start

  • Arrange integration access and a test company with Curio Time at info@curiotime.com.
  • An active company owner, manager or accountant with web-login access provisions the terminal.
  • Employees must be active, have Curio Station access enabled and have a four-digit numeric Station PIN.
  • Keep provisioning credentials and device tokens in a protected backend or secure terminal storage. Never embed them in public browser JavaScript, source control, URLs or logs. Do not retain employee PINs.

Examples below contain placeholders, not working credentials. Successful responses use the normal { "success": true, "data": …, "errors": null } envelope. Responses may include additional fields.

1. Register the terminal for the company

An authorized company administrator enters their email and password during terminal setup:

POST https://pro.curiotime.com/api/v1/kiosk/register-device
Content-Type: application/json

{
  "email": "terminal-admin@company.example",
  "password": "YOUR_ADMIN_PASSWORD"
}
{
  "success": true,
  "data": {
    "deviceToken": "YOUR_DEVICE_TOKEN",
    "companyId": "COMPANY_GUID",
    "companyName": "Example Company"
  },
  "errors": null
}

Store the returned device token securely and use the returned companyId. This is device provisioning, not employee sign-in. Do not register a new device for every employee punch. The registration response does not include a refresh token; the JWT /auth/refresh endpoint does not refresh device tokens.

2. Display the employee list

GET https://pro.curiotime.com/api/v1/users/punch-list?companyId=COMPANY_GUID
Authorization: Bearer YOUR_DEVICE_TOKEN

Example row from data:

{
  "id": "EMPLOYEE_GUID",
  "name": "Alex Example",
  "departmentIds": [],
  "isClockedIn": false,
  "openEntryStartAtUtc": null,
  "requiresProjectSelection": false,
  "requireGpsOnClockIn": false
}

Display name and keep id as the selected employee’s employeeId. The list is for selection and status; it does not authenticate an employee and must never be treated as permission to punch. PINs are not returned. An employee appearing in the list must still pass the Station access checks in the next step.

Use the company ID returned during provisioning, not a company ID typed by the employee. Refresh the list when returning to the employee selector so clock-in status does not become stale.

3. Verify the employee’s existing Station PIN

After the employee selects their name, ask for their PIN and send it to Curio over HTTPS:

POST https://pro.curiotime.com/api/v1/kiosk/auth-pin
Authorization: Bearer YOUR_DEVICE_TOKEN
Content-Type: application/json

{
  "employeeId": "EMPLOYEE_GUID",
  "pin": "1234"
}

1234 is an example only. Keep the PIN as a string to preserve leading zeros. Always send the selected employeeId in this integration. Curio verifies the PIN, active status, company membership and Station access.

{
  "success": true,
  "data": {
    "userId": "EMPLOYEE_GUID",
    "displayName": "Alex Example",
    "isClockedIn": false,
    "employeeToken": "YOUR_EMPLOYEE_TOKEN",
    "expiresAtUtc": "2026-10-22T08:00:00Z",
    "role": "employee"
  },
  "errors": null
}

The response field userId identifies the authenticated employee. Use that same value as employeeId in the punch request. expiresAtUtc is authoritative; do not hard-code a session lifetime. A new PIN authentication is required after expiry. Ask the employee to try again after a rejected PIN; never automatically retry guessed PINs.

4. Clock the authenticated employee in or out

Both tokens are required: the device token in Authorization, and the employee token in X-Employee-Token.

Clock in

POST https://pro.curiotime.com/api/v1/kiosk/punch
Authorization: Bearer YOUR_DEVICE_TOKEN
X-Employee-Token: YOUR_EMPLOYEE_TOKEN
Content-Type: application/json

{
  "employeeId": "EMPLOYEE_GUID",
  "action": "start"
}

Clock out

POST https://pro.curiotime.com/api/v1/kiosk/punch
Authorization: Bearer YOUR_DEVICE_TOKEN
X-Employee-Token: YOUR_EMPLOYEE_TOKEN
Content-Type: application/json

{
  "employeeId": "EMPLOYEE_GUID",
  "action": "stop"
}

The employee ID must match the authenticated employee session. Curio also verifies that the employee session belongs to this device. Optional GPS fields on this endpoint are lat and lng; they are not the latitude and longitude fields of the JWT partner punch endpoint.

Example response:

{
  "success": true,
  "data": {
    "message": "Clocked in.",
    "isClockedIn": true,
    "timeEntryId": "TIME_ENTRY_GUID",
    "punchedAtUtc": "2026-10-21T08:00:00Z"
  },
  "errors": null
}

Show a success confirmation only after a successful API response. Then discard the employee token from the active POS session and return to employee selection. The next employee must authenticate with their own PIN; do not reuse another employee’s session.

Errors and connection failures

HTTP status What to do
400 Check required fields, PIN format and action. Display the returned error.
401 Device credentials, PIN or employee session are invalid, expired or no longer eligible. Re-authenticate the employee; if device access is invalid, ask the administrator to provision access again.
403 Check employee/device/company scope. Do not substitute another company or employee ID.
404 The selected employee or another required resource could not be found. Refresh employee selection.
409 The requested start/stop conflicts with the current punch state. Refresh status before asking the employee what to do.
429 Stop retries and back off before retrying.

Do not blindly repeat a punch after a timeout. Refresh the employee list or authenticate again to check isClockedIn before deciding whether another action is needed. These endpoints do not accept an idempotency key.

Company-specific requirements

The examples cover basic clock in/out. The Station punch request does not accept project, subproject or location-selection IDs. If the employee list reports requiresProjectSelection, or the company requires punch photos or another workflow, agree the complete flow with Curio Time before rollout. Do not silently bypass company requirements. Photo-upload grants may appear in a punch response; a grant does not mean a photo was uploaded.

Verify your integration

In the agreed test company, confirm correct employee selection, leading-zero PINs, rejected PINs, successful start and stop, duplicate-action handling, employee-session expiry and a return to the selector after each operation. Confirm that one employee’s token cannot punch another employee and that a session from another device is rejected. Do not test punches against real payroll records.

See OpenAPI for machine-readable operations and Errors & response format for the shared response envelope. Only the operations documented here are covered by this POS guide; other Station self-service or administration routes are not included.