-
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. Includes tip_payment field.
Body:
{
"name": "Restaurant ABC",
"default_rate": 15.00,
"color": "#3ecf8e",
"overtime_threshold": 40,
"overtime_multiplier": 1.5,
"tip_payment": "cash"
}-
name— 1–100 chars (required) -
default_rate— non-negative number (default0) -
color— hex color (default#f5a623) -
overtime_threshold— weekly hours before OT (default40) -
overtime_multiplier— OT pay multiplier (default1.5) -
tip_payment—"cash"or"paycheck"(default"cash")-
"cash"— tips received nightly in cash; excluded from paycheck gross but still taxed -
"paycheck"— tips included in the paycheck
-
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 all settings from the meta table as key-value pairs, including:
-
pay_week_start_day— 0 (Sun) through 6 (Sat), default 1 (Mon) -
pay_period_type—weekly,biweekly,semimonthly, ormonthly -
pay_period_anchor— anchor date for biweekly periods
Update settings. Body is a flat object of key-value pairs:
{
"pay_week_start_day": "1",
"pay_period_type": "biweekly",
"pay_period_anchor": "2026-01-06"
}List all tax/deduction items, ordered by sort_order.
Replace all tax config items. Body is an array:
[
{"key": "federal", "label": "Federal Income Tax", "rate": 0.22, "flat_amount": 0, "enabled": true, "sort_order": 0},
{"key": "state", "label": "State Income Tax", "rate": 0.05, "flat_amount": 0, "enabled": true, "sort_order": 1}
]Returns the estimated paycheck for the current pay period.
Response:
{
"period": {
"type": "Weekly",
"start": "2026-03-09",
"end": "2026-03-15",
"total_days": 7,
"elapsed_days": 5,
"remaining_days": 2
},
"current": {
"wages": 600.00,
"tips": 400.00,
"cash_tips": 300.00,
"paycheck_tips": 100.00,
"gross": 1000.00,
"paycheck_gross": 700.00,
"hours": 40.0,
"shifts": 5
},
"previous": {
"gross": 950.00,
"hours": 38.0,
"shifts": 5
},
"taxes": [
{"key": "federal", "label": "Federal Income Tax", "rate": 0.22, "flat_amount": 0, "amount": 220.00}
],
"total_tax": 350.00,
"net_pay": 350.00,
"projected_gross": 980.00,
"projected_net": 490.00,
"projected_cash_tips": 420.00
}Key fields:
-
current.gross— total earnings (wages + all tips) -
current.cash_tips— tips from jobs configured as "cash" (already received nightly) -
current.paycheck_tips— tips from jobs configured as "paycheck" -
current.paycheck_gross— wages + paycheck tips only (what appears on your check) -
total_tax— taxes on ALL earnings including cash tips -
net_pay— paycheck gross minus total tax (your check amount) -
projected_cash_tips— projected cash tips through end of period
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"]
}
}