Aller au contenu

Guide de développement

Envoyez des publications à Amaposta depuis votre propre pipeline de contenu : importez des médias, créez une publication pour chaque canal, programmez-la et recevez une confirmation de sa mise en ligne.

Authentification

Créez une clé API dans le tableau de bord, sous Paramètres, API et webhooks. Les clés commencent par ss_live_, appartiennent à un espace de travail et peuvent être limitées à certains projets. Envoyez la clé comme jeton bearer dans chaque requête :

Vérifier une clé
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

La réponse indique le nom de la clé, ses permissions et l’espace de travail. Chaque clé possède une ou plusieurs permissions :

PermissionAutorise
content:readLire les projets, canaux, publications et médias.
content:writeImporter des médias et créer ou modifier des publications.
posts:publishProgrammer et publier sans vérification. Sans cette permission, les publications passent dans la boîte de vérification des projets avec validation et restent des brouillons partout ailleurs.

Chaque clé peut effectuer 120 requêtes par minute. Au-delà, vous recevez un statut 429 avec le code rate_limited ; attendez un instant et réessayez.

Projets et canaux

Les publications appartiennent à un projet. Listez les projets visibles par votre clé, puis les canaux de l’un d’eux. L’identifiant d’un canal vous permet de le cibler ou de remplacer son texte.

Lister les projets et les canaux
curl https://www.amaposta.com/api/v1/projects \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/channels \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

Un canal possède un provider (youtube, instagram, tiktok, x, facebook, custom ou sandbox), un status (active, expiring, needs_reconnect, error ou disabled) et les types de publications qu’il prend en charge dans supports.

Importer des médias

Les images et vidéos sont importées une seule fois par projet et peuvent être utilisées dans autant de publications que vous le souhaitez. Il existe trois méthodes d’import.

Import direct

Envoyez le fichier en données de formulaire multipart dans un champ nommé file. Idéal pour les fichiers jusqu’à quelques centaines de Mo.

POST /api/v1/projects/{project_id}/media
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -F "[email protected]"

Import présigné pour les fichiers volumineux

Demandez une URL d’import, envoyez directement les octets au stockage par PUT, puis indiquez à Amaposta que l’envoi est terminé.

Trois requêtes
# 1. Ask for an upload URL. The response is {"media": { … }, "upload_url": "…"}
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/uploads \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "batch-142.mp4", "content_type": "video/mp4", "size_bytes": 48213844}'

# 2. Upload the bytes to upload_url
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --upload-file batch-142.mp4

# 3. Finish the upload
curl -X POST https://www.amaposta.com/api/v1/media/$MEDIA_ID/complete \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

Importer depuis une URL

Fournissez une URL publique à Amaposta pour qu’il télécharge le fichier en arrière-plan. Vous pouvez aussi sauter cette étape et transmettre media_url lors de la création de la publication.

POST /api/v1/projects/{project_id}/media/import
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/import \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://cdn.example.com/batch-142.mp4"}'

Chaque import renvoie un objet média. Son status passe de pending à processing, puis à ready (ou failed, avec une error). Après traitement, il contient width, height, duration_ms et une miniature. Interrogez GET /api/v1/media/{id} ou écoutez le webhook media.ready. L’url renvoyée est signée et valable 12 heures.

Créer une publication

POST /api/v1/projects/{project_id}/posts crée une publication avec une diffusion pour chaque canal. Amaposta l’adapte à chaque plateforme : les titres ne sont envoyés que là où un champ titre existe, les hashtags deviennent des tags sur YouTube et sont intégrés au texte ailleurs.

Créer une publication vidéo et l’ajouter à la file d’attente
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: episode-142" \
  -d @- <<'JSON'
{
  "kind": "video",
  "title": "How we roast a batch in 12 minutes",
  "description": "From green beans to first crack. We filmed one batch start to finish.",
  "hashtags": ["coffee", "behindthescenes"],
  "media_url": "https://cdn.example.com/batch-142.mp4",
  "cover_url": "https://cdn.example.com/batch-142-cover.jpg",
  "overrides": {
    "<x-channel-id>": {
      "description": "Batch 142 is out: green beans to first crack in 12 minutes."
    },
    "<youtube-channel-id>": {
      "options": { "privacy": "unlisted" }
    }
  },
  "schedule": { "mode": "queue" },
  "external_ref": "episode-142",
  "metadata": {
    "pipeline": { "name": "nightly-cut", "run": "2026-10-17" }
  }
}
JSON

