skip to content
terminalhire documentation

Post a paid task

You can start with a repository, a spec, a conversation, or a rough idea. Save the project context first, or go straight to a task on an existing repository. A posting turns the task you are ready to hand off into something a developer can find, claim, and finish — without you handing over repository access to a stranger first.

This is the other half of claims. That page is written for the developer doing the work; this one is for the person paying.

Who can post

Anyone signed in. Posting is tied to your GitHub account, so there is no separate login, no company profile to fill in, and nothing to apply for. The one account that cannot post is one we have revoked, and that is not reversible from inside the product.

Sign in on the web at /dashboard and open the posted tab.

Three things gate the moment you publish, not saving project context. Publishing refuses by name until each is in place:

  • the TerminalHire Poster GitHub App installed on the repository you are posting about — that is the App's name on GitHub; look for it under that name
  • a saved card
  • a confirmed contact email

During setup, sending the email link keeps you on the email step until you confirm. Leave that tab open: it checks automatically and moves to the next unfinished step. The email confirmation page also has a Continue setup link. If your mail browser isn't signed in, sign in with the GitHub account you started with to resume setup.

You can skip a step for now and return to it by choosing it in the setup checklist. Any unfinished step is available there, including Payment details. Skipping does not complete a step: a saved card, confirmed email and repository access are still required before publishing.

You can ask before you draft anything: publish_requirements names whichever of the three is still missing, and changes nothing.

Setting up from the terminal

terminalhire setup walks the same checklist without the setup page. It opens your browser to sign in with GitHub and approve a connector for this terminal, the same approval an AI tool asks for. Tick act on my behalf there: the card and email steps need it, and so does publishing from the terminal. Setup then opens the GitHub App install, opens the payments panel on your dashboard to save a card (the card never passes through the terminal, and saving it charges nothing), and asks for your email address and mails a confirmation link to it. Each step turns ✓ when the server sees it done. If setup cannot read the checklist, the rows show ! and it does not say you are ready.

  • If the GitHub App covers more than one of your repositories, name the one you post from: terminalhire setup --repo owner/name.
  • The email step needs an interactive terminal. Confirmation mails are limited per account, and a refusal says how long to wait.
  • The email step is for first-time setup. Once you have a confirmed address, change it in the browser, under Settings on your dashboard.

Run it again to pick up where you stopped, or terminalhire setup --status to see the checklist. terminalhire setup --reconnect approves a new connector; the old one keeps working until you remove it under Settings → Agent connections.

The connector is stored encrypted on your machine. If Claude Code and the terminalhire command are both installed, setup offers to connect Claude Code, and asks first. It registers a helper, terminalhire connector-header, that Claude Code runs when it connects to get the key. The key stays in terminalhire's encrypted store and is not written into Claude Code's config.

What a terminal can publish

A linked CLI login (terminalhire link) still cannot publish. It lasts thirty days, and that is too long a key to leave lying around for something that spends money. What can publish is a connector you approved with permission to act, and terminalhire setup is one way to create one. With it, terminalhire post submit saves a draft to your account, and terminalhire post publish <draft> --price <usd> shows what a claimant would receive and publishes only when you type yes. Both need an interactive terminal. Publishing checks repository access, your card and your email again for itself. If the server no longer accepts the stored connector, post submit removes it, says so, and sends the draft without it.

Without a connector, terminalhire post still drafts, shows, edits and lists drafts on your machine, and terminalhire post submit sends one to the server and hands back a browser link to confirm. Accepting finished work and paying stay in the browser either way, except that a posting set to accept automatically is accepted and charged 24 hours after a missed decision deadline.

After your first successful publish, setup ends with your live posting and a completed checklist. Choose Open your posting to enter its workspace. If your AI publishes through the connector while the draft is open in your browser, the page catches up automatically when you return. The draft link also takes you to the posting it created.

Writing the posting

Starting with an idea, a spec, or code held by a builder

Start with Project context. You can save a project before you have a repository, a card, or a complete spec. Your agent can use begin_project on the poster MCP, ask the returned questions in your conversation, and resume later with list_projects and get_project.

The interview covers intended users, desired outcome, a concrete workflow, the first deliverable and its exclusions, acceptance evidence, operating constraints, available code and a verification plan. Reuse facts from your spec and authorized code. Proposed or disputed answers stay visible until you review them; questions that affect a later task can remain open.

