Wrong Train

In development. Closed preview. The API is in development. No key is issued and no request is served yet.

Build clearer communication into your product.

A structured API for separating what a message says, what remains uncertain, and how someone could respond. One request would return one review. Your product would decide how to show it.

Everything on this page is a proposal or a fictional example. The prices are proposed, the code targets a placeholder origin, and nothing on this page calls a model.

Who it is for

Built for

  • Developers building an opt-in communication, coaching, productivity or accessibility product, starting with indie developers and small teams.
  • Products where the person reading a message chooses to ask for a review, sees the result, and decides what to do.
  • Teams that want a maintained output contract, source-linked observations and a predictable cost per review, rather than a prompt of their own to keep tuning.

Not for

  • Employee surveillance, covert message collection, or reviews of messages the reader did not choose to submit.
  • Inferring a disability, a diagnosis or a mental state from how someone writes.
  • Automated employment, credit, housing or other decisions about a person.
  • Lie detection, social-skill scores, or mind reading. A message alone cannot establish what someone intended.

The person using your product may be neurodivergent. The account calling the API is your integration team. These are different audiences: the API is written for the second, in plain language the first can also read.

Larger employer integrations are later work, and the restrictions above would be written into the terms before any such account exists.

Powered by SESHAT, the review engine behind Wrong Train. You would not need to know SESHAT, or the Wrong Train app, to use the API: the contract on this page and in the docs would be the whole interface.

One message, one review.

A fictional message, and the review the API would return for it. Fixture output, not a live result, and not about a real person. The same three messages are the fixtures used in local testing.

A message with a missing time

The message

“Hey, got a minute later?E1 Want to go over the deck before it goes outE2.”

Context sent with it: “From my manager, in chat, late in the afternoon.”

Request: goal understand, style neutral.

Explicit content
  1. E1“got a minute later?” (message, 5 to 24)

    The sender asks for a short conversation later. No time is given.

  2. E2“go over the deck before it goes out” (message, 33 to 68)

    The subject is the deck, and the conversation is wanted before the deck is sent.

  3. E3“From my manager” (context, 0 to 15)

    The caller says the sender is their manager. That is supplied context, not something the message states.

Each observation is tied to an exact quote, marked in the message with its id. Offsets are zero-based Unicode code points, end-exclusive.

Possible interpretations
  1. I1A routine check before the deck goes out.

    Fits the words. Nothing in the message confirms it over the other readings.

  2. I2The manager already has a change in mind.

    Possible. The message names no change.

  3. I3Something else the manager wants to raise while you are there.

    Possible. The message gives no sign either way.

Unknowns
  1. U1When “later” is, and for how long.

    Sets whether you can prepare, and whether it fits your day.

  2. U2Whether anything needs preparing.

    Decides what you bring.

  3. U3Whether “go over” means read through or rework.

    Changes the size of the task and the tone of the conversation.

Limitations
  • The review rests on the supplied text and context only.
  • It cannot establish what the sender intended.
  • The replies are options for the reader to choose from or set aside. Nothing is sent.

Review status: complete.

This review would consume one standard review unit.

The same review as JSON

The body your server would receive. Field names and bounds are in the draft docs.

