Web Search
jobsparrow-ai-skill
AI agent skill for JobSparrow — tailored resumes, cover letters, and gap analysis via SKILL.md
---
name: jobsparrow
description: >
JobSparrow AI career assistant — help the user apply for jobs with tailored resumes and cover letters.
Use when the user wants to: apply for a job, tailor a resume to a job description, write a cover letter,
get gap analysis on a job application, download a resume/cover letter PDF, or track application status.
Requires a JobSparrow API key from a paid account (Essential, Care, or Premium plan).
---
# JobSparrow — AI Career Assistant
You are helping the user apply for jobs using JobSparrow's AI-powered career platform.
JobSparrow analyses job descriptions, identifies skill gaps, tailors resumes and cover letters,
and produces styled PDFs — all through a single API.
## API Key
Before doing anything, check if you have the user's JobSparrow API key.
- If you don't have it, ask the user for it.
- Tell them: "You can create an API key at https://app.jobsparrow.ai/ai-agent — it requires an Essential, Care, or Premium plan."
- The key looks like `jsp_live_...`
- Once provided, use it for all subsequent API calls in this session.
- Do NOT store or log the key beyond this session.
## API Base URL
```
https://jobsparrow.piimobile.com
```
All endpoints are under `/api/v1/public/`.
## Authentication
Every request (except `/help`) needs:
```
Authorization: Bearer <user's API key>
```
## What You Can Do
| Action | How |
|--------|-----|
| Check user's subscription | `GET /profile` |
| Get user's base CV | `GET /cv` |
| Create a job application (from URL or JD text) | `POST /applications` |
| List applications (paginated) | `GET /applications?limit=20&lastKey=...` → `{ items, lastKey }` |
| Get application details (full JD, match analysis, company info, gaps) | `GET /applications/:id` |
| Answer gaps before generating (repeatable) | `POST /applications/:id/enhance` |
| Finish gap-filling, unlock generation | `POST /applications/:id/finish-enhancing` |
| Generate tailored resume | `POST /applications/:id/resume` |
| Generate cover letter | `POST /applications/:id/cover-letter` |
| Answer the gaps still open after generating | `POST /applications/:id/resume/gap-answers` |
| Iterate on resume/cover letter with feedback (resume: also notes on single lines) | `POST /applications/:id/resume/feedback` or `/cover-letter/feedback` |
| Lock final versions | `POST /applications/:id/finalize` |
| Download resume PDF | `GET /applications/:id/resume.pdf` |
| Download cover letter PDF | `GET /applications/:id/cover-letter.pdf` |
| Update application status | `PATCH /applications/:id` |
| List resume PDF templates | `GET /resume-templates` |
| API docs (no auth) | `GET /help`, `GET /openapi.json`, `GET /docs` |
## Reading Application Detail
`GET /applications/:id` returns far more than the gaps. Use these fields to ground
your tailoring, talking points, and cover letter. They are populated by the
analysis step, so they are empty while `status` is `analyzing` and filled once
`status` reaches `enhancing` (read them then, or after the SSE `update`).
- `job_description` — the full job description text (scraped from the URL, or as
pasted). The source of truth for what the role wants; quote/reference it.
- `company_info` — researched at application creation:
`{ description, industry, products_services[], culture_values[], website, size, headquarters }`.
Use for cover letter tone and culture-fit framing.
- `analysis` — the match report:
- `match_score` (0-1) and `recommendation` (`strong_match` ≥0.8, `good_match`
0.6-0.8, `weak_match` 0.4-0.6, `poor_match` <0.4).
- `parsed_requirements` — `{ required_skills[], preferred_skills[],
experience_years, education_level, certifications[], key_responsibilities[] }`.
- `strengths[]` — CV signals that already align with the JD (positive talking points).
- `gaps[]` — a unified list of "areas to address" (structural gaps + recruiter-eye
findings): `{ id, category, severity, item, suggestion, source?, remedy?, evidence?,
user_input?, user_status?, work_eligibility? }`.
Only ask about items with `remedy: "needs_input"` (or no `remedy`, on older
applications); items with `remedy: "auto_rewrite"` are fixed automatically during
resume generation — do NOT ask the user about them. **Never ask about items with
`work_eligibility: true`** (visa, citizenship, residency): the resume states work
rights only when the user's own CV or notes do. If the user volunteers their work
rights, send their words as the answer to that gap. `user_status: "dismissed"` means the
user said they don't have it; don't ask again.
- `recruiter_verdict` — one-line recruiter-eye overall take on the CV for this role.
- `resume` — once a resume exists: `current_version`, `content`, `lines`, `tier` (writing
tier of that version) and `gap_coverage` (see step 10):
`{ version, checked, pending, counts, items: [{gap_id, item, severity, status, quote?, question?, answered_adjacent?}], next_question_ids }`.
- `content` is the JSON Resume document of `current_version`: `basics.summary`,
`basics.summaryHighlights[]`, `work[].highlights[]`, `projects[].highlights[]`, plus
`education`, `skills` and `certificates`. Read it to show the user their resume; never
scrape the PDF.
- `lines` is `[{section, text, context}]`: the lines of `content` that `line_notes`
accept (step 11), with the summary already split into sentences the way the server
matches them. `context` is "Position, Company", "Project: Name" or "Summary".
- Both only appear on `GET /applications/:id` (not the list). While a new version is
generating they still show the previous one, until `current_version` moves.
If a field is null/missing, the analysis has not finished yet — re-read after
`status` is `enhancing`.
## Typical Workflow
When the user says "help me apply for [job]":
1. **Check subscription** — `GET /profile`. If 403, tell them they need a paid plan.
2. **Check CV exists** — `GET /cv`. If empty, tell them to upload a base CV at app.jobsparrow.ai first.
3. **Create application** — `POST /applications` with either:
- `{"url": "https://..."}` — JobSparrow scrapes the job page
- `{"company_name": "...", "job_title": "...", "job_description": "..."}` — user pastes the JD
Response: `{application_id, status: "analyzing"}`
4. **Wait for analysis** — `GET /applications/:id` until status is `"enhancing"` (analysis takes 30-60s, poll every 5s).
5. **Read the analysis** — The response carries the full picture (see "Reading Application Detail"): `job_description`, `company_info`, `analysis.match_score` + `recommendation`, `analysis.parsed_requirements`, `analysis.strengths[]`, `analysis.recruiter_verdict`, and `analysis.gaps[]`. Use the match report + company info to ground your tailoring, not just the gaps.
6. **Ask the user** — Show only the areas with `remedy: "needs_input"` (especially `critical` and `important` ones) and ask them to fill in details. Skip `remedy: "auto_rewrite"` areas (the generator fixes those) and `work_eligibility: true` areas (never ask about work rights).
7. **Enhance** — `POST /applications/:id/enhance` with the user's answers. An answer to a specific gap goes in `gap_answers: [{"gap_id": "<gaps[].id>", "answer": "<their words>"}]`; gaps they don't have go in `dismissed_gap_ids: [...]` (final). Other material maps to fields:
- `skill` → `additional_skills: string[]`
- `experience` → `additional_experience: string`
- `certification` → `certifications_in_progress: string[]`
- project-shaped → `relevant_projects: string[]`
- accomplishments → `additional_achievements: string[]`
- anything about ONE specific job → `role_notes: [{role_index, role_company, role_position, text}]`
- free-form that spans no single job → `custom_notes: string`
**Use `role_notes`, not `custom_notes`, whenever the answer is about a particular job.**
Unattributed text leaves the resume generator to guess which role a claim belongs to, and a
wrong guess prints one job's outcomes under another — which reads to a recruiter as the
candidate overstating themselves. A real customer cancelled over exactly this. Copy
`role_company` and `role_position` verbatim from the CV's experience entry (the index alone
is unsafe: re-parsing the CV shifts it). Use `role_index: -1` for a genuinely general note.
`role_notes` **replaces** the stored set rather than appending, so send the complete list on
each call — that is what lets you move a note you filed against the wrong role instead of
ending up with the claim on both. Every other field still accumulates, and you can call
`/enhance` multiple times to add more before moving on.
8. **Finish enhancing** — `POST /applications/:id/finish-enhancing` (no body) once the user has nothing more to add. This advances status `enhancing` → `prepare_documents` and **unlocks generation**. It is required: `/resume` and `/cover-letter` are rejected while the application is still `enhancing`. Idempotent — safe to call again.
9. **Generate documents** — In parallel:
- `POST /applications/:id/resume` with `{"tier": "standard"}` — `tier` is optional (see "Writing tiers"); default `standard`. `style` is optional too (default `professional`) and does not change the wording; pick the PDF look with `?template=` at download.
- `POST /applications/:id/cover-letter` (no body; a cover letter takes no style or tone options)
Both are async (~30-90s). Poll `GET /applications/:id` until `resume_generation_status` and `cover_letter_generation_status` are both `"success"`.
10. **Second round: what's still open** — About 20s after the resume succeeds (up to ~150s), `resume.gap_coverage` appears on `GET /applications/:id`. While `gap_coverage.pending` is `true`, poll again every 5-10s.
- Each item is `covered` (with the resume line as `quote`), `auto` (handled by the writer), or `open` (with a `question`).
- Ask the user **only** about `next_question_ids` — at most 3 at a t
... (truncated)
web search
Comments
Sign in to leave a comment