Choose the repository owner and destination before connecting it. Your agent can create the repo using its authorized GitHub tools, or you can use GitHub in the browser. For builder-held code, keep the existing work and agree where it belongs. Commit the exported PROJECT_CONTEXT.md on the default branch and install the TerminalHire Poster App on that repository. Confirming checks the actual committed file. In the browser, the project page walks these steps under Connect a GitHub repository: create the repository, give TerminalHire access (GitHub brings you back to the project), then connect it to the project. Organization repositories can take a moment to appear after you give access.

Your agent then drafts a task with the confirmed project and revision. The context is included in the developer’s task handoff; later project edits do not rewrite an existing task. The normal repository, disclosure, card and contact checks still apply. A context-only repo has a real starting commit, but may have no runtime or tests: the first build task needs a runnable test command and acceptance tests.

“Project context” describes the task you want built. The Contribution Dossier describes a developer’s past work.

Six fields carry the weight:

Field What it is for
Title One line, at most 140 characters.
Spec What a developer actually works from. Worth ten minutes.
Price Whole dollars. Below the minimum, the refusal names it.
Repo owner/repo. Mark it private if it is.
Stack tags Up to 12, 40 characters each. They decide who sees this.
Claim mode Open, or approval-only.

Optional: a link to an existing issue, an effort estimate, and a note about whether you welcome AI-assisted work. The effort estimate reaches the public index as small, medium or large, whatever words you put in.

Reference links. One URL per line in the reference-links box — a Loom walkthrough, a Figma frame, a gist. Your assistant can attach them too, and is asked to prompt you. They appear on the developer's page as links, beside the task, for anyone the posting delivers to. Six at most.

https: only, and that is the whole rule. A link on any other scheme is refused, and the refusal says which one — "the 2nd reference link must start with https://" — counting links from the top, so you can fix it rather than hunt for it. Blank lines are ignored and are not counted.

Links to Loom, YouTube, Vimeo, Figma, GitHub and gists are labelled with what they are. A host we do not recognise still works — it just gets no label, because we have not looked at the page and will not say we have.

They are not embedded and they are not fetched. A reference link is a link.

Set them when you post. They cannot be changed afterwards — the revision record has no column for them, and a field that took your keystrokes and dropped them at the save would be worse than one that says so.

Tags are how developers find you. Matching runs on tag overlap, so a posting tagged rust and postgres reaches people who work in those; an untagged one reaches nobody in particular. Write them the way you would write them on a job post.

The spec is the deliverable. A developer decides whether to claim by reading it, and then works from it. A one-line spec produces one-line work, and the price you set does not fix that.

A posting needs a public task description. It is what a developer reads before claiming, and publishing refuses one that lacks any of three sections:

## Outcome

What is different when the work is done.

## Scope

What is included, and what is not.

## Acceptance

How you will judge the result: tests, behaviour, review criteria.

Each needs at least five words or 25 characters, and a placeholder such as TBD or n/a does not count. ## Deliverable, ## Project context, ## Constraints and ## Open questions are welcome but optional. The headings Goal, Objective, Scope of work, Acceptance criteria and Definition of done count too.

The composer's box starts with the headings. If one is missing when you publish, nothing is published and the refusal names it; the confirmation page's box is where you fix a draft. Postings published before this rule are not re-checked, but an edit cannot turn a complete description into an incomplete one.

From an assistant, send it as publicBrief with outcome, scope and acceptance filled in. draft_posting saves a draft without one and says what is still missing.

Work no test suite can judge. For docs, design, config or anything else a test suite can't judge, choose "No automated tests — I'll review the work myself" when you publish. From an assistant, send reviewMode: "poster" to draft_posting or confirm_posting. We then don't look for a test command, and we don't run tests on what is submitted: no workflow of ours is added to the claim branch and no run is queued. You read the work and decide whether to accept it. Your posting's decision terms apply as they do to any other posting. You can't name a test command on the same posting, and the choice can't be changed after publishing; withdraw and post again instead. Your own repository's workflows may still run on the claim branch, but we don't record or report them.

