Sommaire de la référence

Référence API

Référence de l'API REST

Authentification, limites, pagination, idempotence, erreurs, puis chaque route de l'API REST de Synkro, avec des exemples curl, JavaScript et Python. L'API demande l'offre Essentiel.

Format et authentification

Adresse de base : https://integrations.mysynkro.fr/v1. L'API demande l'offre Essentiel.

Authentification : Clé d'API de l'intégration (syk_key_…), dans l'en-tête Authorization: Bearer …, jamais dans l'adresse.

  • JSON en UTF-8, champs en snake_case, dates ISO 8601 en UTC. Corps de requête de 1 Mio au plus.
  • Aucune réponse CORS : une requête qui porte un en-tête Origin reçoit 403. La clé reste côté serveur.
  • /v1 ne reçoit que des ajouts de champs, de routes et de valeurs d'énumération : ignore les champs que tu ne connais pas.
  • Un chemin inconnu répond 404 not_found, une méthode non prévue 405 method_not_allowed, avec l'en-tête Allow.
  • Chaque réponse porte Synkro-Request-Id : donne-le au support en cas de problème.

Le document complet, au format OpenAPI 3.1.0 : openapi.json.

Version 1.0.0

Clés d'API

Un propriétaire ou un administrateur de l'organisation crée une clé dans Administration › Intégrations, puis « Nouvelle clé d'API ». La clé commence par syk_key_ et n'est affichée qu'une seule fois, à la création : copie-la tout de suite, Synkro n'en garde que l'empreinte.

  • Portées : read (lecture seule) ou read et write (lecture et écriture). Une clé n'a jamais accès aux messages privés.
  • Canaux : tu choisis les canaux ouverts à la clé. Les canaux confidentiels ne peuvent pas lui être ouverts.
  • Expiration : sans expiration (par défaut), 1 an ou 90 jours. Une organisation a 20 clés actives au plus.
  • Révocation : un administrateur révoque une clé à tout moment, et elle cesse de fonctionner tout de suite.

Une clé reste côté serveur : jamais dans un navigateur, une application mobile ou un dépôt de code. Synkro refuse toute requête de l'API qui porte un en-tête Origin (réponse 403).

Limites de débit

120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).

En-têtes

En-têteDescription
Synkro-Request-IdIdentifiant de la requête (req_…), repris dans les erreurs et les journaux.
RateLimit-LimitNombre de requêtes par minute permis à l'intégration (120).
RateLimit-RemainingJetons restants dans le seau de l'intégration.
RateLimit-ResetSecondes avant le prochain jeton.
Retry-AfterSecondes à attendre avant de réessayer (réponse 429).
Idempotent-Replayedtrue quand la réponse est celle d'un premier appel avec la même Idempotency-Key ; rien n'a été recréé.
WWW-AuthenticateBearer realm="synkro-api", avec error="invalid_token" si un jeton était présent.

Pagination

ParamètreTypeRoutesDescription
limitintegerGET /channels, GET /channels/{channel_id}/tickets, GET /tickets/{key}/commentsTaille de la page : 20 par défaut, 100 au plus.
limitintegerGET /channels/{channel_id}/messagesTaille de la page : 20 par défaut, 50 au plus.
cursorstringGET /channels, GET /channels/{channel_id}/messages, GET /channels/{channel_id}/tickets, GET /tickets/{key}/commentsCurseur next_cursor de la page précédente. Opaque, propre à la route qui l'a émis : un curseur invalide ou venu d'une autre route répond 400 validation.

Idempotence

Clé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.

Erreurs

Corps de toute réponse d'erreur.

ChampTypeDescription
codevalidation | unauthenticated | forbidden | plan_required | org_read_only | org_suspended | not_found | method_not_allowed | conflict | payload_too_large | limit_reached | rate_limited | internalCode stable, à tester côté client.
messagestringMessage en français, destiné à une personne.
request_idstringIdentifiant de la requête, repris dans l'en-tête Synkro-Request-Id.
appliedstring[]Seulement dans la réponse d'erreur de PATCH /tickets/{key} quand des champs étaient déjà appliqués : leur liste, dans l'ordre.
CodeHTTPSens
validation400JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
validation422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
unauthenticated401jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
forbidden403intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
plan_required403intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
org_read_only403intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
org_suspended403intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
not_found404ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
method_not_allowed405Un chemin inconnu répond 404 not_found, une méthode non prévue sur un chemin connu 405 method_not_allowed avec l'en-tête Allow.
conflict409une opération est déjà en cours avec cette Idempotency-Key.
payload_too_large413corps de plus de 1 Mio.
limit_reached422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
rate_limited429120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
internal500erreur inattendue ; donne request_id au support.

