145 lines
4.7 KiB
Markdown
145 lines
4.7 KiB
Markdown
# API Flow Maps
|
|
|
|
> Request/response flows for all Resume Matcher endpoints.
|
|
|
|
## Resume Upload
|
|
|
|
```
|
|
POST /api/v1/resumes/upload
|
|
├── Validate file (PDF/DOCX, ≤4MB)
|
|
├── parse_document() → Markdown
|
|
├── db.create_resume(status="processing")
|
|
├── parse_resume_to_json() → LLM
|
|
│ ├── Success: status="ready"
|
|
│ └── Failure: status="failed"
|
|
└── Return {resume_id}
|
|
```
|
|
|
|
## Resume Improvement
|
|
|
|
```
|
|
POST /api/v1/resumes/improve
|
|
├── Fetch resume + job from DB
|
|
├── extract_job_keywords() → LLM
|
|
├── improve_resume() → LLM
|
|
├── [If enabled] generate_cover_letter() → LLM
|
|
├── [If enabled] generate_outreach_message() → LLM
|
|
├── [If enabled] generate_interview_prep() → LLM
|
|
├── db.create_resume(improved)
|
|
├── db.create_improvement()
|
|
└── Return {data, cover_letter, outreach_message, interview_prep}
|
|
```
|
|
|
|
## Interview Prep Generation
|
|
|
|
```
|
|
POST /api/v1/resumes/{id}/generate-interview-prep
|
|
├── Require tailored resume (parent_id)
|
|
├── Fetch improvement record and associated job description
|
|
├── Require processed resume data
|
|
├── generate_interview_prep() → LLM JSON
|
|
├── Validate InterviewPrepData
|
|
├── Save resumes.interview_prep as serialized JSON TEXT
|
|
└── Return {interview_prep, message}
|
|
```
|
|
|
|
## PDF Generation
|
|
|
|
```
|
|
GET /api/v1/resumes/{id}/pdf
|
|
├── Fetch resume from DB
|
|
├── Build URL: {frontend}/print/resumes/{id}?{params}
|
|
├── Playwright render (wait for .resume-print)
|
|
└── Return PDF bytes
|
|
```
|
|
|
|
## Health Check
|
|
|
|
```
|
|
GET /api/v1/health
|
|
└── Return {status: "healthy"} # pure liveness — does NOT call the LLM
|
|
```
|
|
|
|
## System Status
|
|
|
|
```
|
|
GET /api/v1/status # each check isolated → 200 (partial/degraded), never 500
|
|
├── try: get_llm_config()
|
|
│ ├── llm_configured = api_key set OR provider ∈ {ollama, openai_compatible}
|
|
│ └── check_llm_health() → llm_healthy # failure here degrades only this field
|
|
├── try: db.get_stats() # failure → empty stats, still 200
|
|
└── Return {status, llm_configured, llm_healthy, has_master_resume, database_stats}
|
|
```
|
|
|
|
## Configuration Update
|
|
|
|
```
|
|
PUT /api/v1/config/llm-api-key
|
|
├── _load_config()
|
|
├── Merge new NON-SECRET values (provider/model/base/...)
|
|
├── (no longer persists any key — keys go through /config/api-keys)
|
|
├── _save_config()
|
|
└── Return masked config
|
|
```
|
|
|
|
## API Keys (per-provider, encrypted)
|
|
|
|
```
|
|
GET /api/v1/config/api-keys
|
|
└── Return {providers: [{provider, configured, masked_key}]} # always masked
|
|
|
|
POST /api/v1/config/api-keys
|
|
├── For each provided provider key:
|
|
│ └── Fernet-encrypt → upsert into SQLite `api_keys` table # other providers' keys untouched
|
|
└── Return {message, updated_providers}
|
|
|
|
DELETE /api/v1/config/api-keys/{provider} # remove one provider's key
|
|
DELETE /api/v1/config/api-keys?confirm=... # clear all keys
|
|
```
|
|
|
|
## Job Upload
|
|
|
|
```
|
|
POST /api/v1/jobs/upload
|
|
├── For each description:
|
|
│ └── db.create_job()
|
|
└── Return {job_id[]}
|
|
```
|
|
|
|
## Resume Operations
|
|
|
|
| Endpoint | Flow |
|
|
|----------|------|
|
|
| `GET /resumes?id=` | db.get_resume() |
|
|
| `GET /resumes/list` | db.list_resumes() |
|
|
| `PATCH /resumes/{id}` | db.update_resume() |
|
|
| `DELETE /resumes/{id}` | db.delete_resume() |
|
|
|
|
## Application Tracker
|
|
|
|
```
|
|
GET /api/v1/applications
|
|
├── db.list_applications()
|
|
└── Return {columns} # grouped by the 7 status keys (all present):
|
|
# saved/applied/no_response/response/interview/accepted/rejected
|
|
|
|
POST /api/v1/applications # manual add from a pasted JD
|
|
├── db.create_job(jd)
|
|
├── [If company/role missing] extract_job_keywords() → LLM # one best-effort call
|
|
├── db.create_application(status default "applied") # dedupes on (job_id, resume_id)
|
|
└── Return Application
|
|
|
|
GET /api/v1/applications/{id}
|
|
├── db.get_application() + embed job_content + applied resume
|
|
└── Return {..., job_content, resume} # resume: null if it was deleted
|
|
```
|
|
|
|
| Endpoint | Flow |
|
|
|----------|------|
|
|
| `PATCH /applications/{id}` | db.update_application() — status/position/notes/company/role/applied_at; server renumbers `position` |
|
|
| `PATCH /applications/bulk` | db.bulk_update_status() — move many cards to one column |
|
|
| `DELETE /applications/{id}` | db.delete_application() |
|
|
| `POST /applications/bulk-delete` | db.bulk_delete_applications() |
|
|
|
|
> **Auto-create:** `POST /resumes/improve/confirm` (and legacy `POST /resumes/improve`) create an `applied` card after persisting the tailored resume — best-effort (a tracker failure never breaks tailoring); company/role reuse the cached keyword extraction, so no extra LLM call.
|