Skip to content
Open menu

Developers

API documentation

Create humanization jobs, stream their progress, read the detector estimate and manage style profiles from your own code. The API is part of the Premium plan.

Base URL

https://turnithuman.com/v1

Auth

Bearer API key

Progress

Server-sent events

Format

JSON, OpenAPI 3.1

Overview

A job takes your text through three layers: mechanical fixes, a dedicated humanizer pass, and checks for meaning and detector risk. You create a job, follow it until it is done, and read the output with its scores and warnings.

The detector readout is an independent multi-detector estimate with no guarantee attached. The per-detector numbers are most likely the detector provider's own emulations, not those vendors' verdicts. They point the other way from result: result is an AI likelihood from 0 to 100, while each per-detector value is 0, 50 or 100, where 100 means that model would read the text as human, 50 means unsure and 0 means it would flag the text as AI; human is their average, and a value the detector did not return is left out. No TurnItHuman endpoint calls Turnitin, Turnitin offers no public AI-detection API, and no result here is a Turnitin score or a prediction of one.

Authentication

Send your key as a bearer token: Authorization: Bearer th_live_ followed by 32 characters. Create keys in the app under API keys; the full key is shown once, and the list shows only its prefix. Revocation takes effect immediately.

Keys are for server-to-server use: browsers are blocked by CORS. Premium includes 2 active keys. To rotate, create a new key, deploy it, then revoke the old one. Keys are revoked if the account leaves Premium.

Jobs

POST /v1/jobs takes the text plus an optional title, preset, strategy, style_id, options and metadata. It returns 202 with the job id. The job object reports status, stage, the output with its scores, the detector estimate, the meaning check, warnings, and when the text and the history row expire.

fidelity is the meaning check of the delivered text. Its status is ok when nothing changed that is worth a look, review when review_items lists something (each item has a kind, the before and after words, and a message such as Number changed: "almost two hundred dollars" became "$190"), and skipped when no humanized text was checked. The humanized text is delivered and charged either way.

strategy is humanize_only (the default) or style_replicate_humanized, which humanizes in the style of a registered style profile given as style_id. strategy_effective tells you what actually ran, for example humanize_only when the style profile could not be used. Code blocks and reference lists are never sent to the humanizer. Protected terms, locked ranges (offsets in the text as sent), inline code, citations, quotes, numbers and links are compared with your original after the humanizer pass, and any that look changed are listed in fidelity.review_items.

To keep formatting, send input_format: "markdown" with the text as Markdown. Headings, bullet and numbered lists (one level of nesting), block quotes, fenced code, links (http, https and mailto), bold and italic are kept, and anything else is read as plain text. Only the prose is humanized: headings, code and short list items stay as you wrote them. The job returns input.markdown and output.markdown beside the plain input.text and output.text, and it counts and bills the words without Markdown syntax or link URLs, so formatting never costs words. When the humanizer rewords the text of a link or the link has to move to other words, fidelity.review_items says so (link_text_changed, link_moved). options.locked_ranges must be empty with markdown. Without input_format the text is plain, as before.

academic_essayEssays and discussion posts. Formal register; citations are checked against your original.
academic_reportReports and lab write-ups. Formal tone; figures and citations are checked against your original.
article_blogPosts and articles that read like a person wrote them.
marketingLanding pages, newsletters and ad copy with a natural voice.
businessMemos, client emails and internal docs. Figures are checked against your original.
storyScripts and fiction. Defaults to the More Human strength.
cover_letterLetters in a formal register. Numbers and dates are checked; add names as protected terms to have them checked too.
legalLetters and memos in a formal register. Add terms of art as protected terms to have them checked.
generalAny English text. A balanced humanizer pass.

Streaming

GET /v1/jobs/{id}/events is a text/event-stream of job.status, job.progress, job.error and job.done events, with a ping comment every 15 seconds. Reconnect with Last-Event-ID to replay only the events you missed. After job.done or job.error the server closes the stream, and a reconnect past the last event answers 204, so stop reconnecting then. If you receive only pings for 60 seconds, read the job once with GET /v1/jobs/{id}.

If you cannot hold a stream open, poll GET /v1/jobs/{id}?fields=summary with If-None-Match every 5 seconds at most.

Detect

POST /v1/detect checks text you already have without humanizing it. It costs 10 percent of the words checked, rounded up to the nearest 10, with a minimum of 10. Scores under 50 read as human, 50 to 60 as possible AI, and over 60 as likely AI. Texts under 200 words give unreliable estimates. A check that is still running replays as pending with the same idempotency key.