Routes

Intégration

L'intégration de la clé

GET/me

Renvoie l'intégration à laquelle la clé appartient, ses portées, ses canaux ouverts et son titulaire. Sert à vérifier une clé.

Réponses
CodeDescription
200L'intégration.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/me" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch("https://integrations.mysynkro.fr/v1/me", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    "https://integrations.mysynkro.fr/v1/me",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "id": "n57a1b2c3",
  "name": "Script de facturation",
  "organization": {
    "id": "o57m3n4p5",
    "slug": "nova",
    "name": "Nova Studio"
  },
  "scopes": [
    "read",
    "write"
  ],
  "channels": [
    {
      "id": "j57z9q4t1",
      "name": "client-acme"
    }
  ],
  "owner": {
    "name": "Léa Martin"
  }
}

Ce document

GET/openapi.json

Le document OpenAPI de l'API, sans authentification.

Réponses
CodeDescription
200Le document OpenAPI 3.1.
403forbidden : la requête porte un en-tête Origin (la clé n'a rien à faire dans un navigateur).
413payload_too_large : corps de plus de 1 Mio.
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/openapi.json"
const response = await fetch("https://integrations.mysynkro.fr/v1/openapi.json", {
  method: "GET",
});
const data = await response.json();
import requests

response = requests.get(
    "https://integrations.mysynkro.fr/v1/openapi.json",
)
data = response.json()

Réponse · 200

Le document OpenAPI 3.1.

Canaux et messages

Lister les canaux ouverts

GET/channels

Les canaux ouverts à la clé, dans l'ordre de la barre latérale.

Paramètres
NomOùTypeDescription
limitrequêteintegerTaille de la page : 20 par défaut, 100 au plus.
cursorrequêtestringCurseur next_cursor de la page précédente. Opaque, propre à la route qui l'a émis : un curseur invalide ou venu d'une autre route répond 400 validation.
Réponses
CodeDescription
200Une page de canaux.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/channels" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch("https://integrations.mysynkro.fr/v1/channels", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    "https://integrations.mysynkro.fr/v1/channels",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "j57z9q4t1",
      "name": "client-acme",
      "visibility": "public",
      "project_key": "ACME",
      "archived": false
    }
  ],
  "next_cursor": null
}

Lire un canal

GET/channels/{channel_id}

Un canal ouvert à la clé.

Paramètres
NomOùTypeDescription
channel_idrequischeminstringIdentifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404).
Réponses
CodeDescription
200Le canal.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/channels/$CHANNEL_ID" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/channels/${channelId}`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/channels/{channel_id}",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "id": "j57z9q4t1",
  "name": "client-acme",
  "visibility": "public",
  "project_key": "ACME",
  "archived": false
}

Lister les personnes d'un canal

GET/channels/{channel_id}/members

Les personnes visibles dans le canal, avec le jeton mention qui les notifie. Une seule page : next_cursor est toujours null.

Paramètres
NomOùTypeDescription
channel_idrequischeminstringIdentifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404).
Réponses
CodeDescription
200Les personnes du canal.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/channels/$CHANNEL_ID/members" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/channels/${channelId}/members`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/channels/{channel_id}/members",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "k5712f3x9",
      "name": "Léa Martin",
      "external": false,
      "mention": "<@k5712f3x9>"
    }
  ],
  "next_cursor": null
}

Lister les membres internes

GET/members

Les membres internes actifs de l'organisation, avec leur e-mail, pour faire correspondre un compte d'un autre outil à une personne de Synkro. Jamais une personne externe. Une seule page : next_cursor est toujours null.

Réponses
CodeDescription
200Les membres internes.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/members" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch("https://integrations.mysynkro.fr/v1/members", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    "https://integrations.mysynkro.fr/v1/members",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "k5712f3x9",
      "name": "Léa Martin",
      "email": "lea@nova.example",
      "role": "member"
    }
  ],
  "next_cursor": null
}

