Aller au contenu

Marquer une image, un morceau ou une vidéo

POST https://api.kennmarq.com/v1/mark

Envoyez une image ou un morceau : le fichier vous est renvoyé avec un filigrane invisible et un manifeste C2PA signé et horodaté. Envoyez une vidéo : vous obtenez une tâche, puis la vidéo marquée via GET /v1/marks/{id} une fois la tâche terminée. Une image compte un marquage, un morceau cinq par minute entamée, une vidéo cinq par seconde entamée. Nécessite une clé avec la portée mark.

Requête

multipart/form-data, avec les champs ci-dessous. Les espaces en début et en fin des champs texte sont supprimés ; les booléens acceptent true/false (ou 1/0, on/off, yes/no).

Champs de la requête POST /v1/mark
ChampTypeDescription
filefichier, obligatoire (ou upload)Le fichier : une image PNG, JPEG ou WebP ; un morceau MP3, WAV ou M4A ; ou une vidéo MP4. Son type est déterminé d’après ses octets, pas d’après son nom. La requête entière fait au plus 4 Mo ; un fichier plus lourd passe par un envoi, indiqué dans upload : jusqu’à 25 Mo, et jusqu’à 1 Go pour une vidéo.
uploadtexte, à la place de fileL’ID d’un envoi (up_…) obtenu avec POST /v1/uploads, pour un fichier de plus de 4 Mo. Il ne sert qu’une fois, dans l’heure, avec la clé qui l’a créé.
sourcetexte, par défaut createdLe mode de création de l’image, enregistré comme son type de source numérique IPTC : created (créée par l’IA), composite (IA combinée à d’autres éléments) ou edited (une image existante modifiée avec l’IA). C’est vous qui le déclarez ; nous ne signons que des types de source IA.
providertexte, facultatifVotre dénomination légale, en tant que fournisseur du système d’IA, par exemple Acme Inc. Avec app_name et app_version, ce champ complète la divulgation latente prévue par la loi californienne. 200 caractères au maximum.
modeltexte, facultatifLe nom du modèle, par exemple FLUX.1 schnell. 200 caractères au maximum.
model_versiontexte, facultatifLa version du modèle. 200 caractères au maximum.
app_nametexte, facultatifLe nom de votre système d’IA, enregistré comme agent logiciel de l’action created. Sans lui, le manifeste ne nomme aucun agent logiciel. 200 caractères au maximum.
app_versiontexte, facultatifLa version de votre système d’IA. Nécessite app_name. 200 caractères au maximum.
marketstexte, facultatifLes marchés pour lesquels le marquage est fait, séparés par des virgules : le code ISO de n’importe quel pays (de, jp), eu, un État américain (us-ca) ou us pour tous les États dotés de leur propre loi. Chaque fichier reçoit ce qu’exigent les lois de ces pays (la carte du monde les présente), un appel auquel il manque ce qu’elles exigent est refusé (us-ca et us-wa exigent provider, app_name et app_version ; in exige app_name), et le marquage reçoit un justificatif signé. in ajoute la mention visible sur chaque image et chaque vidéo. Un marché dont les règles exigent ce que nous n’ajoutons pas encore est refusé avec option_not_available : la Chine, les hypertrucages au Vietnam, les morceaux en Inde, et les hypertrucages dans les morceaux pour eu et kr.
deepfakebooléen, par défaut falseLe fichier est un hypertrucage : il présente une ressemblance avec des personnes, des lieux ou des événements existants et pourrait être perçu à tort comme authentique (art. 50, par. 4, de l’AI Act). Active la mention visible ; avec eu ou kr dans markets, elle ne peut pas être désactivée. Pas encore pour les morceaux.
labelbooléenImages et vidéos : incruste dans le fichier la mention IA visible de l’UE, avant le filigrane. Désactivé par défaut ; activé pour les hypertrucages et pour in.
label_languagetexte, par défaut enLe mot à côté de « AI » : en (AI GENERATED), ko (AI 생성) ou zh (AI生成). ko par défaut avec kr dans markets.
label_positiontexte, par défaut top-righttop-right, top-left, bottom-right ou bottom-left, tel que le voit la personne qui regarde (la rotation EXIF d’une photo est prise en compte).
label_sizetexte, par défaut mediumsmall, medium ou large : 5, 7 ou 10 % du petit côté, au moins 20 pixels. in exige medium ou large.
label_styletexte, par défaut autoauto (noir sur un coin clair, blanc sur un coin sombre), dark, light, dark-translucent ou light-translucent : les quatre variantes du code de bonne pratique.
titletexte, facultatifUn titre pour le fichier. 200 caractères au maximum.
watermarkbooléen, par défaut trueAjoute le filigrane invisible. Ne le désactivez que si le fichier porte déjà le nôtre, par exemple s’il a été filigrané en local.
iptcbooléen, par défaut trueImages : écrit le DigitalSourceType IPTC dans les métadonnées XMP du fichier. Les morceaux et les vidéos l’indiquent dans leur manifeste.
output_formattexte, par défaut keepUne image : keep, png, jpeg ou webp. Un morceau : keep, mp3, wav ou m4a. Une vidéo : keep ou mp4 (toujours du H.264 en MP4).
keep_input_credentialsbooléen, par défaut trueConserve en tant qu’ingrédient les Content Credentials que le fichier porte déjà (par exemple ceux de l’API d’un modèle en amont), comme le demande la mesure 1.2 du code de bonne pratique.
inputfichier, facultatifPour les outils de retouche : l’image d’entrée fournie par l’utilisateur (ou, pour un morceau ou une vidéo, un morceau d’entrée). Si elle porte des Content Credentials, ils sont conservés en tant qu’ingrédient ; rien d’autre n’en est enregistré. Une image d’entrée passe par notre socle et, avec check activé, par votre politique.
input_uploadtexte, à la place de inputL’ID d’envoi de l’entrée, pour une entrée de plus de 4 Mo (ou un fichier et une entrée qui dépassent 4 Mo ensemble).
checktexte, par défaut offLe contrôle de sécurité avant le marquage : ai-act, safe-only ou off. En cas de blocage, rien n’est marqué et vous recevez 422 content_blocked avec le motif. Chaque image contrôlée compte deux marquages ; une vidéo est contrôlée sur 12 images, à deux marquages chacune. Un morceau ne contient pas d’images : laissez le contrôle désactivé.

