fluyio.
Referencia de la API

Errores

Estados HTTP, códigos y diagnóstico de peticiones a la API de Fluyio.

La API combina estados HTTP con un cuerpo JSON estructurado. Guarda el request_id para identificar la petición al solicitar ayuda.

Formato

{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_param",
    "message": "limit must be a positive integer.",
    "param": "limit",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}
CampoTipoDescripción
error.typestringCategoría general para el tratamiento del error.
error.codestringMotivo específico que puede interpretar el cliente.
error.messagestringDescripción para diagnóstico; no es texto final de tu aplicación.
error.paramstring, opcionalParámetro relacionado, si se conoce.
error.request_idstringCoincide con Fluyio-Request-Id.

Los errores de versión añaden supported_versions. Los de permisos pueden incluir required_scope y granted_scopes; su comprobación ya está activa.

Estados HTTP

EstadoSignificadoSiguiente paso
200Lectura, actualización, eliminación o acción completada.Procesa la respuesta.
201Suscriptor creado.Guarda el ID.
400Parámetro, campo, cursor, versión o clave de idempotencia no válido.Corrige la petición.
401Credencial ausente o no válida.Revisa la clave.
403Permiso insuficiente, modo test no disponible o límite del plan.Resuelve el motivo antes de repetir.
404Suscriptor no encontrado en la marca.Revisa el ID.
409Conflicto de email, idempotencia o canal suprimido.Resuelve el conflicto.
422Falta un dato necesario para aplicar la operación.Corrige la identidad del canal.
429Límite de peticiones por clave.Espera Retry-After.
5xxError del servidor; una escritura puede tener resultado incierto.Consulta el estado y la política de idempotencia.

Tipos y códigos

TipoCódigos principales
invalid_request_errorinvalid_param, invalid_cursor, invalid_version_header, unknown_version, invalid_idempotency_key, profile_limit, resource_not_found, resource_already_exists, channel_unavailable, missing_required_field.
authentication_errormissing_api_key, invalid_api_key, missing_tenant.
authorization_errormissing_scope, test_mode_unavailable.
idempotency_errorConflictos de la reserva; consulta idempotencia.
rate_limit_errorrate_limit_exceeded.
api_errordatabase_error, auth_lookup_failed y errores internos.

Petición o datos no válidos

  • invalid_param (400): campo desconocido, requerido ausente, tipo o valor incorrectos; incluye propiedades reservadas con prefijo __.
  • invalid_cursor (400): cursor mal formado o no compatible con los parámetros; reinicia la paginación.
  • invalid_version_header / unknown_version (400): formato o versión no admitidos.
  • invalid_idempotency_key (400): debe tener entre 8 y 128 caracteres A-Z a-z 0-9 _ -.
  • profile_limit (403): la creación superaría el límite de perfiles aplicable.
  • resource_not_found (404): el suscriptor no existe en esta marca.
  • resource_already_exists (409): el email ya existe al crear. No presupongas que un conflicto al actualizar utiliza el mismo código.
  • channel_unavailable (409): el canal está suprimido por rebote, queja o bloqueo, o el número WhatsApp pertenece a otro suscriptor.
  • missing_required_field (422): falta email o teléfono según el canal.

Autenticación y permisos

  • missing_api_key (401): cabecera ausente, vacía o mal formada.
  • invalid_api_key (401): clave desconocida, caducada o revocada.
  • missing_tenant (401): comprobación defensiva si no se puede resolver la marca; comunica el ID de petición.
  • missing_scope (403): falta el permiso de lectura o escritura requerido. Su aplicación está activa.
  • test_mode_unavailable (403): las claves test no pueden acceder a datos reales y no existe un sandbox.

Idempotencia y errores internos

idempotency_key_reuse devuelve 409 si reutilizas una clave para otro cuerpo. Una clave nueva corresponde a una operación nueva, no sirve para forzar la repetición de una escritura incierta.

