Chersus

Documentation

API reference

The complete POST /v1/run contract: headers, request and response fields, statuses, per-step billing, and the audit trace. Plus the service descriptor.

Last updated

One endpoint runs everything. A second returns the contract for each service. This page specifies both, plus every field a response carries. For the walk-through, start with the quickstart; for per-service examples, browse the services directory.

POST /v1/run

Runs a pipeline of one or more steps against an input. A single service call and a multi-step chain look the same on the wire.

POST /v1/run
Authorization: Bearer chrs_live_YOUR_KEY
Content-Type: application/json
Idempotency-Key: order-8812-check
Header Required Purpose
Authorization yes Bearer API key
Content-Type yes application/json
Idempotency-Key no Deduplicates retries for 24 hours. A repeat returns status: "duplicate" and the original trace_id, runs nothing, bills nothing.

Every response also carries the rate-limit window in X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. Limits are per account; a chained run counts as one request. Maximum input is 100,000 characters per run, and the validator rejects larger inputs before anything runs. Region is an account setting, not a request field.

Request body

{
  "request_id": "order-8812-check",
  "input": {
    "type": "text",
    "value": "Hi, I'm Jan de Vries, my IBAN is NL91ABNA0417164300, fix this or else."
  },
  "pipeline": [
    {
      "id": "shield",
      "service": "security.jailbreak.shield",
      "stop_if": { "verdict": "flag" }
    },
    {
      "id": "redact",
      "service": "governance.pii.redact",
      "params": { "entities": ["name", "iban"] }
    },
    { "id": "tox", "service": "security.toxicity.flag" },
    {
      "id": "intent",
      "service": "routing.intent.map",
      "params": { "catalog": "support-v3" }
    }
  ]
}
Field Required Description
request_id no Client correlation id, echoed back in the response
input.type yes text today. Reserved: image, audio, video
input.value yes Inline content. Media will use input.uri or input.base64 instead
pipeline yes Ordered list of steps. One step is valid
pipeline[].id yes Unique step id, used as the key in results and audit
pipeline[].service yes Service name, e.g. governance.pii.redact
pipeline[].params no Service-specific, validated against the descriptor
pipeline[].stop_if no Classify steps only: { "verdict": "flag" }, { "verdict": "pass" }, { "score_gte": 0.9 }, or { "score_lte": 0.1 }

Each step receives the payload as the previous step left it. When a stop_if fires, later steps do not run and do not bill. Halting on extract results needs workflows.

GET /v1/services/:name

Returns the descriptor clients need to build valid chains.

{
  "name": "governance.pii.redact",
  "kind": "transform",
  "accepts": ["text"],
  "emits": "same",
  "billing_unit": "characters",
  "languages": "multilingual",
  "params": {
    "entities": { "type": "array", "default": ["name", "email", "id"] },
    "mask": { "type": "string", "default": "[REDACTED]" }
  },
  "model": { "id": "Roblox/roblox-pii-classifier-v2", "license": "Apache-2.0" }
}
Field Description
kind transform, classify, or extract; fixes the result shape
accepts Payload types the service accepts
emits "same" (payload type unchanged) or a concrete type
billing_unit What the service bills on; characters today, pixels and seconds reserved
languages The languages the service handles: an array of ISO 639-1 codes, or "any" when it runs no language model, "multilingual" when its backend supports many without enumerating them, or "unspecified" when the backend publishes no claim
params Typed parameters with defaults, validated per step
labels Classify services only: every label the service can return
output_schema Extract services only: the JSON schema data is validated against
model Backend model id and license

The validator uses accepts and emits to reject impossible chains before anything runs. There is no version in a service name: every call runs the latest version, and the audit trace records the version that ran.

Response body

