---
title: "Content marking for terminal text answers"
description: "Duale AI marks terminal plain-text answers with machine-readable C2PA provenance to support the text-marking requirement in Article 50(2) of Regulation (EU) 2024/1689."
lang: en
status: public-preview
lastUpdated: 2026-09-04
url: https://duale.ai/en/docs/content-marking
---

## AI-generated summary

Duale AI marks terminal plain-text answers with C2PA provenance, supports verification, and explains how to preserve the mark through text transformations.

- The mark is a C2PA 2.4 Annex A.8 manifest with generation time and identifier.
- Verification returns valid, trusted, invalid, or unmarked; trusted requires a trust anchor certificate.
- The mark adds about 1,855 characters of Unicode variation selectors without changing rendered text.
- Reformatting, re-encoding, translation, or Unicode normalization can remove the mark from the text.
- If marking cannot complete, Duale AI delivers the plain-text answer instead of failing the task.

Summaries were generated by AI. Generative AI is experimental.

---

Duale AI marks terminal plain-text answers with machine-readable C2PA provenance. The mark has no visible glyph, so the
answer looks unchanged in a conforming renderer. It supports the text-marking requirement in Article 50(2) of Regulation
(EU) 2024/1689.

## Marked outputs

A terminal answer carries a mark when its declared response format is plain text or unset. Structured formats, streaming
deltas, internal tool messages, reasoning traces, and empty answers do not carry one. The terminal result from
`await response.model()` is the authoritative marked value.

The mark is a C2PA 2.4 Annex A.8 manifest. It records that software generated the text, the generation time, the
`text/plain` format, the software that wrote the mark, and an opaque generation identifier. It contains no prompt,
customer, tenant, task, agent, model, provider, or Web source data.

## Verify a text answer

Install the codec:

```bash
pip install c2patxt
```

Then verify the answer:

```python
from c2patxt import verify

verdict = verify(answer_text)
print(verdict.state.value)
```

The state has four values:

| State      | Meaning                                                                  |
| ---------- | ------------------------------------------------------------------------ |
| `valid`    | The mark, signature, and text hash are valid                             |
| `trusted`  | The mark is valid and chains to a trust anchor supplied to the validator |
| `invalid`  | A mark is present but its validation failed                              |
| `unmarked` | The text has no readable mark                                            |

Branch on `state`; `bool(verdict)` raises. A `valid` result proves that the text has not changed since signing. Signer
identity needs a trusted certificate relationship, so use `trusted` when your workflow requires that check.

To compare a marked answer with an unmarked copy, compare the text returned by `strip()`:

```python
from c2patxt import strip

assert strip(answer_text) == expected_text
```

Use the stripped value only for comparison. Store and forward the original marked answer.

## Read the generation identifier

Each marked answer has one identifier for its generation:

```python
from c2patxt import extract

manifest = extract(answer_text)
print(manifest.claim["instanceID"])  # "xmp:iid:<uuid>"
```

The identifier stays the same when you read the same delivered answer again. A new generation gets a new identifier.
Keep it with the answer when you need to discuss that generation with Duale AI. It does not resolve to prompt, routing,
or account data by itself.

## Preserve the mark

The mark uses Unicode variation selectors. This changes the bytes and character count by about 1,855 characters, or
7.2 kB of UTF-8, while leaving the rendered text unchanged. Compare visible text with `strip()` and allow for the fixed
overhead in byte limits.

Reformatting, re-encoding, translation, Unicode normalization, or another text transformation can remove the mark.
Verify it when you receive the terminal answer and preserve the original bytes when provenance matters.

If marking cannot complete, Duale AI delivers the plain-text answer instead of failing the task. An `unmarked` result
therefore says only that no readable mark is present. Under the [Terms of Use](https://duale.ai/en/legal/cgu.md), customers must not
deliberately remove or hide a mark from an Output.

A mark records generation provenance. It does not prove factual accuracy or identify the Web sources used. [Sources and
factuality](https://duale.ai/en/docs/web/sources-and-factuality.md) covers those checks.

## Related content

- [Intended purpose, excluded uses, and behavioral constraints](https://duale.ai/en/docs/scope-and-limits.md)
- [Know what the platform defends](https://duale.ai/en/docs/security/threat-model.md)
- [Secure your use of the platform](https://duale.ai/en/docs/security.md)
- [Protect data across every boundary](https://duale.ai/en/docs/security/data-protection.md)
- [Security controls for production agents](https://duale.ai/en/product/security.md)
- [Python SDK for bounded agent work with typed results](https://duale.ai/en/docs/sdk.md)

---

## Sitemap

See the full [Markdown sitemap](https://duale.ai/sitemap.md) for all pages.