Ante 5xx, distingue una lectura que puedes repetir de una escritura cuyo resultado debes revisar. Con Idempotency-Key, la reserva incierta dura 24 horas y no se reejecuta automáticamente. Guarda request_id y consulta idempotencia.

Un 503 idempotency_unavailable indica que no se inició la operación; no garantiza una reserva creada. Reintenta la misma operación con la misma clave, sin generar otra para eludir la protección.

Respuestas fuera del formato habitual

Un JSON mal formado o un error de un intermediario puede devolver una respuesta no JSON. Las rutas no publicadas tampoco tienen un contrato v1 garantizado; no las invoques hasta que aparezcan en la referencia.

Ejemplos

400: parámetro no válido

curl -X POST https://fluyio.com/api/v1/subscribers \
  -H "Authorization: Bearer $FLUYIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Ada" }'

401: clave no válida

{
  "error": {
    "type": "authentication_error",
    "code": "invalid_api_key",
    "message": "The provided API key is invalid, revoked, or expired.",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

403: límite de perfiles

{
  "error": {
    "type": "invalid_request_error",
    "code": "profile_limit",
    "message": "Límite de perfiles alcanzado (250/250).",
    "param": "profile_limit",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

404: ID desconocido

{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_not_found",
    "message": "No subscriber found with id sub_00000000000000000000000000000000.",
    "param": "id",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

409: email duplicado

{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_already_exists",
    "message": "A subscriber with this email already exists in this account.",
    "param": "email",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

422: identidad de canal ausente

{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_required_field",
    "message": "Cannot subscribe an SMS channel for a subscriber without a phone number.",
    "param": "phone",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

429: límite de peticiones

HTTP/1.1 429 Too Many Requests
Retry-After: 4
Ratelimit-Limit: 150
Ratelimit-Remaining: 0

{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Burst limit reached (150 req / 10s). Retry after 4 seconds.",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

500: error interno

{
  "error": {
    "type": "api_error",
    "code": "database_error",
    "message": "Failed to create subscriber.",
    "request_id": "req_3f9a1c7e5b2d8f4a6c0e9b1d3f"
  }
}

Tratar errores en tu código

class FluyioError extends Error {
  constructor(
    public status: number,
    public type: string,
    public code: string,
    public requestId: string,
    message: string,
  ) {
    super(message);
  }
}

async function fluyio(path: string, init: RequestInit = {}) {
  const res = await fetch(`https://fluyio.com/api/v1${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.FLUYIO_API_KEY}`,
      'Fluyio-Version': '2026-05-15',
      'Content-Type': 'application/json',
      ...(init.headers ?? {}),
    },
  });
  if (res.ok) return res.json();

  let body: any = null;
  try { body = await res.json(); } catch { /* non-JSON 400 or proxy error */ }
  const err = body?.error ?? {};
  throw new FluyioError(
    res.status,
    err.type ?? 'api_error',
    err.code ?? 'unknown',
    err.request_id ?? res.headers.get('fluyio-request-id') ?? '',
    err.message ?? res.statusText,
  );
}

try {
  await fluyio('/subscribers', { method: 'POST', body: JSON.stringify({ email: '[email protected]' }) });
} catch (e) {
  if (e instanceof FluyioError) {
    if (e.type === 'rate_limit_error') {
      // wait for Retry-After and retry
    } else if (e.code === 'resource_already_exists') {
      // look the subscriber up with filter[email] and PATCH instead
    } else {
      console.error(e.status, e.type, e.code, e.requestId, e.message);
    }
  }
  throw e;
}

Los ejemplos de creación que omiten consentimiento devuelven el email como never_subscribed; no dan de alta al contacto automáticamente.

Compartir un diagnóstico

Al escribir a [email protected], incluye el request_id, la hora aproximada en UTC, la versión y una respuesta depurada de datos privados. No compartas la clave API.

Consulta autenticación, límites, idempotencia y versionado.

En esta página