Docs

CMS Integration Guide

Integrate Verimago signing into your newsroom's publish pipeline. Every video your team publishes gets a cryptographic certificate automatically.

Architecture

Editor uploads video → CMS stores file → Webhook computes SHA-256 hash

→ POST /v1/sign with hash + metadata → Store verifyUrl → Render shield in article

Your video file never leaves your infrastructure. Only the SHA-256 hash is sent to Verimago.

Step 1: Get your API key

See Publisher Quick Start — Step 1. You need a persistent API key (starts with veri_live_).

Step 2: Hash the video on your server

Node.js

const crypto = require('crypto');

const fs = require('fs');

function hashFile(filePath) {

return new Promise((resolve, reject) => {

const hash = crypto.createHash('sha256');

const stream = fs.createReadStream(filePath);

stream.on('data', (chunk) => hash.update(chunk));

stream.on('end', () => resolve('sha256:' + hash.digest('hex')));

stream.on('error', reject);

});

}

Python

import hashlib

def hash_file(path):

sha = hashlib.sha256()

with open(path, 'rb') as f:

for chunk in iter(lambda: f.read(8192), b''):

sha.update(chunk)

return f'sha256:{sha.hexdigest()}'

Shell

echo "sha256:$(shasum -a 256 video.mp4 | cut -d' ' -f1)"

Step 3: Sign via API

const response = await fetch('https://api.verimago.io/v1/sign', {

method: 'POST',

headers: {

'Authorization': Bearer ${VERIMAGO_API_KEY},

'Content-Type': 'application/json',

},

body: JSON.stringify({

contentHash: hash, // sha256:abc123...

headline: article.title, // from your CMS

journalist: article.author, // from your CMS

location: article.dateline, // optional

recordedAt: video.capturedAt, // optional

contentType: 'AUTHENTIC', // or AI_ENHANCED, AI_GENERATED

captureMode: 'VIDEO', // or PHOTO

}),

});

const { verifyUrl, shieldState, signedAt } = await response.json();

Step 4: Store the certificate reference

Add these fields to your video/article model:

ALTER TABLE videos ADD COLUMN verimago_verify_url TEXT;

ALTER TABLE videos ADD COLUMN verimago_shield TEXT;

ALTER TABLE videos ADD COLUMN verimago_signed_at TIMESTAMPTZ;

Store the verifyUrl, shieldState, and signedAt returned from the sign endpoint.

Step 5: Render the shield

In your article template, render the verification badge:

<div class="verimago-badge">

<a href="{{ video.verimago_verify_url }}" target="_blank" rel="noopener">

<img src="https://verimago.io/badge/{{ video.verimago_shield | lower }}.svg"

alt="Verified by Verimago" width="120" />

</a>

</div>

Platform-specific examples

WordPress

Add to your theme's functions.php or as a plugin:

add_action('add_attachment', function($post_id) {

$file = get_attached_file($post_id);

$mime = get_post_mime_type($post_id);

if (strpos($mime, 'video/') !== 0) return;

$hash = 'sha256:' . hash_file('sha256', $file);

$response = wp_remote_post('https://api.verimago.io/v1/sign', [

'headers' => [

'Authorization' => 'Bearer ' . VERIMAGO_API_KEY,

'Content-Type' => 'application/json',

],

'body' => json_encode([

'contentHash' => $hash,

'headline' => get_the_title($post_id),

'contentType' => 'AUTHENTIC',

'captureMode' => 'VIDEO',

]),

]);

if (!is_wp_error($response)) {

$body = json_decode(wp_remote_retrieve_body($response), true);

update_post_meta($post_id, '_verimago_verify_url', $body['verifyUrl']);

update_post_meta($post_id, '_verimago_shield', $body['shieldState']);

}

});

Arc XP / Custom CMS

Add a post-publish webhook:

// webhook handler: POST /hooks/verimago-sign

app.post('/hooks/verimago-sign', async (req, res) => {

const { videoUrl, articleId, title, author } = req.body;

// Download video and compute hash

const hash = await hashFromUrl(videoUrl);

// Sign with Verimago

const cert = await signWithVerimago(hash, { headline: title, journalist: author });

// Update article record

await db.articles.update(articleId, {

verimagoVerifyUrl: cert.verifyUrl,

verimagoShield: cert.shieldState,

});

res.json({ ok: true });

});

Batch signing

For archives or bulk uploads, use a script:

#!/bin/bash

API_KEY="veri_live_your_key_here"

for file in videos/*.mp4; do

HASH="sha256:$(shasum -a 256 "$file" | cut -d' ' -f1)"

TITLE=$(basename "$file" .mp4)

curl -s -X POST https://api.verimago.io/v1/sign \

-H "Authorization: Bearer $API_KEY" \

-H "Content-Type: application/json" \

-d "{\"contentHash\": \"$HASH\", \"headline\": \"$TITLE\", \"contentType\": \"AUTHENTIC\", \"captureMode\": \"VIDEO\"}" \

| jq '.verifyUrl'

sleep 0.5 # respect rate limits

done

Error handling

ErrorCauseFix
401 UnauthorizedInvalid or expired API keyRegenerate key at Settings → API Keys
400 "contentHash must start with sha256:"Missing sha256: prefixPrepend sha256: to your hex hash
400 "contentType must be..."Invalid content typeUse AUTHENTIC, AI_ENHANCED, or AI_GENERATED
409 Conflict (hash already signed)This file was already signedThe response includes the existing certificate details
429 Too Many RequestsRate limit exceededImplement exponential backoff or reduce batch rate

Testing

Use the staging environment for development:

Base URL: https://staging-api.verimago.io

Staging uses software keys (not HSM) and a separate database. Certificates created on staging are not publicly verifiable.