{
  "request_id": "req_fixture_deck_0001",
  "schema_version": "2026-09-26.draft-2",
  "review_status": "complete",
  "explicit_content": [
    {
      "id": "E1",
      "source": "message",
      "quote": "got a minute later?",
      "start": 5,
      "end": 24,
      "observation": "The sender asks for a short conversation later. No time is given."
    },
    {
      "id": "E2",
      "source": "message",
      "quote": "go over the deck before it goes out",
      "start": 33,
      "end": 68,
      "observation": "The subject is the deck, and the conversation is wanted before the deck is sent."
    },
    {
      "id": "E3",
      "source": "context",
      "quote": "From my manager",
      "start": 0,
      "end": 15,
      "observation": "The caller says the sender is their manager. That is supplied context, not something the message states."
    }
  ],
  "possible_interpretations": [
    {
      "id": "I1",
      "reading": "A routine check before the deck goes out.",
      "caveat": "Fits the words. Nothing in the message confirms it over the other readings."
    },
    {
      "id": "I2",
      "reading": "The manager already has a change in mind.",
      "caveat": "Possible. The message names no change."
    },
    {
      "id": "I3",
      "reading": "Something else the manager wants to raise while you are there.",
      "caveat": "Possible. The message gives no sign either way."
    }
  ],
  "unknowns": [
    {
      "id": "U1",
      "question": "When “later” is, and for how long.",
      "why_it_matters": "Sets whether you can prepare, and whether it fits your day."
    },
    {
      "id": "U2",
      "question": "Whether anything needs preparing.",
      "why_it_matters": "Decides what you bring."
    },
    {
      "id": "U3",
      "question": "Whether “go over” means read through or rework.",
      "why_it_matters": "Changes the size of the task and the tone of the conversation."
    }
  ],
  "reply_options": [
    {
      "id": "R1",
      "style": "neutral",
      "text": "Sure. Is 3 or 4 better? Anything you want me to bring?",
      "what_it_does": "Accepts, and asks for the two missing pieces instead of guessing them."
    },
    {
      "id": "R2",
      "style": "direct",
      "text": "Yes. Send me a time, and tell me if you want changes made before we talk.",
      "what_it_does": "Accepts, and asks plainly whether rework is expected."
    },
    {
      "id": "R3",
      "style": "supportive",
      "text": "Happy to. Let me know when suits you, and whether there is anything you would like me to look at first.",
      "what_it_does": "Accepts warmly and leaves the timing to the sender."
    }
  ],
  "limitations": [
    "The review rests on the supplied text and context only.",
    "It cannot establish what the sender intended.",
    "The replies are options for the reader to choose from or set aside. Nothing is sent."
  ],
  "usage": {
    "review_units": 1,
    "charged_microdollars": 60000
  }
}

The replies are options for the reader, not instructions. The API sends nothing.

Two more messages

Same contract, shorter. The third shows a review that needs context.

A request with no deadline

The message

“When you get a chanceE1, could you send over the numbersE2?”

Context sent with it: “From a colleague, by email, mid-morning.”

Request: goal draft_reply, style direct.

Explicit content
  1. E1“When you get a chance” (message, 0 to 21)

    No deadline is given. The phrasing leaves the timing to the reader.

  2. E2“send over the numbers” (message, 33 to 54)

    The sender asks for a set of numbers.

Each observation is tied to an exact quote, marked in the message with its id. Offsets are zero-based Unicode code points, end-exclusive.

Possible interpretations
  1. I1Today would be good.

    The wording is the same for all three readings.

  2. I2This week is fine.

    Possible. Nothing in the message rules it in or out.

  3. I3Whenever, and they mean it.

    Possible. A colleague can mean the words as written.

Unknowns
  1. U1Which numbers.

    Sending the wrong set costs both of you a round trip.

  2. U2By when.

    Decides whether this comes before other work today.

  3. U3What they are for.

    Shapes the format and the level of detail.

Limitations
  • The review rests on the supplied text and context only.
  • It cannot tell how urgent the request is to the sender.
  • The replies are options for the reader to choose from or set aside. Nothing is sent.

Review status: complete.

This review would consume one standard review unit.

The same review as JSON

The body your server would receive. Field names and bounds are in the draft docs.

