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
Originreçoit403. La clé reste côté serveur. /v1ne 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évue405 method_not_allowed, avec l'en-têteAllow. - 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) oureadetwrite(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ête | Description |
|---|---|
Synkro-Request-Id | Identifiant de la requête (req_…), repris dans les erreurs et les journaux. |
RateLimit-Limit | Nombre de requêtes par minute permis à l'intégration (120). |
RateLimit-Remaining | Jetons restants dans le seau de l'intégration. |
RateLimit-Reset | Secondes avant le prochain jeton. |
Retry-After | Secondes à attendre avant de réessayer (réponse 429). |
Idempotent-Replayed | true quand la réponse est celle d'un premier appel avec la même Idempotency-Key ; rien n'a été recréé. |
WWW-Authenticate | Bearer realm="synkro-api", avec error="invalid_token" si un jeton était présent. |
Pagination
| Paramètre | Type | Routes | Description |
|---|---|---|---|
limit | integer | GET /channels, GET /channels/{channel_id}/tickets, GET /tickets/{key}/comments | Taille de la page : 20 par défaut, 100 au plus. |
limit | integer | GET /channels/{channel_id}/messages | Taille de la page : 20 par défaut, 50 au plus. |
cursor | string | GET /channels, GET /channels/{channel_id}/messages, GET /channels/{channel_id}/tickets, GET /tickets/{key}/comments | Curseur 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.
| Champ | Type | Description |
|---|---|---|
code | validation | unauthenticated | forbidden | plan_required | org_read_only | org_suspended | not_found | method_not_allowed | conflict | payload_too_large | limit_reached | rate_limited | internal | Code stable, à tester côté client. |
message | string | Message en français, destiné à une personne. |
request_id | string | Identifiant de la requête, repris dans l'en-tête Synkro-Request-Id. |
applied | string[] | Seulement dans la réponse d'erreur de PATCH /tickets/{key} quand des champs étaient déjà appliqués : leur liste, dans l'ordre. |
| Code | HTTP | Sens |
|---|---|---|
validation | 400 | 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. |
validation | 422 | validation (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). |
unauthenticated | 401 | jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
forbidden | 403 | 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). |
plan_required | 403 | 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). |
org_read_only | 403 | 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). |
org_suspended | 403 | 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). |
not_found | 404 | ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
method_not_allowed | 405 | Un 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. |
conflict | 409 | une opération est déjà en cours avec cette Idempotency-Key. |
payload_too_large | 413 | corps de plus de 1 Mio. |
limit_reached | 422 | validation (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_limited | 429 | 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
internal | 500 | erreur 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é.
| Code | Description |
|---|---|
200 | L'intégration. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Code | Description |
|---|---|
200 | Le document OpenAPI 3.1. |
403 | forbidden : la requête porte un en-tête Origin (la clé n'a rien à faire dans un navigateur). |
413 | payload_too_large : corps de plus de 1 Mio. |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
limit | requête | integer | Taille de la page : 20 par défaut, 100 au plus. |
cursor | requête | string | Curseur 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. |
| Code | Description |
|---|---|
200 | Une page de canaux. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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é.
| Nom | Où | Type | Description |
|---|---|---|---|
channel_idrequis | chemin | string | Identifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404). |
| Code | Description |
|---|---|
200 | Le canal. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
channel_idrequis | chemin | string | Identifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404). |
| Code | Description |
|---|---|
200 | Les personnes du canal. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Code | Description |
|---|---|
200 | Les membres internes. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
channel_idrequis | chemin | string | Identifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404). |
around | requête | string | Identifiant d'un message autour duquel lire. |
limit | requête | integer | Taille de la page : 20 par défaut, 50 au plus. |
cursor | requête | string | Curseur 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. |
| Code | Description |
|---|---|
200 | Une page de messages. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
channel_idrequis | chemin | string | Identifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404). |
Idempotency-Key | en-tête | string | 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. |
| Champ | Type | Description |
|---|---|---|
textrequis | string | Texte en Markdown, 4 000 caractères au plus ; mentions <@id> des personnes visibles dans le canal ; @canal refusé. |
reply_to_id | string | null | Message auquel répondre. |
| Code | Description |
|---|---|
201 | Le message créé. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
409 | conflict : une opération est déjà en cours avec cette Idempotency-Key. |
413 | payload_too_large : corps de plus de 1 Mio. |
422 | validation (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). |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
channel_idrequis | chemin | string | Identifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404). |
status | requête | todo | in_progress | in_review | done | Statut du ticket. |
assignee_id | requête | string | Personne assignée. |
sprint_id | requête | string | Sprint. |
limit | requête | integer | Taille de la page : 20 par défaut, 100 au plus. |
cursor | requête | string | Curseur 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. |
| Code | Description |
|---|---|
200 | Une page de tickets. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
channel_idrequis | chemin | string | Identifiant d'un canal ouvert à la clé. Un canal hors de portée répond comme un canal inconnu (404). |
Idempotency-Key | en-tête | string | 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. |
| Champ | Type | Description |
|---|---|---|
titlerequis | string | Titre. |
type | epic | story | task | bug | subtask | Type ; task par défaut. |
priority | low | medium | high | critical | Priorité. |
assignee_id | string | Personne assignée. |
description | string | Description en Markdown. |
due_date | string (date) | Échéance, AAAA-MM-JJ. |
labels | string[] |
| Code | Description |
|---|---|
201 | Le ticket créé. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
409 | conflict : une opération est déjà en cours avec cette Idempotency-Key. |
413 | payload_too_large : corps de plus de 1 Mio. |
422 | validation (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). |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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é.
| Nom | Où | Type | Description |
|---|---|---|---|
keyrequis | chemin | string | Clé du ticket (ACME-42). |
| Code | Description |
|---|---|
200 | Le ticket. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
keyrequis | chemin | string | Clé du ticket (ACME-42). |
Idempotency-Key | en-tête | string | 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. |
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).
| Champ | Type | Description |
|---|---|---|
status | todo | in_progress | in_review | done | Nouveau statut. |
assignee_id | string | null | Nouvelle personne assignée ; null pour retirer l'assignation. |
priority | low | medium | high | critical | Nouvelle priorité. |
title | string | Nouveau titre. |
description | string | null | Nouvelle description ; null pour l'effacer. |
sprint_id | string | null | Nouveau sprint ; null pour retirer le ticket du sprint. |
| Code | Description |
|---|---|
200 | Le ticket modifié et les champs appliqués. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
409 | conflict : une opération est déjà en cours avec cette Idempotency-Key. |
413 | payload_too_large : corps de plus de 1 Mio. |
422 | validation (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). |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
keyrequis | chemin | string | Clé du ticket (ACME-42). |
limit | requête | integer | Taille de la page : 20 par défaut, 100 au plus. |
cursor | requête | string | Curseur 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. |
| Code | Description |
|---|---|
200 | Une page de commentaires. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
keyrequis | chemin | string | Clé du ticket (ACME-42). |
Idempotency-Key | en-tête | string | 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. |
| Champ | Type | Description |
|---|---|---|
textrequis | string | Texte en Markdown. |
| Code | Description |
|---|---|
201 | Le commentaire créé. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
409 | conflict : une opération est déjà en cours avec cette Idempotency-Key. |
413 | payload_too_large : corps de plus de 1 Mio. |
422 | validation (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). |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Code | Description |
|---|---|
200 | Les abonnements. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
Idempotency-Key | en-tête | string | 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. |
| Champ | Type | Description |
|---|---|---|
urlrequis | string (uri) | Adresse HTTPS qui reçoit les événements ; elle doit être joignable depuis Synkro. |
eventsrequis | EventType[] | Types d'événements demandés. |
channel_ids | string[] | Canaux suivis, parmi ceux de la clé ; absent : tous les canaux ouverts. |
| Code | Description |
|---|---|
201 | L'abonnement créé, avec son secret (rendu cette seule fois ; absent d'un rejeu). |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
409 | conflict : une opération est déjà en cours avec cette Idempotency-Key. |
413 | payload_too_large : corps de plus de 1 Mio. |
422 | validation (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). |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
idrequis | chemin | string | Identifiant d'un abonnement de cette intégration. |
| Code | Description |
|---|---|
200 | L'abonnement. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
idrequis | chemin | string | Identifiant d'un abonnement de cette intégration. |
| Code | Description |
|---|---|
204 | L'abonnement est supprimé. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
413 | payload_too_large : corps de plus de 1 Mio. |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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.
| Nom | Où | Type | Description |
|---|---|---|---|
idrequis | chemin | string | Identifiant d'un abonnement de cette intégration. |
Idempotency-Key | en-tête | string | 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. |
| Code | Description |
|---|---|
200 | Le résultat de l'envoi. |
400 | validation : 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. |
401 | unauthenticated : jeton absent, inconnu, révoqué ou expiré, organisation en suppression ou intégration supprimée. |
403 | forbidden, 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). |
404 | not_found : ressource inconnue ou hors de portée de la clé (canal non ouvert, canal confidentiel, message privé) ; les deux cas sont indiscernables. |
409 | conflict : une opération est déjà en cours avec cette Idempotency-Key. |
413 | payload_too_large : corps de plus de 1 Mio. |
422 | validation (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). |
429 | rate_limited : 120 requêtes par minute et par intégration, 30 écritures par minute en plus (seaux à jetons). |
500 | internal : 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
}