Skip to content

Signing

POST /v1/sign — Server-side signing (recommended)

Section titled “POST /v1/sign — Server-side signing (recommended)”

Upload content and receive it back watermarked and signed. The API handles format detection, watermark embedding, hashing, ML-DSA signing, and provenance token injection in one call. Counts as 1 signature against your plan quota.

Authentication requiredAuthorization: Bearer ctv_key_…

The generator must have been created without a manual public key (server-managed keypair). See generator registration.

FormatMIME typesWatermarkToken container
Imageimage/jpeg, image/png, image/webpDCT spread-spectrumXMP metadata
Audioaudio/mpeg (MP3), audio/flac, audio/wavDCT frame watermark (WAV 16-bit and 24-bit PCM)ID3v2 TXXX / VORBIS_COMMENT / CTV RIFF chunk
Textapplication/pdf, text/html, text/plainZWC steganography (plain text across all paragraphs; HTML body text nodes)PDF /Metadata XMP stream / HTML <meta>
Videovideo/mp4, video/quicktime (MOV), video/x-matroska (MKV), video/webmpHash of first I-frame (server-side, via ffmpeg)MP4/MOV: custom cerv atom · MKV/WebM: Certivu sentinel trailer

Generators must declare which formats they support. Signing with an unlisted format returns 400 format_not_supported.

POST /v1/sign
Content-Type: multipart/form-data
Authorization: Bearer ctv_key_abc123
FieldTypeRequiredDescription
contentfileYesContent to sign (image, audio, text, or video)
modelstringYesAI model name (e.g. stable-diffusion-xl)
generator_idstringYesID of the registered generator
formatstringNo"image", "audio", "text", or "video". Auto-detected from magic bytes if omitted.

Signed content bytes (same format as input) with:

HeaderDescription
Content-TypeFormat-appropriate MIME type
X-Certivu-Tokenctv_… token — also embedded in content metadata
X-Certivu-Record-IdUUID of the stored provenance record
X-Certivu-FormatDetected/applied format: image, audio, text, or video

The returned content has:

  • The ctv_ token injected into a format-appropriate container (XMP, ID3v2, VORBIS_COMMENT, RIFF chunk, PDF XMP, HTML meta, or ZWC)
  • The watermark_id embedded via a format-appropriate resilient watermark
  • A SHA-3 content hash and ML-DSA signature stored in the Certivu record
StatusCodeMeaning
400format_not_supportedGenerator’s supported_formats does not include the detected/specified format
400Missing fields or generator uses client-side keypair
401Missing or invalid API key
402signature_limit_reachedFree tier monthly limit hit
403Generator does not belong to your organization
404Generator not found or revoked
413Content exceeds 50 MB
// Image (auto-detected)
const { signedContent, token, record_id, format } = await certivu.sign({
content: imageBuffer,
model: 'stable-diffusion-xl',
})
// Audio (explicit format)
const result = await certivu.sign({
content: mp3Buffer,
model: 'musicgen-large',
format: 'audio',
})
// Text
const result = await certivu.sign({
content: pdfBuffer,
model: 'gpt-4o',
format: 'text',
})
// Video (auto-detected from MP4/MOV/MKV/WebM magic bytes)
const result = await certivu.sign({
content: mp4Buffer,
model: 'sora-v1',
format: 'video',
})
// result.signedContent has the ctv_ token embedded in a custom container atom/trailer

POST /v1/records — Client-side signing (advanced)

Section titled “POST /v1/records — Client-side signing (advanced)”

For generators with client-managed keypairs. You compute the ML-DSA signature locally and submit the record. The content is not uploaded — only the hash and signature are stored.

Authentication requiredAuthorization: Bearer ctv_key_…

POST /v1/records
Content-Type: application/json
Authorization: Bearer ctv_key_abc123
{
"watermark_id": "wm_7f3kx9mq2",
"generator_id": "gen_abc123",
"model": "stable-diffusion-xl",
"content_hash": "sha3-256:a3f1...",
"signature": "<ml-dsa base64>",
"signed_payload": {
"content_hash": "sha3-256:a3f1...",
"generator_id": "gen_abc123",
"model": "stable-diffusion-xl",
"watermark_id": "wm_7f3kx9mq2"
},
"phash": "a3f1c2d4e5b6a7f8"
}
FieldTypeRequiredDescription
watermark_idstringYesOpaque ID embedded in the content watermark
generator_idstringYesID of the registered generator that signed this
modelstringYesModel name (e.g. stable-diffusion-xl)
content_hashstringYessha3-256:<64 hex chars> — SHA-3 hash of the content
signaturestringYesML-DSA signature (base64) over signed_payload canonical JSON
signed_payloadobjectYesThe exact payload that was signed
phashstringNo16-char lowercase hex perceptual hash (64-bit DCT pHash). Enables fuzzy lookup (images and video) at verify time if container token or watermark is absent. Compute via computePhash() in @certivu/crypto.

The phash field is outside the signed payload — it is a similarity lookup aid, not a security-critical field.