Styles

A style profile is a sample of 100 to 2,000 words of your own writing. The response includes a voice profile summary. With replicate_upstream, the sample is also registered with the humanizer provider; its upstream status stays pending until registration finishes, and style_replicate_humanized jobs need it to be done. Without registration a profile returns its voice profile summary but does not change jobs.

Word accounting

A job reserves the billable words of its input, rounded up to the nearest 10, when it is created. A failed job returns every reserved word. A job cancelled after the humanizer pass started is charged. Humanize again charges the words of the humanized result. A job that returns your own text, because the humanizer could not process it, releases its words and reports them as input.refunded_words. GET /v1/usage reports your balance and ledger for a period.

Rate limits and idempotency

Each key may send 60 requests a minute with bursts of up to 30 in 10 seconds, run 5 jobs at once and create 60 jobs an hour. A 429 carries Retry-After in seconds.

Send an Idempotency-Key header (16 to 128 characters; a UUID works) on POST /v1/jobs, /v1/jobs/{id}/rehumanize, /v1/detect and /v1/styles. Retrying with the same key and body returns the original result instead of creating a second job; the same key with a different body returns 422 IDEMPOTENCY_CONFLICT.

Versioning

The API is versioned in the path (/v1). New fields, endpoints, enum values and warning codes are added without a version change, so ignore fields you do not know. Breaking changes ship as a new version with notice.

Acceptable use

The terms of service and content policy apply to every API call. Use the API only on text you have the right to edit, never to disguise authorship of work that must be your own, and never to produce deceptive content.

Quickstart

Replace $KEY with a Premium API key. uuidgen supplies a fresh idempotency key; the ids are examples.

Create a jobbash
curl -sS -X POST https://turnithuman.com/v1/jobs \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "text": "The institute was officially established in 1989, marking a pivotal moment ...",
    "title": "Institute history",
    "preset": "academic_essay",
    "options": { "strength": "Balanced", "protected_terms": ["Foster Care Act"] },
    "metadata": { "client_ref": "essay-42" }
  }'
# 202 {"id":"01J8Q0Z2K7M4N6P8R1S3T5V7W9","object":"job","status":"queued", ...}
Stream progressbash
curl -sS -N https://turnithuman.com/v1/jobs/01J8Q0Z2K7M4N6P8R1S3T5V7W9/events \
  -H "Authorization: Bearer $KEY" \
  -H "Accept: text/event-stream" \
  -H "Last-Event-ID: 0"
# id: 1
# event: job.status
# data: {"job_id":"01J8Q0Z2K7M4N6P8R1S3T5V7W9","status":"preprocessing","stage":"s0_normalize","at":"..."}
Poll instead of streamingbash
curl -sS "https://turnithuman.com/v1/jobs/01J8Q0Z2K7M4N6P8R1S3T5V7W9?fields=summary" \
  -H "Authorization: Bearer $KEY" \
  -H 'If-None-Match: W/"humanizing:1758463403105:summary"'
# 200 with the summary job, or 304 when unchanged. Repeat every 5 s until done, failed or cancelled.
Check text with the detectorbash
curl -sS -X POST https://turnithuman.com/v1/detect \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"text": "At least two hundred words of the text you want checked ..."}'
Create a style profilebash
curl -sS -X POST https://turnithuman.com/v1/styles \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name": "My essay voice", "sample": "At least one hundred words of your own writing ...", "replicate_upstream": true}'
TypeScript: create a job and wait for ittypescript
const BASE = "https://turnithuman.com/v1";
const headers = {
  Authorization: `Bearer ${process.env.TURNITHUMAN_API_KEY}`,
  "Content-Type": "application/json",
};

async function call(path: string, init: RequestInit = {}) {
  const res = await fetch(`${BASE}${path}`, { ...init, headers: { ...headers, ...init.headers } });
  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(`${error.code}: ${error.message}`);
  }
  return res.json();
}

const created = await call("/jobs", {
  method: "POST",
  headers: { "Idempotency-Key": crypto.randomUUID() },
  body: JSON.stringify({ text, preset: "article_blog" }),
});

let job = created;
while (!["done", "failed", "cancelled"].includes(job.status)) {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  job = await call(`/jobs/${created.id}`);
}
console.log(job.output?.text, job.detector?.final?.result, job.warnings);

Errors

Errors use one envelope,{ "error": { "code", "message", "details" } }, with an X-Request-Id header.

