5 min de lecture

Animer une photo par API : tutoriel d'intégration

Tutoriel pas à pas pour intégrer l'API d'animation de photos Incarn : créer une clé, lancer une animation, récupérer le résultat par webhook signé, gérer crédits et erreurs. Exemples curl et Node.js.

APIdéveloppeurstutorielintégrationwebhook
Thomas Moreau
Thomas Moreau

AI & Technology Writer, Incarn

L'essentiel

Intégrer l'animation de vieilles photos dans votre app tient en quatre étapes : créez une clé, envoyez la photo à POST /v1/animations (fichier ou URL), récupérez la vidéo par polling ou par webhook signé HMAC-SHA256, puis gérez les crédits (402) et l'idempotence. Exemples curl et Node.js, du premier appel au webhook en production, plus bas.

Vous voulez qu'une vieille photo bouge, depuis votre code, sans monter de pipeline vidéo. Ce tutoriel vous emmène du premier appel curl jusqu'au webhook en production, avec l'API Incarn.

Le principe tient en une phrase : vous envoyez une photo, vous recevez une courte vidéo naturelle. Le reste (validation, choix du modèle, HD) est géré côté serveur. Comptez dix minutes pour un premier rendu.

Ce que vous allez construire

Une fonction qui prend une photo ancienne et renvoie l'URL d'une vidéo animée. Deux façons de récupérer le résultat : le polling (simple, pour démarrer) ou le webhook (propre, pour la production). On voit les deux.

Étape 1 : créer votre clé API

Rendez-vous sur votre tableau de bord développeur et créez une clé. Elle n'est affichée qu'une seule fois, stockez-la côté serveur (variable d'environnement), jamais dans un navigateur ni un dépôt public.

export INCARN_KEY="ik_live_xxxxxxxxxxxxxxxxxxxxxxxx"

L'authentification se fait en Bearer token sur chaque requête :

Authorization: Bearer ik_live_xxx

Étape 2 : lancer une animation

L'endpoint est POST /v1/animations. Vous envoyez la photo de deux façons au choix : un fichier en multipart/form-data, ou une URL publique en JSON.

En curl, avec une URL :

curl -X POST https://api.incarn.co/v1/animations \
  -H "Authorization: Bearer $INCARN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"image_url":"https://votre-app.com/grand-mere.jpg","prompt":"léger sourire"}'

En Node.js, la même chose :

const res = await fetch("https://api.incarn.co/v1/animations", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INCARN_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    image_url: "https://votre-app.com/grand-mere.jpg",
    prompt: "léger sourire",
    webhook_url: "https://votre-app.com/incarn", // optionnel, voir étape 3
  }),
});

const job = await res.json();
// { id: "clx...", status: "processing", video_url: null, ... }

Le traitement est asynchrone : la réponse arrive tout de suite avec un id et un statut processing. La vidéo n'est pas encore prête.

Un conseil dès maintenant : ajoutez un en-tête Idempotency-Key. Si votre requête part en double (retry réseau, double clic), la même clé renvoie l'animation d'origine sans relancer de génération ni reconsommer de crédit.

headers: {
  Authorization: `Bearer ${process.env.INCARN_KEY}`,
  "Content-Type": "application/json",
  "Idempotency-Key": `photo-${photoId}`,
}

Étape 3 : récupérer le résultat

Deux options. Choisissez selon votre architecture.

Option A : le polling

Vous interrogez GET /v1/animations/:id jusqu'à ce que le statut passe à succeeded ou failed. Comptez 1 à 3 minutes, interrogez toutes les quelques secondes.

async function waitForVideo(id) {
  while (true) {
    const res = await fetch(`https://api.incarn.co/v1/animations/${id}`, {
      headers: { Authorization: `Bearer ${process.env.INCARN_KEY}` },
    });
    const job = await res.json();
    if (job.status === "succeeded") return job.video_url;
    if (job.status === "failed") throw new Error(job.error);
    await new Promise((r) => setTimeout(r, 3000));
  }
}

Simple, parfait pour un script ou un prototype. En production, préférez le webhook : pas de boucle à maintenir, pas de connexion ouverte pendant des minutes.

Option B : le webhook signé

Passez un webhook_url à la création (étape 2). Dès que le job se termine, Incarn envoie un POST à cette URL avec le même corps que GET /v1/animations/:id.

Chaque livraison est signée. L'en-tête X-Incarn-Signature vaut sha256=<hmac>, un HMAC-SHA256 du corps brut avec votre secret de signature (visible dans le tableau de bord). Vérifiez-le avant de traiter l'événement, sinon n'importe qui pourrait poster de faux résultats.

import { createHmac, timingSafeEqual } from "crypto";

app.post("/incarn", express.raw({ type: "*/*" }), (req, res) => {
  const signature = req.header("X-Incarn-Signature") || "";
  const expected =
    "sha256=" +
    createHmac("sha256", process.env.INCARN_WEBHOOK_SECRET)
      .update(req.body) // le corps BRUT, pas le JSON déjà parsé
      .digest("hex");

  const ok =
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  if (!ok) return res.status(401).end();

  const job = JSON.parse(req.body.toString());
  if (job.status === "succeeded") {
    // job.video_url pointe le MP4 en HD : stockez-le, notifiez l'utilisateur
  }
  res.status(200).end(); // accusez réception vite
});

Deux détails qui évitent des bugs : signez sur le corps brut (pas l'objet déjà parsé, la sérialisation changerait la signature), et dédoublonnez à la réception via le job.id, une livraison peut être ré-essayée.

Étape 4 : gérer les cas réels

Le chemin heureux est simple. Voici les quelques cas à couvrir pour une intégration propre.

  • Plus de crédits (402). Le modèle est prépayé : à court de crédits, l'API renvoie 402 et ne lance rien. Rechargez depuis le tableau de bord, ou activez la recharge automatique (rachat d'un pack borné quand le solde passe sous un seuil) pour ne jamais bloquer votre file.
  • Trop de requêtes ou de générations en parallèle (429). La création est limitée à 10 requêtes/minute, et le nombre de générations simultanées dépend de votre formule (2, 5 ou 15). Sur un 429, réessayez quand une génération se termine.
  • Échec de génération. Le statut passe à failed, error explique, et le crédit est automatiquement remboursé. Prévoyez un message clair côté utilisateur et, si utile, un nouvel essai.
  • Retry sûrs. L'Idempotency-Key de l'étape 2 rend tous vos retries inoffensifs : jamais de double génération, jamais de double débit.

Aller plus loin

Vous avez le squelette d'une intégration complète. Le reste, c'est votre produit : une app de généalogie qui anime les portraits d'un arbre, un service mémoriel qui redonne un mouvement à une photo, une carte de vœux qui bouge.

La documentation complète détaille tous les champs, codes d'erreur et l'exemple de vérification de signature. Si vous hésitez encore entre les solutions du marché, notre comparatif des APIs pour animer une vieille photo situe Incarn par rapport aux primitives génériques. Et pour créer votre clé, tout part de la page développeurs.

Une photo, un appel, une vidéo. Le reste, on s'en occupe.

Thomas Moreau
Thomas Moreau

AI & Technology Writer, Incarn

Thomas covers AI and machine learning applications for creative tools. Former research engineer with a focus on computer vision and video generation.

À lire ensuite