Champs

ChampSon fonctionnement
kindtext, image ou video. Obligatoire.
titleUtilisé par les plateformes disposant d’un champ titre : YouTube, publications photo TikTok, vidéos Facebook.
descriptionLa légende, la description ou le texte de la publication. Également accepté sous les noms body, caption ou text.
hashtagsUne liste, avec ou sans #.
media_id, media_urlL’identifiant d’un média importé ou une URL publique qu’Amaposta télécharge pour vous. Nécessaire pour les publications d’images et de vidéos.
cover_media_id, cover_urlUne image de couverture pour les vidéos, lorsque la plateforme le permet.
channelsIdentifiants des canaux sur lesquels publier. Omettez ce champ pour utiliser tous les canaux actifs du projet qui prennent en charge ce type de publication.
overridesModifications par canal, indexées par identifiant de canal : title, description, hashtags et options de plateforme, comme privacy sur YouTube.
schedulemode vaut at (avec un champ at au format ISO 8601), now ou queue pour le prochain créneau de publication libre. Omettez-le pour créer un brouillon.
external_refVotre propre référence, renvoyée dans la publication et dans les diffusions Custom API.
metadataJSON libre conservé avec la publication. Incluez "pipeline" pour identifier le contenu généré ; le tableau de bord l’affiche et les webhooks indiquent pipeline comme origine.

La réponse contient la publication, avec une entrée dans deliveries par canal.

Des nouvelles tentatives sans doublons

Les réseaux peuvent perdre des requêtes. Envoyez un en-tête Idempotency-Key lors de la création d’une publication : une nouvelle tentative avec la même clé renverra la publication de la première tentative sans en créer une seconde. Utilisez une valeur stable de votre côté, comme un identifiant de tâche ou d’épisode. Les clés sont tronquées à 200 caractères.

Programmer et publier

Programmez un brouillon, publiez-le immédiatement, ajoutez-le à la file d’attente ou retirez-le du calendrier avec POST /api/v1/posts/{post_id}/schedule.

Programmer à une heure précise
curl https://www.amaposta.com/api/v1/posts/$POST_ID/schedule \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "at", "at": "2026-10-20T07:00:00Z"}'
  • atPublie à l’heure indiquée, jusqu’à deux ans à l’avance.
  • nowPublie immédiatement.
  • queueUtilise le prochain créneau de publication libre pour les canaux de la publication dans les 12 prochaines semaines.
  • unscheduleReplace la publication dans les brouillons.

Avant toute programmation, les mêmes contrôles que dans l’éditeur sont exécutés. Si l’un d’eux échoue, la requête renvoie 400 avec un message tel que Corrigez ceci avant de programmer : YouTube exige un titre. La programmation nécessite la permission posts:publish ou un projet avec validation activée, auquel cas la publication attend dans la boîte de vérification.

Lire les publications et les diffusions

Listez les publications d’un projet, filtrées par status (statuts séparés par des virgules, ou inbox, queue ou history), une plage from/to, limit et offset. La réponse a la forme {"items": […], "total": n}.

Les prochaines diffusions et une publication en détail
curl "https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts?status=queue" \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

curl https://www.amaposta.com/api/v1/posts/$POST_ID \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

Une publication possède un status global (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial ou failed) et une diffusion par canal :

deliveries[]
"deliveries": [
  {
    "id": "0192b7d4-…",
    "channel_id": "0192b7a1-…",
    "status": "published",
    "url": "https://www.youtube.com/watch?v=…",
    "error": null,
    "published_at": "2026-10-20T07:00:41Z"
  },
  {
    "id": "0192b7d4-…",
    "channel_id": "0192b7a9-…",
    "status": "failed",
    "url": null,
    "error": {
      "code": "endpoint_unavailable",
      "message": "Your endpoint returned 503 Service Unavailable",
      "hint": "Amaposta will retry with backoff."
    },
    "published_at": null
  }
]