Lister les messages d'un canal

GET/channels/{channel_id}/messages

Les messages, du plus récent au plus ancien. around ancre la page sur un message.

Paramètres
NomOùTypeDescription
channel_idrequischeminstringIdentifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404).
aroundrequêtestringIdentifiant d'un message autour duquel lire.
limitrequêteintegerTaille de la page : 20 par défaut, 50 au plus.
cursorrequêtestringCurseur next_cursor de la page précédente. Opaque, propre à la route qui l'a émis : un curseur invalide ou venu d'une autre route répond 400 validation.
Réponses
CodeDescription
200Une page de messages.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/channels/$CHANNEL_ID/messages" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/channels/${channelId}/messages`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/channels/{channel_id}/messages",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "k17c0m8s2",
      "channel_id": "j57z9q4t1",
      "text": "Facture envoyée.",
      "author": {
        "type": "app",
        "id": "n57a1b2c3",
        "name": "Script de facturation"
      },
      "reply_to_id": null,
      "attachments": [
        {
          "id": "f57d0c1e5",
          "name": "devis.pdf",
          "mime_type": "application/pdf",
          "size": 48213
        }
      ],
      "created_at": "2026-10-09T14:03:00.000Z",
      "edited_at": null,
      "url": "https://app.mysynkro.fr/nova/c/j57z9q4t1?message=k17c0m8s2"
    }
  ],
  "next_cursor": null
}

Publier un message

POST/channels/{channel_id}/messages

Publie un message au nom de l'intégration dans un canal ouvert à la clé. Portée write.

Paramètres
NomOùTypeDescription
channel_idrequischeminstringIdentifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404).
Idempotency-Keyen-têtestringClé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.
Corps
ChampTypeDescription
textrequisstringTexte en Markdown, 4 000 caractères au plus ; mentions <@id> des personnes visibles dans le canal ; @canal refusé.
reply_to_idstring | nullMessage auquel répondre.
Réponses
CodeDescription
201Le message créé.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
409conflict : une opération est déjà en cours avec cette Idempotency-Key.
413payload_too_large : corps de plus de 1 Mio.
422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X POST "https://integrations.mysynkro.fr/v1/channels/$CHANNEL_ID/messages" \
  -H "Authorization: Bearer $SYNKRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Facture envoyée.",
  "reply_to_id": null
}'
const response = await fetch(`https://integrations.mysynkro.fr/v1/channels/${channelId}/messages`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "text": "Facture envoyée.",
    "reply_to_id": null
  }),
});
const data = await response.json();
import os

import requests

response = requests.post(
    f"https://integrations.mysynkro.fr/v1/channels/{channel_id}/messages",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
    json={
        "text": "Facture envoyée.",
        "reply_to_id": None,
    },
)
data = response.json()

Réponse · 201

{
  "id": "k17c0m8s2",
  "channel_id": "j57z9q4t1",
  "text": "Facture envoyée.",
  "author": {
    "type": "app",
    "id": "n57a1b2c3",
    "name": "Script de facturation"
  },
  "reply_to_id": null,
  "attachments": [
    {
      "id": "f57d0c1e5",
      "name": "devis.pdf",
      "mime_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_at": "2026-10-09T14:03:00.000Z",
  "edited_at": null,
  "url": "https://app.mysynkro.fr/nova/c/j57z9q4t1?message=k17c0m8s2"
}

Tickets

Lister les tickets d'un projet

GET/channels/{channel_id}/tickets

Les tickets du projet du canal, au format complet. Les filtres se cumulent.

Paramètres
NomOùTypeDescription
channel_idrequischeminstringIdentifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404).
statusrequêtetodo | in_progress | in_review | doneStatut du ticket.
assignee_idrequêtestringPersonne assignée.
sprint_idrequêtestringSprint.
limitrequêteintegerTaille de la page : 20 par défaut, 100 au plus.
cursorrequêtestringCurseur next_cursor de la page précédente. Opaque, propre à la route qui l'a émis : un curseur invalide ou venu d'une autre route répond 400 validation.
Réponses
CodeDescription
200Une page de tickets.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/channels/$CHANNEL_ID/tickets" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/channels/${channelId}/tickets`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/channels/{channel_id}/tickets",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "t57e8w2r4",
      "key": "ACME-42",
      "channel_id": "j57z9q4t1",
      "title": "Corriger la facture 2026-118",
      "type": "task",
      "status": "todo",
      "priority": "medium",
      "assignee": {
        "id": "k5712f3x9",
        "name": "Léa Martin"
      },
      "reporter": {
        "type": "app",
        "id": "n57a1b2c3",
        "name": "Script de facturation"
      },
      "sprint_id": null,
      "due_date": "2026-10-20",
      "labels": [
        "facturation"
      ],
      "description": "Le montant HT est faux.",
      "created_at": "2026-10-09T14:03:00.000Z",
      "updated_at": "2026-10-09T14:03:00.000Z",
      "url": "https://app.mysynkro.fr/nova/t/ACME-42"
    }
  ],
  "next_cursor": null
}