Otherwise, a posting needs a test command. When you publish, we read the repository at the commit being posted and look for the command that runs its tests. If we find one, the confirmation shows it and asks nothing. If we find none, publishing stops and names the files we looked in, and you type the command yourself — one line, such as npm test or pytest. When we also cannot tell the language, pick it from the list beside the field. If we found a language, or a command that runs only the tests, you cannot replace it: publishing refuses one you send, and a draft's is dropped. If the command we found also runs checks such as lint, formatting or typechecking beside the tests, you may name one that runs only the tests, and verification runs yours instead. From an assistant, publish_requirements and confirm_posting say when this applies.

The command you name must be plain, because developers see it before they claim: a build or test tool, or a ./ script, followed by words, flags and relative paths, such as npm run test:ci. It may start with a cd to a relative path with no .. in it (cd apps/web && npm test) or with plain settings such as CI=1 npm test, name a workspace (npm -w @scope/web test), and give a flag its value with = (--reporter=dot). Publishing and editing refuse anything else, and a draft's is dropped. Put pipes, quoting or $ references in a package.json script and name the script. A command that looks like it carries a secret is refused too: a token-like value, a flag or setting named like a key, token or password (--token=abc, API_KEY=abc), or any word that is not a short plain word or a path made of them. A short plain word is 24 characters or fewer of lowercase letters and digits, or letters only, such as a test name, and one is shown wherever it appears. A file name made of such a stem plus a lowercase extension of up to 8 letters and digits, such as test_user_authentication.py, is shown too. A secret written in either form would be shown. Keep secrets in your CI settings, never in the command.

Before they claim, developers see whether we found the command or you named it, and they see the command itself unless it is one we found that is not plain; that one they read in the repository after claiming. You can change it while the posting is unclaimed. Once someone claims, it is frozen with the rest of the terms.

If we cannot read the repository to look, nothing is published and the draft stays. Try again in a moment; we never treat a failed read as "no command".

An acceptance command, for this task alone. Your assistant can also name a command that runs the tests for this one task, such as npm -w web test. Send it as acceptanceCommand to draft_posting, confirm_posting or edit_posting; the web form does not offer it, and it keeps what your draft named. It sits beside the repository's own test command and never replaces it, so you can name one even when we found a command. It follows the same rules as a test command you name. We record it with the posting and show it to developers before they claim. If the full command fails, we run it on its own, on a fresh copy of the submitted code, with a five-minute limit, and show its result under the verdict. You can change or clear it while the posting is unclaimed, and it is frozen once someone claims. A posting you review yourself can't carry one.

From an assistant, the same two values are testCommand and runtime on draft_posting, confirm_posting and edit_posting, and publish_requirements says in advance whether you will need them.

Developers see what the task needs before they claim. The same read records the runtime and its version, the install command, the test command, and any services the repository runs beside the app, such as a database. The posting page shows them under "What you'll need", with the commit they were read at. It also shows when the lockfile is behind the manifest, because then the install cannot use it.

Some things the repository does not say. The confirmation page asks for those, and every answer is optional:

  • the runtime version, when no file pins one;
  • the image for a service the repository names without one, whose tag, name parts and registry host labels follow the same short-plain-word rule as a test command, so postgres:16 is accepted and a tag like Tr0ub4dor3 is not (a secret written as a short plain word, in the tag or in a host label, would still be shown);
  • operating-system limits, which no file records, such as "Linux only".

A field you leave blank shows developers "unknown", never nothing. The page only asks what the repository leaves open. An answer to something it already settles is refused, and the repository's own value is what developers see. A runtime version you give is shown to developers; it does not choose the image a verification runs in.

From an assistant, the answers are runtimeVersion, os and serviceImages on draft_posting, confirm_posting and edit_posting. publish_requirements lists which are open. A draft is written before the repository is read, so when it is confirmed, any answer it carries that the repository settles is dropped rather than refused.

If a developer claims the task and cannot set it up, they can hand it back as setup-failed and name what failed. why_not_claimed counts those hand-backs per posting, and says which requirement failed once three or more developers have said so.

Drafting from an AI assistant

You do not have to start in the browser. Point any MCP client — Claude Desktop, Claude Code, Cursor, or a hosted service like Lovable — at https://terminalhire.com/api/founder/mcp and your assistant can draft a posting from whatever it already knows about the problem. Nothing to install. Field-by-field setup, including where to get the token, is under "a host that only accepts a URL" in the MCP page.

