Skip to content

API Design

The kairos.api module exposes all core functionality as a versioned REST API following Microsoft REST API Guidelines and RFC 9457 for error responses.

Server

Framework FastAPI (async, Pydantic v2)
ASGI server uvicorn
Default port 8880
API prefix /api/v1/
Serialization JSON (snake_case, matching Python convention)
API docs Scalar at /docs

Design Principles

  • RESTful resource-oriented — plural nouns, kebab-case URLs
  • No response envelope — success responses return the resource directly
  • RFC 9457 errors — all errors return application/problem+json
  • Cursor-based pagination — stable ordering, no page skipping issues
  • Typed responses — every endpoint has a Pydantic response model

Response Format

Success (single resource)

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "platform": "adzuna",
  "title": "Senior Python Developer",
  "company": "Acme Corp",
  ...
}

Success (list)

{
  "results": [ ... ],
  "next": "eyJzY3JhcGVkX2F0Ijo...",
  "previous": null
}

Error (RFC 9457)

Content-Type: application/problem+json

{
  "type": "https://kairosjobs.app/errors/not-found",
  "title": "Resource not found",
  "status": 404,
  "detail": "Job not found: a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "instance": "/api/v1/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Validation errors include field-level details:

{
  "type": "https://kairosjobs.app/errors/validation-error",
  "title": "Validation error",
  "status": 422,
  "detail": "One or more request parameters are invalid.",
  "instance": "/api/v1/jobs/search",
  "errors": [
    { "field": "body → keywords", "message": "Field required" }
  ]
}

Endpoints

Jobs

Method Path Operation ID Description
POST /api/v1/jobs/search Jobs.Search Search across platforms, save results
GET /api/v1/jobs Jobs.List List jobs (cursor-paginated, filterable, sortable)
GET /api/v1/jobs/{id} Jobs.Get Get job details
POST /api/v1/jobs/{id}/fetch-details Jobs.FetchDetails Re-scrape full description from platform
DELETE /api/v1/jobs/{id} Jobs.Delete Delete a job (returns 204)

Config

Method Path Operation ID Description
GET /api/v1/config Config.Get Get configuration (sensitive fields removed)
PATCH /api/v1/config Config.Update Update preference fields (dot notation keys)

Health

Method Path Operation ID Description
GET /api/v1/health Health.Check Server health check

Planned

Method Path Description
POST /api/v1/jobs/{id}/analyze Trigger JD analysis via LLM
GET /api/v1/jobs/{id}/analysis Get analysis results
POST /api/v1/resumes/import Import a resume
GET /api/v1/resumes List resumes
POST /api/v1/resumes/{id}/tailor Tailor resume for a job
POST /api/v1/applications Create application record
GET /api/v1/applications List applications

Pagination

Cursor-based pagination using (scraped_at, id) composite cursor, base64url-encoded.

Query parameters:

Param Default Description
cursor null Opaque cursor token from previous next
page_size 20 Results per page (max 100)
ordering -scraped_at Comma-separated fields, prefix - for descending

Allowed ordering fields: scraped_at, title, company, salary_min, salary_max, platform, status

Example:

GET /api/v1/jobs?ordering=-salary_min&page_size=10&platform=adzuna

Filtering

List endpoints support filtering via query parameters:

GET /api/v1/jobs?platform=adzuna&status=new

Error Mapping

Domain Error HTTP Status Problem Type
NotFoundError 404 /errors/not-found
ValidationError 422 /errors/validation-error
RequestValidationError 422 /errors/validation-error
ScrapingError 502 /errors/upstream-error
DomainError (base) 502 /errors/upstream-error
Unhandled Exception 500 /errors/internal-error

API Documentation

  • Scalar UI: http://localhost:8880/docs
  • OpenAPI JSON: http://localhost:8880/openapi.json

Generated from Pydantic models with typed response schemas, field descriptions, and concrete examples.

Authentication

Not implemented yet. For local use, the API runs on localhost with no auth. JWT auth is planned for remote/shared deployments.