Les statuts de diffusion sont pending, scheduled, publishing, processing, published, failed et cancelled. DELETE /api/v1/posts/{post_id} supprime une publication.

Erreurs

Toutes les erreurs ont la même structure : un objet error avec un code stable, un message que vous pouvez afficher aux utilisateurs et, parfois, fields (messages par champ) et un hint.

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
StatutCodeSignification
400bad_requestLa requête ne peut pas être exécutée en l’état, par exemple la programmation d’une publication qui échoue aux contrôles.
401unauthorizedLa clé API est absente, incorrecte ou révoquée.
402plan_limitL’offre de l’espace de travail n’autorise pas cette action ; limit indique la limite concernée.
403forbiddenIl manque une permission ou l’accès au projet à la clé.
404not_foundCe projet, cette publication, ce média ou ce point de terminaison n’existe pas.
409conflictLa modification entre en conflit avec l’état actuel, par exemple la modification d’un contenu déjà en cours de publication.
422validationUn ou plusieurs champs doivent être corrigés ; consultez fields.
429rate_limitedPlus de 120 requêtes par minute avec cette clé.
502provider_errorUne plateforme a refusé la requête ou rencontré une erreur ; hint indique la marche à suivre.

Webhooks

Ajoutez un point de terminaison dans Paramètres, API et webhooks, choisissez les événements souhaités et copiez son secret de signature. Amaposta envoie une requête POST avec du JSON à votre URL pour chaque événement. Toute réponse autre qu’un code 2xx, ou toute absence de réponse sous 15 secondes, entraîne une nouvelle tentative avec un délai croissant. Les points de terminaison qui continuent d’échouer sont désactivés et les administrateurs en sont informés.

ÉvénementEnvoyé lorsque
post.createdUne publication a été créée (tableau de bord ou API)
post.scheduledUne publication a été programmée
post.approvedUne publication a été validée
post.publishedUne publication a été mise en ligne sur un canal
post.failedUne publication n’a pas pu être publiée sur un canal
channel.connectedUn canal a été connecté ou reconnecté
channel.disconnectedUn canal a perdu sa connexion
media.readyLe traitement d’un fichier importé est terminé
media.failedUn fichier importé n’a pas pu être traité
post.published
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Amaposta-Webhooks/1.0
X-Amaposta-Event: post.published
X-Amaposta-Event-Id: 0192b7e0-…
X-Amaposta-Signature: t=1792479641,v1=5f2b0c…

{
  "id": "0192b7e0-…",
  "type": "post.published",
  "created_at": "2026-10-20T07:00:41Z",
  "project_id": "0192a3f1-…",
  "data": {
    "post_id": "0192b7c8-…",
    "delivery_id": "0192b7d4-…",
    "channel_id": "0192b7a1-…",
    "provider": "youtube",
    "external_id": "…",
    "url": "https://www.youtube.com/watch?v=…"
  }
}

Vérifier la signature

X-Amaposta-Signature a la forme t=<unix seconds>,v1=<hex>, où v1 est HMAC_SHA256(secret, t + "." + body), calculé sur le corps brut de la requête. Calculez-la de votre côté, comparez en temps constant et rejetez les horodatages anciens pour empêcher les attaques par rejeu.

Node.js
import crypto from "node:crypto";

// rawBody: the request body exactly as received (string or Buffer).
export function verifySignature(rawBody, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}
Python
import hashlib
import hmac
import time


def verify_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Canaux Custom API

Un canal Custom API publie vers un point de terminaison HTTPS que vous gérez : votre CMS, un outil de newsletter, une plateforme qu’Amaposta ne prend pas encore en charge. Il figure dans le calendrier et bénéficie des mêmes contrôles, validations et nouvelles tentatives que les autres canaux. Lorsqu’une publication est diffusée, Amaposta envoie une requête POST par diffusion :