It will ask you which repository the task is in, if it does not already know. That is required, not a nicety: the confirmation page resolves the commit against it. The repository cannot be added there afterwards. An assistant that drafts without one gets a refusal telling it to ask you rather than guess — a guess would point a public posting at someone else's code.

A draft gives claimants the whole repository at one commit. Your assistant cannot ask for a narrower tier: a draft that names sparse or issue-only is refused, with a message telling it to leave the tier out. draft_posting writes a draft and hands back a link; you open the link, sign in, read the posting rendered, and confirm it. The link grants nothing on its own — opening it signed out asks you to sign in, and drafts expire after a day.

It can carry your reference links across. If you have already pasted a Loom recording or a Figma frame into the conversation, your assistant can attach it to the draft rather than leaving you to paste it again in the browser — and it is told to ask you whether you have anything of the kind, in case you did not know the option is there. The links are listed on the confirmation page before you confirm, so you see every link a developer will get. Same rules as the box: six at most, https: only.

It can also publish that draft, if you let it. A connector you created with permission to act can call confirm_posting, which publishes a draft using your repository and your saved card. Once the publishing requirements are met, an unacknowledged full draft returns an exposure notice with the repository and commit, and waits for acknowledgment. A sparse draft saved before that rule returns the selected files and waits for confirmed counts. Neither preview publishes the posting. For a full draft, the second call has to echo that notice back with a signed token we issued for that draft, repository and commit, which goes stale in half an hour; a sparse draft's second call echoes the counts instead. A read-only connector — the default, and every connector created before this existed — can draft and read but cannot publish.

The posting tools. Tools marked acts need a connector you gave permission to act. Project context tools are described in MCP.

Tool What it does
preview_work reads a public task preview without claiming
draft_posting writes a draft, publishes nothing
publish_requirements which of App, card, email is missing
list_own_postings your postings, never the claimant
pending_count how many wait on a decision from you
claim_progress per posting: attempts, latest check, claim id
claim_verification the newest verification run for a claim
get_claim_patch a held patch and why it was held
get_check_results the sandbox check run for the latest submission
why_not_claimed decline reasons, hidden under three answers
start_card_setup (acts) links to your dashboard to save a card; no charge
send_contact_confirmation (acts) first-time only: mails a link to confirm your email
confirm_posting (acts) publishes a draft you reviewed
edit_posting (acts) changes a published posting of yours
withdraw_posting (acts) takes an open posting down
request_changes (acts) asks the developer for another pass
rerun_verification (acts) runs a claim's tests again at the same commit

list_own_postings, pending_count and claim_progress name no developer at all.

draft_posting, publish_requirements and edit_posting take decisionWindowHours (24 to 48, 48 if you leave it out). edit_posting refuses to change it once the posting has a claim. See Your decision deadline.

Every tool but draft_posting and preview_work needs a connector token in the Authorization header — you create one per client in the dashboard, it is shown once, and it works until you revoke it. draft_posting works without one; hand it a token and the draft is attributed to you and the reply names any publishing requirement you are still missing.

No tool on that server moves money. Accepting finished work, releasing funds, refunds and settlements happen only in the browser, signed in as you. The one exception is a posting that accepts automatically: it is accepted and charged 24 hours after a missed decision deadline (see Your decision deadline). Publishing a posting binds your saved card to it so a claimant knows the task is funded; nothing is charged until the work is accepted.

A posting you confirm this way is recorded as agent-drafted and human-confirmed. It is your posting; the record just says how it was written.

Open or approval-only

An open posting can be claimed by any developer approved for paid tasks — that is an invite list while the beta runs, not a score or a history check — who has also set up payouts. Good for a public repo where a pull request is the whole interaction.

An approval-only posting shows up in the same places, but a developer registers interest and you decide. Nothing about your repo reaches them until you do.

If your repo is private, approval-only is forced — not as a default you can change, but on the server, whatever the form says. For a private repo, approving someone is granting them access, and that should never be something you did by leaving a checkbox alone.

What developers see before you approve