{
  "request_id": "order-8812-check",
  "trace_id": "chs_01J9X4Q7K2M8N3P5R6T7V8W9X0",
  "status": "completed",
  "halted_at": null,
  "output": {
    "type": "text",
    "value": "Hi, I'm [REDACTED], my IBAN is [REDACTED], fix this or else."
  },
  "results": {
    "shield": { "kind": "classify", "status": "ok", "verdict": "pass", "score": 0.03, "labels": [] },
    "redact": { "kind": "transform", "status": "ok", "changes": 2 },
    "tox": { "kind": "classify", "status": "ok", "verdict": "flag", "score": 0.84, "labels": ["threat"] },
    "intent": { "kind": "extract", "status": "ok", "data": { "intent": "billing.dispute", "confidence": 0.91 } }
  },
  "usage": {
    "billed": [
      { "unit": "characters", "quantity": 70, "steps": ["shield"] },
      { "unit": "characters", "quantity": 70, "steps": ["redact"] },
      { "unit": "characters", "quantity": 60, "steps": ["tox"] },
      { "unit": "characters", "quantity": 60, "steps": ["intent"] }
    ]
  },
  "audit": {
    "region": "eu-nl-1",
    "retention": "none",
    "received_at": "2026-09-03T09:14:22.118Z",
    "total_ms": 41,
    "steps": [
      {
        "id": "shield",
        "service": "security.jailbreak.shield",
        "version": 1,
        "model": "meta-llama/Prompt-Guard-86M",
        "status": "ok",
        "input_type": "text",
        "input_size": 70,
        "input_sha256": "9f2c...a1",
        "output_type": "text",
        "output_size": 70,
        "output_sha256": "9f2c...a1",
        "verdict": "pass",
        "duration_ms": 6
      },
      {
        "id": "redact",
        "service": "governance.pii.redact",
        "version": 2,
        "model": "Roblox/roblox-pii-classifier-v2",
        "status": "ok",
        "input_type": "text",
        "input_size": 70,
        "input_sha256": "9f2c...a1",
        "output_type": "text",
        "output_size": 60,
        "output_sha256": "4b71...c9",
        "changes": 2,
        "duration_ms": 11
      }
    ]
  }
}
Field Description
request_id Echoed correlation id
trace_id Unique run id
status completed, halted, failed, or duplicate
halted_at Step id where the run halted, else null
output The final payload, { type, value }
results One result per executed step, keyed by step id
usage.billed One line per executed step, with the characters that step processed
audit The trace: region, retention, timing, and a record per step

Status values

Status Meaning
completed Every step ran
halted A stop_if fired (or a cap was hit). halted_at holds the step id; output is the payload at that point
failed A step failed or validation rejected the pipeline. An error object replaces output; results still holds every step that completed before the failure
duplicate The Idempotency-Key was seen in the last 24 hours. Only trace_id is returned

Result shapes by kind

Every result carries kind and status. The rest depends on the kind only.

Kind Fields
transform changes (integer). governance.pii.tokenize adds map (token to original value)
classify verdict (pass, flag, uncertain), score (0 to 1), labels. A flag always means the thing the service guards against was found; score is the probability of flag, so a pass has a low score
extract data (JSON, validated against the service’s output_schema)

Billing fields

Each line in usage.billed carries unit (characters; pixels and seconds reserved), quantity, and the steps it covers. The short version of the rules:

  • Every executed step bills the characters it processes, at the tier rate. There is one rate table and no included steps.
  • A transform changes the payload, so a step after one bills what it was left rather than the original input.
  • A stop_if halt stops the count: steps that never ran never bill.
  • A run that fails part-way bills only the steps that completed. Chersus bills nothing for rejected requests or for its own failures.

The account’s total billed characters for the calendar month set the tier. Rates are marginal. The pricing page has the rate table and a calculator.

Audit trace fields

Per run: region, retention ("none"), received_at, total_ms. Per step: the service and version, the model, input and output type and size, SHA-256 hashes of input and output, the verdict or change count, and duration_ms.

No payload content is ever included. Hashes are SHA-256 over the UTF-8 bytes of the payload exactly as received or produced, with no trimming or normalization. Recompute them and you can prove a given text went through a given step with a given result, without Chersus storing the text.

Errors

Every error carries one envelope and one code to branch on, whether the request was rejected before it ran or a run failed part-way through. Errors documents the full set, the two levels they arrive at, and what each one bills.

Key handling is covered in authentication, and throttling in rate limits.