API Documentation
Complete reference for the Danaya REST API. All endpoints require authentication unless stated otherwise.
https://api.danaya.tech/api/v1Every response uses the same envelope: success responses carry a data object, errors carry an error code and a human-readable message.
// Success
{ "success": true, "data": { /* ... */ }, "message": "..." }
// Error
{ "success": false, "error": "ERROR_CODE", "message": "Human-readable message" }Quickstart
Go from zero to a verified seal in five steps. You seal media by sending its SHA-256 hash, computed on the device at the moment of capture, so the file itself never has to leave your app.
- Get your API key. Sign in and create a key from your dashboard. It looks like
rs_live_.... API access requires the API or Enterprise plan. - Authenticate. Send the key as the
X-API-Keyheader on every request. - Capture and hash. Capture the photo or video live, then compute a SHA-256 hash of the exact bytes (see the snippet below).
- Create a seal. POST the hash to
/api/v1/seals. You get back ashortIdand averifyUrl. - Verify. Open the
verifyUrlor callGET /api/v1/verify/:shortId. Anyone can check the seal, with no account.
Capture a photo and seal it, entirely in the browser:
// Capture a still frame from the live camera
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
const video = Object.assign(document.createElement("video"), { srcObject: stream });
await video.play();
const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext("2d").drawImage(video, 0, 0);
const blob = await new Promise((res) => canvas.toBlob(res, "image/jpeg", 0.95));
stream.getTracks().forEach((track) => track.stop());
// Compute the SHA-256 hash of the exact captured bytes
const bytes = await blob.arrayBuffer();
const digest = await crypto.subtle.digest("SHA-256", bytes);
const hash = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
// Seal it (only the hash leaves the device; upload the file later only if you want)
const seal = await fetch("https://api.danaya.tech/api/v1/seals", {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": "rs_live_..." },
body: JSON.stringify({ name: "Field photo", hashes: [{ hash, index: 0, type: "photo" }], mediaType: "photo" }),
}).then((r) => r.json());
console.log(seal.data.verifyUrl); // share or store thisPrefer not to write code? Collect a sealed photo from someone else with no integration at all: see Capture links below.
Authentication
All API requests must include your API key in the X-API-Key header. API keys follow the format rs_live_*.
// Example request header
X-API-Key: rs_live_abc123def456...
// Key format: rs_live_<48 hex> (production) or rs_test_<48 hex> (test)You can generate and manage API keys from your Dashboard. Keep your keys secret and never expose them in client-side code.
API access is available on the API and Enterprise plans only. The API plan includes 100 seals per month, 100 AI analyses per month, video sealing (up to 60s), optional private media storage, and up to 5 API keys.
Authentication and quota failures return one of these codes:
// Authentication errors
401 NO_API_KEY // X-API-Key header missing
401 INVALID_API_KEY_FORMAT // not rs_live_/rs_test_ + 48 hex
401 INVALID_API_KEY // unknown or revoked key
403 API_ACCESS_DENIED // your plan has no API access
403 EMAIL_NOT_VERIFIED // verify your email first
429 RATE_LIMIT_EXCEEDED // too many requestsRate limits: for API-plan keys, the /seals and /verify endpoints are unlimited. A shared IP limiter of 300 requests per 15 minutes applies to /ai-analysis and /check-duplicate. Every authenticated response includes X-RateLimit headers.
// Rate-limit response headers
X-RateLimit-Limit-Minute: unlimited
X-RateLimit-Remaining-Minute: unlimited
X-RateLimit-Limit-Day: unlimited
// API-plan keys are unlimited on /seals and /verify.
// A shared IP limiter (300 req / 15 min) applies to
// /ai-analysis and /check-duplicate. Max 5 API keys per account.Seals
Create, list, retrieve, and delete content authenticity seals.
Each hash object accepts: hash (64 hex, required), index (required), type ("photo" or "video_frame"), and optionally phash (16 hex perceptual hash for similarity), mediaIndex and frameIndex. The markers group a video's frames by source in the report and detail view (Video 1, Frame 1, 2...). Omit them for flat numbering.
/api/v1/seals// JSON body with pre-computed SHA-256 hashes
// Hashes must be computed client-side (no file uploads)
// Max items per seal = your plan's limit (API plan = 5)
{
"name": "My Evidence",
"hashes": [
// Each hash: hash (64 hex, required), index (required),
// type ("photo" | "video_frame"), phash (optional, 16 hex,
// perceptual hash for similarity), mediaIndex / frameIndex (optional).
{ "hash": "a1b2c3d4e5f6...", "index": 0, "type": "photo", "phash": "103030b1f0e8dcd0", "mediaIndex": 0 }
],
"location": { "latitude": 48.8566, "longitude": 2.3522 },
"mediaType": "photo" // "photo" | "video" | "mixed"
}
// Sealing a VIDEO: send one "video_frame" hash per sampled frame.
// Share mediaIndex across frames of the same video, increment frameIndex,
// so the report / detail view groups them ("Video 1 - Frame 1, 2, ...").
// Two videos = mediaIndex 0 and 1. Omit the markers for flat numbering.
{
"name": "Two clips",
"hashes": [
{ "hash": "aaaa...", "index": 0, "type": "video_frame", "mediaIndex": 0, "frameIndex": 0 },
{ "hash": "bbbb...", "index": 1, "type": "video_frame", "mediaIndex": 0, "frameIndex": 1 },
{ "hash": "cccc...", "index": 2, "type": "video_frame", "mediaIndex": 1, "frameIndex": 0 }
],
"mediaType": "video"
}/api/v1/seals// Query parameters
page=1
limit=10
search="inspection" // Optional: search by name/shortId
mediaType="photo" // Optional: photo | video | mixed
sortOrder="newest" // Optional: newest | oldest
isAiGenerated="true" // Optional: filter by AI detection/api/v1/seals/:shortId{
"success": true,
"data": {
"seal": {
"shortId": "pfrr-ggjz",
"hashes": [{ "hash": "a1b2c3d4...", "index": 0, "type": "photo", "phash": "103030b1f0e8dcd0", "mediaIndex": 0 }],
"mediaType": "photo",
"timestamp": "2026-04-05T10:30:00Z",
"hmacValid": true,
"location": { "latitude": 48.8566, "longitude": 2.3522 },
"createdAt": "2026-04-05T10:30:00Z"
},
"verifyUrl": "https://danaya.tech/verify/pfrr-ggjz"
}
}/api/v1/seals/:shortId{
"success": true,
"data": null,
"message": "Seal deleted successfully"
}
// Requires a key with delete permission (API / Enterprise plans).
// Errors: 403 DELETE_NOT_ALLOWED, 404 SEAL_NOT_FOUND.
// Deleting a seal also erases any stored media + report./api/v1/seals/:shortId/certificate?lang=fr// Query parameter
lang="fr" // Optional: en (default), fr, es, zh, ar/api/v1/seals/:shortId/media// multipart/form-data
// Field name: "images" (up to 10 files)
// Header: X-API-Key: rs_live_...
images=@photo1.jpg
images=@photo2.jpg/api/v1/seals/:shortId/expiration// Set expiration (ISO 8601 date)
{
"expiresAt": "2031-04-05T00:00:00Z"
}
// Remove expiration
{
"expiresAt": null
}/api/v1/check-duplicate// Check a single hash
{
"hash": "a1b2c3d4e5f6..."
}
// Or check multiple hashes (max 50)
{
"hashes": [
"a1b2c3d4e5f6...",
"b2c3d4e5f6a1..."
]
}Capture Sessions
Capture sessions allow users to create seals via web browser by scanning a QR code from the dashboard. This enables capturing photos on any device with a browser.
Capture sessions are not available for API-only plans. Sessions expire after 15 minutes and can be reused within that time window.
/api/capture-sessions{
"sessionToken": "abc123def456...",
"expiresAt": "2025-06-15T10:45:00Z",
"expiresIn": 900
}/api/capture-sessions/validate/:token{
"user": {
"email": "user@example.com",
"name": "John Doe",
"plan": "api",
"sealsThisMonth": 12
},
"sessionToken": "abc123def456..."
}/api/capture-sessions/:token{
"isValid": true,
"isUsed": false,
"expiresAt": "2025-06-15T10:45:00Z",
"remainingSeconds": 720
}/api/capture-sessions/:token/seals{
"name": "My Photo Evidence",
"hashes": [
{ "hash": "a1b2c3...", "index": 0, "type": "photo" }
],
"location": { "latitude": 48.8566, "longitude": 2.3522, "accuracy": 10 },
"deviceInfo": { "platform": "web", "deviceId": "...", "appVersion": "web-1.0" },
"mediaType": "photo" // "photo" | "video" | "audio"
}Capture links (no-code)
Not every integration needs code. A capture link lets you collect a sealed photo or video from someone else without building anything. You create a link in your dashboard, send it to the person who needs to provide proof, and they capture live in their browser. No app, no account, nothing to install on their side.
- Create a capture link from your dashboard, with an optional label and a single-use or reusable mode.
- Share it by message, email or QR code.
- They capture live in the browser. Import and file upload are blocked, so the seal still proves a genuine live capture.
- The sealed proof lands in your dashboard, with the same cryptographic seal and public verify link as the API.
AI Analysis
Submit images, audio, or video for AI-powered deepfake and manipulation detection.
/api/v1/ai-analysis// multipart/form-data: supports images, audio & video
Content-Type: multipart/form-data
// Form field
images: <binary file(s)>
// Supported formats:
// Images: JPEG, PNG
// Audio: MP3, WAV, AAC, OGG, FLAC, M4A
// Video: MP4, WEBMVerification
Verify the authenticity of sealed content. These endpoints are open to everyone and require no account or API key.
/api/v1/verify/:shortId{
"success": true,
"data": {
"verified": true,
"seal": {
"shortId": "pfrr-ggjz",
"timestamp": "2026-04-05T10:30:00Z",
"location": { "latitude": 48.8566, "longitude": 2.3522 },
"hmacValid": true,
"mediaType": "photo",
"hashCount": 3,
"createdAt": "2026-04-05T10:30:00Z"
}
}
}/api/v1/verify/check{
"shortId": "pfrr-ggjz",
"hash": "a1b2c3d4e5f6..." // 64 hex chars (SHA-256)
}