Démarrer avec l'API REST de Shager : d'une clé toute neuve à une publication suivie sur 40 comptes en sept appels

Visite guidée de l'API REST de Shager en curl et TypeScript : authentification, espaces, canaux, médias, brouillons par compte, programmation, statut et clics.

L'API REST de Shager se trouve à l'adresse https://app.shager.ai/api/v1, s'authentifie avec Authorization: Bearer shgr_live_..., renvoie chaque résultat sous la forme { "data": ... } et chaque erreur sous la forme { "error": { "code", "message" } }. Une diffusion complète tient en sept appels : vérifier la clé, choisir un espace de travail, lister ses canaux, importer le média, créer un brouillon avec une légende par compte, le programmer, puis consulter le statut et les clics. La spécification lisible par machine est le document OpenAPI 3.1.

Tout ce qui suit fonctionne tel quel dès que vous avez défini deux variables d'environnement. Les mêmes opérations existent sous forme d'outils MCP portant les mêmes noms : ce que vous apprenez ici vaut aussi pour Claude Code, Codex ou Cursor.

Que faut-il avant le premier appel ?

Une clé. Soit une personne en crée une sur https://app.shager.ai/dashboard/agents avec les portées nécessaires, soit votre agent crée un environnement de test par inscription autonome. Les portées fonctionnent ainsi :

Portée Autorise
read whoami, la liste des espaces, canaux, groupes et publications, la lecture d'une publication, les statistiques
write La création d'espaces, de groupes, de médias, de brouillons, de liens de connexion, de demandes de validation, et l'annulation
publish La programmation, y compris create_post avec schedule_at
export SHAGER_API_KEY="shgr_live_your_key_here"
export API="https://app.shager.ai/api/v1"
Une requête curl qui crée une publication avec l'API REST de Shager : légende, groupe de comptes, lien suivi et heure de publication.
Les mêmes opérations que le serveur MCP, documentées en OpenAPI.

Appel 1 : qui suis-je ?

curl -s "$API/me" -H "Authorization: Bearer $SHAGER_API_KEY"
{ "data": { "organization_id": "...", "organization": "Northwind Agency", "sandbox": false, "scopes": ["read", "write"] } }

Si sandbox vaut true, la programmation et les liens de connexion sont bloqués tant qu'une personne n'a pas revendiqué la clé. Si publish est absent de scopes, vous pouvez créer des brouillons mais pas les programmer. Le savoir dès le départ vous épargne plus tard une erreur 403 déroutante.

Appel 2 : choisir ou créer un espace de travail

Un espace de travail correspond en général à un client ou à une marque.

curl -s "$API/workspaces" -H "Authorization: Bearer $SHAGER_API_KEY"

curl -s -X POST "$API/workspaces" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Summit Physio", "timezone": "Europe/London", "approval_required": true}'

Définissez toujours timezone (un nom IANA) à la création. Les horaires de programmation sont stockés avec leur décalage, et c'est le fuseau de l'espace qui sert à les afficher aux personnes. approval_required: true signifie que les publications doivent être validées par le client avant de pouvoir être programmées, un bon réglage par défaut pour un nouveau client.

Exportez l'id renvoyé pour que les commandes suivantes fonctionnent :

export WS="the_workspace_id"

Appel 3 : lister les canaux et les groupes

curl -s "$API/workspaces/$WS/channels" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/groups" -H "Authorization: Bearer $SHAGER_API_KEY"

Chaque canal est renvoyé avec id, network, name, handle, status, les formats qu'il prend en charge et connected_by_client. Les groupes renvoient id, name et channel_ids. C'est grâce aux groupes que vous ciblez quarante comptes sans énumérer quarante id : passez group_ids à create_post et tous les canaux actifs du groupe sont inclus.

Si l'espace n'a pas encore de canaux, créez un lien de connexion avec POST /workspaces/{workspace_id}/connect-links et envoyez-le au propriétaire du compte. Il connecte lui-même Facebook, Instagram, YouTube, X, LinkedIn ou TikTok, sans identifiant Shager.