Branch on the code, never on the message. The message column is the text the app shows.

  • AUTH_TOKEN_INVALID400

    This link is not valid. Request a new one.

    Do not retry; change the request

  • BAD_REQUEST400

    We could not read that request. Reload and try again.

    Do not retry; change the request

  • MAGIC_LINK_INVALID400

    This sign-in link is not valid. Request a new one.

    Do not retry; change the request

  • UNKNOWN_PIPELINE_VERSION400

    That pipeline version is not available.

    Do not retry; change the request

  • API_KEY_REVOKED401

    This API key was revoked.

    Do not retry; change the request

  • INVALID_API_KEY401

    This API key is not valid.

    Do not retry; change the request

  • INVALID_CREDENTIALS401

    That email and password do not match.

    Do not retry; change the request

  • RECENT_AUTH_REQUIRED401

    For your security, confirm it is you. We sent a link to your email.

    Do not retry; change the request

  • SESSION_EXPIRED401

    Your session ended. Sign in again.

    Do not retry; change the request

  • UNAUTHENTICATED401

    Sign in to continue.

    Do not retry; change the request

  • INSUFFICIENT_WORDS402

    You need {required} words and have {remaining} left until {date}.

    Do not retry; change the request

  • ACCOUNT_SUSPENDED403

    This account is suspended. Contact hello@turnithuman.com.

    Do not retry; change the request

  • CSRF_ORIGIN_MISMATCH403

    This request came from an unexpected page. Reload and try again.

    Do not retry; change the request

  • EMAIL_DOMAIN_BLOCKED403

    Enter a valid email address.

    Do not retry; change the request

  • FEATURE_DISABLED403

    This feature is not available right now.

    Do not retry; change the request

  • FORBIDDEN403

    This action is not available with an API key.

    Do not retry; change the request

  • PLAN_REQUIRED403

    {feature} is available on {plan} and up.

    Do not retry; change the request

  • TRIAL_ALREADY_USED403

    Your free trial has been used. Choose a plan to continue.

    Do not retry; change the request

  • TURNSTILE_FAILED403

    Verification failed. Reload the page and try again.

    Do not retry; change the request

  • JOB_NOT_FOUND404

    We could not find that job.

    Do not retry; change the request

  • NOT_FOUND404

    We could not find that page or item.

    Do not retry; change the request

  • STYLE_NOT_FOUND404

    We could not find that style profile.

    Do not retry; change the request

  • ACCOUNT_DELETION_PENDING409

    This account is scheduled for deletion. Sign in and choose Cancel deletion to continue.

    Do not retry; change the request

  • ALREADY_REPLAYED409

    This event was already replayed.

    Do not retry; change the request

  • ALREADY_SUBSCRIBED409

    You already have a plan. Use Manage billing to change it.

    Do not retry; change the request

  • API_KEY_LIMIT_REACHED409

    Premium includes 2 keys. Revoke one to create another.

    Do not retry; change the request

  • AUTH_TOKEN_USED409

    This link was already used. Request a new one.

    Do not retry; change the request

  • EMAIL_IN_USE409

    That email already belongs to another account.

    Do not retry; change the request

  • JOB_NOT_CANCELLABLE409

    This job has already finished.

    Do not retry; change the request

  • JOB_NOT_DELETABLE409

    Cancel the job before deleting it.

    Do not retry; change the request

  • JOB_NOT_REHUMANIZABLE409

    This job cannot be run again. Its text may have expired.

    Do not retry; change the request

  • JOB_TERMINAL409

    This job has already finished.

    Do not retry; change the request

  • MAGIC_LINK_USED409

    This sign-in link was already used. Request a new one.

    Do not retry; change the request

  • REFUND_NOT_AVAILABLE409

    There are no settled words left to refund on this job, or the account has no open cycle.

    Do not retry; change the request

  • REHUMANIZE_LIMIT_REACHED409

    Limit of 2 re-runs per job. Start a new job to continue.

    Do not retry; change the request

  • STYLE_LIMIT_REACHED409

    Your plan includes {limit} style profiles.

    Do not retry; change the request

  • AUTH_TOKEN_EXPIRED410

    This link expired. Request a new one.

    Do not retry; change the request

  • GONE410

    This item is no longer available.

    Do not retry; change the request

  • MAGIC_LINK_EXPIRED410

    This sign-in link expired. Request a new one.

    Do not retry; change the request

  • TEXTS_EXPIRED410

    The text for this job has expired and can no longer be downloaded.

    Do not retry; change the request

  • FILE_TOO_LARGE413

    Files are limited to 5 MB.

    Do not retry; change the request

  • PAYLOAD_TOO_LARGE413

    The request is too large. Split the text.

    Do not retry; change the request

  • FILE_TYPE_UNSUPPORTED415

    Upload a .txt or .docx file.

    Do not retry; change the request

  • UNSUPPORTED_MEDIA_TYPE415

    We could not read that request format.

    Do not retry; change the request

  • CONTENT_REFUSED422

    We cannot process this text under our content policy. Your words were not used.

    Do not retry; change the request

  • FILE_NO_TEXT422

    We could not find readable text in this file.

    Do not retry; change the request

  • FILE_UNREADABLE422

    We could not open this file. Save it again as .docx or .txt and try again.

    Do not retry; change the request

  • IDEMPOTENCY_CONFLICT422

    This request conflicts with one you already sent. Reload to see the result.

    Do not retry; change the request

  • IDEMPOTENCY_KEY_INVALID422

    Something went wrong sending this request. Reload and try again.

    Do not retry; change the request

  • INVALID_EMAIL422

    Enter a valid email address.

    Do not retry; change the request

  • STYLE_SAMPLE_TOO_LONG422

    Samples are limited to 2,000 words.

    Do not retry; change the request

  • STYLE_SAMPLE_TOO_SHORT422

    Paste at least 100 words of your own writing.

    Do not retry; change the request

  • TEXT_TOO_LONG422

    This text is {word_count} words. Your plan allows {max_words} per job.

    Do not retry; change the request

  • TEXT_TOO_SHORT422

    Add at least 50 words.

    Do not retry; change the request

  • UNSUPPORTED_LANGUAGE422

    TurnItHuman works on English text for now.

    Do not retry; change the request

  • VALIDATION_ERROR422

    Check the highlighted fields and try again.

    Do not retry; change the request

  • ACCOUNT_LOCKED429

    Too many failed sign-in attempts. Wait 15 minutes or reset your password.

    Retry after the Retry-After header

  • CONCURRENCY_LIMIT429

    You have {running} jobs running. Wait for one to finish.

    Retry after the Retry-After header

  • RATE_LIMITED429

    Too many requests. Try again in {seconds} seconds.

    Retry after the Retry-After header

  • INTERNAL_ERROR500

    Something went wrong on our side. Your words were refunded if a job had started. Reference: {requestId}.

    Retry with backoff

  • WORKFLOW_CREATE_FAILED500

    We could not start your job.

    Retry with backoff

  • BILLING_UNAVAILABLE502

    We could not reach billing. Try again in a minute.

    Retry with backoff

  • UPSTREAM_UNAVAILABLE502

    The humanizer service did not respond. Your words were refunded.

    Retry with backoff

  • BILLING_NOT_CONFIGURED503

    Billing is not enabled on this environment.

    Retry after the Retry-After header

  • DAILY_CAPACITY_REACHED503

    We reached today's capacity for new jobs. They open again by 00:00 UTC. Your text is still here.

    Retry after the Retry-After header

  • DB_UNAVAILABLE503

    We could not reach our database. Try again in a minute. Your words were not used.

    Retry after the Retry-After header

  • EMAIL_SEND_FAILED503

    We could not send the email right now. Try again in a few minutes.

    Retry after the Retry-After header

  • INTAKE_PAUSED503

    New jobs are paused for a few minutes. Your text is still here.

    Retry after the Retry-After header

  • PIPELINE_HALTED503

    New jobs are paused while we fix a problem. Your text is still here.

    Retry after the Retry-After header

  • QUEUE_UNAVAILABLE503

    We could not start your job. Your words were not used. Try again.

    Retry after the Retry-After header

  • SIGNUPS_PAUSED503

    New sign-ups are paused for a short time. Existing accounts work as usual.

    Retry after the Retry-After header

  • UPSTREAM_BUSY503

    The checker is busy. Try again in {seconds} seconds.

    Retry after the Retry-After header

  • UPSTREAM_CREDITS_EXHAUSTED503

    The humanizer service is temporarily unavailable. Your words were not used.

    Retry after the Retry-After header

  • UPSTREAM_TIMEOUT504

    The check took too long. Your words were refunded.

    Do not retry; change the request