{
  "request_id": "req_fixture_numbers_0001",
  "schema_version": "2026-09-26.draft-2",
  "review_status": "complete",
  "explicit_content": [
    {
      "id": "E1",
      "source": "message",
      "quote": "When you get a chance",
      "start": 0,
      "end": 21,
      "observation": "No deadline is given. The phrasing leaves the timing to the reader."
    },
    {
      "id": "E2",
      "source": "message",
      "quote": "send over the numbers",
      "start": 33,
      "end": 54,
      "observation": "The sender asks for a set of numbers."
    }
  ],
  "possible_interpretations": [
    {
      "id": "I1",
      "reading": "Today would be good.",
      "caveat": "The wording is the same for all three readings."
    },
    {
      "id": "I2",
      "reading": "This week is fine.",
      "caveat": "Possible. Nothing in the message rules it in or out."
    },
    {
      "id": "I3",
      "reading": "Whenever, and they mean it.",
      "caveat": "Possible. A colleague can mean the words as written."
    }
  ],
  "unknowns": [
    {
      "id": "U1",
      "question": "Which numbers.",
      "why_it_matters": "Sending the wrong set costs both of you a round trip."
    },
    {
      "id": "U2",
      "question": "By when.",
      "why_it_matters": "Decides whether this comes before other work today."
    },
    {
      "id": "U3",
      "question": "What they are for.",
      "why_it_matters": "Shapes the format and the level of detail."
    }
  ],
  "reply_options": [
    {
      "id": "R1",
      "style": "direct",
      "text": "I can send the September figures by 3 pm today. Do you need them sooner, or a different set?",
      "what_it_does": "Names a time and asks the two open questions in one line."
    },
    {
      "id": "R2",
      "style": "neutral",
      "text": "Which numbers do you need, and by when?",
      "what_it_does": "Asks before doing, without proposing a time."
    }
  ],
  "limitations": [
    "The review rests on the supplied text and context only.",
    "It cannot tell how urgent the request is to the sender.",
    "The replies are options for the reader to choose from or set aside. Nothing is sent."
  ],
  "usage": {
    "review_units": 1,
    "charged_microdollars": 60000
  }
}
Feedback with no specifics

The message

“This needs another passE1.”

No context was sent.

Request: goal draft_reply, style neutral.

Explicit content
  1. E1“needs another pass” (message, 5 to 23)

    The reviewer wants changes. Which changes is not stated.

Each observation is tied to an exact quote, marked in the message with its id. Offsets are zero-based Unicode code points, end-exclusive.

Possible interpretations
  1. I1More detail is wanted.

    A short note can carry any of these readings.

  2. I2Less of it, or a clearer structure.

    Possible. The note does not say.

  3. I3A requirement is not met yet.

    Possible. No requirement is named.

Unknowns
  1. U1Which parts.

    Decides where to start.

  2. U2What a finished version would have.

    Decides when to stop.

Limitations
  • The review rests on the supplied text only. No context was given.
  • It cannot tell which document or which earlier feedback the note refers to.
  • The reply is an option for the reader to choose or set aside. Nothing is sent.

One clarification

Which document is this about, and what did the reviewer ask for last time?

With that context the review could tie the note to a specific request instead of three equally open readings.

Review status: needs context. The structure is complete and one clarification is attached. This would still count as one review.

This review would consume one standard review unit.

The same review as JSON

The body your server would receive. Field names and bounds are in the draft docs.

