API Documentation

Hoursight is an HTTP API. Every endpoint is bearer-token authenticated except /health, /readiness, /metrics, and /docs. All responses are JSON except PDF reports.

Alpha access. Bearer tokens are issued manually during the pilot. Email dbacon@aioelectric.org.

Authentication

Include your bearer token in the Authorization header on every request:

curl https://api.hoursight.ai/v1/jobs \
  -H "Authorization: Bearer YOUR_TOKEN"

Submitting a calibration

  1. Upload your labor DB, historical bids, and job cost export.
  2. Submit a job referencing those uploads.
  3. Poll until status: "completed".
  4. Download the signed report and CSV.

1. Upload files

curl -X POST https://api.hoursight.ai/v1/uploads/labor_db \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@labor_db.xlsx"

# Returns: { "upload_id": "up_abc123", "kind": "labor_db", "sha256": "..." }

2. Submit the job

curl -X POST https://api.hoursight.ai/v1/jobs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nightly-2026-09-17" \
  -d '{
    "labor_db_upload_id": "up_abc123",
    "bids_upload_id":     "up_def456",
    "job_cost_upload_id": "up_ghi789",
    "options": { "matcher": "fuzzy" }
  }'

# Returns: { "job_id": "job_xyz", "status": "queued" }

3. Poll status

curl https://api.hoursight.ai/v1/jobs/job_xyz \
  -H "Authorization: Bearer $TOKEN"

# Returns: { "job_id": "job_xyz", "status": "running" | "completed" | "failed",
#            "progress": 0.65, "eta_seconds": 42 }

4. Download outputs

curl https://api.hoursight.ai/v1/jobs/job_xyz/report \
  -H "Authorization: Bearer $TOKEN" \
  -o calibration-report.pdf

curl https://api.hoursight.ai/v1/jobs/job_xyz/diff.pdf \
  -H "Authorization: Bearer $TOKEN" \
  -o month-over-month-diff.pdf

Webhooks

Register an HTTPS URL to receive job lifecycle events. Every payload is signed with an HMAC-SHA256 header for verification.

curl -X POST https://api.hoursight.ai/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/hooks/hoursight",
    "events": ["job.completed", "job.failed"]
  }'

# Returns: { "webhook_id": "wh_...", "signing_secret": "whsec_..." }

Verify with:

expected = hmac_sha256(signing_secret, timestamp + "." + body)
provided = request.headers["Hoursight-Signature"]  # "v1,"
assert constant_time_compare(expected, provided)

Rate limits

Per tenant: 60 uploads/hr, 240 job submissions/day, 3600 read requests/hr. Exceeded requests return 429 with a Retry-After header.

Idempotency

Every mutating endpoint accepts an Idempotency-Key header. Replaying a request with the same key within 24 hours returns the original response.

Error responses

{
  "error": {
    "code": "invalid_upload_kind",
    "message": "labor_db must be an .xlsx file",
    "request_id": "req_abc..."
  }
}

Always include the request_id when reporting issues — it lets us trace the full request through logs.

Status codes we return

OpenAPI

Interactive docs live at /docs on the live API (once your token is issued).