Créer un ticket

POST/channels/{channel_id}/tickets

Crée un ticket dans le projet du canal, au nom de l'intégration. Portée write.

Paramètres
NomOùTypeDescription
channel_idrequischeminstringIdentifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404).
Idempotency-Keyen-têtestringClé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.
Corps
ChampTypeDescription
titlerequisstringTitre.
typeepic | story | task | bug | subtaskType ; task par défaut.
prioritylow | medium | high | criticalPriorité.
assignee_idstringPersonne assignée.
descriptionstringDescription en Markdown.
due_datestring (date)Échéance, AAAA-MM-JJ.
labelsstring[]
Réponses
CodeDescription
201Le ticket créé.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
409conflict : une opération est déjà en cours avec cette Idempotency-Key.
413payload_too_large : corps de plus de 1 Mio.
422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X POST "https://integrations.mysynkro.fr/v1/channels/$CHANNEL_ID/tickets" \
  -H "Authorization: Bearer $SYNKRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Corriger la facture 2026-118",
  "type": "task",
  "priority": "medium",
  "assignee_id": "k5712f3x9",
  "description": "Le montant HT est faux.",
  "due_date": "2026-10-20",
  "labels": [
    "facturation"
  ]
}'
const response = await fetch(`https://integrations.mysynkro.fr/v1/channels/${channelId}/tickets`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "title": "Corriger la facture 2026-118",
    "type": "task",
    "priority": "medium",
    "assignee_id": "k5712f3x9",
    "description": "Le montant HT est faux.",
    "due_date": "2026-10-20",
    "labels": [
      "facturation"
    ]
  }),
});
const data = await response.json();
import os

import requests

response = requests.post(
    f"https://integrations.mysynkro.fr/v1/channels/{channel_id}/tickets",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
    json={
        "title": "Corriger la facture 2026-118",
        "type": "task",
        "priority": "medium",
        "assignee_id": "k5712f3x9",
        "description": "Le montant HT est faux.",
        "due_date": "2026-10-20",
        "labels": [
            "facturation",
        ],
    },
)
data = response.json()

Réponse · 201

{
  "id": "t57e8w2r4",
  "key": "ACME-42",
  "channel_id": "j57z9q4t1",
  "title": "Corriger la facture 2026-118",
  "type": "task",
  "status": "todo",
  "priority": "medium",
  "assignee": {
    "id": "k5712f3x9",
    "name": "Léa Martin"
  },
  "reporter": {
    "type": "app",
    "id": "n57a1b2c3",
    "name": "Script de facturation"
  },
  "sprint_id": null,
  "due_date": "2026-10-20",
  "labels": [
    "facturation"
  ],
  "description": "Le montant HT est faux.",
  "created_at": "2026-10-09T14:03:00.000Z",
  "updated_at": "2026-10-09T14:03:00.000Z",
  "url": "https://app.mysynkro.fr/nova/t/ACME-42"
}

Lire un ticket

GET/tickets/{key}

Le détail d'un ticket, par sa clé.

Paramètres
NomOùTypeDescription
keyrequischeminstringClé du ticket (ACME-42).
Réponses
CodeDescription
200Le ticket.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/tickets/$KEY" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/tickets/${key}`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/tickets/{key}",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "id": "t57e8w2r4",
  "key": "ACME-42",
  "channel_id": "j57z9q4t1",
  "title": "Corriger la facture 2026-118",
  "type": "task",
  "status": "todo",
  "priority": "medium",
  "assignee": {
    "id": "k5712f3x9",
    "name": "Léa Martin"
  },
  "reporter": {
    "type": "app",
    "id": "n57a1b2c3",
    "name": "Script de facturation"
  },
  "sprint_id": null,
  "due_date": "2026-10-20",
  "labels": [
    "facturation"
  ],
  "description": "Le montant HT est faux.",
  "created_at": "2026-10-09T14:03:00.000Z",
  "updated_at": "2026-10-09T14:03:00.000Z",
  "url": "https://app.mysynkro.fr/nova/t/ACME-42"
}