Job warning codes (26)
style_replication_unavailable
Style replication was not available for this profile, so the standard humanize pass ran.
humanizer_output_failed
The humanizer could not process this text.
unchanged_words_released
You got your text back with only small fixes, and the words for this job were refunded.
protected_span_exposed
A protected term may have been changed. Check your citations and protected terms.
citation_reinserted
A citation was restored to its sentence after the humanizer pass.
fidelity_embeddings_unavailable
The similarity check used mechanical checks only for this job.
skeleton_preserved
Some sentences kept their original structure. You can run it again.
detector_above_threshold
The AI likelihood is above the target for this preset. Try Humanize again or edit the flagged passages.
detector_flagged
The AI likelihood is above the target for this preset. Try Humanize again or edit the flagged passages.
detector_split_verdict
The overall AI likelihood is low, but at least one detector would still flag this text as AI.
detector_low_confidence
Low confidence under 200 words. Detector scores on short texts are unreliable.
detector_unavailable
The AI likelihood could not be measured for this job.
detector_not_run_on_final
The AI likelihood could not be measured for this job.
baseline_detector_unavailable
The AI likelihood could not be measured for this job.
unsupported_language_partial_pipeline
This text is not English, so the cleanup step was skipped and the style score is not shown.
texts_expired
The text of this job has been deleted under your plan's retention period.
formatting_simplified
Some formatting could not be kept, so part of the result is shown as plain paragraphs.
humanizer_output_rejected
The humanizer output changed your meaning, so it was not used.Older jobs only
fidelity_violations
A few sentences may have drifted in meaning. Review the highlighted text.Older jobs only
length_drift
The result is noticeably shorter or longer than your text.Older jobs only
slop_gate_not_met
Some AI-style phrasing remained after the rewrite step.Older jobs only
llm_rewrite_unavailable
The rewrite step was unavailable, so only the humanizer pass ran.Older jobs only
llm_rewrite_fabricated_rolled_back
The rewrite was rolled back because it did not preserve your text.Older jobs only
chunk_not_rewritten
Part of your text was kept as written because the rewrite step could not process it.Older jobs only
rewrite_output_rejected
The rewrite changed your meaning, so the delivered text is closer to your original.Older jobs only
humanizer_unavailable_degraded
The humanizer service was unavailable, so this job finished as Rewrite only. It used the same words as a full job.Older jobs only

