SubReply

Referencia

Códigos de error

Todos los endpoints devuelven los mismos códigos, con la misma forma de cuerpo. Esta página dice qué hacer con cada uno.

Referencia completa

CódigoSignificadoCausa frecuenteSolución
400Bad RequestCuerpo JSON ilegible, parámetro ausente o fuera de límites.Comprueba el formato de los parámetros. El campo error nombra exactamente el que da problemas.
401UnauthorizedClave API ausente, inválida o revocada.Comprueba la cabecera Authorization: Bearer sr_live_….
402Payment RequiredCréditos insuficientes: saldo nulo o inferior al coste de la acción pedida.Recarga en subreply.io/billing.
422UnprocessablePublicación de Reddit bloqueada, archivada o eliminada, o cuenta de publicación no disponible.Elige una publicación reciente y comentable. Reintentar igual no cambiará nada.
500Internal Server ErrorFallo de la cadena de IA o del scraping.Reintenta en 30 s; contacta con soporte si el error persiste.
503Service UnavailableServicio de publicación no disponible o ninguna cuenta con la que publicar.Reintenta en 60 s.

202 no es un error

POST /api/v1/publish puede responder 202: la petición ha salido, la publicación no está confirmada y no se descuenta nada. No es un fallo que haya que reintentar — ver publicar un comentario.

Formato de los errores

Toda respuesta fallida lleva un objeto JSON con un único campo, error, en francés y legible por una persona:

Cuerpo de error
{
  "error": "Message explicite en français"
}

No hay código de error interno ni campo details: el estado HTTP lleva la categoría y el mensaje lleva el detalle. Nunca analices el texto del mensaje para decidir una acción — bifurca según el estado.

Gestión de errores en producción

Con tres reglas basta para sostener un flujo:

  • Reintentar 500 y 503, con una espera creciente y dos intentos como máximo. Son los únicos códigos que se resuelven solos.
  • Cortar en 402 y avisar: todas las llamadas siguientes fallarán igual mientras no se recargue el saldo.
  • No reintentar nunca 400, 401 ni 422 — hay algo que corregir en la petición, en la clave o en la publicación elegida.
Cliente con reintentos y control de créditos
type ApiError = { error: string };

// Reintenta solo lo que merece la pena: 503 (servicio de publicación
// momentáneamente no disponible) y 500 (fallo de la cadena de IA o del scraping).
// Un 400, un 401 o un 422 no se arreglan solos.
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: sin créditos. No sirve de nada insistir — cortamos la cadena y
  // avisamos, en vez de encadenar llamadas que van a fallar todas.
  if (response.status === 402) {
    await notifyOutOfCredits(error);
    throw new Error(`Créditos agotados: ${error}`);
  }

  if (RETRYABLE.has(response.status) && attempt < 2) {
    // 30 s y después 60 s: el tiempo que tarda un servicio en volver.
    await new Promise((resolve) => setTimeout(resolve, 30_000 * (attempt + 1)));
    return callSubreply<T>(endpoint, body, attempt + 1);
  }

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

Las repeticiones no cuestan nada

La generación y la publicación se facturan de forma idempotente por URL de publicación: un segundo intento sobre la misma publicación devuelve credits_used: 0. Un reintento tras un timeout es por tanto gratuito — en /publish, comprueba de todos modos la publicación antes de repetir, para no dejar dos comentarios.