# humanize API — draft Send AI-written text, a human editor rewrites it, you get the text back. **Status: draft.** The shapes below are settled enough to build a client against. The storage and auth behind them are not built — `POST /api/commission` prices and validates a job, returns it, and does not keep it. Every response from a not-yet-real surface carries `"draft": true`. Base URL is the deployment origin. All bodies are JSON; all responses are JSON except the static pages. --- ## GET /api/pricing The current rate card. The landing page reads this rather than hardcoding numbers, so the price a visitor sees is the price the API will charge. ```json { "currency": "USD", "ratePerWordCents": 3, "minimumCents": 500, "firstJobFree": true, "turnaroundHours": 24, "maxWords": 20000 } ``` --- ## POST /api/quote What would this text cost? Nothing is stored and no editor is assigned. Pure function of the text, so it is safe to call on every keystroke. **Request** | field | type | required | meaning | |---|---|---|---| | `text` | string | yes | the AI-written text to be edited | | `isFirstJob` | boolean | no | price it as the customer's free first commission | ```sh curl -X POST https:///api/quote \ -H 'content-type: application/json' \ -d '{"text": "Your AI-written draft goes here."}' ``` **200** ```json { "wordCount": 6, "currency": "USD", "ratePerWordCents": 3, "minimumCents": 500, "priceCents": 500, "free": false, "minimumApplied": true, "turnaroundHours": 24 } ``` `minimumApplied` is how a client explains a bill that is larger than `wordCount × ratePerWordCents`. --- ## POST /api/commission Hand a piece of text to a human editor. **Request** | field | type | required | meaning | |---|---|---|---| | `text` | string | yes | the text to be edited; must contain at least one word | | `brief` | string | no | what "human" means here — audience, voice, what to keep | | `email` | string | no | where to send it back, until accounts exist | | `isFirstJob` | boolean | no | claim the free first commission | ```sh curl -X POST https:///api/commission \ -H 'content-type: application/json' \ -d '{ "text": "Your AI-written draft goes here.", "brief": "Trade press. Keep the numbers, lose the throat-clearing.", "email": "you@example.com" }' ``` **201** ```json { "id": "job_m5x8k2p1a4b7c9d0", "status": "quoted", "createdAt": "2026-08-14T11:20:05.412Z", "dueAt": "2026-08-15T11:20:05.412Z", "wordCount": 6, "currency": "USD", "ratePerWordCents": 3, "priceCents": 500, "free": false, "minimumApplied": true, "brief": "Trade press. Keep the numbers, lose the throat-clearing.", "email": "you@example.com", "editedText": null, "editor": null, "draft": true, "persisted": false } ``` `persisted: false` is the honest part: the id is well-formed and will 404 on read until storage lands. --- ## GET /api/jobs/:id One commission, and its edited text once an editor has delivered it. **Placeholder** — returns 404 for every id, with the shape it will return: ```json { "id": "job_…", "status": "quoted | assigned | editing | delivered | cancelled", "createdAt": "ISO-8601", "dueAt": "ISO-8601", "wordCount": 0, "priceCents": 0, "currency": "USD", "free": false, "brief": "", "editedText": "string, once status is delivered", "editor": { "handle": "string, once assigned" } } ``` ### Job status | status | meaning | |---|---| | `quoted` | priced and accepted; not yet with an editor | | `assigned` | an editor has claimed it | | `editing` | work in progress | | `delivered` | `editedText` is populated; the customer can collect | | `cancelled` | withdrawn by the customer, or refused | --- ## Errors Every failure is a JSON object with an `error` string and a matching HTTP status. Bad input is a 400 with a reason, never a bare 422. ```json { "error": "`text` is required and must be a non-empty string" } ``` | status | when | |---|---| | 400 | malformed body, missing or wrong-typed field, invalid email | | 404 | no such job | | 405 | wrong method for the route | | 413 | text longer than `maxWords` — split it into several commissions | --- ## Word counting A word is a whitespace-separated token containing at least one letter or digit. Markdown bullets, stray punctuation and horizontal rules therefore do not inflate a bill. **For `text/html`, words are counted from TEXT NODES ONLY** — script and style contents, comments and tags are stripped first. Counting the raw string would bill you for our own markup: `
` is two tokens containing letters, which on markup-heavy input is a materially wrong invoice. The implementation is `countWords` in `src/lib/pricing.ts`, and it is the same function the quote, the commission and the delivered word count all use. --- ## Not built yet Written down so no one designs around their absence: - **Auth.** Google and GitHub OAuth, issuing a session an API call can present. The sign-up buttons on the landing page are stubs. - **Persistence.** A jobs table and a home for the text itself. - **`GET /api/jobs`** — a customer's own jobs, paged. - **`PATCH /api/jobs/:id`** — how an editor delivers the finished text. - **Payment.** Nothing is charged. The free-first-job rule needs a customer record to be true of; today the client simply asserts `isFirstJob`.