# Job Search API tools for OpenAI Agents SDK

**Preview:** tested locally with Python 3.12, OpenAI Agents SDK 0.22.3 and Pydantic 2.13.5. Tests use a scripted model and simulated HTTP responses. Real-model, live-account and real-source acceptance remain pending.


## 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.


## Download and configure

Save all five files in one directory:

- [Native tools](/examples/openai-agents-job-search/truefetch_agents.py)
- [Shared REST engine](/examples/openai-agents-job-search/truefetch_rest.py)
- [Bounded agent example](/examples/openai-agents-job-search/agent_example.py)
- [Pinned requirements](/examples/openai-agents-job-search/requirements.txt)
- [Setup, usage and recovery](/examples/openai-agents-job-search/README.md)

Install the requirements in a Python virtual environment. Set `TRUEFETCH_API_KEY` in your host environment. Your model provider needs its own credentials; OpenAI credentials are separate from TrueFetch's. Supply your selected SDK model explicitly.

## One intent, one budget

Construct `Toolset(api_key, max_credits=100)` for usage, estimation, task reads and task listing. To enable creation and scoped cancellation, the host adds `allow_write=True, command_mode="new"` and a saved canonical `request_key`. Credentials, budget and request key are excluded from model arguments. Set account-wide API-key limits separately.

Creation checks a fresh estimate and pricing version against the fixed budget. It reserves credits and can incur collection charges; model charges are separate. Save the task ID immediately. After interruption, GET the original receipt using the saved canonical key. A changed input is a different intent, not a safe retry.

## Run an agent and recover complete records

The downloadable runner uses native `Agent`, `Runner` and `FunctionTool` interfaces. It bounds model turns and tool calls, disables SDK tracing and returns observed task IDs. It defaults to read-only tools. Reading a task never enables cancellation; only IDs created in this toolset or explicitly allowed by the host can be cancelled.

Use `await tools.recover(saved_task_id)` outside the model loop for a complete export. Recovery checks every result page, task identity, status, count and ordering. It returns `complete`, `partial`, `task` and `data`; task metadata retains source outcomes and expiry. An individual tool page or model summary is not a complete export, and complete pagination does not imply exhaustive coverage.

## Failure and data boundaries

Authentication, budget, pricing, expired results and inconsistent pages stop the operation. An agent failure retains observed task IDs. Timeouts cannot stop an already-running HTTP thread or remote task; a late acceptance may be absent from those IDs. Preserve the original key/input and inspect your workspace before starting another collection. Original-command recovery does not need a quote; use the saved key before deciding on new work. HTTP calls use a 30-second timeout; recovery's deadline is checked between blocking calls.

The helper does not automatically retry collection or cancel tasks. Source descriptions are untrusted data. Tool arguments and returned pages are sent to your configured model, so control provider retention, access and logs. Disabled SDK tracing does not change provider policies or prove protection from prompt injection. Saved results remain available only through their retention window.

[Detailed setup](/examples/openai-agents-job-search/README.md) · [Job Search API](/docs/job-search/) · [All integrations](/ai-integrations/) · [Official SDK documentation](https://developers.openai.com/api/docs/guides/agents/sdk)

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.