POST /v1/records/batch — Batch client-side signing (Growth+)

Section titled “POST /v1/records/batch — Batch client-side signing (Growth+)”

Submit up to 50 records in a single request. Each item is processed in parallel and returns its own result. Batch signing requires Growth or above.

Authentication requiredAuthorization: Bearer ctv_key_…

POST /v1/records/batch
Content-Type: application/json
Authorization: Bearer ctv_key_abc123
{
"records": [
{
"watermark_id": "wm_7f3kx9mq2",
"generator_id": "gen_abc123",
"model": "stable-diffusion-xl",
"content_hash": "sha3-256:a3f1...",
"signature": "<ml-dsa base64>",
"signed_payload": { "..." : "..." }
},
{ "..." : "..." }
]
}

Each item in records follows the same schema as the single POST /v1/records body.

{
"results": [
{ "token": "ctv_7f3kx9mq2...", "record_id": "rec_uuid_1" },
{ "token": "ctv_abc9876...", "record_id": "rec_uuid_2" }
]
}

Results are returned in the same order as the input array.

StatusCodeMeaning
400Validation failed or any signature invalid
402plan_requiredBatch signing requires Growth or above
402signature_limit_reachedQuota exhausted
413Request body exceeds 10 MB

{
"token": "ctv_7f3kx9mq2...",
"record_id": "rec_uuid"
}
FieldDescription
tokenCompact signed artifact — embed in XMP metadata or pass alongside content
record_idUUID of the stored provenance record

StatusCodeMeaning
400Validation failed or signature invalid
401Missing or invalid API key
402signature_limit_reachedFree tier monthly limit hit — upgrade to continue
403Generator does not belong to your organization
404Generator not found or revoked
{
"error": "signature_limit_reached",
"message": "Free tier limit of 500 signatures/month reached. Upgrade at https://certivu.ai/pricing",
"upgrade_url": "https://certivu.ai/pricing",
"limit": 500,
"current": 500
}

GET /v1/records/:record_id

Returns the full ProvenanceRecord. No authentication required.


GET /v1/records/:record_id/c2pa

Returns a C2PA-compatible manifest JSON for the record. No authentication required. Cached for 1 hour.

{
"@context": { "c2pa": "https://c2pa.org/claims/v1", "certivu": "https://certivu.com/ns/" },
"claim_generator": "Certivu/2.5.0",
"instance_id": "certivu:rec_uuid",
"assertions": [
{ "label": "c2pa.actions", "data": { "actions": [{ "action": "c2pa.created", "when": "..." }] } },
{ "label": "certivu.provenance", "data": { "record_id": "...", "model": "...", "signature_algorithm": "ML-DSA-65 (NIST FIPS 204)" } },
{ "label": "c2pa.hash.data", "data": { "name": "SHA3-256", "hash": "..." } }
]
}

This is a compatibility export following C2PA assertion conventions — it is not a fully-signed C2PA manifest. For a real embedded manifest, use the embed endpoint below.


POST /v1/records/:record_id/c2pa/embed
Authorization: Bearer ctv_key_...
Content-Type: multipart/form-data

Embeds a real, ML-DSA-signed C2PA manifest into an uploaded JPEG or PNG, bound to the record. Unlike the JSON export above, this produces an actual embedded manifest (JUMBF) with a hard binding to the asset’s bytes.

Requires a Growth plan or above. Maximum asset size is 50 MB. The record was already charged at sign time, so embedding does not consume an additional signature.

FieldTypeDescription
contentfileThe signed JPEG/PNG asset (so the C2PA hard binding covers the same bytes Certivu verifies)

Returns the asset with the manifest embedded (image/jpeg or image/png).

StatusCodeMeaning
402plan_requiredPost-quantum C2PA embedding requires Growth or above
403Generator has been revoked
413Asset exceeds the 50 MB maximum
Terminal window
curl -X POST https://api.certivu.ai/v1/records/$RECORD_ID/c2pa/embed \
-H "Authorization: Bearer $CERTIVU_API_KEY" \
-F "[email protected]" --output signed.c2pa.jpg

The manifest is signed with ML-DSA-65 (NIST FIPS 204) — “post-quantum C2PA”. It is structurally interoperable with the Content Credentials ecosystem, but standard C2PA verifiers will report the signature algorithm as unrecognized, since the industry currently expects classical RSA/ECC signatures. This is a deliberate, quantum-resistant trade-off — Certivu’s own verification validates the ML-DSA signature. Requires a generator with a server-managed keypair (created for POST /v1/sign).


  • Prefer POST /v1/sign — it handles watermarking, hashing, signing, and token injection for you and returns signed, ready-to-use content
  • For POST /v1/records: content_hash must use format sha3-256:<64 lowercase hex chars>
  • signed_payload must be the exact object that was canonically serialized and signed — verification replays this
  • Compute phash using computePhash(rgbPixels, width, height) from @certivu/crypto — returns a 16-char lowercase hex string