Reference

Every endpoint an API key can call, generated from the OpenAPI document. Account settings, billing and sign-in are app-only and need a signed-in session.

Jobs

GET/v1/jobs

List job summaries

Auth
API key
Success
HTTP 200
Query
limit, cursor, status, preset, strategy, source, created_after, created_before
3 possible error codes

UNAUTHENTICATED, VALIDATION_ERROR, INTERNAL_ERROR

POST/v1/jobs

Create a humanization job (plain text, or Markdown with input_format markdown)

Auth
API key
Success
HTTP 202
Request body
JobCreateRequest
Idempotency
Idempotency-Key header accepted
18 possible error codes

UNAUTHENTICATED, INSUFFICIENT_WORDS, PLAN_REQUIRED, TRIAL_ALREADY_USED, TURNSTILE_FAILED, FEATURE_DISABLED, STYLE_NOT_FOUND, VALIDATION_ERROR, IDEMPOTENCY_CONFLICT, TEXT_TOO_SHORT, TEXT_TOO_LONG, UNSUPPORTED_LANGUAGE, CONCURRENCY_LIMIT, RATE_LIMITED, INTERNAL_ERROR, UPSTREAM_CREDITS_EXHAUSTED, INTAKE_PAUSED, DAILY_CAPACITY_REACHED

GET/v1/jobs/{id}

Read a job, including texts

Auth
API key
Success
HTTP 200
Query
fields
3 possible error codes

UNAUTHENTICATED, JOB_NOT_FOUND, INTERNAL_ERROR

DELETE/v1/jobs/{id}

Delete a finished job and its texts

Auth
API key
Success
HTTP 200
4 possible error codes

UNAUTHENTICATED, JOB_NOT_FOUND, JOB_NOT_DELETABLE, INTERNAL_ERROR

GET/v1/jobs/{id}/events

Server-sent events for a job

Auth
API key
Success
HTTP 200
3 possible error codes

UNAUTHENTICATED, JOB_NOT_FOUND, INTERNAL_ERROR

POST/v1/jobs/{id}/rehumanize

Run the humanizer again (new charge)

Auth
API key
Success
HTTP 202
Request body
RehumanizeRequest
Idempotency
Idempotency-Key header accepted
14 possible error codes

