# Features | Chersus

![](/brand/carapace-hero-tilted-columbia.svg)
Features

# Small jobs that stop
big mistakes.

Detects judge the text. Extracts read it. Transforms rewrite it. Relay hands the result to your model, and you pay for the characters in and the characters handed back.

## One request, read line by line.

*POST /v1/run, request and response*

```
<span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap text-code-comment">// request</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">{</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "input": "Hi, I am Jane Doe. Can you check invoice #4821?",</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "detect": [</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="1">1</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">    { "op": "secrets.credentials", "block": true },</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="4">4</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">    "pii.entities"</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  ],</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "extract": ["language.code"],</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="1">1</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "transform": [</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="2">2</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">    { "op": "pii.tokenize", "when": ["detect.pii.entities:flagged"] }</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="3">3</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  ]</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">}</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap"> </span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap text-code-comment">// response</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">{</span>
                <span></span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "decision": "allow",</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="5">5</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "output": "Hi, I am <PERSON_eb7805d77a13>. Can you check invoice #4821?",</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="5">5</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">  "usage": { "billed": { "input": 47, "output": 60, "quantity": 107 } }</span>
                <span class="flex h-[1lh] items-center">
                    <span class="inline-grid h-5 w-5 flex-none place-items-center rounded-full bg-support font-mono text-[10.5px] text-brand data-active:outline-2 data-active:outline-offset-2 data-active:outline-support" data-mark="6">6</span>
                  </span>
              </span><span class="grid grid-cols-[1fr_20px] items-start gap-3">
                <span class="whitespace-pre-wrap">}</span>
                <span></span>
              </span>
```

1. 1`detect`, `extract`Run side by side on your input. They never change it.
1. 2`transform`Runs in the order you list. Each step gets the text the previous one handed back.
1. 3`when`Runs a transform only when its outcome holds, like `detect.pii.entities:flagged`.
1. 4`block`Stops the run when that detect flags. Nothing derived from the text comes back.
1. 5`decision`, `output`Allow or block, and the text the last step handed back.
1. 6`usage.billed`Characters in plus characters out. That's the bill.

Three shapes come back: a verdict and a score, a value, or rewritten text with a count of changes.

detect

## Catch attacks first.

