# Workflows

Six sequences this corpus is for. Each names the calls in order, what to carry
from one step to the next, and what it costs — so you can propose the right
sequence before writing any code. Costs are in CALLS, because the caps are per
call and per distinct corpus question; see [tokens, caps, logging](/docs/auth).

> **Note** — One rule runs through all of them: **the per-posting tools are feed-scoped**.
> `get_job`, `get_buyer`, `get_buyer_quality` and `get_job_score` answer only for
> postings this corpus has already shown you — your own search results and your
> saved-search matches. Every sequence below therefore starts with a search or a
> saved search.

## Find work worth answering today

Turn a filter into a short list you would actually spend connects on.

| call                                                  | carry forward                                                             |
| ----------------------------------------------------- | ------------------------------------------------------------------------- |
| 1. `check_prefilter(prefilter)`                       | `matched` — if it is 0 or 40,000, fix the filter before spending a search |
| 2. `search_jobs(phrase \| filters, limit: 50)`        | `jobs[].upwork_id`, and `matched` to know how much you did not see        |
| 3. `get_job_score(upwork_id)` per candidate           | `score` plus the breakdown that produced it                               |
| 4. `get_buyer_quality(upwork_id, ask)` on the top few | the verdict AND `coverage` — read coverage first                          |

**Cost:** 1 + 1 + n + m calls. `check_prefilter` is free of the corpus-question cap; the rest are not.

Score before you qualify. `get_job_score` is about the posting; `get_buyer_quality`
is about whether the client pays, and it is the expensive judgement.

## Survey a market exhaustively

Answer "show me everything matching X", and know when you are done.

| call                                               | carry forward                                          |
| -------------------------------------------------- | ------------------------------------------------------ |
| 1. `search_jobs(phrase: "…", limit: 50)`           | `matched` — this is the size of the job; `next_cursor` |
| 2. `search_jobs(same search, cursor: next_cursor)` | the next page; repeat                                  |
| 3. …until `next_cursor` is absent                  | its absence is the only proof the survey is complete   |

**Cost:** `ceil(matched / 50)` calls. Read `matched` first: 900 matches is 18 calls against your daily cap.

> **Gotcha** — Do NOT narrow the filter to work around the page size. Narrowing answers a
> different question, and sub-searches you invent overlap and double-count without
> telling you.

## Qualify one client before spending connects

Decide whether this buyer is worth a proposal, and how much to ask.

| call                                       | carry forward                                                   |
| ------------------------------------------ | --------------------------------------------------------------- |
| 1. `get_buyer(upwork_id)`                  | `company` and `confidence` — below "high" is a lead, not a fact |
| 2. `get_buyer_quality(upwork_id, ask: 65)` | `coverage` FIRST, then the verdict and `ask_vs_record`          |

**Cost:** 2 calls.

Both are feed-scoped: they answer for a posting a search or a saved match has
already shown you. An id from elsewhere is refused.

## Be told instead of asking

Stop polling. Have new matches pushed as they appear.

| call                                           | carry forward                                                               |
| ---------------------------------------------- | --------------------------------------------------------------------------- |
| 1. `check_prefilter(prefilter)`                | `matched` — a filter that catches nothing looks exactly like a quiet market |
| 2. `save_search(name, prefilter, webhook_url)` | the slug, and the signing secret — shown ONCE                               |
| 3. verify each delivery                        | `X-Moxlade-Signature`; see [webhooks](/docs/webhooks)                       |

**Cost:** 2 calls, then nothing. Deliveries are not calls and do not touch your caps.

Saving back-fills recent matches, so deliveries start immediately rather than at
the next posting.

## Decide whether a skill is worth positioning around

Is there demand, and what does it pay?

| call                                        | carry forward                                       |
| ------------------------------------------- | --------------------------------------------------- |
| 1. `skill_demand(skill, window: "quarter")` | `trend` and `per_day` — never the raw bucket counts |
| 2. `rate_benchmark(skill, country?)`        | p25 / median / p75 / p90 of LISTED rates            |

**Cost:** 2 calls.

The newest weekly bucket is always short. Compare `per_day`, or read `trend`,
which excludes partial weeks. And these are rates freelancers ASK — what buyers
paid is `get_buyer_quality`.

## Audit your own agent

Find out what it actually did, and why it stopped.

| call                         | carry forward                                                          |
| ---------------------------- | ---------------------------------------------------------------------- |
| 1. `get_plan()`              | `usage` — today's calls and distinct questions against their caps      |
| 2. `get_activity(limit: 50)` | every call with its arguments, and the refusal text when there was one |

**Cost:** free. Neither counts against the corpus-question cap.

Refusals are the useful half: a cap being hit looks identical to a broken tool
without them.