Schéma : brief, brouillon par compte, validation du client, publication étalée et rapport de clics par compte.
Cinq étapes fixes. La validation de chaque publication est facultative selon le client ; pour publier, il faut un propriétaire humain et un droit de publication distinct sur la clé.

Appel 4 : importer un média depuis une URL

curl -s -X POST "$API/workspaces/$WS/media" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "url": "https://cdn.summitphysio.example/video/knee-mobility.mp4",
    "file_name": "knee-mobility.mp4",
    "width": 1080, "height": 1920, "duration_ms": 42000
  }'

Shager télécharge le fichier côté serveur et renvoie id, kind, mime_type et size_bytes. Pour les Reels, les Shorts et les stories, transmettez width, height et duration_ms si vous les connaissez. Shager peut ainsi vérifier les règles de format de chaque réseau avant la programmation, au lieu d'échouer au moment de l'envoi.

Appel 5 : créer un brouillon avec une légende par compte

C'est l'appel qui compte. Le voici en TypeScript avec un simple fetch, accompagné d'une petite fonction utilitaire que vous réutiliserez :

const API = "https://app.shager.ai/api/v1";

async function shager<T>(method: string, path: string, body?: unknown): Promise<T> {
  const res = await fetch(`${API}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.SHAGER_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) {
    const err = new Error(`${res.status} ${json.error.code}: ${json.error.message}`);
    (err as any).status = res.status;
    throw err;
  }
  return json.data as T;
}

const WS = process.env.SHAGER_WORKSPACE_ID!;
const mediaId = process.env.SHAGER_MEDIA_ID!; // the id returned by call 4
const channels = await shager<Array<{ id: string; name: string; network: string; status: string }>>(
  "GET", `/workspaces/${WS}/channels`,
);

const post = await shager<{ id: string; status: string }>("POST", `/workspaces/${WS}/posts`, {
  label: "Knee mobility, week 42",
  caption: "Three minutes a day for stiffer knees. Full routine in the video.",
  channel_ids: channels.map((c) => c.id),
  media_ids: [mediaId],
  format: "REEL",
  youtube_title: "3-minute knee mobility routine",
  youtube_privacy: "public",
  per_channel: channels.map((c) => ({
    channel_id: c.id,
    caption: `${c.name}: three minutes a day for stiffer knees. Our physios show the routine.`,
  })),
  cta_url: "https://summitphysio.example/book",
  cta_text: "Book an assessment",
  campaign: "knee-mobility-oct",
  first_comment: "Book an assessment: https://summitphysio.example/book",
  stagger_seconds: [45, 120],
});

console.log(post.id, post.status); // DRAFT

Ce que vous apporte chaque champ :

  1. per_channel donne à chaque compte sa propre légende. En pratique, c'est un agent qui les rédige ; le modèle ci-dessus tient la place de cette étape, et le raisonnement est expliqué dans comment éviter les blocages pour spam à grande échelle.
  2. format s'applique là où le réseau le prend en charge, et SHORT ou REEL est sinon résolu réseau par réseau. youtube_title et youtube_privacy ne concernent que les canaux YouTube.
  3. cta_url génère un lien court et un jeu d'UTM distincts pour chaque compte, avec un utm_content qui identifie le compte. C'est la base de l'attribution des prospects par compte.
  4. stagger_seconds définit une pause aléatoire entre les comptes d'un même réseau. La valeur par défaut est [30, 90].

Sans schedule_at, le résultat est un brouillon. Gardez son id pour les appels suivants avec export POST_ID="the_post_id".

Appel 6 : validation ou programmation

Si l'espace exige une validation, envoyez la publication au client. La réponse contient une approval_url que le relecteur ouvre sans se connecter :

curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/approval" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"reviewer_name": "Dana Ellis", "reviewer_email": "dana@summitphysio.example", "send_email": true}'

Une fois la publication validée (ou si la validation n'est pas requise), programmez-la. Il faut pour cela la portée publish, et schedule_at doit être au format ISO 8601 avec un décalage :

curl -s -X POST "$API/workspaces/$WS/posts/$POST_ID/schedule" \
  -H "Authorization: Bearer $SHAGER_API_KEY" -H "Content-Type: application/json" \
  -d '{"schedule_at": "2026-10-20T08:30:00+01:00"}'

À cette étape, Shager vérifie chaque destination par rapport aux règles de son réseau. Si un compte risque de rejeter la publication, vous recevez une erreur 422 rejected avec la raison, et rien n'est mis en file d'attente.

Appel 7 : consulter le statut et les clics

curl -s "$API/workspaces/$WS/posts/$POST_ID" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/posts?status=SCHEDULED&limit=50" -H "Authorization: Bearer $SHAGER_API_KEY"
curl -s "$API/workspaces/$WS/analytics?from=2026-10-01T00:00:00Z&to=2026-10-31T23:59:59Z" \
  -H "Authorization: Bearer $SHAGER_API_KEY"

get_post renvoie une ligne par destination avec state, dispatch_at, published_at, l'url en ligne et l'éventuelle error. Les statistiques renvoient total_clicks, by_account et by_post, en comptant les clics sur les liens suivis de Shager, trafic des robots exclu. Pour annuler une publication programmée dont la diffusion n'a pas commencé : POST /workspaces/{workspace_id}/posts/{post_id}/cancel.

Quelles erreurs votre code doit-il gérer ?

Statut et code Signification Que faire
401 unauthorized En-tête absent ou mal formé Envoyer Authorization: Bearer shgr_...
403 forbidden La clé n'a pas la portée nécessaire Créer un brouillon à la place, ou demander publish à une personne
403 sandbox_unclaimed Clé d'inscription autonome non revendiquée Transmettre le lien de revendication à une personne
404 not_found Mauvais id d'espace, de publication ou de canal Relister ; les id sont propres à chaque espace
409 not_scheduled Annulation d'une publication non programmée Lire d'abord le statut de la publication
422 rejected Une règle d'un réseau n'est pas respectée Afficher le message ; il nomme le problème
429 rate_limited Plus de 120 requêtes par minute et par clé Ralentir et réessayer

La limite de débit gêne rarement un client bien conçu. Un seul create_post couvre des centaines de comptes : vous ne devriez pas avoir besoin d'un appel par compte.

FAQ

Existe-t-il un SDK ?

Pas de SDK dédié. Le document OpenAPI 3.1 fonctionne avec les générateurs standard si vous voulez des clients typés, et l'API est assez compacte pour qu'une fonction utilitaire comme celle ci-dessus suffise en général. Une CLI Shager est en préparation.

REST et MCP, c'est vraiment la même chose ?

Oui. Chaque outil MCP correspond à une opération REST, avec le même nom, les mêmes paramètres, les mêmes portées, la même limite de débit et la même journalisation d'audit. Utilisez REST depuis vos scripts et services, et MCP depuis les clients d'agents. La liste complète des outils se trouve sur la page des agents.

Comment publier un texte différent sur Facebook et sur YouTube dans la même publication ?

Utilisez per_channel avec l'id et la légende de chaque canal, et youtube_title pour le titre YouTube. Un seul objet publication, de nombreuses destinations, chacune avec sa légende, son format et, si besoin, son premier commentaire.

Prochaine étape

Quand cette séquence tourne sans accroc à la main, transformez-la en quelque chose qu'un agent peut répéter. Créer une skill Claude pour Shager montre comment regrouper ces appels et les règles qui les encadrent dans une skill réutilisable.

Essayez sur vos propres comptes.

Gratuit pendant le lancement, sans carte. Connectez, écrivez une fois, et Shager publie et mesure.