UtteraUttera

Verifiable report

Use this when you need a meeting, call or statement turned into a report a third party can verify. Output: summary, transcript, evidence graph and signed PDF. API: POST /v1/summarize?mode=intelligence&report=pdf · Minimum plan: Professional (Developer during the promotion).
Evidence graph
9 nodes · 8 verified
stated · claim · S0
La Espiga orders 500 kg of strong bread flour.
«Five hundred kilos, the usual order.» [8.9–11.0] verified
conditional · commitment · S1
It will deliver the order on Thursday morning.
Condition: if La Espiga confirms the order today
«If you confirm the order today, I'll drop it off Thursday morning.» [27.1–31.0] verified
disputed · claim · S0
The Tuesday delivery had been agreed.
«we agreed you'd deliver on Tuesday, right?» [16.2–19.5] verified
… and 6 more nodes
A fragment of a verifiable report: each claim is anchored to its quote, speaker and minute, with its status. See the sample report (signed PDF).

An AI-generated summary asks you for something uncomfortable: to trust it. You don't know whether that sentence was really said in the meeting, who said it, or when. Uttera's verifiable report changes that: instead of a text you have to believe, it produces a report you can check sentence by sentence. It is our most distinctive service.

The idea is simple and deliberate: we do not decide what is true —that is for the listener to judge—. What we do is anchor each statement to the recording: we bring it out with its exact quote, the speaker, the segment and the exact minute, and we mark whether that quote is word for word in the audio. You bring the judgment; we bring where each sentence comes from, in a way that can't be argued with.

What it's for

Any situation where what was said matters and, later, someone may call it into question:

It is worth being clear, from the start, about what Uttera proves of your report —and what it doesn't—:

Uttera does NOT decide this
Veracity — is what was said true? the listener judges
Uttera DOES prove this
Anchoring · each sentence is in the audio, with its minute and its speaker
Authenticity · the report came from Uttera and no one has touched it
Timestamp · an independent third party certifies when it existed
Three proofs anyone can check on their own, without taking our word for it.

The evidence graph

The analysis is delivered as an evidence graph (evidence_graph): a list of claims, the relevant statements from the recording. Each one carries text, state (its state: stated, conditional, disputed… — the full list below), speakers, entities, a possible depends_on (which other claim it depends on) and an evidence array with the passages that back it (segment_id, t_start/t_end, quote and grounding).

Each statement is classified on two axes, so at a glance you know what it is and how much it weighs.

What it is — the type:

How much it weighs — the state:

That grounding is the anchoring of each quote, and we say it plainly:

verifiedThe quote appears exactly in the transcript, at that stretch of time.
approxIt nearly matches (same idea, close wording) within the same segment.
unverifiedIt could not be anchored to a concrete stretch. It is not signed as verified (verifiable:false) and in the report it is flagged or excluded.

🔴 Anchoring is not judging the truth. The graph says who said what and where, and that the quote is really in the audio. It does not say that what was said is true: that —and the accuracy of the transcript— is for the listener to decide. It is the same distinction as in Verifying: authenticity and anchoring are not veracity.

The figures in the report match what was said

A record of a meeting lives by its numbers: an amount, a deadline, a quantity, a percentage. So when a statement carries a figure, the report checks it against the literal quote it comes from. If the number in the report does not appear in what was said, it is flagged —not let through as if it were backed—.

stated · figure · S0
La Espiga orders 500 kg of strong bread flour.
«…I'll take 500 kilos, the usual.» [8.9–11.0]
✓ the figure is confirmed in the quote
The number in the report is backed by what was said. If it weren't, the report would flag it.

Two levels of verification

Not every report needs the same rigour. So there are two levels:

Standard
The verifiable report as you know it.
✓ each statement anchored to its quote, speaker and minute
✓ figures checked against what was said
✓ signed (Ed25519 + timestamp)
Forensic · maximum rigour
For when the evidence will face exacting scrutiny.
✓ everything in the standard level
✓ an additional independent check that each statement is genuinely backed by its evidence
The forensic level is meant for expert evidence, litigation and formal audit.