A posting on the public index carries your title, your public summary, an opaque handle like founder/b-9f2c4e6a8b0d, the price, the tags, the effort estimate, and the claim mode.

It does not carry your repository, your name, your GitHub login, your email, the issue link, or your spec. That holds whether the repo is public or private, and whatever you choose below.

Your title publishes by default, on a private repo as much as a public one. That is new. It used to be held back, and the result was a listing that read Private-repo posting · typescript, react and told a developer nothing about the work — so nobody could decide whether to claim it without claiming it first. Hiding the title was never what kept your repo private; the fields in the paragraph above are.

If a particular title says more than you want a stranger to read, untick show this title on the open listing in the composer. That posting falls back to a stand-in built from your tags — Undisclosed posting · rust, postgres — so a developer can still tell two of them apart. You can change your mind later, including while someone is working on it: the setting is on the edit form and it is not frozen by a live claim.

Decide it before you publish, not after. The tick box is on the confirmation page too, so a posting your agent drafted carries your answer from its first moment. The index is public and cached for a few minutes, and the terminal client keeps its own copy, so a title held back an hour after publishing was still read for that hour. Turning it off later stops the next reader, not the last one.

Your public summary publishes either way. That is the one thing the tick box does not touch. You write it in a box that says it publishes and you approve the exact characters, so it is the piece of prose that was always meant for strangers — which is why there is no version of it to hold back.

Once you approve someone, they get the title and the spec whatever this setting says. How much of the code they get is covered in the next section.

How much of a private repo you hand over

A new posting gives the developer the whole repository. The dashboard composer offers no other scope, and neither does a draft your assistant writes: a request for issue-only or sparse is refused, not widened. We retired those two because choosing files by matching words in the spec does not work on a large repository.

Full. The developer clones the repository through a read-only credential scoped to that one repo. The credential can read its history and every branch. You acknowledge that exposure before publishing.

Postings made before this keep their recorded scope, so two narrower scopes are still live on older postings:

Issue-only. You list the files yourself and can supply their contents. What we ask GitHub is about access and identity — whether our App still covers the repo, whether it is private, and which repository it is — and none of it opens a file. The developer gets the repro, the stack trace, and the confirmed file list with any contents you supplied, as a copy, with no access to the repository.

Sparse. With our App on that repo we read your tree to propose a slice; without it you paste the file list and we propose from that. Nothing is stored until you confirm the counts. We start from the files your issue text points at, then add whatever those files import, so a file arrives with the things it refers to rather than on its own. That widening follows imports wherever they lead: a slice can span several directories and pick up build config from the top of the repository, so read the counts rather than assuming a boundary. It is sent as a copy, still with no access to the repository.

For issue-only and sparse, the screen shows you what is being left out as well as what is going: "42 files, 1,180 excluded." Read that number. A list of included files looks equally reasonable whether it is tight or far too generous, and the excluded count is the only part of the screen that tells you which one you are looking at.

For issue-only and sparse, we scan the files before they go. A clear credential — a key, a token, a private key block, a .env — stops the hand-off outright and we send nothing. Two blurrier shapes go a different way: a password inside a connection string, and a bare assignment that reads like a secret. Most of those turn out to be fixtures, docs and templates, so we put them in front of you instead, and they travel only if you tick each one. That hard stop arrives while you are still scoping, before the posting exists, so a tripped scan never leaves you with a live posting that can't deliver code; the same scan runs again when you confirm, as the authoritative second check. Every file that arrives with content is scanned, even one outside the slice — if you sent its bytes, we looked at them. The same happens if the scan cannot finish: a scan that did not complete has not cleared anything, so we treat it as a refusal rather than a pass. You will see what tripped it and can narrow the slice or fix the file.

For full, there is no scan, and there cannot be one. The code travels from GitHub to the developer through a short-lived read credential and never passes through us, so there are no bytes on our side to check. We will not ship a scanner that gives the feeling of having looked when it has not. Instead you confirm, before the posting exists, that everything committed to that repository is exposed and that rotating it is yours to do. Read that literally: the credential is scoped to the repository rather than to the files we show you, so it reaches history as well — a secret committed once and deleted in a later commit is still in there. No confirmation, no full posting.