Modifier un ticket

PATCH/tickets/{key}

Modifie au moins un champ du ticket. Les champs sont appliqués dans l'ordre du corps ; si l'un est refusé après d'autres, la réponse d'erreur porte en plus applied, la liste des champs déjà appliqués. Portée write.

Paramètres
NomOùTypeDescription
keyrequischeminstringClé du ticket (ACME-42).
Idempotency-Keyen-têtestringClé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.

Au moins un champ. Les champs sont appliqués dans l'ordre du corps ; à la première erreur, la réponse dit ce qui a déjà été appliqué (applied).

Corps
ChampTypeDescription
statustodo | in_progress | in_review | doneNouveau statut.
assignee_idstring | nullNouvelle personne assignée ; null pour retirer l'assignation.
prioritylow | medium | high | criticalNouvelle priorité.
titlestringNouveau titre.
descriptionstring | nullNouvelle description ; null pour l'effacer.
sprint_idstring | nullNouveau sprint ; null pour retirer le ticket du sprint.
Réponses
CodeDescription
200Le ticket modifié et les champs appliqués.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
409conflict : une opération est déjà en cours avec cette Idempotency-Key.
413payload_too_large : corps de plus de 1 Mio.
422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X PATCH "https://integrations.mysynkro.fr/v1/tickets/$KEY" \
  -H "Authorization: Bearer $SYNKRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "in_progress",
  "assignee_id": "k5712f3x9"
}'
const response = await fetch(`https://integrations.mysynkro.fr/v1/tickets/${key}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "status": "in_progress",
    "assignee_id": "k5712f3x9"
  }),
});
const data = await response.json();
import os

import requests

response = requests.patch(
    f"https://integrations.mysynkro.fr/v1/tickets/{key}",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
    json={
        "status": "in_progress",
        "assignee_id": "k5712f3x9",
    },
)
data = response.json()

Réponse · 200

{
  "ticket": {
    "id": "t57e8w2r4",
    "key": "ACME-42",
    "channel_id": "j57z9q4t1",
    "title": "Corriger la facture 2026-118",
    "type": "task",
    "status": "todo",
    "priority": "medium",
    "assignee": {
      "id": "k5712f3x9",
      "name": "Léa Martin"
    },
    "reporter": {
      "type": "app",
      "id": "n57a1b2c3",
      "name": "Script de facturation"
    },
    "sprint_id": null,
    "due_date": "2026-10-20",
    "labels": [
      "facturation"
    ],
    "description": "Le montant HT est faux.",
    "created_at": "2026-10-09T14:03:00.000Z",
    "updated_at": "2026-10-09T14:03:00.000Z",
    "url": "https://app.mysynkro.fr/nova/t/ACME-42"
  },
  "applied": [
    "status",
    "assignee_id"
  ]
}

Lister les commentaires d'un ticket

GET/tickets/{key}/comments

Les commentaires, du plus ancien au plus récent. La lecture est bornée à 1 000 commentaires : au-delà, les plus anciens manquent (seuls les plus récents sont listés) et next_cursor vaut null à la fin de la liste tronquée.

Paramètres
NomOùTypeDescription
keyrequischeminstringClé du ticket (ACME-42).
limitrequêteintegerTaille de la page : 20 par défaut, 100 au plus.
cursorrequêtestringCurseur next_cursor de la page précédente. Opaque, propre à la route qui l'a émis : un curseur invalide ou venu d'une autre route répond 400 validation.
Réponses
CodeDescription
200Une page de commentaires.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/tickets/$KEY/comments" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/tickets/${key}/comments`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/tickets/{key}/comments",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "c57h4v7b1",
      "ticket_key": "ACME-42",
      "text": "Corrigé dans la version 2.",
      "author": {
        "type": "person",
        "id": "k5712f3x9",
        "name": "Léa Martin",
        "external": false
      },
      "created_at": "2026-10-09T14:03:00.000Z",
      "edited_at": null
    }
  ],
  "next_cursor": null
}

