# Job Search API tools for CrewAI

## Canonical submission and original-command recovery

Use `POST /v1/quotes`, then `POST /v1/tasks` with `productId`, the approved input, the quoted `expectedPricingVersion`, and the canonical `Idempotency-Key`. The host creates a key **once for explicitly approved new work**, using `new_command_key()` from the bundled REST engine, and persists it with the approved input before submission. Keys have the form `v1.<13-digit Unix milliseconds>.<22–64 base64url characters>`. Do not generate keys in model arguments or a workflow retry expression.

Creation requires `allow_write=True`, the persisted `request_key`, and explicit `command_mode="new"`. The default `command_mode="recover"` performs an original-command GET and never obtains another quote or submits again. Repeated invocation in the same new-intent toolset also reads the original after its first submission attempt. Changed input is refused. After a restart or any lost response, use this host-only path; it needs no input, quote or model call:

```python
import os
from truefetch_rest import recover_command, observed_task_id

original = recover_command(
    os.environ["TRUEFETCH_API_KEY"], "tasks.create", os.environ["TRUEFETCH_COMMAND_KEY"]
)
if original["receipt"]["outcome"] == "accepted":
    saved_task_id = observed_task_id(original)
    # Persist the reference before reading the existing task/results.
```

HTTP 200 is not acceptance. Inspect `receipt.outcome`: `accepted` supplies an authoritative task reference even when the current `task` projection is null; `processing` and `unknown` require another explicit original GET, respecting the returned retry interval. `rejected` and `not_accepted_final` establish nonacceptance and require deliberate new work if you still want a collection. `receipt_expired` remains unconfirmed: inspect Tasks or contact support. The helper never seals, replays a POST or creates a replacement key. The server accepts new keys for 24 hours, allows five minutes of future clock skew, and retains terminal receipts for at least seven days; it can still answer a retained original after the new-command window ends. A current Task state does not establish a particular command's outcome.

Cancellation is a separate command: bind a different persisted canonical `cancel_key` for the newly created task, or pass `cancel_task_ids=(saved_task_id,)` and `cancel_command_keys={saved_task_id: saved_cancel_key}` for an explicitly approved saved target. `POST /v1/tasks/{taskId}/cancel` sends `{}` and that cancellation key. Unknown cancellation is recovered with `recover_command(api_key, "tasks.cancel", saved_cancel_key)`, never the creation key. Acceptance of cancellation does not promise the task ended as cancelled. The budget, keys and cancellation mapping remain host-only arguments.


## Native tools for one collection intent

**Preview:** Python 3.12 and `crewai==1.15.22`. Real-model and live-account acceptance are pending. This REST example uses native BaseTool objects and does not require MCP to be open.

**Dependency security:** the pinned CrewAI tree includes ChromaDB with unresolved code execution and authorization advisories. Keep this preview in an isolated development environment with memory and knowledge integrations disabled. Review the [dependency risks and usage boundaries](/examples/crewai-job-search/README.md) before installation or production use.

Download these files into the same directory:

- [CrewAI adapter](/examples/crewai-job-search/truefetch_crewai.py)
- [Bounded single-agent workflow](/examples/crewai-job-search/agent_example.py)
- [Shared REST engine](/examples/crewai-job-search/truefetch_rest.py)
- [Pinned requirements](/examples/crewai-job-search/requirements.txt)
- [Complete workflow and recovery instructions](/examples/crewai-job-search/README.md)

Your application supplies the API key, credit budget and one persisted request key. Default tools are read-only: usage, estimates, task/results reads and listing. Enabling writes adds budget-checked collection and scoped cancellation. Creating a task can incur charges; the application budget is per collection, not a monthly limit.

The tools decline cached results. Disable caching on the Agent and Crew too, so a previous estimate or queued status cannot stand in for a fresh read. Review telemetry and provider settings before handling customer data. Keep credentials out of prompts and source text out of control decisions.

Recover saved task IDs with the deterministic recovery function. It reads every page, verifies order/count and retains partial status; it does not start another collection. A timeout or lost response does not prove the remote task stopped. Retain the original key and input when acceptance is uncertain.

The included single-Agent Crew disables caching and delegation, limits iterations and tool calls, and preserves observed task IDs on failure. Native SDK tests use a scripted model; real-model tool selection remains unverified. Timeouts cannot forcibly stop in-flight requests. This example is not a marketplace listing or a claim of live-model support. See the [Job Search API](/docs/job-search/), [pricing](/pricing/) and [API conventions](/docs/conventions/).

The shared REST reader requires UTF-8 JSON and rejects nonfinite numbers, including numeric overflow. Interrupted or oversized HTTP error bodies keep a redacted HTTP-status diagnostic, and truncated successful transfers fail without returning data. It also rejects duplicate member names within an object, including names written with equivalent Unicode escapes; repeated names in separate records are allowed. Recovery also rejects nonfinite values from tool output instead of marking the export complete. Preserve the saved task ID and original request key after an error; these checks do not create or retry collection.

For each available result page, the reader checks the task again after receiving the page, including the final page. This adds one read-only task GET per page. Changed identity, status, availability or count, or a failed recheck, prevents a complete export. The check is a point-in-time observation; files can expire or become unavailable afterward.

Before collection, the creation tool checks that the estimate matches the requested input and that reserved billing units equal the record limit multiplied by the unit price. Amounts must be positive ASCII integers, and the reservation must fit the host credit budget. Invalid estimates stop before submission; server-side pricing and budget checks still apply.