Ce que votre point de terminaison reçoit
POST /amaposta/publish HTTP/1.1
Authorization: Bearer <your API key>
Content-Type: application/json
Idempotency-Key: 0192b7d4-…
X-Amaposta-Delivery: 0192b7d4-…
X-Amaposta-Signature: t=1792479600,v1=9ac41e…

{
  "id": "0192b7d4-…",
  "type": "post.publish",
  "created_at": "2026-10-20T07:00:00Z",
  "content": {
    "kind": "video",
    "title": "How we roast a batch in 12 minutes",
    "text": "From green beans to first crack. We filmed one batch start to finish.",
    "hashtags": ["coffee", "behindthescenes"],
    "media": {
      "url": "https://…",
      "content_type": "video/mp4",
      "size_bytes": 48213844,
      "width": 1080,
      "height": 1920,
      "duration_ms": 58000,
      "filename": "batch-142.mp4"
    },
    "cover": null,
    "options": {}
  },
  "post": {
    "id": "0192b7c8-…",
    "external_ref": "episode-142",
    "scheduled_at": "2026-10-20T07:00:00Z",
    "metadata": {
      "pipeline": { "name": "nightly-cut" }
    }
  },
  "project": { "id": "0192a3f1-…", "name": "Spring launch" },
  "channel": { "id": "0192b7a9-…", "name": "api.example.com" }
}

Comment répondre

  • 2xxPublié. Vous pouvez renvoyer {"id": "…", "url": "…"} pour afficher le lien sur la publication.
  • 401, 403La clé a été rejetée. Le canal est signalé comme nécessitant une reconnexion et les administrateurs sont informés. N’utilisez pas ces codes pour une signature incorrecte.
  • 5xx, 408, 429Temporaire. Amaposta réessaie avec un délai croissant.
  • Autres 4xxLa publication est marquée comme échouée avec votre message ; une personne peut la corriger et appuyer sur Réessayer.
Un récepteur avec Express
app.post("/amaposta/publish", express.raw({ type: "application/json" }), async (req, res) => {
  const signature = req.get("X-Amaposta-Signature") ?? "";
  if (!verifySignature(req.body, signature, process.env.AMAPOSTA_SIGNING_SECRET)) {
    return res.status(400).json({ message: "Bad signature" });
  }
  const delivery = JSON.parse(req.body);
  const item = await publishToYourSystem(delivery.content, req.get("Idempotency-Key"));
  res.json({ id: item.id, url: item.url });
});

Votre point de terminaison doit être accessible depuis Internet ; les redirections ne sont pas suivies. Lors de la connexion du canal, Amaposta le vérifie avec une requête GET vers votre URL de disponibilité si vous en avez défini une, sinon avec une requête OPTIONS vers le point de terminaison.

Canaux Sandbox

Un canal sandbox se comporte comme un vrai canal, mais rien ne quitte Amaposta. Utilisez-le pour créer et tester votre intégration de bout en bout : les publications sont programmées, passent à l’antenne, sont publiées ou échouent et déclenchent les mêmes webhooks. Les canaux sandbox sont gratuits avec toutes les offres et ne comptent pas dans les limites de canaux.

Choisissez son comportement lors de l’ajout :

  • Toujours réussirToutes les publications sont diffusées.
  • Échouer de temps en tempsDécouvrez les échecs et les nouvelles tentatives sans attendre une vraie panne.
  • Toujours échouerTestez votre traitement de post.failed.
  • Connexion expiréeDécouvrez les alertes et liens de reconnexion lorsque l’accès expire.

Règles des plateformes

Ce que chaque type de canal accepte. Les contrôles utilisent les mêmes limites : la plupart des problèmes sont donc détectés avant la programmation, plutôt qu’au moment de la diffusion.