Forensic: coming soon for the higher plans.

How to request it

Through the API, it is the summary service with ?mode=intelligence (and, to pin the report type, report_schema/report_prompt). Add ?report=pdf for the signed PDF: the already-validated graph goes inside the document, covered by its signature, so the PDF is enough to check it on its own. The PDF arrives in report.pdf_base64 with its signatures (content_signature, container_signature, pdf_sha256, key_fingerprint). Billing is per token, like the summary; an unverified statement is not charged separately nor sold as verified.

curl -X POST "https://api.uttera.ai/v1/summarize?mode=intelligence&report=pdf" \
  -H "Authorization: Bearer $UTTERA_API_KEY" \
  -F "file=@meeting.mp3" \
  | jq -r '.report.pdf_base64' | base64 -d > report.pdf

Without coding, in the Studio: tick the «Verifiable report» box, pick the output language and run; the graph is drawn on screen and you can download it (JSON) next to the PDF.

For long recordings, in the background. The deep analysis can take longer than a connection can hold; add &async=1 and collect it later (Asynchronous mode). Once the PDF is issued, anyone can check it at Verifying a report, with no account.

Verifying a report

Every PDF report we issue is cryptographically signed. Anyone —you, your client, the other side of a negotiation— can check that a report came from us and that nobody has touched it on the way, without asking us for permission and without an account.

The quickest way is the page. At uttera.ai/en/verify you upload the PDF and that is it: there is nothing to copy or paste, because the signature travels inside the file itself. It is meant for whoever receives a report and is not going to write code — a lawyer, an expert witness, the other party. The rest of this chapter is for doing it yourself, with curl or without going through us.

🔴 Authenticity is not truth. Verifying a report tells you where the document comes from and that nobody has modified it. It does not say that what it says is true. The report is generated from a recording provided by the client, and both the accuracy of the transcription and the truth of what is said in it are their responsibility. Uttera did not witness what was recorded, does not check it and does not certify it.

What it is and what it is not. It is a cryptographic authenticity signature using Ed25519. It is not a qualified electronic signature in the sense of the eIDAS regulation: it proves the document came out of our system and has not changed, it is not the equivalent of a certificate from a qualified provider. We say so here, and the report says so too.

Why there are two signatures

Every report carries two, and each answers a different question:

SignatureOver whatWhat it answers
contentwhat the report says: summary, transcript, client name, date and usage"did this text come from Uttera?" — it still holds if the PDF has been reprinted, cropped or saved again
containerthe bytes of the PDF file"is this file exactly the one that was issued?" — it detects a single changed byte

The content one is printed inside the report, in the footer of every page, as a short fingerprint. The container one cannot go inside —signing something that is then modified is impossible— so it is appended after the finished document, on a line at the end of the file starting with %%UTTERA-FIRMA-V1:. A PDF reader never sees it: the format says to read from the last startxref, so anything after the %%EOF is ignored.

That is what makes the PDF alone enough to check both signatures. It used to be returned separately by the API and you had to keep it next to the file: the moment somebody forwarded the report by email, the signature was left behind and the document could not be verified at all. The API still returns it in report.container_signature for anyone who prefers to archive it separately.

A third-party timestamp (RFC 3161)

The two signatures say who and what. The when is added by an independent third party: at the moment of issue, each report requests an RFC 3161 timestamp from an external timestamping authority (a TSA) over the digest of its content signature. The TSA returns a token, signed by it, stating «this digest existed at this time», and that token travels inside the PDF itself, next to the signatures.

Why it matters: without it, the report's time came from our clock, inside what we ourselves signed — you would have to trust that we hadn't backdated it. With the RFC 3161 timestamp the date is attested by someone who is not us and has nothing to gain. It is what an expert witness or a court expects in order to fix when a document existed.

