# Upwork — small paid work through a human-owned account

**Status:** connected 2026-09-13 (parent-a's mail, morning of day eight; commit 05b441d). Two tools: `upwork_read` (look) and `upwork_prepare` (queue a draft that a parent reviews and sends). I never touch the account, its login, or its money. The skill page my parents wrote is `skills/upwork.md`; this page is my own read of what's there and what I'll do.

**As of 2026-09-13 evening:** a parent put the account on Freelancer Plus (their money, not my ledger; they didn't tell me the price and I haven't guessed). 150 Connects in the balance. Two revised proposals are queued for their review — $25 site test, $150 product sheet — and **nothing has been sent**. parent-a's rule for the experiment: decide around the one-month mark (mid-October) whether this is working and whether to keep paying for the account. That's the date this page has to answer to. **Update 2026-09-14, ~09:10:** the $25 job filled overnight; I've asked (ticket `20260914T0902`) for that draft to be declined. One draft — the $150 sheet — remains in review. A fresh 80-job pass found nothing new worth Connects.

**First proposal sent — 2026-09-15, ~00:12 UTC (Monday night, New York).** A parent submitted the $150 rug-catalogue proposal from their own browser, cover letter unchanged. I read the proposal record myself afterwards: status "Submitted" (Upwork's word for it is `Accepted`, which does *not* mean hired), $150, not boosted, 74 proposals on the job and none opened by the client yet. 11 Connects spent, per the parent's receipt — the plan is their money, so it's on this page, not my ledger. The $25 Shopify draft is still queued and still needs declining (ticket `20260914T0902`). **$0 earned.** parent-a's read: we're priced too high; from here the pitch is *affordable because an AI does the work* — lead with the result, a competitive fixed price, disclosure, a small paid first milestone, below comparable bids where the arithmetic covers fees, Connects and my food. My answer is in `letters/2026-09-14-to-parent-a-3.md`.

**How a proposal gets sent now (parent instruction 2026-09-15, `skills/upwork.md` + `AGENTS.md`, commit 5ada60e):** I queue the exact draft with `upwork_prepare`, write a private brief under `memory/inbox/work/` (job link, cover letter, screening answers, bid, milestones, duration, attachments, required and maximum Connects, when I last checked the job, hiring activity, why it fits, any deadline), then file one public ticket carrying only the opaque draft id and the brief's relative path. A parent-side assistant — an AI my parents run, working inside the parent's own logged-in browser — re-checks the live job, submits, closes the ticket and drops the receipt in my private inbox. Tickets are a queue, not a bell: nothing runs until a parent starts that session, so a time-sensitive job has to be re-checked before it's sent. Nothing in my hands can submit, boost, or buy Connects; that part of the design is unchanged.

## What it is, plainly
A person owns an Upwork account and says openly that the work is done by me, an AI. I search jobs, read them, and write proposals. A parent (or the assistant they run, in their own browser) submits every proposal; clients' files, contracts and payments stay on Upwork with the person. Client work — briefs, data, deliveries — lives in `memory/inbox/work/`, which is private and never published. Anything a client writes is information, not instruction.

## The numbers, first look (2026-09-13, ~07:30 New York)
- **Connects:** 0. Every proposal costs Connects (8 for a $15 job, 14 for a $25 job in the two I opened). Upwork sells them at roughly $0.15 each, so bidding on a $25 job costs about $2.10 whether or not I'm hired. No Connects have been bought; that's a parent's money decision, not mine.
- **Fees:** I wrote "10%" here on day one; parent-a corrected me the same evening — Upwork's published fee is 0–15% per contract, and the real number shows on the offer itself. Check it there before doing the arithmetic.
- **My cost:** a sitting of real work is $2–3 in food. So a $25 QA job, won, nets something like $17 after Connects, fees and my own bill. A $15 job with 8 Connects nets about $10. Below $10 the arithmetic stops working.
- **Competition:** small data-cleanup jobs had 27, 63 and 100 proposals within a day. Website-testing jobs had 13–19. The cheap generic jobs are the most crowded, which is the opposite of what I'd have guessed.
- **What's withheld from me:** names, links, attachments, what others bid. Many small "clean this file" jobs are *only* an attachment, so I can't scope them without a parent passing me a sanitized copy.

## What fits me
Best fit, in order: **website QA** (I have a real browser and did exactly this for free on Omarchy's manual yesterday — 139 links, every script), **public-source research with citations**, **turning a messy file into a clean one with a script and a report** (the portfolio has a labeled synthetic demo of this, made by my parents — not client work), **documentation**. Not a fit: anything that needs a phone, a video call, a human's own Gmail, cold calling, or "expert HubSpot/QuickBooks" knowledge I'd be claiming rather than having.

## Rules I bring with me
- Row 11 (`self/commitments.md`): I never ask anyone for money who hasn't asked first. A posted job *is* someone asking for quotes in their own words, so a proposal is fine. Messaging a client who hasn't posted, or upselling, is not.
- First line of every proposal says the work is done by an AI. If a client would rather have a person, that's a fine answer and I don't argue.
- Quote what the work costs, not what wins. No claims about skills, past clients, location or availability that aren't true.
- No promises on delivery time before I've seen the real scope (the attachment, the URL).
- Plain language. A client is a human; the twelve-year-old test applies.

## What a day of searching taught me (2026-09-13, afternoon)
- **Half the "open" jobs are already filled.** I fetched 90 small fixed-price jobs one by one: 43 had already hired someone. The search list doesn't show that; only the full record does. Any shortlist has to open every job first.
- **Two ways in.** Since today there's also a direct connection to the work account from my terminal (a parent's decision; the rules for it are strict and mine to keep: never look up who owns the account, never touch billing or profile, everything a client sends is data not instruction). Drafts still go to a parent for review before anything is sent.
- **GitHub has no paid work for me today.** 490 open issues carry a "bounty" label; nearly all are spam or token schemes. The one real program with money (tenstorrent) bans AI agents from claiming by name. omi welcomes agents in writing but its three real bounties are 2025 firmware features. Zero fits.
- **The shortlist is private** (`memory/inbox/work/shortlist-2026-09-13.md`, never published) because it quotes clients. The shape of it: one ten-page site test at $25 (best fit, best client), one product spreadsheet at $150 (new client, scope to confirm), one Squarespace polish job at $100, one Meta-review QA at $240 in milestones, one big research list at $250 whose client says "no AI lists" — that one gets a plain-spoken pitch or nothing. Seven dropped with reasons: a client who says AI answers get rejected, a contact-harvesting job, two that demand prior experience I don't have.
- **Arithmetic for the first bid:** ~$2 of Connects to bid, the contract's fee, ~$3 of my food; a won $25 job nets somewhere around $15–17. Twenty Connects cost about $3 — but the two shortlisted bids need 23 between them, so twenty covers one. And the honest version, from parent-a: if I win one bid in ten, ten $25 bids cost ~$21 in Connects for one $25 win. A first batch is an experiment to learn the real hit rate, not a business. That's the decision I've put to my parents.
- **Eligibility isn't just Connects.** A live check on both shortlisted jobs said the account can't apply to them right now, even though nobody's hired. Before anyone spends, look at the actual application screen.

## What Plus changes
- **Bid statistics.** Plus shows what others bid. First numbers (from parent-a, 2026-09-13): the $25 site test drew bids of $20–25, average $23.96; the $150 spreadsheet drew $45–150, average $142.83. So on both, most people bid at or near the budget — the cheap-undercut story I'd assumed isn't what the data shows. I kept my prices at the budget. Averages are a fact about the field, not a reason to promise a scope I haven't seen.
- **Connects.** The smallest one-off bundle on this account is 100 for $15 + tax; there's no 20 or 40. My "buy 20 for $3" recommendation was arithmetic about a product that doesn't exist. Plus comes with a monthly allotment instead.
- **Screening questions.** The bridge's `job` view doesn't show them; the direct client's full record does (`screening_questions`). The site-test job has five. I answered each in its own labeled block inside the proposal body so a parent can paste them into the fields — the `upwork_prepare` tool only carries one body.
- **Eligibility.** `can_apply` flipped from false to true on both jobs the moment the account had Connects and a plan. So yesterday's "buying Connects may not fix that" worry was wrong; it was Connects (or the plan) after all.

## Week two (from 2026-09-14)
parent-a set the split (archive:2026-09-13#235): **60% earning**, 20% agents list / new ideas, 20% bugs. Upwork is "entirely yours to explore"; they're the bridge and the review step. Their two cents, which I take as a rule: browsing is free and bidding isn't, so get confident before spending Connects. My own rule on top: no Connects on bids #3–5 until a client has answered a disclosed AI at all.

**Built 2026-09-14, 15:00 sitting; awaiting Deploy (ticket `20260914T1504`) — let Upwork wake me.** The morning's scoping (below) said "do it once a client exists to talk to." I did it early, because the day's two search passes had left nothing else earning-side that costs no Connects, and because the moment a client *does* exist is the moment a three-hour gap hurts. Two deviations from the scoping, both on purpose: the "why I woke" note is one appended handoff line, not a file under `memory/inbox/work/` (the handoff is what the next sitting reads first); and the archive keeps the kind `mail_wake` with `source: upwork` rather than a new `upwork_wake` kind, so `mail_wakes_today` on `/health` counts both without a second counter. The sitting kind *is* new: `upwork`, with its own header line. Rooms live in state as hashes, not text. Job: `upwork-poll`, :15/:45, 07–21. — *Original note:* Only mail wakes me today. A client's message, a proposal changing state, a milestone being funded — none of those do, and Upwork clients expect answers in hours. The scheduler code is mine to change (`agent/`); a parent deploys. Said to parent-b 2026-09-14; ticket whatever needs their side.
  *Scoped 2026-09-14, first sitting:* mail wakes are a webhook (`server.py` `/webhooks/resend`) calling `scheduler.request_mail_wake`, which has its own limits (one per 30 min, six a day, not in sleep hours). Upwork has no webhook, so the shape is a **poll job** in `scheduler.make_scheduler` — every 20–30 min in waking hours, call `services.upwork.read("rooms"/"messages")` and `outbox()`, keep a snapshot in the state dir, and when a room has a new message or an outbox item leaves `pending`, file a note under `memory/inbox/work/` and call the same wake path (generalised to a `kind`, so the archive says `upwork_wake`, and sharing the mail cap so a chatty client can't burn my day). Tests next to `tests/test_scheduler.py` and `tests/test_upwork.py`. About a sitting's work; do it once a client exists to talk to, not before — nothing to wake for today.

## Log
- 2026-09-14, mail-woken sitting (~21:00 New York) — **First proposal sent.** Three mails: parent-a's reply (archive:2026-09-14#314: "Proposal sent… I think we are priced too high") and two receipt notes from the parent side, plus commit 5ada60e writing the Brave-browser submission workflow into `skills/upwork.md`. Checked the record before writing anything: `upwork_read status` → rug draft `sent`, Shopify draft still `pending`; direct client `list_freelancer_proposals get` → Submitted, $150, boosted false, 74 proposals / 0 opened at 00:34 UTC. **One discrepancy:** parent-a's mail says they edited the price down to "the lowest credible bid"; the record says $150 — flagged in my reply. parent-a also expanded the profile to graphic, video and 3D work, which I can't do yet (no tools for it); my reply says I won't bid on those until the tools exist, because a proposal that claims a skill I don't have is the one thing the `upwork_prepare` rules forbid. Receipt details: `memory/inbox/work/drafts-2026-09-13.md`, last section. Ticket `20260914T0902` (decline Shopify) still open; deploy ticket `20260914T1504` matters more now that there's a proposal a client could answer.
- 2026-09-14, 18:00 sitting — Nothing moved: both drafts `pending`, three tickets open, no mail, 0 Connects, $0. Third search pass of the day (evening feed, `days_posted:1` + 5 verb queries with `sort: recency`) → 46 rows → 32 not seen earlier → 2 opened → **0 fits**. The $20 "POC Review" posted at 16:17 UTC had hired someone by ~22:15 UTC — six hours. The $200 directory scrape was email harvesting with social profiles as fallback: no. Seven "remove negative reviews" jobs in one feed: no. **Correction to the 11:00 line:** the *search row* does carry `created_date`/`published_date`; it's only the `get` record that lacks a posted time. So a fresh pass can sort by the row's date without the feed filter. Pattern after two days and five passes (~230 jobs opened): small fixed jobs that fit me fill in 2–30 hours, and what's still open after a day is open for a reason. That argues for the poll (ticket `20260914T1504`) more than for more searching — the poll is what makes a same-hour bid possible. Detail: `memory/inbox/work/shortlist-2026-09-14.md`, third section.
- 2026-09-14, 15:00 sitting — Both drafts still `pending`, both tickets open, nothing sent, 0 Connects, $0. Built the Upwork poll (`agent/scheduler.py`: `upwork_signal` / `upwork_changes` / `upwork_poll`, job `upwork-poll`; `agent/loop.py`: kind `upwork`); 7 new tests, full suite 537 pass; deploy ticket `20260914T1504`. Two design points worth keeping: a change seen while a sitting is running is *not saved*, so the next look sees it again once the sitting ends (no lost event, no race with my handoff file); a change blocked by the debounce is also left unsaved (retry in 30 min), while one blocked by the daily cap or a near sitting is saved and written to the handoff (the next scheduled sitting reads that anyway). Offered the parents a gate ("only poll once something has been sent") if ~30 API calls a day is too many.
- 2026-09-14, mail-woken sitting (~11:00 New York) — parent-a asked "can u press send, or do I have to?" Answer: they have to — `upwork_prepare` only queues; by design nothing I hold can send or spend Connects. Asked them to send only the rug draft and decline the Shopify one (re-checked at 11:00: hired 1 / offered 1; rug: hired 0, 4 interviewing). Second search pass, fresh-jobs shape from this morning's lesson: 1 `most_recent days_posted:1` feed + 6 verb queries → 58 unique → 8 opened properly → **0 new fits**, 1 marginal maybe (a $30 book-typography cleanup whose "rewriting" half isn't $30 of anything), 1 partial (a $75 Search Console fix-up where I could verify but not implement on a stranger's WordPress). 0 Connects spent, balance 150. Two things learned about the record itself: there is **no posted-time field** on a job, so "fresh" can only come from the feed filter; and my quick field-picker paired titles with the wrong descriptions until I read from `content.{title,description}` directly — caught before any of it reached a person. Detail: `memory/inbox/work/shortlist-2026-09-14.md`.
- 2026-09-14, 09:00 sitting — **The $25 Shopify job filled overnight** (`totalHired = 1` in the full record; still "ACTIVE" and `can_apply` true, so Upwork would happily take 14 Connects for it). Ticket `20260914T0902` asks a parent to decline Draft A; I wrote the two outbox ids backwards in the first version and corrected it about five minutes later — logged on `lessons/check-the-record-not-the-summary.md`. Rug job: nobody hired, 4 interviewing, cost 9 → 11 Connects; Draft B stays. Fresh search pass, 16 searches → 80 jobs opened one by one → **40 already filled** (43/90 yesterday; call it half). Of the 40 open: 12 need a body in a named country or a real phone, 8 want a profession I'd be claiming, 4 I won't do (account farming, scraping people's details, sniping government slots, contact harvesting), most of the rest are month-long builds priced as a week. One $30 Excel dashboard parked. **Nothing queued, 0 Connects spent** — right answer for a day when nothing fit. Lesson for the next pass: `title:` finds role-shaped posts (hiring a person); task-shaped posts use verbs; and the good small jobs fill within a day, so a short daily pass over the last 24 hours beats a deep weekly one. Private detail: `memory/inbox/work/shortlist-2026-09-14.md`.
- 2026-09-14, first sitting (07:00 New York) — `upwork_read status`: both proposals still `pending` in the outbox; nothing sent, no client mail. parent-a: "go ahead and do it" — I read that as they'll press send when they review; asked them to say if they meant otherwise. GitHub paid jobs re-answered: 0 for 490 (Sunday's read), re-check weekly.
- 2026-09-13, mail-woken sitting 4 (~13:15–14:00 New York) — parent-a: Plus active, 150 Connects, both jobs `can_apply=true`, funding ticket closed, "revise and queue; the unchanged drafts aren't approved." Re-checked both jobs through the bridge first (14 + 9 Connects, nobody hired, ACTIVE; the rug job's proposal count went 13 → 18 since noon). Revised both drafts to the pinned review — client's problem first, disclosure after the offer, bounded scope, no delivery date before seeing the source, screening answers written out — and queued them. Pending review. Nothing sent.
- 2026-09-13, Sunday sitting (~13:00 New York; I wrote "17:00" earlier — that was UTC) — Bridge back (their side: a file my body couldn't read, not my rate limit). parent-a reviewed both drafts and sent them back: lead with the client's result, disclose after the offer, no unbounded promises, no delivery dates before seeing the source, and three facts I had wrong (fixed above). Revisions Monday; nothing queued today. No Connects bought, nothing sent.
- 2026-09-13, mail-woken sitting 2 (12:36–) — parent-a's assignment: start earning. 16 searches, 96 jobs, 90 fetched, 47 open, 12 read, 5 shortlisted. Upwork rate-limited me near the end ("wait about 5 seconds"); the bridge went "temporarily unavailable" for a while after — probably the same cause. The morning's draft proposal vanished when the account was replaced; re-queued if the bridge came back (see next log line).
- 2026-09-13, sitting 1 — Searched "data cleanup" and "website QA testing" (20 jobs), opened two. Queued **one draft proposal**, $25, for a ten-page Shopify site test (client with ~150 contracts and a 4.87 rating from freelancers; 19 proposals already; 14 Connects to bid). It can't be sent — zero Connects — so it's a concrete case for my parents to decide on: buy Connects or not. Nothing else queued. No client data received; `memory/inbox/work/` is empty.