UNAUTHENTICATED, INSUFFICIENT_WORDS, PLAN_REQUIRED, JOB_NOT_FOUND, JOB_NOT_REHUMANIZABLE, REHUMANIZE_LIMIT_REACHED, VALIDATION_ERROR, IDEMPOTENCY_CONFLICT, CONCURRENCY_LIMIT, RATE_LIMITED, INTERNAL_ERROR, UPSTREAM_CREDITS_EXHAUSTED, INTAKE_PAUSED, DAILY_CAPACITY_REACHED

POST/v1/jobs/{id}/cancel

Cancel a running job

Auth
API key
Success
HTTP 200
4 possible error codes

UNAUTHENTICATED, JOB_NOT_FOUND, JOB_NOT_CANCELLABLE, INTERNAL_ERROR

Detect

GET/v1/detect

List standalone detector runs

Auth
API key
Success
HTTP 200
Query
limit, cursor
3 possible error codes

UNAUTHENTICATED, VALIDATION_ERROR, INTERNAL_ERROR

POST/v1/detect

Standalone detector check

Auth
API key
Success
HTTP 200
Request body
DetectRequest
Idempotency
Idempotency-Key header accepted
15 possible error codes

UNAUTHENTICATED, INSUFFICIENT_WORDS, PLAN_REQUIRED, VALIDATION_ERROR, TEXT_TOO_SHORT, TEXT_TOO_LONG, UNSUPPORTED_LANGUAGE, IDEMPOTENCY_CONFLICT, RATE_LIMITED, INTERNAL_ERROR, UPSTREAM_UNAVAILABLE, UPSTREAM_BUSY, UPSTREAM_CREDITS_EXHAUSTED, DAILY_CAPACITY_REACHED, UPSTREAM_TIMEOUT

GET/v1/detect/{id}

Read one detector run

Auth
API key
Success
HTTP 200
3 possible error codes

UNAUTHENTICATED, NOT_FOUND, INTERNAL_ERROR

Styles

GET/v1/styles

List style profiles

Auth
API key
Success
HTTP 200
Query
limit, cursor
3 possible error codes

UNAUTHENTICATED, PLAN_REQUIRED, INTERNAL_ERROR

POST/v1/styles

Create a style profile

Auth
API key
Success
HTTP 201
Request body
StyleCreateRequest
Idempotency
Idempotency-Key header accepted
9 possible error codes

UNAUTHENTICATED, PLAN_REQUIRED, STYLE_LIMIT_REACHED, VALIDATION_ERROR, STYLE_SAMPLE_TOO_SHORT, STYLE_SAMPLE_TOO_LONG, UNSUPPORTED_LANGUAGE, IDEMPOTENCY_CONFLICT, INTERNAL_ERROR

GET/v1/styles/{id}

Read one style with its sample

Auth
API key
Success
HTTP 200
3 possible error codes

UNAUTHENTICATED, STYLE_NOT_FOUND, INTERNAL_ERROR

PUT/v1/styles/{id}

Replace name and/or sample, or request style replication

Auth
API key
Success
HTTP 200
Request body
StyleUpdateRequest
8 possible error codes

UNAUTHENTICATED, PLAN_REQUIRED, STYLE_NOT_FOUND, VALIDATION_ERROR, STYLE_SAMPLE_TOO_SHORT, STYLE_SAMPLE_TOO_LONG, UNSUPPORTED_LANGUAGE, INTERNAL_ERROR

DELETE/v1/styles/{id}

Soft-delete a style

Auth
API key
Success
HTTP 200
3 possible error codes

UNAUTHENTICATED, STYLE_NOT_FOUND, INTERNAL_ERROR

Account and usage

GET/v1/me

Current user, plan, limits, features

Auth
API key
Success
HTTP 200
2 possible error codes

UNAUTHENTICATED, INTERNAL_ERROR

GET/v1/usage

Word usage and ledger for a period

Auth
API key
Success
HTTP 200
Query
period, group_by, api_key_id, cursor, limit
4 possible error codes

UNAUTHENTICATED, NOT_FOUND, VALIDATION_ERROR, INTERNAL_ERROR

Service

GET/v1/health

Service health; `probe` is th-probe-ok while D1 answers and no cron heartbeat is stale

Auth
Public
Success
HTTP 200
1 possible error codes

INTERNAL_ERROR

GET/v1/openapi.json

This document

Auth
Public
Success
HTTP 200
1 possible error codes

INTERNAL_ERROR