Skip to content
Back to skills

Upwork Proposal

ASecurity

Write a short Upwork proposal from a job description and the user's resume, in a plain human voice that doesn't repeat the user's recent proposals.

  • 80 stars
  • 0 votes
  • 0 copies
  • 6 views
  • Added May 27, 2026
developmentgobashreactapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add suxrobGM/jobpilot --skill upwork-proposal --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Upwork Proposal?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Upwork Proposal
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/suxrobgm-upwork-proposal/badge)](https://www.skillsdirectory.com/skills/suxrobgm-upwork-proposal)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: upwork-proposal
description: Write a short Upwork proposal from a job description and the user's resume, in a plain human voice that doesn't repeat the user's recent proposals.
argument-hint: "<proposal_id | job_description>"
---

# Upwork Proposal

Clients skim proposals on their phones between other things. If one takes more than about 30 seconds to read, it's gone. Write for that skim: their problem first, one piece of proof, one question.

## Setup

Follow `../_shared/setup.md`, then load the structured resume for `user.primaryResumeId` (`jobpilot-api GET /api/resumes/<id>`) and write from its `content`. Don't `Read` the source PDF.

## Step 1: Resolve the input

The argument is a **proposal id** (from the JobPilot UI) or a raw **job description** (manual use).

- **Id** → fetch the draft and use its `jobDescription` as the posting, with `jobTitle` / `clientName` / `jobUrl` for context. Keep the id for Step 6. Drafts from an Upwork search recommendation (`source:"search"`) work the same way.

  ```bash
  jobpilot-api GET "/api/upwork/proposals/$ARG"
  ```

- **Anything else** → the argument is the job description. There's no row yet; Step 6 creates one.

## Step 2: Read the job and your recent proposals

From the posting, note what the client needs built or fixed, the tech, scope and timeline hints, and any direct questions they ask. Pick one detail unique to this posting to show you read it.

Then read your last five proposals so this one doesn't come out the same:

```bash
jobpilot-api GET /api/upwork/proposals --query page=1 --query limit=5
```

From each `.items[].proposalText`, note the first line, the project it used, and the closing question.

## Step 3: Pick one case study

Choose the single project from the resume that matches their problem, not just their stack. Keep its link if it has one. Prefer a different project from the last two proposals when another one fits just as well.

## Step 4: Write

Under 150 words, in this order:

1. **Hook (line 1, in `**bold**`).** Why you in one line: the closest thing you've done to their problem ("I built the Stripe Connect payouts for a two-sided marketplace last year."). Upwork renders the bold, so this line is what gets read. Don't paraphrase their posting back to them; they know what they wrote. Not "Hi", "Dear client", "I'm excited to apply", or "I came across your posting".
2. **Proof (one or two lines).** The case study: what you built, one real result, and the link. Don't narrate your career.
3. **Question (one line).** One specific question about their project that shows you thought about it (a scope choice, an edge case, a decision they'll face). This is the call to action; don't add "Looking forward to hearing from you" after it. Use a calendar link instead only if the profile has one.

Match their tone: a casual posting gets a casual reply. Don't mention Top Rated or JSS unless the posting asks; the profile already shows it. No lists, headers, or emoji in the body. Don't reuse the first line or closing question of a recent proposal.

**Screening questions.** Answer each one short and direct. "How many years with React?" → "4 years, most recently on [project]." A one-line question gets a one- or two-line answer.

## Step 5: Humanize

Invoke the `humanizer` skill in embedded mode on the proposal, then on the screening answers.

## Step 6: Save

Write the body to `"$JOBPILOT_TEMP/proposal.json"` (proposal text breaks inline quoting). `screeningAnswers` is `[{ "question", "answer" }]`, or `[]` when there were none.

- **Launched with an id** → `PATCH` the draft; its status stays `draft`. Body `{ "proposalText", "screeningAnswers" }`:

  ```bash
  jobpilot-api PATCH "/api/upwork/proposals/$ARG" --data @"$JOBPILOT_TEMP/proposal.json"
  ```

- **Launched with a job description** → `POST` a new row. Body `{ "jobTitle", "clientName", "jobUrl", "jobDescription", "proposalText", "screeningAnswers" }`; `jobTitle` is required (derive it from the posting), and include `clientName` / `jobUrl` when the posting gives them:

  ```bash
  jobpilot-api POST /api/upwork/proposals --data @"$JOBPILOT_TEMP/proposal.json"
  ```

Then print the proposal, and each screening answer under its question, so the user can paste them into Upwork.

## Rules

1. **No fabrication.** Every project, metric, and link comes from the resume.
2. **One case study, one question.** Relevance beats volume.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.