# Coding assistants

> Give the assistant the contract, not a description of it. Then check its output against the same contract.

- **HTML:** https://offendersearch.app/docs/ai/coding-assistants
- **Base URL (live):** https://api.offendersearch.app — live keys (`os_live_…`)
- **Base URL (sandbox):** https://sandbox.offendersearch.app — test keys (`os_test_…`), never billed
- **Authentication:** `X-API-Key` request header
- **OpenAPI:** https://offendersearch.app/openapi.json · https://offendersearch.app/openapi.yaml
- **Docs index for agents:** https://offendersearch.app/llms.txt

## The context to provide

| Give it | URL | Why |
| --- | --- | --- |
| The OpenAPI document | `https://offendersearch.app/openapi.json` | Exact paths, parameters, schemas, enums and status codes. Prevents invented endpoints and field names. |
| The product's Markdown | `/docs/all.md`, `/docs/criminal/all.md`, `/docs/monitoring/all.md` | Behaviour the schema cannot express: matching rules, completeness, billing, what to retry. |
| The platform Markdown | `/docs/platform.md` and its pages (`/docs/platform/errors-and-retries.md`, …) | Authentication, environments, errors, async, data model rules. |
| A guide, when the task matches one | `/docs/solutions/skilled-nursing.md`, `/docs/guides/criminal/charges.md`, … | Worked, verified code and interpretation rules for the use case. |

In Claude Code, Cursor or any assistant that can fetch URLs, paste the links; in one that cannot, paste the `.md` contents. Prefer the Markdown over the HTML pages: it is the same content without the chrome.

## A prompt that works

```plain
You are integrating the OffenderSearch API. Use ONLY these documents as the source of truth:
- https://offendersearch.app/openapi.json        (every endpoint, schema and enum)
- https://offendersearch.app/docs/all.md          (the Sex Offender API, as Markdown)
- https://offendersearch.app/docs/criminal/all.md (the Criminal Records API, as Markdown)
- https://offendersearch.app/docs/platform.md     (authentication, errors, async, data model)
- https://offendersearch.app/docs/monitoring/all.md, /docs/map/all.md, /docs/fcra/all.md (the other products)
  (/docs.md is an alias of /docs/all.md; /docs/criminal.md is the criminal index, /docs/criminal/all.md the whole reference)
Rules: authenticate with the X-API-Key header; read status, counts and sourceStatus before records;
treat every record as a candidate with matchConfidence, never a confirmed identity; an arrest is not a
conviction; an empty result is not proof of a clean history; do not invent endpoints or enum values.
```

Then state the task plainly: "Add a function that screens an applicant against the sex offender registries and returns clear / review / match, with the documented retry policy." The assistant has everything it needs to produce the code in the [recipes](/docs/ai/recipes) without guessing.

## Checking generated code

- Every path it calls appears in `openapi.json`; every field it reads appears in the response schema; every enum value it compares against appears in the enum.
- It sends `X-API-Key`, not a bearer token; it reads `status` and `sourceStatus[]` before `records[]`; it handles `422` (criminal: `error.code`), `429` with `Retry-After`, and a pending job by polling the path the acknowledgement names (`resultsUrl`, `poll` or `pollUrl` by entry point; a criminal poll answers `202` while pending and puts the envelope under `result`).
- It runs against the sandbox with the fixed test names before a live key is involved.

If the assistant got something wrong because a page was unclear, that is a documentation bug: [support@offendersearch.app](mailto:support@offendersearch.app).

## Related

- [Recipes](https://offendersearch.app/docs/ai/recipes)
- [Getting started](https://offendersearch.app/docs/platform/getting-started)