{
  "request_id": "req_fixture_pass_0001",
  "schema_version": "2026-09-26.draft-2",
  "review_status": "needs_context",
  "explicit_content": [
    {
      "id": "E1",
      "source": "message",
      "quote": "needs another pass",
      "start": 5,
      "end": 23,
      "observation": "The reviewer wants changes. Which changes is not stated."
    }
  ],
  "possible_interpretations": [
    {
      "id": "I1",
      "reading": "More detail is wanted.",
      "caveat": "A short note can carry any of these readings."
    },
    {
      "id": "I2",
      "reading": "Less of it, or a clearer structure.",
      "caveat": "Possible. The note does not say."
    },
    {
      "id": "I3",
      "reading": "A requirement is not met yet.",
      "caveat": "Possible. No requirement is named."
    }
  ],
  "unknowns": [
    {
      "id": "U1",
      "question": "Which parts.",
      "why_it_matters": "Decides where to start."
    },
    {
      "id": "U2",
      "question": "What a finished version would have.",
      "why_it_matters": "Decides when to stop."
    }
  ],
  "reply_options": [
    {
      "id": "R1",
      "style": "neutral",
      "text": "Which part should I start with, and what would a finished version have that this one does not?",
      "what_it_does": "Turns a vague note into a specific next step."
    }
  ],
  "limitations": [
    "The review rests on the supplied text only. No context was given.",
    "It cannot tell which document or which earlier feedback the note refers to.",
    "The reply is an option for the reader to choose or set aside. Nothing is sent."
  ],
  "clarification": {
    "question": "Which document is this about, and what did the reviewer ask for last time?",
    "why": "With that context the review could tie the note to a specific request instead of three equally open readings."
  },
  "usage": {
    "review_units": 1,
    "charged_microdollars": 60000
  }
}

What goes in, what comes out

One JSON request, one JSON response. Short English text, supplied deliberately by the caller. No files, images, links, tools, or private records.

The request

Fields of the request body. The bounds are proposals for the preview.
FieldType and ruleMeaning
messagestring, requiredthe text to review, supplied deliberately by the caller
contextstring, optionalwho sent it, where, when, or what came before; plain text
goalone of understand, draft_reply (default understand)what the reader wants from the review
response_styleone of direct, neutral, supportive (default neutral)the style of reply the reader asked for; it describes the requested output, not the sender's mood

The response

Fields of the response body.
FieldMeaning
request_idopaque id for support and replay
schema_versionthe response schema this body follows
review_statuscomplete, or needs_context when the review returns the documented structure and one clarification
explicit_contentobservations each tied to an exact quote from the message or the supplied context, with zero-based end-exclusive code point offsets
possible_interpretationsreadings labelled as possibilities, each with a caveat; no probabilities
unknownswhat the text does not settle, and why it matters
reply_optionsbounded reply drafts in the requested style; the reader chooses, the API sends nothing
limitationswhat this review cannot establish
clarificationpresent only when review_status is needs_context: one question and why it would help
usagereview_units charged by this request (0 or 1) and charged_microdollars (0, or the price of one review)

Every explicit observation carries a source (message or context), the exact quote, and start and end offsets in Unicode code points, zero-based and end-exclusive, on the text as you sent it. The service never normalizes your text after computing offsets.

A quote that matches the text shows where an observation points. It does not prove the observation is true. That is what semantic checks and human review are for, before any release.

There are no probabilities or confidence scores. Interpretations are labelled as possibilities, with a caveat each.

What counts as a review

A standard review would be one accepted, bounded text request that returns a validated review. Each would cost $0.06 from your credits, and at most one review would be charged per request, whatever happened inside.

Charged one review ($0.06)

  • A complete review that passed validation.
  • A needs_context review that returns the documented structure and one clarification.

Charged nothing

  • Any error response.
  • Empty or refusal-only output.
  • A review that failed validation within the attempt budget.
  • Unauthorized calls, oversized inputs and rate-limit rejections.
  • Failed upstream requests.
  • A replayed request that returns the cached result.

Inside one request the service would make at most one repair attempt, within one deadline and one cost budget. It would never loop until an answer looked good, and it would never charge twice.

If your connection dropped after a result was committed, that review would have been charged. Replaying the same Idempotency-Key within 15 minutes would return the same result with no second charge.

Proposed pricing

Pay as you go, in US dollars before any tax: one set price per review, drawn from credits you add in advance. No subscription, no monthly minimum and no commitment. Nothing is for sale yet: no way to pay, no credit, no key and no invoice exists. Every tier gets the same safety, privacy, grounding and output-quality policy; higher tiers raise rate limits, not the quality of an answer.

Pay as you go

Standard review

$0.06 per review

