Skip to content

API Reference

jackie grey edited this page Mar 16, 2026 · 6 revisions

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.


Authentication

GET /api/auth/status

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

POST /api/auth/setup

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.

POST /api/auth/login

Body:

{
  "username": "admin",
  "password": "secret"
}

Response: 200 with user object. Sets sl_session cookie (30-day expiry).

Errors: 401 invalid credentials.

POST /api/auth/logout

Clears the session cookie and deletes the server-side session record.

Response: {"success": true}

POST /api/auth/register (admin only)

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.


Users

GET /api/users

List all users. Returns id, username, display_name, is_admin, color, created_at.

PUT /api/users/:id

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.

DELETE /api/users/:id (admin only)

Permanently delete a user and all their sessions.

Errors: 400 if trying to delete yourself.


Shifts

POST /api/shifts

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
}
  • dateYYYY-MM-DD format (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 (default null)

Server computes: total_tips, wage_total, grand_total.

Response: {"id": 1, "total_tips": 120, "wage_total": 120, "grand_total": 240}

GET /api/shifts

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.

PUT /api/shifts/:id

Update a shift. Same body as POST. You must own the shift or be an admin.

DELETE /api/shifts/:id

Soft-delete a shift (sets deleted_at timestamp). You must own it or be an admin.

POST /api/shifts/:id/restore

Restore a soft-deleted shift (clears deleted_at).


Jobs

GET /api/jobs

List all jobs, ordered by name ascending. Includes tip_payment field.

POST /api/jobs

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 (default 0)
  • color — hex color (default #f5a623)
  • overtime_threshold — weekly hours before OT (default 40)
  • overtime_multiplier — OT pay multiplier (default 1.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

PUT /api/jobs/:id

Update a job. Same body as POST.

DELETE /api/jobs/:id

Archive a job (sets archived = 1). Does not delete associated shifts.


Templates

GET /api/templates

List all templates with joined job name.

POST /api/templates

Body:

{
  "name": "Weeknight Shift",
  "job_id": 1,
  "hourly_rate": 15.00,
  "hours_worked": 6.0,
  "tip_mode": "total",
  "tip_input": 80.00,
  "notes": ""
}

DELETE /api/templates/:id

Permanently delete a template.


Goals

GET /api/goals

List all goals, newest first.

POST /api/goals

Body:

{
  "period": "weekly",
  "target_amount": 1000.00,
  "active": true
}
  • period"weekly" or "monthly"
  • target_amount — non-negative number
  • active — boolean, optional (default true)

Setting active: true deactivates any other goal with the same period.

PUT /api/goals/:id

Update a goal. Same body as POST.

DELETE /api/goals/:id

Permanently delete a goal.


Settings

GET /api/settings

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_typeweekly, biweekly, semimonthly, or monthly
  • pay_period_anchor — anchor date for biweekly periods

PUT /api/settings (admin only)

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"
}

Tax Configuration

GET /api/tax-config

List all tax/deduction items, ordered by sort_order.

PUT /api/tax-config/:id (admin only)

Update a single tax config item by ID.

Body: label, rate (0–1 decimal), flat_amount, enabled.

POST /api/tax-config (admin only)

Add a new tax/deduction item.

Body: key (unique slug), label, rate (0–1), flat_amount.

DELETE /api/tax-config/:id (admin only)

Remove a tax/deduction item.


Tax Profile Presets

Preset rate bundles based on 2026 IRS brackets and national-average state rates. Designed for quick setup — not a substitute for tax advice.

GET /api/tax-profiles

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.

POST /api/tax-profiles/apply/:profileId (admin only)

Apply a preset to tax_config, overwriting Federal, State, Social Security, and Medicare rates.

Response: { "success": true, "profile": "single_50k", "applied": { "federal": "updated", ... } }

POST /api/tax-profiles/refresh (admin only)

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_enabled meta key to 0 in the database.


Paycheck Estimate

GET /api/paycheck-estimate

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

Analytics & Summary

GET /api/summary

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.

GET /api/trends

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.

GET /api/overtime

Current-week overtime status.

Response: total_hours, threshold, multiplier, regular_hours, overtime_hours, is_overtime.

GET /api/tax-estimate

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.

GET /api/analytics/effective-rate

Average effective hourly rate by day of week (0=Sun through 6=Sat).

GET /api/analytics/extremes

Top and bottom shifts by grand total.

Query params: count — number of results per list (default 5).

Response: {"best": [...], "worst": [...]}

GET /api/analytics/tip-ratio

Monthly tip percentage trend.

Returns array of: period, total_tips, grand_total, tip_pct.


Export & Import

GET /api/export/pdf

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.

GET /api/export/csv

Download shifts as CSV.

Query params: from, to — date range (optional).

Returns text/csv.

POST /api/import/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}


Validation Errors

All endpoints validated by Zod return 400 with:

{
  "error": "Validation failed",
  "details": {
    "field_name": ["Error message"]
  }
}

Clone this wiki locally