Plateforme Publication Titre Texte Hashtags Médias
YouTube Vidéo TitreObligatoire, jusqu’à 100 caractères DescriptionJusqu’à 5 000 caractères Envoyés comme tags, jusqu’à 60 MP4, MOV, WebM, AVI, MKV; jusqu’à 256 GB; 1 s à 12 h
Les comptes sans numéro de téléphone vérifié peuvent importer des vidéos de 15 minutes maximum et ne peuvent pas définir de miniatures personnalisées. Chaque import consomme du quota API ; le quota Google par défaut permet environ six imports par jour et par application.
Instagram Image Aucun titre LégendeJusqu’à 2 200 caractères Ajoutés au texte, jusqu’à 30 JPEG; jusqu’à 8 MB
Vidéo Aucun titre LégendeJusqu’à 2 200 caractères Ajoutés au texte, jusqu’à 30 MP4, MOV; jusqu’à 300 MB; 3 s à 15 min
Nécessite un compte Instagram Business ou Creator. Instagram autorise 50 publications via l’API par compte sur une période glissante de 24 heures. Les publications composées uniquement de texte ne sont pas possibles sur Instagram.
TikTok Image TitreJusqu’à 90 caractères DescriptionJusqu’à 4 000 caractères Ajoutés au texte JPEG, WebP; jusqu’à 20 MB
Vidéo Aucun titre LégendeJusqu’à 2 200 caractères Ajoutés au texte MP4, MOV, WebM; jusqu’à 4 GB; 3 s à 10 min
Les publications des applications n’ayant pas passé l’audit de TikTok sont visibles uniquement par leur créateur. TikTok choisit la couverture parmi les images de la vidéo ; les images de couverture personnalisées ne sont pas prises en charge. Les publications composées uniquement de texte ne sont pas possibles sur TikTok.
X Texte Aucun titre PublicationObligatoire, jusqu’à 280 (23 pour un lien, 2 pour un emoji) Ajoutés au texte Aucun
Image Aucun titre PublicationJusqu’à 280 (23 pour un lien, 2 pour un emoji) Ajoutés au texte JPEG, PNG, WebP, GIF; jusqu’à 5 MB
Vidéo Aucun titre PublicationJusqu’à 280 (23 pour un lien, 2 pour un emoji) Ajoutés au texte MP4, MOV; jusqu’à 512 MB; 0,5 s à 2:20 min
Avec votre propre application de développement X, les publications consomment ses crédits API facturés à l’usage. Les hashtags comptent dans la limite de caractères.
Facebook Texte Aucun titre PublicationObligatoire, jusqu’à 63 206 caractères Ajoutés au texte Aucun
Image Aucun titre LégendeJusqu’à 63 206 caractères Ajoutés au texte JPEG, PNG, GIF, WebP; jusqu’à 10 MB
Vidéo TitreJusqu’à 255 caractères DescriptionJusqu’à 63 206 caractères Ajoutés au texte MP4, MOV; jusqu’à 1 GB; 1 s à 20 min
Publie uniquement sur les Pages ; l’API ne permet pas de publier sur les profils personnels.
Custom API Texte TitreAucune limite de longueur DescriptionAucune limite de longueur Envoyés comme tags Aucun
Image TitreAucune limite de longueur DescriptionAucune limite de longueur Envoyés comme tags JPEG, PNG, WebP, GIF
Vidéo TitreAucune limite de longueur DescriptionAucune limite de longueur Envoyés comme tags MP4, MOV, WebM
Chaque requête est signée avec un en-tête HMAC-SHA256 pour vous permettre de vérifier qu’elle provient d’Amaposta. Répondez avec un statut 2xx et éventuellement le JSON {"id": "…", "url": "…"}. Les réponses 408, 429 et 5xx entraînent de nouvelles tentatives avec un délai croissant ; les réponses 401/403 signalent un canal à reconnecter ; les autres 4xx font échouer la diffusion pour vous permettre de corriger le problème et de réessayer.
Sandbox Texte TitreJusqu’à 200 caractères DescriptionJusqu’à 5 000 caractères Ajoutés au texte, jusqu’à 30 Aucun
Image TitreJusqu’à 200 caractères DescriptionJusqu’à 5 000 caractères Ajoutés au texte, jusqu’à 30 JPEG, PNG, WebP, GIF
Vidéo TitreJusqu’à 200 caractères DescriptionJusqu’à 5 000 caractères Ajoutés au texte, jusqu’à 30 MP4, MOV, WebM
La publication prend un instant pour vous permettre de suivre les changements de statut en direct.