# Building with HeadHoncho

Short rules for a coding agent writing against the HeadHoncho API: auth, idempotency, pages, errors and which endpoint does which job.

Page: https://headhoncho.app/docs/api/agents | Part of the HeadHoncho API docs: https://headhoncho.app/docs/api | Machine-readable: https://headhoncho.app/docs/api/openapi.json | Everything in one file: https://headhoncho.app/docs/api/llms-full.txt

## Rules for a coding agent

This page is written for the coding agent that is about to write code against the HeadHoncho API, and for the person who gave it the task. It is short on purpose. Every rule is the same on every endpoint. The facts about one endpoint are on its own page, and the whole surface is one file: [catalog.json](https://headhoncho.app/docs/api/catalog.json). The [OpenAPI file](https://headhoncho.app/docs/api/openapi.json) ([YAML](https://headhoncho.app/docs/api/openapi.yaml)) generates a client. All the docs are also one Markdown file: [llms-full.txt](https://headhoncho.app/docs/api/llms-full.txt). If the agent is connected over MCP, it can read these pages as resources.

## Paste this into AGENTS.md

Put this block in the AGENTS.md, CLAUDE.md or rules file of the repository that calls HeadHoncho. It is generated from the same catalog as the reference, so it stays true.

```markdown
## HeadHoncho API

- Base: https://api.headhoncho.app/api/v1. Docs: https://headhoncho.app/docs/api. Whole surface: https://headhoncho.app/docs/api/catalog.json.
- Auth: send `Authorization: Bearer hh_live_...` on every call. Never put the key in code
  that ships to a browser or a repo. A key reaches only the companies and areas it was given.
- Start with GET /whoami. It lists the companies, the access per area, and the status,
  job type and technician ids that commands take.
- Every POST /commands/<verb> needs an `Idempotency-Key` header. Make one per intended action
  and send the same one when you retry. A different body with the same key is refused.
- Lists answer {items, next, count}. Pass `next` back as `cursor` until it is null.
- After a command, read with `?after=<position>` to see your own write.
- Money is a number in the company currency. Times carry an offset. Ids are opaque text.
- Branch on `error.code`. A refused field is named in `error.fields`. Do not retry a 4xx.
- Each answered call costs 1 credit. There is no rate limit. Do not poll in a loop.
```

## Auth and reach

- One header on every call: `Authorization: Bearer hh_live_...`. Keys are made in the app; see [API keys](https://headhoncho.app/docs/api/keys).
- A key holds each area at **read** or **do**. A read-only key calling a command is refused with [`read_only`](https://headhoncho.app/docs/api/errors#read_only). An area the key lacks is [`no_access`](https://headhoncho.app/docs/api/errors#no_access).
- A key reaches only the companies it was given. When it reaches several, pass `company_id` on every call that takes one. A company outside the key answers [`out_of_scope`](https://headhoncho.app/docs/api/errors#out_of_scope).
- When personal details are hidden, names, phones and streets come back masked. Do not try to work around it.

## Idempotency

- Every command needs an `Idempotency-Key` header. Make one for each intended action, and send the same one when you retry that action.
- Build the key from something stable, such as your own record id. A random key made inside the retry loop defeats the point.
- The same key with a different body is a 409, [`idempotency_mismatch`](https://headhoncho.app/docs/api/errors#idempotency_mismatch). No key is a 400.
- Reads need no key. Retry a read freely, but each answered call costs a credit.

## Pages and your own writes

- A list answers `{items, next, count}`. Pass `next` back as `cursor` and change nothing else. Stop when `next` is `null`. Never build a cursor yourself.
- A raw table pages with `limit` and `offset` instead, because its order is fixed.
- A command answers `accepted` and a `position`. Pass `?after=<position>` to the next read and it waits up to two seconds for the read model to catch up.

## Values

- Ids are opaque text. Keep them exactly as given.
- Money is a plain number in the company's currency, named by `currency` on the answer. A command answer may print an amount as decimal text. Read the Fields table on each page.
- Times are ISO 8601 with an offset. A date range is read in the company's time zone.

## Errors

- Branch on `error.code`, never on the message. Every code is on [errors](https://headhoncho.app/docs/api/errors) with what causes it and how to fix it.
- A 422 [`invalid`](https://headhoncho.app/docs/api/errors#invalid) names each refused field in `error.fields`. Fix the field. Do not resend unchanged.
- A 402 [`no_credits`](https://headhoncho.app/docs/api/errors#no_credits) means the workspace is out of credits. Stop and tell the person. Retrying does not help.
- A 4xx is the caller's to fix. Resend only after a change, and a command only with the same `Idempotency-Key`.

## Which endpoint for which job

| To do this | Call |
| --- | --- |
| See who the key is, its companies and the ids commands take | [Whoami](https://headhoncho.app/docs/api/whoami) |
| List jobs and leads, newest first, a page at a time | [List jobs](https://headhoncho.app/docs/api/list_jobs) |
| Read one job by id or number | [Get job](https://headhoncho.app/docs/api/get_job) |
| Open a job or a lead | [Create job](https://headhoncho.app/docs/api/create_job) |
| Move a job to another status | [Change job status](https://headhoncho.app/docs/api/change_job_status) |
| Put a technician on a job | [Assign worker](https://headhoncho.app/docs/api/assign_worker) |
| Add a note to a job | [Add job note](https://headhoncho.app/docs/api/add_job_note) |
| See what a job quoted, collected and left as profit | [Get job money](https://headhoncho.app/docs/api/get_job_money) |
| Record a payment on a job | [Record payment](https://headhoncho.app/docs/api/record_payment) |
| See what each technician is owed | [List balances](https://headhoncho.app/docs/api/list_balances) |
| Read the raw rows an ad platform or call tracker reported | [Read table](https://headhoncho.app/docs/api/read_table) |
| Get the file Google Ads needs to credit the jobs | [Export click id report](https://headhoncho.app/docs/api/export_click_id_report) |

Every endpoint, by group. Each id is a page at `/docs/api/<id>` and, over MCP, a tool of the same name:

- **Account**: `whoami`, `list_companies`, `get_company`, `get_usage`, `get_credits`, `list_plans`
- **Changes**: `list_events`, `list_event_types`
- **Webhooks**: `list_webhooks`, `get_webhook`, `list_webhook_deliveries`
- **Webhook commands**: `add_webhook`, `change_webhook`, `remove_webhook`, `send_webhook_test`, `redeliver_webhook`
- **Jobs**: `list_jobs`, `get_job`, `get_job_history`, `get_job_log`, `list_situations`, `list_job_photos`, `get_photo`, `get_calendar`, `list_unscheduled_jobs`, `export_jobs`, `search_jobs`, `get_automatic_status`, `get_job_detour`, `list_job_reassignments`, `list_calendar_reassignments`
- **Job commands**: `create_job`, `update_job`, `change_job_status`, `assign_worker`, `unassign_worker`, `add_job_note`, `update_job_note`, `delete_job_note`, `set_job_cut`, `release_job_status`, `resolve_job_detour`, `archive_job`, `unarchive_job`, `trash_job`, `restore_job`, `upload_photo`, `delete_photo`
- **Many jobs at once**: `bulk_change_status`, `bulk_assign_worker`, `bulk_unassign_worker`, `bulk_add_note`, `bulk_archive_jobs`, `bulk_unarchive_jobs`, `bulk_trash_jobs`, `bulk_restore_jobs`
- **Clients**: `list_clients`, `get_client`
- **Client commands**: `create_client`, `update_client`, `delete_client`
- **Leads**: `list_leads`, `get_lead`, `list_needs_you`, `list_conversion_reviews`
- **Lead commands**: `create_lead`, `update_lead`, `mark_lead_lost`, `pass_lead`, `book_lead`, `dismiss_lead`, `review_conversion`, `schedule_follow_up`, `update_follow_up`, `resolve_follow_up`, `cancel_follow_up`
- **Job money**: `get_job_money`, `list_field_reports`
- **Money commands**: `record_payment`, `void_payment`, `record_expense`, `void_expense`, `book_field_report`, `dismiss_field_report`, `add_expense_type`, `update_expense_type`
- **Balances**: `list_balances`, `get_balance_statement`, `list_closed_balances`
- **Balance commands**: `close_balance`, `record_payout`, `void_closed_balance`, `hold_back_job`, `put_back_job`, `post_monthly_pay`
- **Team**: `list_technicians`, `get_worker`
- **Team commands**: `create_worker`, `update_worker`
- **Statistics**: `get_statistics_summary`, `get_statistics_areas`, `list_area_jobs`, `list_job_pins`
- **Marketing**: `list_ad_accounts`, `list_campaigns`, `list_agency_jobs`, `get_agency_feed`
- **Raw data**: `list_tables`, `read_table`, `export_table`
- **Click IDs and conversions**: `get_job_conversion`, `get_click_id_report`, `export_click_id_report`
- **Company settings**: `get_settings`
- **Settings commands**: `update_company`, `create_company`, `update_organization`, `add_job_type`, `update_job_type`, `update_job_status`, `set_status_band`, `replace_job_status`, `add_job_status`, `reorder_line`, `update_agency_feed`, `sync_callrail`, `update_callrail_tracking`, `refresh_sources`, `sync_ad_platform`
- **Imports**: `list_import_systems`, `get_import_fields`, `get_import_template`, `stage_import`, `list_import_statuses`, `list_import_job_types`, `preview_import`, `rehearse_import`, `commit_import`

## Do not

- Do not scrape the app or call its own routes. The API is the supported door.
- Do not poll in a tight loop. Every answered call costs a credit. Read only what changed, for example the job log with `since`, instead of everything again.
- Do not ask for a board, a dashboard or a layout. The API gives records, raw rows and single commands; see [the model](https://headhoncho.app/docs/api/model#building-blocks).
- Do not store the key in a repository, a log or a page a browser loads.

See also: [recipes](https://headhoncho.app/docs/api/recipes), [the MCP server](https://headhoncho.app/docs/api/mcp) and the [changelog](https://headhoncho.app/docs/api/changelog).
