SubReply

Référence

Codes d'erreur

Tous les endpoints renvoient les mêmes codes, avec la même forme de corps. Cette page dit quoi faire de chacun.

Référence complète

CodeSignificationCause fréquenteSolution
400Bad RequestCorps JSON illisible, paramètre manquant ou hors limites.Vérifier le format des paramètres. Le champ error nomme précisément celui qui pose problème.
401UnauthorizedClé API absente, invalide ou révoquée.Vérifier l'en-tête Authorization: Bearer sr_live_….
402Payment RequiredCrédits insuffisants : solde nul, ou inférieur au coût de l'action demandée.Recharger sur subreply.io/billing.
422UnprocessablePost Reddit verrouillé, archivé, supprimé, ou compte de publication indisponible.Choisir un post récent et commentable. Retenter à l'identique ne changera rien.
500Internal Server ErrorÉchec du pipeline IA ou du scraping.Réessayer dans 30 s ; contacter le support si l'erreur persiste.
503Service UnavailableService de publication indisponible ou aucun compte publiable.Réessayer dans 60 s.

202 n'est pas une erreur

POST /api/v1/publish peut répondre 202: la demande est partie, la publication n'est pas confirmée, rien n'est débité. Ce n'est pas un échec à retenter — voir publier un commentaire.

Format des erreurs

Toute réponse en échec porte un objet JSON à un seul champ, error, en français et lisible par un humain :

Corps d'erreur
{
  "error": "Message explicite en français"
}

Il n'y a ni code d'erreur interne, ni champ details: le statut HTTP porte la catégorie, le message porte le détail. Ne parsez jamais le texte du message pour décider d'une action — branchez sur le statut.

Gestion des erreurs en production

Trois règles suffisent à tenir un workflow :

  • Retenter 500 et 503, avec un délai croissant et deux tentatives au plus. Ce sont les seuls codes qui se résolvent d'eux-mêmes.
  • Couper sur 402 et prévenir : tous les appels suivants échoueront pareil tant que le solde n'est pas rechargé.
  • Ne jamais retenter 400, 401 et 422 — il y a quelque chose à corriger dans la requête, la clé ou le post visé.
Client avec retry et garde-fou crédits
type ApiError = { error: string };

// Retente uniquement ce qui mérite de l'être : 503 (service de publication
// momentanément indisponible) et 500 (échec du pipeline IA ou du scraping).
// Un 400, un 401 ou un 422 ne s'arrangeront pas tout seuls.
const RETRYABLE = new Set([500, 503]);

async function callSubreply<T>(
  endpoint: string,
  body: unknown,
  attempt = 0,
): Promise<T> {
  const response = await fetch(`https://subreply.io/api/v1/${endpoint}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.SUBREPLY_API_KEY}`,
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(180_000),
  });

  if (response.ok) return (await response.json()) as T;

  const { error } = (await response.json()) as ApiError;

  // 402 : plus de crédits. Inutile d'insister — on coupe la chaîne et on
  // prévient, plutôt que d'enchaîner des appels qui échoueront tous.
  if (response.status === 402) {
    await notifyOutOfCredits(error);
    throw new Error(`Crédits épuisés : ${error}`);
  }

  if (RETRYABLE.has(response.status) && attempt < 2) {
    // 30 s puis 60 s : le temps qu'un service reparte.
    await new Promise((resolve) => setTimeout(resolve, 30_000 * (attempt + 1)));
    return callSubreply<T>(endpoint, body, attempt + 1);
  }

  throw new Error(`SubReply ${response.status} : ${error}`);
}

Les rejeux ne coûtent rien

La génération et la publication sont facturées de façon idempotente par URL de post : une seconde tentative sur le même post renvoie credits_used: 0. Un retry après timeout est donc gratuit — sur /publish, vérifiez quand même le post avant de rejouer, pour ne pas y laisser deux commentaires.