Threats and secrets can be stopped before they reach a model, a log or a tool. Each check returns a verdict and a score, and a check set to block stops the request when it flags.
PII and secretsNames, addresses, account numbers, API keys and credentials, flagged with a score.Prompt injection and jailbreaksInstructions hiding in user text, caught before the model reads them.Hate, abuse and threatsThree separate scores from one model, ready to route on.Agents, tooRun the same detects on tool output, web pages and documents, not only on what users type.
- detect.pii.entities
- detect.toxicity.hate
- detect.toxicity.abuse
- detect.toxicity.threat
- detect.prompt.injection
- detect.prompt.jailbreak
- detect.secrets.credentials
[See the security examples →](/en/examples/#examples-security)

*detect*

```
"detect": {
  "secrets.credentials": {
    "verdict": "flag", "score": 0.97, "labels": ["api_key"]
  },
  "prompt.injection": { "verdict": "pass", "score": 0.02 },
  "toxicity.hate": { "verdict": "pass", "score": 0.03 }
}
```

extract

## Fields, without a parser.

Route, file and act on a message without a parser of your own. An extract returns a value and leaves the text alone. That value can still decide what runs next.
Language and topicsLanguage picks the model or the queue; topics decide which steps run.Fields from a schemaTyped values your systems can insert without a parsing layer.Titles, descriptions and emojiShort text for an inbox, a feed or a notification.
- extract.fields.schema
- extract.language.code
- extract.content.title
- extract.content.description
- extract.content.topics
- extract.content.emoji
[See the content examples →](/en/examples/#examples-content)

*extract*

```
"extract": {
  "language.code": { "value": "en", "confidence": 0.99 },
  "fields.schema": {
    "value": { "invoice": "4821", "amount": 129.5 }
  }
}
```

transform

## Only clean text leaves.

What leaves your system is already clean, with a count of what changed. Transform operations run in the order you list them, and you are billed for what the last one returns.
RedactReplace personal data with a marker before it reaches a log or a model.TokenizeReplace it with a typed placeholder you can put back later.Scrub secretsCredentials removed, the rest untouched.
- transform.pii.redact
- transform.pii.tokenize
- transform.secrets.redact
[See the data examples →](/en/examples/#examples-data)

*transform*

```
"output": "Contact <PERSON_eb7805d77a13> at <EMAIL_33b537052b41> about invoice #4821.",
"transform": [
  { "op": "pii.tokenize", "changes": 2 }
],
"tokens": {
  "<PERSON_eb7805d77a13>": "Jane Doe",
  "<EMAIL_33b537052b41>": "jane.doe@example.com"
}
```

## Pick the model per message.

One relay can pick a model per message. It decides on one value: a detect, the language, or a custom condition your code sets, like `high_security`. So exactly one route holds, every time.
Personal data found?Tokenized, to a model in the EU. Clean, to the model you prefer.Contract says EU-only?Your code sets `high_security` for that customer, and their prompts stay in the EU.A German message?To the model that writes German best.

relaydetect.pii.entitiesflaggedEU modelcleanyour model
*With your own key, today: Mistral, Azure OpenAI in an EU Data Zone, Bedrock `eu.*` profiles, Vertex AI in the EU.*

relay

## The model never sees the name.

*What the user typed*

Hi, I am Jane Doe. Can you check invoice #4821?

*What the model saw*

Hi, I am <PERSON_eb7805d77a13>. Can you check invoice #4821?

*What the user read*

Hi Jane Doe, invoice #4821 was paid on 3 September.
Your own keyUsed once, never stored, only its fingerprint in the audit trace.Values come backAfter a tokenize, relay puts the real names back before you see the answer.Same placeholder, every turnJane gets the same token every turn; a session key separates conversations.StreamingThe answer streams back with the real values already in place.Provided modelsNo key needed, provider cost plus 5% on its own line. Not available yet.
## Two ways in.

### The API

`POST /v1/run`. Send text and steps, get the result. Start here.

*POST /v1/run*

```
curl -X POST https://api.chersus.com/v1/run \
  -H "Authorization: Bearer $CHERSUS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "Hi, I am Jane Doe. Can you check invoice #4821?",
    "transform": ["pii.tokenize"]
  }'
```
[Read the quickstart →](/en/docs/quickstart/)
### Your SDK

`gw.chersus.com`. Already on the OpenAI or Anthropic SDK? Change the base URL and keep your code. Your default policy runs on every call.

*gw.chersus.com*

```
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://gw.chersus.com/openai/v1",
  defaultHeaders: { "X-Chersus-Key": process.env.CHERSUS_KEY },
});
```
[Gateway guide →](/en/docs/calling-your-model/#gateway)
### If you need a big model, who calls it?
Chersus, with relayOne request; the clean prompt goes to your model, and the answer comes back with the real values.YouTake the clean text and the token map, call your model wherever it runs, and put the values back yourself.
## Control and proof.

### Save the workflow as a policy.

A policy is a saved workflow: reference it by name from every application, and publish a new version to change it. Pin `customer-support@3` in the app you're testing and run `@4` everywhere else. Nobody redeploys.
[How policies work →](/en/docs/policies/)
policy

customer-support@4

├──detectsecrets.credentials (block), prompt.injection (block)

├──extractlanguage.code, content.topics

├──transformpii.tokenize

├──relayprovider: your account | provided model

└──answerdetect.secrets.credentials (block), rehydrate, automatic

### Prove what ran, without the content.

Every response carries an audit trace of hashes, models and timings, never the key, never the text. An auditor sees which operations ran, which version, when, and on which hashed text. They never see the text itself.

*audit trace (trimmed)*

```
{
  "trace_id": "chs_01J9X6C2K2M8N3P5R6T7V8W9X0",
  "region": "eu-nl-1",
  "retention": "none",
  "input_sha256": "9f2c1e...a1",
  "ops": [
    { "op": "detect.pii.entities", "verdict": "flag" },
    { "op": "transform.pii.tokenize", "changes": 1 }
  ],
  "relay": {
    "provider": "openai",
    "model": "gpt-4.1-mini",
    "key_fingerprint": "sha256:3f9a...c1"
  }
}
```

## Fine print, answered.
What is the difference between a detect, an extract and a transform?+−
A detect or an extract reads the text and never changes it: a detect returns a verdict, a score and labels, an extract returns a value. A transform returns rewritten text and a count of changes; detects and extracts run in parallel, transforms in the order you list them, each reading the previous one's output.
How fast is it?+−
Every operation runs on a small model built for one job, not a general model that thinks it over. Try it on your own traffic in early access.
Which languages?+−
It depends on the operation: personal data in 27, language detection in 84, topics in 101. Each operation lists its own.
Does the model ever see personal data?+−
It depends on your workflow. With a tokenize step, personal data becomes typed placeholders before the prompt goes to a model, so the model sees placeholders instead. Without one, the model gets the text as it is. With relay, the real values come back automatically.
What happens to my provider key?+−
With your own key, the key travels in a header, is used once for that request, and is never stored; only its fingerprint appears in the audit trace. Provided models need no key at all.
What does a block do?+−
A block is the decision a flagged blocking check makes: the request stops before it reaches a model, a log or a tool, transform output is discarded, and you get the verdicts back. With relay, a blocking answer check stops the answer, or the stream.
Can I change a policy without redeploying?+−
Yes: a policy is referenced by name, and you change it by publishing a new version; nothing is edited in place. Any application using the unpinned name picks up the change at once, while a pinned reference like customer-support@3 keeps running the version it names.

## Your first request is one curl away.

Access opens in batches. Request yours, get your key, and start with 1M characters free every month.
[Read the quickstart](/en/docs/quickstart/)