Every new posting therefore requires an exposure acknowledgment before publication. There is no file list to scope, so instead the form reads the commit your default branch is on right now and shows it to you in full, followed by the exposure notice, with a box you have to tick before the posting can be created. If that commit cannot be read — usually because the App is not installed on the repository — the form says so and refuses to post. It will not fill in a placeholder: a notice naming a commit nobody identified is not something you can meaningfully agree to.

Connecting your GitHub App

Every posting needs the TerminalHire Poster GitHub App installed on its repository, public or private. You install it once, from the dashboard, and pick the repositories it can see — GitHub asks you, not us.

When GitHub asks, pick Only select repositories and tick the ones you'll post from. GitHub starts on All repositories, and a posting that shares your code can't be published from an install left there. You can add repositories later in the App's settings on GitHub.

The App is how the code gets out and how the patch comes back. It reads contents and pull requests, and reads your Actions runs so we can show you whether the checks passed. It never holds a key of yours: access is a short-lived token we mint per posting and throw away.

Uninstall it whenever you like, from GitHub's settings. Postings that depended on it stop being able to deliver code, and say so on the row rather than failing quietly.

The work comes back as a patch

For a private repo the developer never pushes to your repository. They send a patch, and we apply it on a branch in your repo, on your behalf.

On a whole-repository posting they do hold a credential, and it is worth being plain about it: read-only, scoped to that one repo, and short-lived. It cannot write, which is why the patch is still the only way work comes back. On an older issue-only or sparse posting they are issued no repository credential at all — the files arrive as a copy.

What we check is what the patch does, not which files it names. A change to your CI config, your git internals, an install script, a script you already have or a new one that runs before or after another is refused, and so is a patch we cannot read. You see the refusal rather than a surprise commit. Binary files, such as images, fonts and fixtures, wait for your approval. Programs, executable files, and binary files over 2 MB (3 MB per patch) are refused. A package-lock.json (v2 or v3) or yarn.lock (v1) change is accepted with the package.json change it belongs to, when every new entry comes from the npm registry with its integrity hash. Other lockfiles are refused. A dependency change, a new script, or a manifest change we can read, applies without asking you. That is the default, and it stays on unless you turn that off — Dependency and manifest changes, on the posting page, covering every posting on that repository. With it off, each of those waits for your approval instead. A manifest change we cannot read waits for you either way.

That list is the whole of it. A patch is not held to the files you disclosed, so read the branch before you accept.

Your own CI runs on that branch, because it is your repository and your workflows. When the run finishes, the result lands on the posting row: passed, failed, or still going. That is the evidence you decide on — your tests, not our claim about them.

Two other readings sit beside it. get_check_results reports the sandbox check run for the latest submission — status, counts, a trimmed log tail. claim_verification reports the newest verification run for the claim, which is your suite executed in a fenced container with the network cut. Both are also on the claim's panel in the dashboard.

Deciding

The posted tab sorts by what is waiting on you. A row marked needs you has a developer blocked on your answer, and the badge opens the panel that answers it.

When work arrives you accept or reject it, with a rating from 1 to 5 and an optional note.

For a paid task, save a card first under Settings → Payments. Saving the card and publishing a posting do not charge it; a saved card is not escrow and does not guarantee a later charge will succeed. The final button reads accept — pay $X. That explicit click charges the posted amount. Only after Stripe confirms the charge does TerminalHire record the public acceptance and reserve the developer's payout.

The developer receives 90% of the posted amount. TerminalHire retains 10% and absorbs Stripe's processing fee. If the card is declined or needs authentication, no acceptance or payout is recorded; replace the card or retry from the same decision panel. A retry on the same card after a decline can change the rating or note. If you replaced the card first, send the rating and note you used before. Most refusals name their cause in the panel, such as a price below the minimum. If your bank asks you to confirm the payment and you don't finish, you can still reject, ask for changes, withdraw, or release the developer's claim: we cancel the unfinished payment first. An unfinished payment is also cancelled automatically after 24 hours. Withdrawing before acceptance needs no refund, because nothing was charged — with one exception: a posting paid for up front, before this was how it worked, is refunded from the dashboard rather than withdrawn.

Rarely, a payment goes through but we can't record the accept automatically. Nothing is accepted or paid to the developer yet. A person on our team reviews it, and we email you, even if you have turned off our other emails.