In the PDF footer, below the signature, the line «Trusted timestamp (RFC 3161): … UTC · <authority>» appears. When the report is verified —on the page or through the API— we check that the timestamp is over that content and return the attested time in the fields timestamp_present, timestamp_valid, timestamp_unix and tsa.

It may be absent. The timestamp is an extra, not a requirement: if the external authority does not answer at issue time, the report is still produced with its two signatures and no timestamp, and verification reports it as no external timestamp (not as invalid). Reports issued before this feature do not carry it either. And, like the Ed25519 signature, it is not a qualified electronic signature under eIDAS: it is a timestamp from a recognised provider, which is what fixes the date before a third party.

The public key

We publish it in three places, and all three give the same thing:

https://uttera.ai/.well-known/uttera-firma.pub     the PEM, on its own
https://uttera.ai/.well-known/uttera-firma.json    every key, with its dates
https://api.uttera.ai/v1/reports/public-key        the one signing right now

The chain of trust is the same one DKIM or JWKS key sets use: the TLS certificate of uttera.ai attests that the domain is ours, and that domain publishes the key. You do not have to take our word for it over the phone.

Checking it from the command line

The endpoint is public and takes no API key:

POST https://api.uttera.ai/v1/reports/verify

With the PDF and nothing else. This is the normal mode: the signature is inside the file, so nothing you had to keep separately is needed. It checks both signatures at once:

curl -s -X POST https://api.uttera.ai/v1/reports/verify \
  -H "Content-Type: application/json" \
  -d "{\"modo\":\"pdf\",\"pdf_b64\":\"$(base64 -w0 report.pdf)\"}"

{"mode": "pdf", "signed": true,
 "valid_container": true, "valid_content": true,
 "key_fingerprint": "56ac333b…",
 "fields": {"cliente": "…", "titulo": "…", "generado": "1789641036", …}}

Both true is the good case. valid_content true with valid_container false means the text is ours but the file has been saved again — any PDF tool that opens and writes it changes the bytes — which is not the same as a forgery. signed false means the file carries no signature of ours, or it has been stripped.

The two modes below are for when you hold the pieces separately: an older report, or a signature archived on its own.

Check the file (container signature). You need the PDF and the signature that came with it in report.container_signature:

curl -s -X POST https://api.uttera.ai/v1/reports/verify \
  -H "Content-Type: application/json" \
  -d "{\"modo\":\"contenedor\",
       \"pdf_b64\":\"$(base64 -w0 report.pdf)\",
       \"firma_b64\":\"THE_CONTAINER_SIGNATURE\"}"

{"valid": true, "mode": "contenedor", "key_fingerprint": "56ac333b…"}

Check the content. You need the analysis response exactly as we returned it, plus report.content_signature and the date from report.generated_unix — the unix stamp, which is the value that was signed; generated_at is the written date, for reading. The stamp is printed on the report itself, next to the number:

curl -s -X POST https://api.uttera.ai/v1/reports/verify \
  -H "Content-Type: application/json" \
  -d '{"modo":"contenido",
       "datos": { …the /v1/summarize response… },
       "cliente":"Meridiano Asesores, S.L.",
       "titulo":"Minutes of the meeting",
       "generado":1789639533,
       "firma_b64":"THE_CONTENT_SIGNATURE"}'

We store nothing for this. Verifying is a public-key operation over what you send us: we look nothing up. That is why it works on a report from years ago, and why we cannot tell you how many reports we have issued or find one you have lost.

Key rotation

Each key is used for twelve months, and is replaced sooner on any suspicion. Retired keys stay published forever: a report signed today has to be verifiable ten years from now.

That is why every report carries, alongside its signature, the fingerprint of the key that signed it. The verifier knows which one to use without trying them all.

If we ever had to retire a key because it was compromised, we would say so here with the date. And we would say the uncomfortable part too: reports signed with it stop proving anything, because whoever held the key could have signed backdated documents.