-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
All API endpoints return JSON. Authenticated endpoints require a valid sl_session cookie. Unauthorized requests return 401. Admin-only endpoints return 403 for non-admin users.
Check the current auth state.
Response:
-
{"status": "setup"}— no users exist, first-run setup needed -
{"status": "login"}— users exist but no valid session -
{"status": "authenticated", "user": {...}}— logged in; includes user object
Create the first admin account. Only works when no users exist.
Body:
{
"username": "admin",
"display_name": "Admin User",
"password": "secret",
"color": "#f5a623"
}-
username— 2–50 chars, alphanumeric + underscores (required) -
display_name— 1–100 chars (required) -
password— 4–200 chars (required) -
color— hex color, optional (auto-assigned if omitted)
Response: 200 with user object. Sets sl_session cookie.
Errors: 400 if setup already completed.
Body:
{
"username": "admin",
"password": "secret"
}Response: 200 with user object. Sets sl_session cookie (30-day expiry).
Errors: 401 invalid credentials.
Clears the session cookie and deletes the server-side session record.
Response: {"success": true}
Create a new regular user. Same body format as /api/auth/setup.
Response: 200 with new user object.
Errors: 409 if username already taken. 403 if not admin.
List all users. Returns id, username, display_name, is_admin, color, created_at.
Update a user. Admins can update any user; regular users can only update themselves.
Body (all fields optional):
{
"display_name": "New Name",
"color": "#3ecf8e",
"password": "newpass",
"is_admin": true
}is_admin can only be changed by an admin.
Permanently delete a user and all their sessions.
Errors: 400 if trying to delete yourself.
Create a new shift.
Body:
{
"date": "2026-03-10",
"hourly_rate": 15.00,
"hours_worked": 8.0,
"tip_mode": "total",
"tip_input": 120.00,
"notes": "Busy Saturday night",
"job_id": 1
}-
date—YYYY-MM-DDformat (required) -
hourly_rate— non-negative number (required) -
hours_worked— non-negative number (required) -
tip_mode—"total"or"per_hour"(required) -
tip_input— non-negative number (required) -
notes— string, optional (default"") -
job_id— integer or null, optional (defaultnull)
Server computes: total_tips, wage_total, grand_total.
Response: {"id": 1, "total_tips": 120, "wage_total": 120, "grand_total": 240}
List shifts. Soft-deleted shifts are excluded.
Query params:
-
from— start date (YYYY-MM-DD) -
to— end date (YYYY-MM-DD) -
user_id— filter by user
Returns shifts joined with user display name/color and job name/color. Ordered by date DESC.
Update a shift. Same body as POST. You must own the shift or be an admin.
Soft-delete a shift (sets deleted_at timestamp). You must own it or be an admin.
Restore a soft-deleted shift (clears deleted_at).
List all jobs, ordered by name ascending.
Body:
{
"name": "Restaurant ABC",
"default_rate": 15.00,
"color": "#3ecf8e",
"overtime_threshold": 40,
"overtime_multiplier": 1.5
}All fields except name are optional with defaults.
Update a job. Same body as POST.
Archive a job (sets archived = 1). Does not delete associated shifts.
List all templates with joined job name.
Body:
{
"name": "Weeknight Shift",
"job_id": 1,
"hourly_rate": 15.00,
"hours_worked": 6.0,
"tip_mode": "total",
"tip_input": 80.00,
"notes": ""
}Permanently delete a template.
List all goals, newest first.
Body:
{
"period": "weekly",
"target_amount": 1000.00,
"active": true
}-
period—"weekly"or"monthly" -
target_amount— non-negative number -
active— boolean, optional (defaulttrue)
Setting active: true deactivates any other goal with the same period.
Update a goal. Same body as POST.
Permanently delete a goal.
Returns earnings aggregations for multiple periods.
Query params: user_id — optional filter.
Response keys: this_week, last_week, biweekly, this_month, last_month, ytd, all_time
Each contains: shifts, total_hours, total_wages, total_tips, grand_total, avg_shift, avg_tips_per_hour.
Aggregated time-series data for charts.
Query params: period — "week" (12 weeks), "month" (12 months, default), or "year" (3 years).
Returns array of: period, shifts, total_hours, total_wages, total_tips, grand_total, tips_per_hour, effective_rate.
Current-week overtime status.
Response: total_hours, threshold, multiplier, regular_hours, overtime_hours, is_overtime.
Estimated tax liability.
Query params: period — "month" or "ytd" (default).
Response: wages, tips, total, est_wage_tax, est_tip_tax, est_total_tax, wage_rate, tip_rate.
Average effective hourly rate by day of week (0=Sun through 6=Sat).
Top and bottom shifts by grand total.
Query params: count — number of results per list (default 5).
Response: {"best": [...], "worst": [...]}
Monthly tip percentage trend.
Returns array of: period, total_tips, grand_total, tip_pct.
Download a styled PDF report.
Query params:
-
from,to— date range (optional; omit for all-time) -
label— custom title for the report header
Returns application/pdf.
Download shifts as CSV.
Query params: from, to — date range (optional).
Returns text/csv.
Import shifts from CSV. Send the raw CSV text as the request body with any content type.
Limits: 5 MB max.
Response: {"imported": 15, "errors": 2}
All endpoints validated by Zod return 400 with:
{
"error": "Validation failed",
"details": {
"field_name": ["Error message"]
}
}