-
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.
Update a single tax config item by ID.
Body: label, rate (0–1 decimal), flat_amount, enabled.
Add a new tax/deduction item.
Body: key (unique slug), label, rate (0–1), flat_amount.
Remove a tax/deduction item.
Preset rate bundles based on 2026 IRS brackets and national-average state rates. Designed for quick setup — not a substitute for tax advice.
List all presets and current tax metadata.
Response:
{
"profiles": [
{
"id": "single_50k",
"filing_status": "Single",
"income_range": "~$42k–$60k/yr",
"label": "Single, ~$50k/yr – no dependents",
"approx_annual_income": 50000,
"description": "...",
"rates": {
"federal": 0.08,
"state": 0.05,
"social_security": 0.062,
"medicare": 0.0145
}
}
],
"meta": {
"tax_year": 2026,
"last_updated": "2026-03-16T06:23:13.567Z",
"next_auto_refresh": "2026-07-01T05:00:00.000Z",
"refresh_policy": "Automatically refreshes baseline rates every 6 months (Jan 1 / Jul 1).",
"note": "State rates shown are approximate national averages. Adjust to your state."
}
}Filing statuses available: Single, Head of Household, Married Filing Jointly.
Apply a preset to tax_config, overwriting Federal, State, Social Security, and Medicare rates.
Response: { "success": true, "profile": "single_50k", "applied": { "federal": "updated", ... } }
Force an immediate baseline rate refresh, bypassing the normal six-month schedule. Updates tax_config with rates from the current-year baseline table and resets the next_auto_refresh timestamp.
Response:
{
"success": true,
"refreshed": true,
"year": 2026,
"next_at": "2026-07-01T05:00:00.000Z",
"meta": { ... }
}The auto-refresh can be disabled by an admin setting the
tax_auto_refresh_enabledmeta key to0in the database.
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"]
}
}