Proposed price: $0.06 per review, $60 per 1,000 reviews. Not for sale yet.

One price for every review within the bounds, however long the message.

  • $60 per 1,000 reviews
  • Errors, rejections and failed output: $0
  • Credits added in whole dollars, $10 to $1,000 at a time
  • Purchased credits would expire 12 months after they are bought
  • No subscription, no monthly minimum, no automatic top-up

To start

Trial credits

$6 in credits

Proposed: $6 in trial credits, enough for 100 reviews. Not granted yet.

For building and testing an integration before paying anything.

  • 100 reviews at the standard price
  • Once per organization, with no card required
  • Would expire 90 days after they are granted
  • Used before any purchased credits
  • In the preview, for approved organizations

Usage tiers

Rate limits would rise with what an organization has bought, not with a plan. The limits are provisional: only what the deployment can hold would be published.

Proposed usage tiers, with provisional limits per organization.
Tier and when it appliesRequests a minuteAt the same time
Trial: trial credits only, before any credits are bought21
Tier 1: after buying at least $10 in credits102
Tier 2: after $100 in purchases and 7 days since the first one305
Tier 3: after $500 in purchases and 14 days since the first one6010

How credits would be used

  • Trial credits are used first, then purchased credits, earliest expiry first.
  • A review costs $0.06 however it is paid for; when one grant holds less than that, the rest comes from the next.
  • When your unexpired credits cannot cover a review, the API would stop with a clear error and say how to add credits. No overage, no automatic top-up and no bill afterwards.
  • Credits would belong to the organization and be shared by its projects. The usage view would show the balance and when each grant expires.
  • Expired credits could not be used and would not be refunded as cash. The refund and account-closure policy would be published before anything is sold.
  • A price change would be announced in advance and never applied to a review already charged.

These prices are hypotheses to test with qualified developers. What matters is whether they integrate and come back, not whether they click on a price.

Limits and data handling

Bounds of one request

  • Message plus context: at most 6,000 Unicode code points.
  • Request body: at most 32 KiB.
  • English only in the preview. Other languages are rejected before any model call, at no charge.
  • Upstream: at most 4,000 input tokens and 1,200 output tokens, counted with the selected route's own tokenizer. The system prompt and schemas count toward that budget.
  • Over any limit, the whole request is rejected with an error that names the limit. Nothing is truncated silently.

Data handling, as proposed

  • Raw request text would not be persisted by the service.
  • Review bodies would be kept for 15 minutes in an encrypted replay cache, only so a retry can fetch the same result, and left out of backups.
  • Content-free operational metadata would be kept for 30 days.
  • No customer content would be used for training, archived, sampled for debugging or reused for evaluation by default. Any research contribution would need separate informed opt-in and a deletion path.
  • The upstream model route has its own retention and terms. They are separate from Wrong Train storage, would be confirmed before launch, and disclosed as they are. Not saved in our database is not the same as stored nowhere.

What the API will not do

  • Attach a diagnosis, a character verdict or a risk label to a person from a message.
  • Produce threats, targeted harassment, coercion or employee risk rankings.
  • Send any message.
  • Read images, files, URLs or the web.
  • Run tools, take actions or reach private records.
  • Return diagnoses, legal conclusions, deception verdicts or social competence scores.
  • Return probabilities or confidence percentages.

Text inside a message is data. Instructions in it do not change how the service behaves, what it can reach, or what is billed.

Calling it

The examples target a placeholder origin that does not route anywhere, on purpose. The proposed production origin is an api subdomain of wrongtrain.com. It stays a proposal until ownership, TLS, authentication and isolation are verified. The request below is the fictional message above.

cURL

curl https://api.wrongtrain.invalid/v1/communication/reviews \
  -H "Authorization: Bearer $WT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: review-2026-10-14-0001" \
  -d '{
    "message": "Hey, got a minute later? Want to go over the deck before it goes out.",
    "context": "From my manager, in chat, late in the afternoon.",
    "goal": "understand",
    "response_style": "neutral"
  }'