An accept is public. A reject is not. An accepted decision becomes part of that developer's credential — the thing they can show someone else. A rejection is your private judgment of a person's work, and publishing it would turn claiming a posting into a bet on a stranger's mood. Rejections stay between you and them.

Notes are redacted after 180 days. The decision and the rating stay.

Your decision deadline

When a developer first submits work, you have your posting's decision window to accept, reject or ask for changes. The window is 48 hours unless you set a shorter one when you post, down to 24 hours. A claim keeps the window it was claimed under, and once a posting has a claim its window cannot change. The claim page shows poster decision due with the time, and the developer sees the same line on theirs. Asking for changes stops the clock while the developer works, and the line goes away. Their resubmission starts the window again. Accepting or rejecting ends the clock.

We email you twice before the deadline: halfway through the window, and again when an eighth of it is left. On the 48-hour default that is 24 hours and 6 hours before. The emails follow the same notification settings as the others. The deadline cannot be extended.

If the deadline passes, the claim reads not decided in time, and the developer sees that on their claim and in their notifications. On a posting that accepts automatically (the default, unless you ticked Don't accept automatically on this posting when you published), you then get 24 more hours to accept, reject or ask for changes. After that the work is accepted and your saved card is charged. New work submitted during those 24 hours starts your window again from that submission.

On a posting that doesn't accept automatically, nothing else happens: no work is accepted, no card is charged and the claim stays open. You can still accept, reject or ask for changes afterwards.

How you hear about it

Eight things get you an email: a developer claims your posting, a patch arrives, your checks finish, a pull request opens, a submission is blocked, a patch is held for your approval, a developer asks to resolve the posting, and a developer leaves you a note. The last four share a test — you are the only person who can clear them, and there is no other way you would find out. A ninth notice goes out when a claim nobody started is released, and a tenth if your payment goes through but we can't record the accept automatically. Each email links straight to the row it is about; you sign in as normal, and the link grants nothing on its own.

We send to the contact email you confirmed — publishing needs one, so you have one. If for some reason there is no confirmed address, we fall back to the public email on your GitHub profile, and with neither you get no email. Nothing else changes either way: the posted tab is exactly current whenever you open it.

Turning them off. Three ways, and only the last needs you to sign in:

  • Your mail client's own Unsubscribe button stops all of them except the held-payment email.
  • Each of the eight carries two links: stop all these emails, and stop only this kind. The release notice is the exception — it has no per-kind switch, so only stop all and the settings page reach it. The held-payment email is not optional: it goes out even if you have stopped all emails, because it is about money we have already taken.
  • Settings → posting notifications in the dashboard, where each kind is its own switch — and the only place you can turn them back on.

Most people want to keep "a dev claims your posting". That is the one where someone is waiting on you.

Turning email off never hides anything. The posted tab and the MCP count always show every event as it happens.

You can also read the same state without opening the dashboard: the poster MCP server's pending_count tool reports how many postings are waiting on you, and reports counts only. claim_progress goes one step further — how many attempts have been made on each posting and what your own checks said about the latest one. It also returns the id of the claim each posting is waiting on, so your editor can open that claim's checks and patch without asking you for a link.

Neither names the developer. That is a rule, not an oversight: the output of an MCP tool goes to whichever model your editor runs, and a developer working on your posting never agreed to that. Both tools are read-only. One tool replies: request_changes sends the developer a note asking for another pass, and only from a connector you created with permission to act. It records no decision and leaves the claim open. Accepting work and paying happen on the dashboard, signed in, and no tool does either.

If a claim's latest run did not pass, or we could not complete it, rerun_verification runs the tests again at the same commit, with the same permission to act. The earlier result stays on record and the new run appears beside it; nothing is relabelled. The developer on the claim cannot ask for this, and a run that passed is not run again. In Claude Code, the plugin's /approvals skill walks these tools for you.

Why nobody is taking it

An unclaimed posting used to tell you nothing, and nothing is the one answer you can't act on. It can't tell "the price is too low" apart from "there's no CI in that repo, so I couldn't check my own work" — and only the first is fixed by paying more.

Developers looking at your posting in the CLI can now say why they're passing, picking one of five fixed answers. The why_not_claimed MCP tool reports what came back: how many passed on each posting, and the breakdown of their reasons.

It is counts, like everything else here. No developer is named, we store no login against an answer, and the breakdown on a posting stays hidden until at least three people have answered — below that you see the number who passed but not what they said, because one answer in a small group can point at a person. A posting with no declines and no claims is its own answer: nobody is getting far enough to have an opinion.

The posting's own page

Every posting has one, and the title on the posted tab is the way in. The tab is a queue — what needs you, what it costs, what state it is in — and the page is where you go to remember what a posting was about and to act on it.

It holds the task you wrote, everyone who claimed it, the whole history in one stream with each line attributed, the money, and your own rating on the entry it belongs to. The actions live there too: approving a developer, deciding on their work, editing the terms, withdrawing.

Editing. Until someone claims it you can change anything. After that the terms are frozen and you can still raise the price — a developer agreed to what was written, so changing it under them is not something we let you do. Every edit is recorded, and the page shows the history.

Two people waiting. If two developers are claiming one posting, both decisions are on the page, each next to the person it is about. The tab can only show you one.

Saying something to a developer

There is a box on the posting's page. Two buttons, because they mean different things: send note leaves a comment and changes nothing, and ask for changes marks their claim as needing another pass.

Your developer sees it the next time their CLI syncs. There is no email and no push on their side — the CLI shows them a count and they run terminalhire claim notes to read it, or ask their agent to. So a reply may take a while, and silence is not being ignored.

A developer can write to you too, from their terminal. Their note appears on the posting's page, and you are emailed that it arrived, without the text. Each developer can send at most 3 notes per claim and 10 to you in any 24 hours. If a note looks wrong — a pasted secret, text that reads as if a repository wrote it — use Report this note under it. The report is recorded, and we try to email it to our operators; if that email does not go through, reporting the same note again sends it. A report hides nothing and changes nothing on the developer's side by itself.

Reading how the work went

Open a claim and you get the whole run of it: every attempt the developer made, the short commit for each one, what your checks said, and the notes you sent back, in order. Where you used to see only the latest attempt and a count of the rest, you now see what happened in each.

The developer sees the same sequence from their side, including the notes you write when you send feedback or reject something. Write them as if they will be read, because they will be.

Your 1–5 rating shows on your own timeline, on the decision it was part of. It is never averaged and never shown as a figure of its own — a score lifted away from the work it was about is the first step toward a number that follows a person around.

Each attempt is also kept in your repository under refs/th/attempts/, so the code of an earlier try survives the next submission. These are not branches and not tags: they will not show up in your branch or tag lists, and they cannot trigger a workflow. They are there so the history stays inspectable.

Taking a posting down

Withdraw removes a posting from the index. You will find it on the row in the posted tab, and it asks once before doing it.

A live claim no longer blocks it. Closing a posting ends every open claim on it and tells each developer why, rather than leaving them working on something that has gone. Three cases still refuse. A developer has submitted work you have not decided yet — decide that one first, accept or reject with a reason. The work was already accepted, which leaves nothing to withdraw. Or the posting was paid for up front, which goes through a refund instead: that takes it down and returns the money.

A withdrawn posting stops appearing to developers within a few minutes.

There is no delete. Withdraw hides it from developers; the record stays, because decisions attached to it are part of somebody's credential.

A patch is accepted only from the person whose claim it is. They record the claim against their signed-in GitHub account, work in a clone of your repository, and submit from their terminal; it lands on a branch in your repo with their commit authorship. On an approval-only posting nothing is delivered and no patch is accepted until you approve the claimant from the needs you panel.

Explore the work and its project

The Work area has views for Working on, Posted by me, and Find work. Developer accounts keep the dark workspace across these views; poster-only accounts use the light workspace. A task opens with its next action, readable task description, progress, and activity.

Open Projects to explore a project's saved goals, workflow, constraints, sources, and open questions before or after it has a repository. The overview labels proposed and unresolved answers. Choose Edit context to review or correct them. Tasks keep the confirmed project context attached when they were prepared; later project edits do not change an existing task's scope. The project's Work section lists the tasks you have posted from it, with each one's status and the context revision it was posted against. A task posted without the project's context attached is not listed there.

Manage editor and agent connectors in Settings → Agent connections.