Commenter un ticket

POST/tickets/{key}/comments

Ajoute un commentaire au nom de l'intégration. Portée write.

Paramètres
NomOùTypeDescription
keyrequischeminstringClé du ticket (ACME-42).
Idempotency-Keyen-têtestringClé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.
Corps
ChampTypeDescription
textrequisstringTexte en Markdown.
Réponses
CodeDescription
201Le commentaire créé.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
409conflict : une opération est déjà en cours avec cette Idempotency-Key.
413payload_too_large : corps de plus de 1 Mio.
422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X POST "https://integrations.mysynkro.fr/v1/tickets/$KEY/comments" \
  -H "Authorization: Bearer $SYNKRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Corrigé dans la version 2."
}'
const response = await fetch(`https://integrations.mysynkro.fr/v1/tickets/${key}/comments`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "text": "Corrigé dans la version 2."
  }),
});
const data = await response.json();
import os

import requests

response = requests.post(
    f"https://integrations.mysynkro.fr/v1/tickets/{key}/comments",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
    json={
        "text": "Corrigé dans la version 2.",
    },
)
data = response.json()

Réponse · 201

{
  "id": "c57h4v7b1",
  "ticket_key": "ACME-42",
  "text": "Corrigé dans la version 2.",
  "author": {
    "type": "person",
    "id": "k5712f3x9",
    "name": "Léa Martin",
    "external": false
  },
  "created_at": "2026-10-09T14:03:00.000Z",
  "edited_at": null
}

Abonnements

Lister les abonnements

GET/webhooks

Les abonnements REST Hooks de cette intégration (10 au plus), du plus récent au plus ancien, sans secret. Une seule page : next_cursor est toujours null.

Réponses
CodeDescription
200Les abonnements.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/webhooks" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch("https://integrations.mysynkro.fr/v1/webhooks", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    "https://integrations.mysynkro.fr/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "data": [
    {
      "id": "w57q2e9d6",
      "url": "https://ton-serveur.fr/synkro",
      "events": [
        "ticket.created"
      ],
      "channel_ids": [
        "j57z9q4t1"
      ],
      "status": "active",
      "created_at": "2026-10-09T14:03:00.000Z"
    }
  ],
  "next_cursor": null
}

Créer un abonnement

POST/webhooks

Crée un abonnement REST Hooks : une destination sortante de l'intégration, avec les mêmes événements, la même signature et les mêmes relances que les webhooks sortants de l'app. Les canaux demandés doivent être ouverts à la clé et lisibles par son titulaire ; sinon 404. 10 abonnements au plus par intégration (422 limit_reached). Portée write. Avec Idempotency-Key, un rejeu renvoie le même abonnement sans secret, avec Idempotent-Replayed: true : garde le secret de la première réponse, Synkro ne le conserve pas pour le rejeu.

Paramètres
NomOùTypeDescription
Idempotency-Keyen-têtestringClé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.
Corps
ChampTypeDescription
urlrequisstring (uri)Adresse HTTPS qui reçoit les événements ; elle doit être joignable depuis Synkro.
eventsrequisEventType[]Types d'événements demandés.
channel_idsstring[]Canaux suivis, parmi ceux de la clé ; absent : tous les canaux ouverts.
Réponses
CodeDescription
201L'abonnement créé, avec son secret (rendu cette seule fois ; absent d'un rejeu).
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
409conflict : une opération est déjà en cours avec cette Idempotency-Key.
413payload_too_large : corps de plus de 1 Mio.
422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X POST "https://integrations.mysynkro.fr/v1/webhooks" \
  -H "Authorization: Bearer $SYNKRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://ton-serveur.fr/synkro",
  "events": [
    "ticket.created"
  ],
  "channel_ids": [
    "j57z9q4t1"
  ]
}'
const response = await fetch("https://integrations.mysynkro.fr/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "url": "https://ton-serveur.fr/synkro",
    "events": [
      "ticket.created"
    ],
    "channel_ids": [
      "j57z9q4t1"
    ]
  }),
});
const data = await response.json();
import os

import requests