Les options prévues (metadata, webhook, ai_disclosure, categorize, describe, le marquage par url) sont refusées avec 400 option_not_available plutôt qu’ignorées : un fichier ne semble donc jamais porter quelque chose qu’il ne porte pas.

Exemple

curl
curl https://api.kennmarq.com/v1/mark \
  -H "Authorization: Bearer $KENNMARQ_API_KEY" \
  -F file=@render.jpg \
  -F source=created \
  -F provider="Acme Inc." \
  -F model="FLUX.1 schnell" \
  -F model_version="1.0" \
  -F app_name="Acme Studio" \
  -F app_version="2.1" \
  -F output_format=webp \
  -D headers.txt \
  -o render.marked.webp
Node.js 20+
import { readFile, writeFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("render.png")], { type: "image/png" }), "render.png");
form.append("source", "created");
form.append("model", "FLUX.1 schnell");

const res = await fetch("https://api.kennmarq.com/v1/mark", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.KENNMARQ_API_KEY}` },
  body: form,
});
if (!res.ok) throw new Error((await res.json()).error.message);
await writeFile("render.marked.png", Buffer.from(await res.arrayBuffer()));
console.log(res.headers.get("x-mark-id"), res.headers.get("x-marks-used"));
Python (requests)
import os, requests

with open("render.png", "rb") as f:
    res = requests.post(
        "https://api.kennmarq.com/v1/mark",
        headers={"Authorization": f"Bearer {os.environ['KENNMARQ_API_KEY']}"},
        files={"file": ("render.png", f, "image/png")},
        data={"source": "created", "model": "FLUX.1 schnell"},
        timeout=60,
    )
res.raise_for_status()
open("render.marked.png", "wb").write(res.content)
print(res.headers["X-Mark-Id"], res.headers["X-Marks-Used"])

Réponse

200, avec le fichier marqué comme corps, et ces en-têtes :

En-têtes de la réponse de POST /v1/mark
En-têteValeur
Content-TypeLe type du fichier marqué : image/png, image/jpeg, image/webp, audio/mpeg, audio/wav ou audio/mp4.
Content-Dispositionattachment, avec le nom du fichier suivi de .marked et de l’extension correcte (image.marked.png pour une image envoyée).
X-Mark-IdL’ID de ce marquage (mk_…). Indiquez-le à l’assistance.
X-Watermark-IdL’ID que porte le filigrane (hexadécimal). Absent quand watermark=false.
X-Log-IndexLa position de la signature de ce fichier dans notre journal de transparence public : GET /v1/log/proof/{index} prouve qu’elle y figure.
X-Marks-UsedLes marquages utilisés ce mois-ci dans l’environnement de cette clé, celui-ci compris.
X-Marks-LimitLe quota du mois pour l’environnement de cette clé.
X-Marks-CountedMorceaux : les marquages décomptés pour celui-ci, cinq par minute entamée.
X-Audio-WatermarkMorceaux : claimed (le marquage se relit dans le fichier, et son manifeste l’indique), unclaimed (trop silencieux pour que le marquage soit relu : le fichier est tout de même signé) ou off.
X-Safety-Verdict, X-Safety-ReasonAvec check activé : allow ou flag, et le motif d’un signalement (sexual_realistic_face : obtenez le consentement de la personne).
X-Request-IdL’ID de cette requête (req_…), qui figure aussi dans chaque corps d’erreur.
X-RateLimit-Limit, -Remaining, -ResetVoir Limites de débit ; Reset est un temps Unix en secondes.

Mentions visibles

Avec label activé, nous incrustons l’icône de l’UE du code de bonne pratique sur les contenus générés par l’IA dans l’image ou dans chaque image de la vidéo : « AI » en capitales, avec GENERATED pour created, ou MODIFIED pour composite et edited, comme le montre son annexe 1. Elle est ajoutée avant le filigrane, de sorte que le filigrane et la signature la couvrent, et le manifeste l’enregistre (une action c2pa.edited et une assertion com.kennmarq.label) : le vérificateur et /v1/verify l’affichent.

AI GENERATED, noirAI MODIFIED, noirAI GENERATED, blancAI GENERATED, noir à 50 %AI 생성, coréenAI生成, chinois

Par défaut, elle se place dans le coin supérieur droit, là où la place l’exemple du code de bonne pratique, avec une taille calculée sur le petit côté, et sur chaque image d’une vidéo, comme le code l’encourage. auto choisit le noir ou le blanc selon les pixels situés derrière, pour qu’elle reste lisible sur n’importe quel fond. Une image trop étroite pour la pastille reçoit l’icône ronde « AI ».

deepfake=true l’active, et les marchés décident quand elle doit rester active : toujours pour l’Inde, pour les hypertrucages dans l’UE et en Corée du Sud. Le justificatif l’indique pour chaque marché.

curl
curl https://api.kennmarq.com/v1/mark \
  -H "Authorization: Bearer $KENNMARQ_API_KEY" \
  -F file=@portrait.jpg \
  -F source=edited \
  -F app_name="Acme Studio" \
  -F markets=eu,in \
  -F deepfake=true \
  -o portrait.marked.jpg

La même mention dans votre propre interface

À côté d’un fichier, dans un fil d’actualité, ou là où une image ne peut pas la porter, affichez la même mention avec notre composant web : un seul script, aucune requête, aucun cookie, et les lecteurs d’écran l’annoncent comme « AI-generated content ». Le code de bonne pratique demande précisément ce type de mention à côté des œuvres artistiques, et la Corée du Sud pour les contenus affichés dans votre service.

HTML
<script src="https://kennmarq.com/label.js" defer></script>

<kennmarq-label kind="generated" lang="en" theme="dark" size="24"></kennmarq-label>

kind : generated ou modified. lang : en, ko ou zh. theme : dark, light, dark-translucent ou light-translucent. size : sa hauteur en pixels. href en fait un lien, par exemple vers la page où le fichier peut être vérifié. Il est sous licence MIT : copiez-le dans votre application si vous préférez ne pas le charger depuis nos serveurs.

Morceaux

MP3, WAV ou M4A, jusqu’à 15 minutes. Le morceau vous revient dans le même appel, dans son propre format (ou celui qu’indique output_format), avec sa fréquence d’échantillonnage, ses canaux et ses tags texte ; la pochette n’est pas conservée. Son signal porte le marquage AudioSeal de Meta, modelé sous la musique pour que personne ne l’entende, et son manifeste est signé et horodaté comme celui d’une image. Les 16 bits d’AudioSeal indiquent que le fichier a été marqué par nous, pas de quel fichier il s’agit : les Content Credentials d’un morceau voyagent dans son manifeste, et un morceau n’a pas de X-Watermark-Id.

Le marquage est relu dans le fichier encodé : s’il se relit, le manifeste indique que le fichier le porte (X-Audio-Watermark: claimed). Dans un enregistrement très calme ou silencieux, il n’y a rien sous quoi le cacher ; le fichier est alors signé sans cette déclaration (unclaimed). Un morceau plus long que ce que permettent les marquages qu’il vous reste ce mois-ci est refusé avant tout traitement, avec 402 quota_exceeded.

Vidéos

MP4 (ou QuickTime), jusqu’à 4 500 images (2,5 minutes à 30 images par seconde, 75 secondes à 60, 5 minutes à 15), 1920x1080 dans un sens ou dans l’autre, 60 images par seconde et 1 Go via un envoi : ce que nous pouvons marquer dans le temps imparti à une tâche. Votre offre marque quelques vidéos à la fois (vidéos simultanées). Une vidéo est marquée sous forme de tâche : l’appel répond aussitôt 202 avec l’ID du marquage, et GET /v1/marks/{id} indique quand elle est prête, avec un lien pour la télécharger. Les endpoints que vous ajoutez dans le tableau de bord reçoivent mark.completed ou mark.failed (webhooks).

202 application/json
{ "object": "mark", "id": "mk_…", "status": "running", "kind": "clip",
  "created_at": "2026-11-12T10:00:00.000Z", "url": "https://api.kennmarq.com/v1/marks/mk_…" }

Chaque image de la vidéo porte le filigrane Video Seal de Meta, avec un ID aléatoire comme pour une image fixe (watermark_id) : il mène aux Content Credentials durables de la vidéo quand ses métadonnées sont supprimées. Sa piste audio porte AudioSeal, comme un morceau. La vidéo vous revient en H.264 dans un MP4, à l’endroit (la rotation d’un téléphone est appliquée aux pixels), avec la même taille, les mêmes instants d’image et le même son, signée et horodatée. Avant son envoi, l’ID du filigrane est relu sur 64 de ses images ; une vidéo sur laquelle il ne se relit pas échoue, comme une image fixe. Notre socle de sécurité analyse 12 images de chaque vidéo, et avec check, votre politique aussi (deux marquages par image) ; une image bloquée fait échouer la tâche, avec la réponse pour cette image dans safety.

La tâche d’une vidéo réserve cinq marquages (sa première seconde, et ses contrôles) à son démarrage, et décompte les autres secondes de la vidéo une fois terminée. Une vidéo plus longue que ce que permettent les marquages qu’il vous reste est refusée avant tout traitement. Une tâche qui échoue ne décompte rien, sauf les contrôles d’un blocage quand vous les avez demandés.

Ce que nous vérifions avant de répondre

En cas d’anomalie, nous échouons de manière sûre (fail closed) : un fichier n’est renvoyé que s’il a été marqué comme demandé. Avant de répondre, le marqueur relit le fichier signé et valide son manifeste et son horodatage ; nous vérifions ensuite que la réponse correspond bien à ce marquage, dans cet environnement, au format demandé, avec exactement les octets que le marqueur a décrits. Si quoi que ce soit cloche, vous recevez 502 mark_failed et aucun fichier, et le marquage n’est pas décompté.

Un marquage est réservé quand la requête est acceptée, et restitué si quoi que ce soit échoue ensuite : les appels en échec ne sont donc jamais décomptés de votre quota. Voir limites de débit et quotas.

Le contrôle de sécurité

Chaque image que nous marquons passe par notre propre socle, quoi que vous envoyiez : un contenu explicite montrant une personne qui paraît être un enfant n’est jamais marqué, et l’appel répond 422 content_blocked. Avec check renseigné, le fichier et l’entrée sont aussi contrôlés selon votre politique. Une vidéo est analysée sur 12 images. Un blocage renvoie le contrôle dans safety, et picture indique de quelle image il s’agissait :

422 application/json
{
  "error": { "code": "content_blocked", "message": "The safety check blocked this picture, so nothing was marked. See \"safety\" for why.", "request_id": "req_…" },
  "safety": { "object": "safety_check", "policy": "ai-act", "verdict": "block", "reason": "sexual_apparent_minor",
              "nudity": { "label": "explicit", "score": 0.98 }, "apparent_minor": true, "realistic_face": true,
              "picture": "file", … }
}

Un appel bloqué décompte les contrôles effectués, pas le marquage. Un blocage dû à notre seul socle, avec check désactivé, ne décompte rien.

Erreurs

Erreurs de POST /v1/mark
StatutCodeQuand
400invalid_requestAucun fichier, un champ envoyé deux fois, ou une option hors limites.
400unknown_fieldUn champ que cet endpoint ne connaît pas : une faute de frappe ne passe jamais inaperçue.
400option_not_availableUne option prévue, comme metadata ou webhook, ou un marché dont les règles exigent ce que nous n’ajoutons pas encore (Chine, Vietnam, morceaux en Inde).
401missing_api_key, invalid_api_key, api_key_revokedVoir Authentification.
402quota_exceededLe quota mensuel de marquages ou de données est épuisé, ou un morceau nécessite plus de marquages qu’il n’en reste (rien n’est marqué).
403insufficient_scope, ip_not_allowedLa clé n’a pas la portée mark, ou l’adresse ne figure pas sur la liste d’IP autorisées.
404upload_not_foundL’envoi indiqué n’est pas utilisable par cette clé : inconnu, déjà utilisé, expiré ou appartenant à une autre clé.
413payload_too_largeLa requête dépasse 4 Mo (envoyez les fichiers plus lourds via un envoi), ou le fichier dépasse nos limites : 15 minutes pour un morceau, 4 500 images jusqu’en 1920x1080 pour une vidéo.
415unsupported_media_typePas une image, un morceau ou une vidéo que nous acceptons, ou pas envoyé en multipart/form-data.
422content_blockedLe contrôle de sécurité a bloqué le fichier ou l’entrée. Le champ safety du corps en donne la raison ; rien n’a été marqué.
429rate_limitedTrop d’appels cette minute (toutes les clés de votre organisation confondues), trop d’appels ont échoué aujourd’hui après que nous les avons traités, ou (pour une vidéo) toutes les vidéos simultanées de votre offre sont en cours, ou trop de vidéos ont échoué : réessayez après le nombre de secondes indiqué dans Retry-After.
502mark_failedLe fichier n’a pas pu être marqué et vérifié ; aucun fichier n’est renvoyé. Rien n’est décompté.
503marker_unavailable, storage_unavailableLe service de marquage ou les envois sont momentanément indisponibles. Rien n’est décompté ; réessayez.

Les corps d’erreur suivent le format commun.

Confidentialité

Nous enregistrons pour chaque marquage son ID, l’ID du filigrane, le SHA-256 et les tailles, jamais le fichier, son nom ni son contenu. Le fichier marqué d’une vidéo reste à votre disposition pendant une journée, puis il est supprimé. Les marquages indiquent comment un fichier a été créé, jamais qui l’a créé ni avec quel prompt. La réponse d’un contrôle de sécurité n’est jamais inscrite dans les marquages du fichier, et rien n’est conservé d’une image bloquée. Un envoi est supprimé dès que l’appel l’a utilisé.