Publiez et programmez sur 13 réseaux avec une seule API REST. Lisez les statistiques, répondez aux commentaires et DM, et recevez un webhook signé dès qu'un post est publié, échoue ou est validé.
Authorization: Bearer YOUR_API_KEY {"content":"Our autumn menu lands on Friday.","social_profile_ids":[101,102],"publish_time":"2026-11-13T09:00","timezone":"America/New_York","status":"scheduled"}
{"id":18342,"status":"scheduled","publish_time":"2026-11-13T09:00:00-05:00","timezone":"America/New_York","social_profiles":[{"name":"Northwind Instagram","platform":"instagram","status":"pending"},{"name":"Northwind LinkedIn","platform":"linkedin","status":"pending"}]}
X-RobinReach-Event: post.published X-RobinReach-Signature: t=1794150000,v1=5257a869e7… {"id":"evt_1c626cf6bcb5","type":"post.published","data":{"post":{"id":18342,"status":"published","summary":{"total":2,"published":2,"failed":0}}}}
Un exemple d'échange avec des identifiants inventés. Les webhooks arrivent sous forme de POST signé sur votre endpoint HTTPS.
Envoyez le texte, les profils et l'heure. RobinReach vérifie le post selon les règles de chaque réseau, le place dans le calendrier et le publie à l'heure.
GET /api/v1/social_profiles pour obtenir les identifiants des comptes sur lesquels publier.timezone que vous indiquez : 09:00 signifie donc 9h00 pour votre audience.Pas sûr qu'un post passe ? POST /api/v1/posts/validate lance les mêmes vérifications sans rien enregistrer.
curl -X POST https://robinreach.com/api/v1/posts \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Our autumn menu lands on Friday.", "social_profile_ids": [ 101, 102 ], "publish_time": "2026-11-13T09:00", "timezone": "America/New_York", "status": "scheduled" }'
require "net/http" require "json" uri = URI("https://robinreach.com/api/v1/posts") request = Net::HTTP::Post.new(uri, "Authorization" => "Bearer YOUR_API_KEY", "Content-Type" => "application/json") request.body = { content: "Our autumn menu lands on Friday.", social_profile_ids: [101, 102], publish_time: "2026-11-13T09:00", timezone: "America/New_York", status: "scheduled" }.to_json response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) } puts response.body
const response = await fetch("https://robinreach.com/api/v1/posts", { method: "POST", headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ content: "Our autumn menu lands on Friday.", social_profile_ids: [101, 102], publish_time: "2026-11-13T09:00", timezone: "America/New_York", status: "scheduled", }), }); console.log(await response.json());
import requests response = requests.post( "https://robinreach.com/api/v1/posts", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={ "content": "Our autumn menu lands on Friday.", "social_profile_ids": [101, 102], "publish_time": "2026-11-13T09:00", "timezone": "America/New_York", "status": "scheduled", }, timeout=30, ) print(response.json())
L'API couvre le travail que votre équipe fait dans le tableau de bord, pour que votre code puisse le faire aussi.
Créez, validez, publiez tout de suite, reprogrammez et supprimez des posts, avec les options propres à chaque réseau.
/posts/posts/validate/posts/{id}/posts/{id}/publish_now/posts/{id}/rescheduleImportez images, vidéos et PDF, rangez-les dans des dossiers et réutilisez-les dans vos posts.
/attachments/attachments/{id}/folders/folders/{id}Listez les marques et les profils connectés, ainsi que les tableaux Pinterest, l'audio Instagram, les catégories et étiquettes WordPress, les lieux et les groupes de hashtags.
/brands/brands/{id}/social_profiles/social_profiles/{id}/social_profiles/{id}/pinterest_boards/locations/hashtag_groupsL'engagement par profil et par post, vos meilleurs posts, cette semaine face à la précédente, et le calendrier.
/analytics/summary/analytics/top_posts/posts/{id}/analytics/social_profiles/{id}/analytics/calendarLisez les fils de commentaires, les DM Facebook et Instagram et les avis Google de vos profils, et répondez au nom de la marque.
/comment_threads/comment_threads/{id}/replyCréez, mettez en pause et relancez les automatisations Comment to DM, les flux RSS et les tâches de recyclage. Vérifiez un flux avant de l'ajouter et lancez une tâche à la demande.
/comment_automations/comment_automations/{id}/pause/rss_feeds/rss_feeds/validate/rss_feeds/{id}/sync/repurposing_tasks/repurposing_tasks/{id}/runModifiez la page bio de la marque et ses blocs, réorganisez-les, consultez les statistiques de la page et récupérez son QR code.
/bio_page/bio_page/blocks/bio_page/blocks/{id}/bio_page/blocks/reorder/bio_page/analytics/bio_page/qrLes Connect Links permettent aux clients d'ajouter leurs propres comptes. Les Approval Links leur permettent de relire les posts sans se connecter.
/connect_links/connect_links/{id}/approval_links/approval_links/{id}Ajoutez des endpoints, envoyez des événements de test, renouvelez les secrets de signature et consultez le journal des livraisons.
/webhook_endpoints/webhook_endpoints/{id}/webhook_endpoints/{id}/test/webhook_endpoints/{id}/rotate_secret/webhook_endpoints/{id}/enable/webhook_endpoints/{id}/deliveriesAjoutez un endpoint HTTPS et RobinReach envoie un POST signé pour les événements choisis. Alertez votre équipe, synchronisez votre CRM ou répondez à un commentaire ou un DM à la seconde où il arrive.
post.publishedUn post a été publié sur chaque profil sélectionné.post.partially_publishedUn post a été publié sur certains profils et a échoué sur d'autres.post.failedUn post a échoué sur chaque profil sélectionné, avec la raison pour chacun.post.approvedUn post a été validé dans le tableau de bord ou via un Approval Link.post.rejectedUn post a été refusé, avec la note du relecteur.comment.receivedQuelqu'un d'autre a commenté sur un profil connecté. Vos propres réponses ne le déclenchent jamais.review.receivedUn nouvel avis est arrivé sur votre Fiche d'établissement Google.message.receivedQuelqu'un a envoyé un message privé à une Page Facebook ou un compte Instagram connecté.Jusqu'à 10 endpoints par marque. Avec la clé principale, un endpoint sur votre marque par défaut peut écouter toutes les marques à la fois.
{
"id": "evt_1c626cf6bcb54613a8053e24d5361527",
"type": "post.published",
"created_at": "2026-11-13T14:00:00Z",
"api_version": "2026-11-01",
"data": {
"brand": {
"id": "northwind",
"name": "Northwind Coffee"
},
"post": {
"id": 18342,
"status": "published",
"content": "Our autumn menu lands on Friday.",
"social_profiles": [
{
"id": 101,
"platform": "instagram",
"status": "published",
"remote_url": "https://www.instagram.com/p/C9xYz12AbCd/"
},
{
"id": 102,
"platform": "linkedin",
"status": "published",
"remote_url": "https://www.linkedin.com/feed/update/urn:li:share:7392018841650188288/"
}
],
"summary": {
"total": 2,
"published": 2,
"failed": 0
}
}
}
}
Chaque requête porte X-RobinReach-Signature: t=…,v1=…, un HMAC-SHA256 de l'horodatage et du corps brut, signé avec le secret de signature de votre endpoint. Vérifiez-le avant de faire confiance au contenu, et rejetez les horodatages de plus de cinq minutes.
class RobinreachWebhooksController < ActionController::API def create body = request.raw_post return head :unauthorized unless valid_signature?(body, request.headers["X-RobinReach-Signature"]) event = JSON.parse(body) # Skip event["id"] if you have seen it, then queue the work. head :ok end private def valid_signature?(body, header, tolerance: 300) parts = header.to_s.split(",").map { |p| p.split("=", 2) }.select { |kv| kv.size == 2 }.to_h t = Integer(parts["t"].to_s, exception: false) return false unless t && parts["v1"] && (Time.now.to_i - t).abs <= tolerance expected = OpenSSL::HMAC.hexdigest("SHA256", ENV.fetch("ROBINREACH_WEBHOOK_SECRET"), "#{t}.#{body}") ActiveSupport::SecurityUtils.secure_compare(expected, parts["v1"]) end end
import crypto from "node:crypto"; import express from "express"; const app = express(); app.post("/webhooks/robinreach", express.raw({ type: "application/json" }), (req, res) => { const raw = req.body.toString("utf8"); if (!verify(raw, req.get("X-RobinReach-Signature"), process.env.ROBINREACH_WEBHOOK_SECRET)) { return res.sendStatus(401); } const event = JSON.parse(raw); // Skip event.id if you have seen it, then queue the work. res.sendStatus(200); }); function verify(rawBody, header, secret, toleranceSec = 300) { const parts = Object.fromEntries((header || "").split(",").map((p) => p.split("=", 2))); const t = Number(parts.t); if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); const a = Buffer.from(expected), b = Buffer.from(parts.v1); return a.length === b.length && crypto.timingSafeEqual(a, b); }
import hashlib, hmac, os, time from flask import Flask, request, abort app = Flask(__name__) @app.post("/webhooks/robinreach") def robinreach_webhook(): raw = request.get_data() if not verify(raw, request.headers.get("X-RobinReach-Signature", ""), os.environ["ROBINREACH_WEBHOOK_SECRET"]): abort(401) event = request.get_json() # Skip event["id"] if you have seen it, then queue the work. return "", 200 def verify(raw_body, header, secret, tolerance=300): parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p) try: t = int(parts["t"]) except (KeyError, ValueError): return False if "v1" not in parts or 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["v1"])
Une livraison échouée est relancée 7 fois sur environ 28 heures, avec des intervalles de plus en plus longs.
Après 10 livraisons échouées d'affilée, l'endpoint se met en pause et l'administrateur de la marque reçoit un e-mail. Relancez-le en un clic.
Chaque événement a un identifiant unique, pour que votre code puisse ignorer le rare doublon.
Chaque tentative, code de statut et corps de réponse est conservé 30 jours, et vous pouvez envoyer un événement de test à tout moment.
https://robinreach.com/api/v1. Tous les chemins de cette page sont relatifs à cette URL.Authorization: Bearer YOUR_API_KEY. Un paramètre api_key dans l'URL fonctionne aussi, mais il peut finir dans les journaux du serveur : préférez l'en-tête.brand_id. Une clé de marque n'atteint qu'une seule marque : vous pouvez la confier sans risque à un client ou à un outil dédié.America/New_York, ou dans celui de la marque si vous n'en indiquez pas.401 pour une clé absente ou erronée, 404 pour tout ce qui sort de la portée de la clé, 422 avec un message clair quand un post enfreint les règles d'un réseau.429 avec les en-têtes Retry-After et X-RateLimit-*, pour que votre client puisse ralentir de lui-même./api/v1, et chaque webhook indique la version de son contenu dans api_version.Chaque réseau garde ses propres options, comme les Reels Instagram, les titres YouTube, les tableaux Pinterest ou les posts document LinkedIn, définies post par post dans platform_options.
Offrez à vos utilisateurs la publication sur 13 réseaux sans construire ni maintenir vous-même l'API de chaque réseau.
Gérez chaque marque cliente depuis un seul compte, avec une clé de marque et ses propres webhooks pour chaque client.
Transformez les nouveaux produits, annonces, articles ou événements en posts programmés dès qu'ils existent.
Envoyez les posts en échec vers Slack, poussez les résultats dans votre CRM et récupérez les statistiques dans vos propres tableaux de bord.
Claude, ChatGPT, Cursor et vos propres agents obtiennent les mêmes actions sous forme d'outils, via une URL et la même clé.
Voir le serveur MCPInstallez n8n-nodes-robinreach et programmez des posts depuis n'importe quel workflow n8n.
Voir sur GitHubAppelez l'API depuis une étape webhook dans Zapier ou un module HTTP dans Make. La page Intégrations a les réglages prêts à copier.
Ouvrir IntégrationsAuthorization: Bearer YOUR_API_KEY. Un paramètre api_key dans l'URL fonctionne aussi, mais l'en-tête garde la clé hors des journaux du serveur.brand_id. Chaque marque peut aussi avoir sa propre clé de marque qui ne fonctionne que pour elle : elle n'a pas besoin de brand_id, ne voit pas vos autres marques et ne peut ni créer ni supprimer de marques.POST /api/v1/posts avec le content, les social_profile_ids, un publish_time en ISO 8601, un timezone comme America/New_York, et status à scheduled. Appelez d'abord POST /api/v1/posts/validate pour le vérifier selon les règles de chaque réseau sans l'enregistrer.t et v1 dans l'en-tête X-RobinReach-Signature, calculez un HMAC-SHA256 de t.raw_body avec votre secret de signature et comparez les deux en temps constant. Rejetez les horodatages de plus de cinq minutes. Les exemples ci-dessus le font en Ruby, Node et Python.GET /api/v1/comment_threads liste les fils de commentaires, les conversations Facebook et Instagram et les avis Google, et POST /api/v1/comment_threads/{id}/reply répond à l'un d'eux au nom de la marque : sous le commentaire, en privé pour un DM, ou publiquement sur Google pour un avis. Associez-le aux webhooks comment.received et message.received pour répondre dès qu'un élément arrive.Générez une clé, programmez un post en une requête et ajoutez un webhook. La référence contient chaque champ.
L'accès à l'API est inclus avec Bloom, Thrive et Scale.