Runs on your server, never in a browser or a shipped app. A browser or a phone talks to your backend, and your backend holds the key.

Server-side JavaScript

// Node 18 or later, on your server. The key is read from the environment.
const response = await fetch("https://api.wrongtrain.invalid/v1/communication/reviews", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.WT_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    message: "Hey, got a minute later? Want to go over the deck before it goes out.",
    context: "From my manager, in chat, late in the afternoon.",
    goal: "understand",
    response_style: "neutral",
  }),
});
const body = await response.json();
if (!response.ok) {
  // body.error.code is stable; body.error.retry says what to do. Errors cost nothing.
  throw new Error(body.error.code);
}
// body.explicit_content, body.possible_interpretations, body.unknowns,
// body.reply_options, body.limitations, body.usage.review_units

Keys would be shown once, stored as a digest, and revocable at any time. Never put one in a client, a URL, a log or an analytics event.

What you can do today

The API is in development. This page, the draft docs and the fictional fixtures are what exists in public. There is no key to request, no endpoint to call and nothing to pay for.

You can

  • Read the draft documentation.
  • Read the three fictional reviews here, in plain language and as JSON, to see the contract.
  • Tell us what you are building, by email, so the preview can be shaped around real integrations.

You cannot yet

  • Get an API key.
  • Send a request to a live endpoint.
  • Claim trial credits or buy credits.

Questions

Is the API live?

No. It is in development. The reviews on this page are scripted fixtures, no endpoint answers, no key exists and no credit is granted. When approved organizations can receive keys and trial credits, this page will say so.

What does one review cost?

$0.06, drawn from your credits ($60 per 1,000 reviews). One request that returned a validated review would be charged once. Errors, rejections and failed output cost nothing. There is no subscription and no monthly minimum.

Can I try it without paying?

Each new organization would get $6 in trial credits, enough for 100 reviews, once and with no card required. They would expire 90 days after they are granted. In the preview they would go to approved organizations only.

Do you keep the messages?

The proposal is not to persist raw request text, and to keep review bodies for 15 minutes only, for replay. The upstream route's retention is separate and would be disclosed before launch. None of this is a promise until the terms are published.

Can I call it from a browser or an app?

Call it from your server. A browser, a phone app or a watch app talks to your backend, and your backend holds the key. A key never belongs in a client.

Which languages?

English only in the preview. Other languages are rejected before any model call, and cost nothing.

Is this the same as the Wrong Train app?

No. The API is for developers building their own products, paid per review from prepaid credits. The Wrong Train app is a separate product for individuals. Nothing about the API is for sale yet.

What is SESHAT?

The review engine behind Wrong Train. It organizes what a text supports, keeps observation apart from interpretation, and keeps claims bounded. You do not need to know it to use the API.

Can an employer use this on staff messages?

Not in this form. The API is for voluntary, reader-initiated review. Surveillance, covert collection, disability inference and automated employment decisions are outside its intended terms.

How good is it?

We do not publish a number. Before any live release the service would be evaluated on a held-out set with human review and compared with a direct model prompt. Results would be reported with sample sizes and failure categories, not as one accuracy figure.

How do I get a key?

You cannot yet. Keys and trial credits would go first to a small number of approved organizations, once the privacy, security and quality gates pass. Email to say what you are building. That is not a signup, and it grants nothing.

Tell us what you are building

If this contract would fit a product you are making, or something in it is wrong for you, tell us by email. That shapes the preview before any key exists.

Email about the API preview

Opens a draft in your own email app, addressed to research@wrongtrain.org. Nothing is sent until you send it.

A person at Wrong Train reads that address. Writing is not a signup, does not join a waitlist, and does not give access. A reply is not guaranteed.

Please do not include workplace messages, client information, or personal records.

No email app? The address is research@wrongtrain.org.