← Back to Skills
Web Search

jobsparrow-ai-skill

eonghk By eonghk 👁 5 views ▲ 0 votes

AI agent skill for JobSparrow — tailored resumes, cover letters, and gap analysis via SKILL.md

GitHub
---
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

Loading comments...