api.hunky.dev/v1
Hunky Cloud API
Two planes, one host. The management plane (sign up, keys, plan) is what you
do as the account owner; the use plane (/v1/pretty)
is the product — staging hunks. Each example below is shown three ways where it applies: curl, the hunky-cli, and the dashboard.
Introduction
Everything lives under https://api.hunky.dev/v1.
The two planes use different credentials, on purpose (least privilege):
- Management — a short-lived bearer token from
/v1/login. Manages keys, plan, billing. - Use — an API key (
hk_live_…). Stages hunks via/v1/pretty, and nothing else.
A key cannot reach the management plane and a token cannot reach the pretty API — so a leaked staging key can't change your billing. Anything that speaks HTTP can drive it: a shell, CI, or an agent.
Authentication
Sign up or log in to get a management token (valid 1 hour). Pass it as Authorization: Bearer <token> on management calls.
Sign up
curl -s https://api.hunky.dev/v1/signup \
-H "Content-Type: application/json" \
-d '{"email":"you@company.com","password":"correct-horse","name":"You"}'
# → 201 { "token": "…", "account_id": "…", "expires_in": 3600 }hunky-cli signup # interactive (hidden password prompt)
# scriptable / agent-friendly:
HUNKY_EMAIL=you@company.com HUNKY_PASSWORD=correct-horse hunky-cli signup- Open
app.hunky.dev/signup - Enter your email and a password
- You're dropped into the dashboard, logged in
Log in
curl -s https://api.hunky.dev/v1/login \
-H "Content-Type: application/json" \
-d '{"email":"you@company.com","password":"correct-horse"}'
# → 200 { "token": "…", "expires_in": 3600 }hunky-cli login # stores a 1h session token- Open
app.hunky.dev/login - Enter your credentials
Account
Your plan, both usage pools — credit_cents (promo, starts at $5) and balance_cents (real, topped up) — usage count, and the list of API keys (hashes never returned). Usage drains
promo first, then balance, which may run $5 negative before /v1/pretty returns 402 until you top up.
curl -s https://api.hunky.dev/v1/account \
-H "Authorization: Bearer $TOKEN"
# → { name, plan,
# credit_cents, // promo credit (starts at $5)
# balance_cents, // real balance, recharged via top-up
# stages_count,
# keys: [{ prefix, name, env, created_at }] }hunky-cli account- The dashboard home shows your plan, promo credit, balance, usage, and keys
API keys
Keys are the use-plane credential. Mint them from the management plane; the raw key is shown once and only its hash is stored.
Create a key
curl -s -X POST https://api.hunky.dev/v1/keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"ci","env":"prod"}'
# → 201 { "api_key": "hk_live_…", "prefix": "hk_live_…", "name": "ci", "env": "prod" }
# the raw api_key is shown ONCE — store it now.hunky-cli keys create ci
# mints the key AND stores it as your active key,
# so `hunky-cli <file> <hunk>` works immediately.- Dashboard → API Keys
- Click
Create key, give it a name - Copy the key — it is shown only once
List keys
curl -s https://api.hunky.dev/v1/account \
-H "Authorization: Bearer $TOKEN" | jq '.keys'
# keys live on the account object (hashes are never returned)hunky-cli keys- Dashboard → API Keys lists active keys (prefix + name)
Revoke a key
curl -s -X DELETE https://api.hunky.dev/v1/keys/hk_live_8f3a \
-H "Authorization: Bearer $TOKEN"
# → 204hunky-cli keys revoke hk_live_8f3a- Dashboard → API Keys →
Revoke
Plans
One of pretty_boy, mamas_boy, or whole_package — or none to drop the
subscription and run free / pay-as-you-go. They're tip jars with benefits — each unlocks more
Brawny review modes and a little usage — and are billed separately from your usage balance.
curl -s -X POST https://api.hunky.dev/v1/plan \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"plan":"whole_package"}'
# → 200 { "plan": "whole_package" }
# Drop the subscription (free / PAYG-only):
# -d '{"plan":"none"}'hunky-cli plan whole_package
hunky-cli plan none # drop to free / PAYG-only- Dashboard → Billing
- Pick a tier (Pretty Boy / Mama's Boy / The Whole Package), or Switch to PAYG-only
none) and switching between paid tiers work straight from the API/CLI once a card is on file —
your request hits our server, which drives Stripe; the card never touches the CLI. Starting a
paid plan for the first time needs a card, so that one call returns a one-time Stripe
checkout link to finish in the browser. After that, every change is instant from the CLI.The diff payload
The pretty API's diff field
is a unified diff, gzip-compressed and base64-encoded — the only data that leaves your machine.
The CLI builds it for you; with curl, build it yourself:
git diff HEAD -- feature.go | gzip | base64 | tr -d '\n'Staging — the handshake
A request is two calls. POST /v1/pretty returns a request_key (202); GET /v1/pretty/<key> redeems
it — 202 while warming, 200 with the result. The server never touches your tree; for staging it
hands back a git apply --cached you run locally.
# 1. base64-gzip the diff, on one line.
DIFF=$(git diff HEAD -- feature.go | gzip | base64 | tr -d '\n')
# 2. submit → a request key
curl -s -X POST https://api.hunky.dev/v1/pretty \
-H "Authorization: Bearer $HUNKY_CLOUD_KEY" \
-H "Content-Type: application/json" \
-d "{\"mode\":\"stage\",\"file\":\"feature.go\",\"hunks\":[1],\"diff\":\"$DIFF\"}"
# → 202 { "request_key": "req_…", "poll_after_ms": 600 }
# 3. redeem → 202 while warming, 200 with { commands, review, meta }
curl -s https://api.hunky.dev/v1/pretty/req_… \
-H "Authorization: Bearer $HUNKY_CLOUD_KEY"
# then run each string in .commands locally (a git apply --cached).hunky-cli feature.go 1
# the CLI does the git diff, gzip, submit, poll, prints Brawny's
# review, and runs the returned git apply locally after you confirm.Operations
Each operation is the same handshake with a different request body. The curl tab shows the body; the CLI tab shows the one-liner.
List hunks
Number the hunks so you can choose which to stage.
{ "mode": "list", "file": "feature.go", "diff": "<base64-gzip>" }hunky-cli --list feature.goCount hunks
Return just the number of hunks.
{ "mode": "count", "file": "feature.go", "diff": "<base64-gzip>" }hunky-cli --count feature.goStage by number
The result's commands[] is what you run locally.
{ "mode": "stage", "file": "feature.go", "hunks": [1, 3, 5], "diff": "<base64-gzip>" }hunky-cli feature.go 1 3 5Stage everything except
Invert the selection.
{ "mode": "stage", "file": "feature.go", "hunks": [4], "invert": true, "diff": "<base64-gzip>" }hunky-cli --invert feature.go 4Stage by line range
Whole hunks overlapping a range of file lines.
{ "mode": "stage", "file": "feature.go", "lines": "455-480", "diff": "<base64-gzip>" }hunky-cli --lines 455-480 feature.goSplit a hunk
Only the +/- lines within a range.
{ "mode": "stage", "file": "CHANGELOG.md", "pick_lines": "25-37", "diff": "<base64-gzip>" }hunky-cli --pick-lines 25-37 CHANGELOG.mdDry run
Return the patch without staging. commands[] is empty.
{ "mode": "dryrun", "file": "feature.go", "hunks": [4], "diff": "<base64-gzip>" }hunky-cli --dry-run feature.go 4The result object
Returned by GET /v1/pretty/<key> with a 200. Run each string in commands locally, in order.
| Field | Type | Description |
|---|---|---|
| output | string | Text to print: the hunk list, the count, or the dry-run patch. |
| commands | string[] | Shell commands to run locally to stage (a git apply --cached). Empty for read-only modes. |
| review | string | Brawny-1's unsolicited code review. |
| meta | object | { gpu, model, millis } — operational metadata. |
{
"output": "",
"commands": [
"git apply --cached <<'HUNKYPATCH'\n…patch…\nHUNKYPATCH"
],
"review": "Hey there, pretty mama. Clean as I part my hair. Ship it.",
"meta": { "gpu": "1080 Ti", "model": "Brawny-1", "millis": 4217 }
}Request parameters (POST /v1/pretty)
| Field | Type | Description |
|---|---|---|
| mode | string | "list", "count", "dryrun", or "stage". Defaults to "stage". |
| file | string | Path of the file within the diff to operate on. Required. |
| diff | string | base64(gzip(unified diff)). The only data that leaves your machine. Required. |
| hunks | integer[] | 1-based hunk numbers, for "stage" and "dryrun". |
| invert | boolean | Stage every hunk EXCEPT those in hunks. Default false. |
| lines | string | "START-END" — stage whole hunks overlapping the line range. |
| pick_lines | string | "A-B" — stage only the +/- lines within the range (splits a hunk). |
| review_mode | string | Brawny review style: standard | terse | verbose | haiku | sonnet | mythos. Each is unlocked by your plan; locked modes fall back to standard. mythos returns the empty string (national security). |
Errors & status codes
| Status | Meaning |
|---|---|
| 400 | The request body or the diff payload could not be parsed. |
| 401 | Missing or invalid credential (management token or API key). |
| 404 | Unknown request key on GET (or it expired). |
| 422 | The diff was valid but hunky could not operate on it (e.g. no such hunk). |
| 202 | On GET: the result is not ready yet. Wait poll_after_ms and retry. |
hunky binary. The API exists for hosts that can't install a binary — or agents that only speak HTTP.