Runs

One draft, one run.

A run is asynchronous. Creating it reserves money and queues the work. You read the rewrite from the run when it finishes.

POST

/v1/runs

Creates a run and returns 202 Accepted. Limited to 6 requests per minute per key. A 429 includes a Retry-After header.

{
  "text": "Your draft, in plain text or Markdown.",
  "quality": "standard",
  "options": {
    "persona": "freelance journalist filing a feature",
    "tone": "warm and plain-spoken",
    "formality": 2,
    "typos_enabled": true,
    "typos_per_100_words": 1.0,
    "common_words_enabled": true,
    "min_zipf": 4.0
  }
}
Field
textRequired. Longer than 5,000 characters returns 400 text_too_long. Text with no prose words returns 400.
qualitylow, standard, or high. An unknown name is 400.
optionsOptional. See below. Omit it to let each run pick a voice.
{
  "id": "run_01J9ABCDEF",
  "status": "queued",
  "quality": "standard",
  "word_count": 11,
  "prose_word_count": 11,
  "estimated_cost_usd": "0.050000",
  "held_usd": "0.050000",
  "is_estimate": true,
  "events_url": "/api/v1/runs/run_01J9ABCDEF/events"
}

held_usd is reserved until the run settles. estimated_cost_usd is not the price. The amount you pay is billing.charged_usd on the finished run. If the available balance cannot cover the hold, the response is 402 and nothing is queued.

Options

Leave a voice field out and that run samples one. Two runs of the same text will not necessarily sound the same. GET /v1/presets returns the lists the sampler draws from.

Field
personaWho is writing. A concrete description works better than a label like “professional”.
toneHow it should sound.
formalityInteger from 1 (conversational) to 5 (formal).
typos_enabledDefaults to true. Short drafts often get none, because a single slip in a short paragraph reads as a mistake.
typos_per_100_wordsRate used when typos are on. The default in the app is 1.0.
common_words_enabledPrefer everyday wording. Defaults to true.
min_zipfHow common a replacement word should be. The app default is 4.0.
random_planWhen true, voice options in the same object are ignored and the whole plan is sampled.

Statuses

Status
queuedAccepted, not started.
runningIn progress. The hold is still reserved.
succeededRewrite is in output_text.
rejectedFinished, and our checks did not pass. You are still charged for the work. Read scores.reasons.
failedThe run stopped on an error. You are charged for work already done.
canceledYou canceled it. Unused hold is released. Work already done is charged.
GET

/v1/runs/{id}

The run, including both texts. A run that belongs to someone else is 404.

{
  "id": "run_01J9ABCDEF",
  "status": "succeeded",
  "quality": "standard",
  "prose_word_count": 11,
  "input_text": "Artificial intelligence has fundamentally transformed the landscape of modern software development.",
  "output_text": "Artificial intelligence has changed modern software development.",
  "text_deleted_at": null,
  "scores": {
    "validation_passed": true,
    "reasons": []
  },
  "billing": {
    "charged_usd": "0.050000",
    "estimated_cost_usd": "0.050000",
    "held_usd": "0.050000",
    "charge_exceeded_hold": false
  },
  "created_at": "2026-10-05T18:00:00+00:00",
  "finished_at": "2026-10-05T18:00:40+00:00"
}

charge_exceeded_hold is true when the final charge was higher than the amount reserved up front. scores.validation_passed is the check result. A few extra fields may be present; you do not need them to read the rewrite or the charge.

GET

/v1/runs

Your runs, newest first. limit defaults to 50 and caps at 100. List items omit input_text and output_text and include input_preview, the first 160 characters of the draft.

{ "data": [ /* run objects */ ], "next_cursor": null }
POST

/v1/runs/{id}/cancel

Cancels a queued or running run and returns the run. Canceling a finished run returns it unchanged.

DELETE

/v1/runs/{id}

Erases the draft and the rewrite. The billing record stays, so the charge still appears in the ledger. Cancel an in-progress run first; otherwise the response is 409 run_in_progress.

{ "id": "run_01J9ABCDEF", "text_deleted": true, "deleted_at": "2026-10-05T19:00:00+00:00" }
DELETE

/v1/runs?confirm=true

Erases the text of every finished run. Without confirm=true the response is 400 confirmation_required. Runs that are still queued or running are skipped.

{ "deleted": 12, "skipped_in_progress": 0 }