Skip to content
Marked outputs
Public Preview

Content marking: C2PA marks on Task Results

Describes how to verify, read, and preserve C2PA generation-provenance marks that Duale AI attaches to eligible plain-text Task Results.

This page covers how Duale AI marks plain-text Task Results with C2PA provenance, how to verify marks, read generation identifiers, and preserve marks.

  • Marked outputs and C2PA manifest structure.
  • Verification states and the c2patxt codec.
  • Generation identifier behavior and usage.
  • Mark preservation, overhead, and failure handling.

Summaries were generated by AI.

For eligible terminal plain-text Task Results, Duale AI attempts to attach a machine-readable generation-provenance mark. If marking cannot complete, Duale AI delivers an unmarked result instead of failing the Task, as described below.

Marked outputs

A completed Task Result 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 Task Result returned by await response.model() is the authoritative marked value.

The mark is a C2PA Manifest Store embedded in the C2PATextManifestWrapper defined by C2PA 2.4 Annex A.8 (opens in a new tab). C2PA states that this unstructured-text carrier remains under review and can change after implementation feedback and interoperability testing. The mark 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 Identity, model, provider, or Web-source data.

This mechanism is one technical input to an Article 50(2) assessment (opens in a new tab). It does not by itself establish that the complete marking solution is effective, interoperable, robust, reliable, or compliant with Regulation (EU) 2024/1689.

Verify a text answer

Install the codec:

pip install c2patxt

Then verify the answer:

from c2patxt import verify

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

c2patxt returns four states for the profile that Duale AI emits:

StateMeaning
validThe mark, signature, and text hash are valid under that profile
trustedThe profile-valid mark chains to a trust anchor supplied to the validator
invalidA mark is present but its validation failed
unmarkedThe text has no readable mark
  • State
    valid
    Meaning
    The mark, signature, and text hash are valid under that profile
  • State
    trusted
    Meaning
    The profile-valid mark chains to a trust anchor supplied to the validator
  • State
    invalid
    Meaning
    A mark is present but its validation failed
  • State
    unmarked
    Meaning
    The text has no readable mark

Branch on state; bool(verdict) raises. A valid result establishes that the delivered text matches the text hash signed in the extracted mark. It does not identify the signer, establish that another C2PA validator supports Annex A.8, or prove regulatory compliance. Use trusted when your workflow requires signer identity through a trusted certificate relationship.

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

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:

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 completed Task Result 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, 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 covers those checks.