response = requests.post(
    "https://integrations.mysynkro.fr/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
    json={
        "url": "https://ton-serveur.fr/synkro",
        "events": [
            "ticket.created",
        ],
        "channel_ids": [
            "j57z9q4t1",
        ],
    },
)
data = response.json()

Réponse · 201

{
  "id": "w57q2e9d6",
  "url": "https://ton-serveur.fr/synkro",
  "events": [
    "ticket.created"
  ],
  "channel_ids": [
    "j57z9q4t1"
  ],
  "secret": "whsec_<ton secret>",
  "created_at": "2026-10-09T14:03:00.000Z"
}

Lire un abonnement

GET/webhooks/{id}

Un abonnement de cette intégration, sans secret. Un abonnement d'une autre intégration répond 404.

Paramètres
NomOùTypeDescription
idrequischeminstringIdentifiant d'un abonnement de cette intégration.
Réponses
CodeDescription
200L'abonnement.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl "https://integrations.mysynkro.fr/v1/webhooks/$ID" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/webhooks/${id}`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.get(
    f"https://integrations.mysynkro.fr/v1/webhooks/{id}",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "id": "w57q2e9d6",
  "url": "https://ton-serveur.fr/synkro",
  "events": [
    "ticket.created"
  ],
  "channel_ids": [
    "j57z9q4t1"
  ],
  "status": "active",
  "created_at": "2026-10-09T14:03:00.000Z"
}

Supprimer un abonnement

DEL/webhooks/{id}

Supprime un abonnement de cette intégration. Portée write.

Paramètres
NomOùTypeDescription
idrequischeminstringIdentifiant d'un abonnement de cette intégration.
Réponses
CodeDescription
204L'abonnement est supprimé.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
413payload_too_large : corps de plus de 1 Mio.
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X DELETE "https://integrations.mysynkro.fr/v1/webhooks/$ID" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/webhooks/${id}`, {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
console.log(response.status);
import os

import requests

response = requests.delete(
    f"https://integrations.mysynkro.fr/v1/webhooks/{id}",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
print(response.status_code)

Réponse · 204

L'abonnement est supprimé.

Tester un abonnement

POST/webhooks/{id}/test

Envoie un événement ping signé à cet abonnement seul. Portée write. Rejouée avec la même Idempotency-Key, la route renvoie le premier résultat sans envoyer un second ping.

Paramètres
NomOùTypeDescription
idrequischeminstringIdentifiant d'un abonnement de cette intégration.
Idempotency-Keyen-têtestringClé d'idempotence (1 à 128 caractères imprimables) : le même appel rejoué dans les 24 h renvoie la première réponse avec Idempotent-Replayed: true. Même clé encore en cours : 409 conflict ; même clé pour une autre opération : 422 validation. Absente : pas d'idempotence.
Réponses
CodeDescription
200Le résultat de l'envoi.
400validation : JSON invalide, champ manquant, en trop ou mal typé, paramètre hors limites, curseur invalide, ou valeur refusée par une règle (texte trop long, adresse non joignable). Une requête mal formée ne consomme pas le quota.
401unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée.
403forbidden, plan_required, org_read_only ou org_suspended : intégration désactivée ou suspendue, titulaire perdu, portée absente, offre insuffisante, organisation en lecture seule ou suspendue, ou requête portant un en-tête Origin (la clé n'a rien à faire dans un navigateur).
404not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables.
409conflict : une opération est déjà en cours avec cette Idempotency-Key.
413payload_too_large : corps de plus de 1 Mio.
422validation (clé d'idempotence déjà employée pour une autre opération) ou limit_reached (limite de l'offre atteinte, par exemple 10 abonnements par intégration).
429rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons).
500internal : erreur inattendue ; donne request_id au support.
curl -X POST "https://integrations.mysynkro.fr/v1/webhooks/$ID/test" \
  -H "Authorization: Bearer $SYNKRO_API_KEY"
const response = await fetch(`https://integrations.mysynkro.fr/v1/webhooks/${id}/test`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SYNKRO_API_KEY}`,
  },
});
const data = await response.json();
import os

import requests

response = requests.post(
    f"https://integrations.mysynkro.fr/v1/webhooks/{id}/test",
    headers={"Authorization": f"Bearer {os.environ['SYNKRO_API_KEY']}"},
)
data = response.json()

Réponse · 200

{
  "ok": true,
  "status_code": 200,
  "error": null,
  "duration_ms": 182
}