CallMeter logoCallMeter Docs

Verifying Reports

Confirm that a signed report is an unmodified CallMeter-issued record, understand exactly what verification proves, and run the check independently.

Every signed report can be checked at CallMeter's public verification page — no account required. Verification answers one question precisely: do these bytes match an unmodified, CallMeter-issued signed-report record, and has that record been revoked?

Verifying at the verification page

Go to /verify and upload the report PDF. CallMeter hashes the bytes you uploaded and matches that hash against its records — then checks the record's Ed25519 signature. The file is checked in your request and never stored.

The result is one of:

ResultMeaning
VerifiedThe file matches an unmodified CallMeter-issued signed report.
Verified, but revokedThe record is authentic, but it has been revoked and should no longer be relied on — ask the sender for a current version.
Not a known reportThe file's bytes do not match any signed report — which is exactly what you would see if even one byte had changed since it was signed.

The page also shows the signing keyId and the report's details, so you can confirm you are looking at the report you expected.

Why you upload the file, not a code

Verification is deliberately file-only. A signed report's PDF prints a Report reference (an invoice-style identifier) on its cover and footer, but that reference is not a verification input — pasting it would prove nothing about the file in front of you. Anyone could alter a PDF and leave the printed reference untouched, so a reference-based check would happily "confirm" a tampered file. Only the bytes can prove the file is unmodified, so only an upload can verify one. The reference is there to identify a report in conversation or support, not to authenticate it.

What verification proves — and what it does not

Verification is record-anchored. A "verified" result means the bytes match a signed record CallMeter issued and has not changed. It is a strong integrity and origin check, and it is exactly that — not a legal attestation, notarization, or third-party certification. If you need a signature embedded inside the PDF that a desktop PDF reader validates on its own, that is a separate capability and is not what this check provides.

Checking a report independently

The signature is a standard Ed25519 signature, so anyone who has been given a report's public key and signature (for example, from the report's owner) can verify it without CallMeter in the loop. The signed message is domain-separated: the literal text callmeter-report-v1, a single newline, then the lowercase hex SHA-256 of the PDF bytes.

With Node.js:

const { createHash, createPublicKey, verify } = require("crypto");
const fs = require("fs");

const pdf = fs.readFileSync("report.pdf");
const sha256hex = createHash("sha256").update(pdf).digest("hex");

// Exact domain-separated payload: "callmeter-report-v1" + "\n" + sha256hex
const payload = Buffer.from("callmeter-report-v1\n" + sha256hex, "utf8");

const publicKey = createPublicKey({
  key: Buffer.from(PUBLIC_KEY_B64, "base64"), // base64 SPKI DER, from the report record
  format: "der",
  type: "spki",
});

const ok = verify(null, payload, publicKey, Buffer.from(SIGNATURE_B64, "base64"));
console.log(ok ? "VALID" : "INVALID");

The equivalent with OpenSSL:

# 1. Hash the PDF and build the exact signed payload (note the newline).
printf 'callmeter-report-v1\n%s' "$(sha256sum report.pdf | cut -d' ' -f1)" > payload.bin

# 2. Turn the base64 SPKI-DER public key into a PEM.
printf '%s' "$PUBLIC_KEY_B64" | base64 -d | openssl pkey -pubin -inform DER -out pub.pem

# 3. Decode the base64 signature, then verify (Ed25519 signs the raw message).
printf '%s' "$SIGNATURE_B64" | base64 -d > sig.bin
openssl pkeyutl -verify -pubin -inkey pub.pem -rawin -in payload.bin -sigfile sig.bin

If the payload prefix or the newline is wrong, verification fails — reproduce callmeter-report-v1\n exactly.

Key rotation

CallMeter can rotate its signing key. Because every signed report stores a snapshot of the public key that signed it, reports signed with an older key keep verifying forever — you always check a report against the key it was actually signed with, identified by its keyId. Rotating the key changes only which key signs new reports.

On this page