SeriousLetter API Schema (cached from /api/v1/discover)
Base URL: https://jobs.seriousletter.com
Auth: X-API-Token header
Rate limits: 100/min, 1000/day per token
Prod token (Matthias): 9955f8699964b70eee3b4acf8b7ca5cf7f6997ddb23d483f1c5f178754f11196
Prod token (Gina): a073d1c4f09049c4d7750a99344a6f9995b0271afff68acb6c85fb636f0b914b
Dev token: d6b3f193ce94f4d42b92dab708c06cb050cf861fdf35050a7f471a8ada1edf2b
jobs
Job applications tracked by the user
- GET /api/v1/jobs - List all jobs (newest first)
- status: optional filter (applied/rejected/not_applying/outdated)
- page: default 1
- page_size: default 20, max 100
- GET /api/v1/jobs/search - Fuzzy search jobs by company or position title
- q: required search query
- page: default 1
- page_size: default 20
- GET /api/v1/jobs/{job_uuid} - Get full job detail by UUID
- GET /api/v1/jobs/{job_uuid}/arbeit - Get job formatted for arbeit.swiss form fields
- GET /api/v1/jobs/{job_uuid}/status-history - Get status change history for a job
- POST /api/v1/jobs - Create a new job application [MUTATES]
- company: required — company name
- position_title: required — job title
- company_uuid: optional — UUID of existing company record; copies address/contact from that company
- status: optional — defaults to 'opportunity'; valid: opportunity/applied/rejected/not_applying/outdated
- source_url: optional — URL where the job listing was found
- application_url: optional — URL of the application form or portal
- location: optional — job location (city, country, or 'Remote')
- salary_range: optional — salary range as free text (e.g. 'CHF 120000-150000')
- priority: optional — integer 1-5 (1=highest priority)
- language: optional — job language code (e.g. 'en', 'de', 'fr')
- job_description: optional — full job description in markdown
- companyaddressline1: optional — street address line 1
- companyaddressline2: optional — street address line 2
- company_city: optional — city
- companypostalcode: optional — postal/ZIP code
- companycountrycode: optional — ISO 2-letter country code (e.g. 'CH', 'DE', 'FR')
- company_country: optional — country name (e.g. 'Switzerland', 'Germany')
- isrecruitingagency: optional — boolean; true if company is a recruiter/agency
- contact_person: optional — name of the contact person at the company
- applied_date: optional — YYYY-MM-DD
- interview_date: optional — YYYY-MM-DD
- rejected_date: optional — YYYY-MM-DD
- notapplyingdate: optional — YYYY-MM-DD
- outdated_date: optional — YYYY-MM-DD
- PUT /api/v1/jobs/{job_uuid} - Update a job (partial updates supported). When status changes, corresponding date is auto-set. [MUTATES]
- All POST fields accepted (all optional), plus:
- statuschangenotes: optional — note to record alongside the status change in history
- DELETE /api/v1/jobs/{job_uuid} - Delete a job and all its job-specific CVs [MUTATES]
- WARNING: Irreversible. Deletes the job and all attached job CVs.
- GET /api/v1/jobs/{job_uuid}/notes - List all notes for a job (newest first). Returns {notes: [...], total: N}
- POST /api/v1/jobs/{job_uuid}/notes - Create a note on a job. Accepts {text, category?, note_date?} or legacy {note, date?} [MUTATES]
- text: required note text (or note for backward compat)
- category: optional: application/interview/rejection/status/general
- note_date: optional YYYY-MM-DD (defaults to today)
- PATCH /api/v1/jobs/{job_uuid}/notes/{note_id} - Update a note. Partial updates: {text?, category?, note_date?} [MUTATES]
- DELETE /api/v1/jobs/{job_uuid}/notes/{note_id} - Delete a note [MUTATES]
companies
Company records (reusable address/contact info across jobs)
- GET /api/v1/companies - List companies (search by name/city/contact with ?q=)
- q: optional search query
- page: default 1
- page_size: default 20
- GET /api/v1/companies/{company_uuid} - Get a single company by UUID
- POST /api/v1/companies - Create a new company [MUTATES]
- name: required — company name
- website: optional — company website URL
- address_line1: optional — street address line 1
- address_line2: optional — street address line 2
- city: optional — city
- postal_code: optional — postal/ZIP code
- country_code: optional — ISO 2-letter country code (e.g. 'CH', 'FR', 'DE')
- country: optional — country name (e.g. 'Switzerland', 'France')
- contact_person: optional — name of primary contact
- contact_email: optional — contact email address
- contact_phone: optional — contact phone number
- notes: optional — free-text notes about the company
- isrecruitingagency: optional — boolean; true if this is a recruiter/staffing agency
- PUT /api/v1/companies/{company_uuid} - Update a company (partial updates, all fields optional) [MUTATES]
- DELETE /api/v1/companies/{company_uuid} - Delete a company [MUTATES]
letters
Cover letters attached to jobs
- GET /api/v1/jobs/{job_uuid}/letters - List all letters for a job
- GET /api/v1/letters/{letter_id} - Get a single letter by ID (integer, not UUID)
- POST /api/v1/jobs/{job_uuid}/letters - Create a letter for a job (manual content, no AI generation) [MUTATES]
- version_name: required
- content: required markdown
- tone: optional
- language: optional
- PUT /api/v1/letters/{letter_id} - Update a letter's content or name [MUTATES]
- final_content: optional
- version_name: optional
- DELETE /api/v1/letters/{letter_id} - Delete a letter [MUTATES]
cvs
CV profiles — general (user-level) and job-specific. Each CV can optionally link an executive summary (prepended) and portfolio document (appended) for combined PDF exports.
- GET /api/v1/profiles - List general CV profiles (not job-specific). Returns executivesummaryuuid and portfoliodocumentuuid if set.
- GET /api/v1/jobs/{job_uuid}/cvs - List job-specific CVs attached to a job. Returns executivesummaryuuid and portfoliodocumentuuid if set.
- POST /api/v1/jobs/{job_uuid}/cvs/copy/{profile_uuid} - Deep-copy a general CV to a job as a job-specific CV [MUTATES]
- name: optional — override the auto-generated name for the copied CV
- portfoliodocumentuuid: optional — UUID of a portfolio document to attach
- executivesummaryuuid: optional — UUID of an executive summary to attach
- PATCH /api/v1/jobs/{job_uuid}/cvs/{profile_uuid}/executive-summary - Set or clear the executive summary on a job CV. Pass null to clear. [MUTATES]
- executivesummaryuuid: UUID or null
- PATCH /api/v1/jobs/{job_uuid}/cvs/{profile_uuid}/portfolio - Set or clear the portfolio document on a job CV. Pass null to clear. [MUTATES]
- portfoliodocumentuuid: UUID or null
- PATCH /api/v1/jobs/{job_uuid}/cvs/{profile_uuid}/letter-template - Set or clear the preferred letter template on a job CV. Pass null to clear. [MUTATES]
- preferredlettertemplate: template ID or null
- DELETE /api/v1/jobs/{job_uuid}/cvs/{profile_uuid} - Delete a job-specific CV [MUTATES]
export
PDF export endpoints — all read-only, return application/pdf
- GET /api/v1/export/letters/{letter_id}/pdf - Export a cover letter as PDF
- template: optional (modern/german/professional/european) — fallback: starred CV preference > user default > system default
- GET /api/v1/export/letters/{letter_id}/combined-pdf - Export combined PDF: exec summary + letter + CV + portfolio
- letter_template: optional — fallback: CV preference > user default > system default
- cv_template: optional — fallback: CV preference > system default
- cv_uuid: optional — override which CV to use
- GET /api/v1/export/cvs/{profile_uuid}/pdf - Export a CV profile as PDF
- template: optional (modern/professional/creative/sidebar) — fallback: CV preference > system default
- GET /api/v1/export/executive-summaries/{summary_uuid}/pdf - Download an executive summary PDF
executive_summaries
Executive summary PDFs (e.g. Elevator Pitch decks) that can be prepended before CVs in combined exports
- GET /api/v1/executive-summaries - List available executive summaries
- language: optional filter (de/en/fr)
- GET /api/v1/export/executive-summaries/{summary_uuid}/pdf - Download an executive summary PDF file
portfolio_documents
Portfolio documents (project portfolios) that can be appended after CVs in combined exports
- GET /api/v1/portfolio-documents - List available portfolio documents
- language: optional filter
Key field names (to avoid discover calls)
Job fields: company, positiontitle, companyuuid, status, applieddate, createdat, interviewdate, rejecteddate, notapplyingdate, outdateddate, sourceurl, applicationurl, location, salaryrange, priority, language, jobdescription, companyaddressline1, companyaddressline2, companycity, companypostalcode, companycountrycode, companycountry, isrecruitingagency, contactperson
Company fields: name, website, addressline1, addressline2, city, postalcode, countrycode, country, contactperson, contactemail, contactphone, notes, isrecruiting_agency
Letter fields: POST: content, versionname, tone, language. PUT: finalcontent, version_name
Note fields: text (or legacy: note), category (application/interview/rejection/status/general), note_date (YYYY-MM-DD)
Valid statuses: opportunity (default on POST), editing, applied, rejected, not_applying, outdated
POST /jobs ignores invalid status - always follow up with PUT to set status to editing/not_applying etc.