Publicación a canales y enriquecimiento T1

Estos endpoints publican (exportan) productos hacia un canal de venta conectado — Shopify, MercadoLibre, Amazon, Walmart, T1/Sears/Sanborns, entre otros — y exponen el estado de esa publicación. También cubren un flujo específico del canal T1: consultar cómo está enriquecida y vinculada tu vitrina de productos frente a lo que existe en T1.

Úsalos cuando necesites:

  • Publicar (o republicar) uno, varios o todos tus productos en un canal.
  • Saber si una publicación masiva terminó, sigue en curso o falló parcialmente.
  • Validar qué productos están listos para publicarse antes de lanzar la operación.
  • Revisar feeds pendientes de un producto específico hacia un canal.
  • Auditar la vinculación de tu catálogo con T1/Sears/Sanborns.

Publicación síncrona o asíncrona, según el volumen

POST /products/export responde de forma síncrona (201) cuando la operación es pequeña, o de forma asíncrona (202 + jobId) cuando el volumen lo justifica. Nunca asumas cuál vas a recibir: revisa el status HTTP de la respuesta y, si es 202, consulta GET /products/export/{jobId} hasta que el trabajo termine. Este es el mismo patrón asíncrono que usan la importación y exportación CSV.


Publicar productos en un canal

Publica productos en un canal de venta conectado. Responde de forma síncrona o asíncrona según el volumen de la operación.

channelIdstringrequired

ID del canal destino.

modestring

Alcance de la selección: single, selected, filtered o all.

selectionModestring

Parámetro alterno/heredado relacionado con el alcance de la selección. Convive con mode en el mismo body.

productIdsstring[]

SKUs o IDs a publicar. Requerido cuando mode es selected.

filtersobject

Filtros de catálogo a aplicar. Requerido cuando mode es filtered.

targetMarketplacesstring[]

Sub-mercados destino dentro del canal, cuando el canal los soporta (por ejemplo, sitios de MercadoLibre).

options.enableCategorizationboolean

Categoriza automáticamente los productos en el canal destino. Default: true.

options.forceRecategorizationboolean

Fuerza una nueva categorización aunque el producto ya tenga una asignada. Default: false.

options.includeInventoryboolean

Incluye el inventario disponible en el payload publicado. Default: true.

options.includeVariantsboolean

Incluye variantes en el payload publicado. Default: true.

options.forceUpdateboolean

Fuerza la actualización aunque el canal no detecte cambios. Default: false.

options.locationIdstring

Ubicación desde la cual calcular el inventario a publicar.

{
  "channelId": "65f2a0b1c4d5e6f7a8b9c0aa",
  "mode": "selected",
  "productIds": ["CAM-ROJO-M", "PAN-AZUL-32"],
  "options": { "includeInventory": true, "includeVariants": true }
}
201Publicación síncrona — la operación se resolvió por completo antes de responder.
{
  "results": [
    { "sku": "CAM-ROJO-M", "success": true },
    { "sku": "PAN-AZUL-32", "success": false, "error": "CHANNEL_INACTIVE" }
  ]
}
202Publicación asíncrona — la operación se encoló, consulta su estado con el jobId.
{
  "jobId": "exp_65f3a1b2c4d5e6f7a8b9c0e1",
  "metadata": { "async": true }
}

Permiso requerido: products:export

Shape de resultados no verificado campo a campo

El nombre de la clave de nivel superior (results en la respuesta síncrona, jobId + metadata.async en la asíncrona) está confirmado en el código. Los campos exactos dentro de cada elemento de results[] más allá de sku/success/error no fueron verificados línea por línea — trátalos como orientativos y confirma contra la respuesta real de tu canal.

Camino directo para Shopify de un solo producto

Cuando mode es single y el canal destino es Shopify, el orquestador invoca directo la integración de Shopify (saltándose el servicio de canales intermedio) para reducir latencia. El contrato de respuesta que ves como consumidor de la API no cambia — sigue siendo un 201 síncrono — es un detalle de implementación interno.

curl -X POST https://api.fenicia.io/products/export \
  -H "Authorization: Bearer fkapi_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "channelId": "65f2a0b1c4d5e6f7a8b9c0aa",
    "mode": "selected",
    "productIds": ["CAM-ROJO-M", "PAN-AZUL-32"],
    "options": { "includeInventory": true, "includeVariants": true }
  }'

Consultar el estado de una publicación

Consulta el estado de una publicación asíncrona previamente encolada.

jobIdstringrequired

ID del trabajo devuelto por POST /products/export (202) o por GET /products/export.

200
{
  "jobId": "exp_65f3a1b2c4d5e6f7a8b9c0e1",
  "status": "processing"
}
404
{ "code": "not-found", "message": "Export job not found" }

Permiso requerido: products:export

Shape del objeto de estado no confirmado más allá de jobId y status

El reporte de auditoría de esta API confirma que este endpoint devuelve "los datos del job", pero no confirma línea por línea el resto de las claves (por ejemplo, si existe un percent o un desglose por producto). No inventes campos adicionales sobre este endpoint — haz polling y confirma la forma exacta contra tu propio job antes de depender de un campo específico más allá de jobId/status.

Tip

Este es el mismo patrón de polling que usan los trabajos de CSV (ver Importación y exportación masiva (CSV)): encola con el endpoint POST, y haz polling sobre GET .../{jobId} hasta que el estado deje de ser transitorio.


Listar las publicaciones recientes

Devuelve las publicaciones más recientes hacia canales (últimas 20).

200
{
  "jobs": []
}

Permiso requerido: products:export

Solo las últimas 20, sin paginación

Este endpoint no acepta parámetros de paginación — siempre devuelve como máximo las 20 publicaciones más recientes del tenant. Si necesitas el detalle completo de una en particular, usa GET /products/export/{jobId}.


Cancelar una publicación en curso

Cancela una publicación asíncrona que todavía está en curso.

jobIdstringrequired

ID del trabajo a cancelar (path).

reasonstring

Motivo de la cancelación, para auditoría.

{ "reason": "Datos incorrectos en el lote, se relanzará corregido" }
200
{
  "success": true,
  "message": "Export job cancelled"
}
404
{ "code": "not-found", "message": "Export job not found" }
400
{ "code": "cancel-failed", "message": "Export job cannot be cancelled in its current state" }

Permiso requerido: products:export


Validar productos antes de publicar

Agrupa productos según qué tan listos están para publicarse en un canal, sin ejecutar la publicación.

channelIdstringrequired

ID del canal destino.

selectionModestringrequired

all, selected o filtered.

skusstring[]

Requerido cuando selectionMode es selected.

filtersobject

Requerido cuando selectionMode es filtered.

pagenumber

Número de página.

limitnumber

Elementos por página. Default: 50.

{
  "channelId": "65f2a0b1c4d5e6f7a8b9c0aa",
  "selectionMode": "filtered",
  "filters": { "status": "active", "category": "playeras" },
  "limit": 50
}
200
{
  "published": [],
  "ready": [],
  "needs_review": [],
  "rejected": []
}

Permiso requerido: products:read

No requiere products:export, pese al path

A diferencia del resto de los endpoints bajo /products/export*, este solo exige products:read. Si tu API key únicamente tiene permiso de lectura sobre productos, este endpoint SÍ te va a funcionar aunque los demás de esta página te devuelvan 403.

Forma exacta de cada grupo no confirmada

Se confirmaron las cuatro claves de agrupación (published, ready, needs_review, rejected) pero no el shape exacto de cada elemento dentro de esos arreglos — trátalos como listas de productos/SKUs con su motivo, y confírmalo contra tu propia respuesta.


Feeds pendientes de un producto

Devuelve los feeds de publicación pendientes de un producto hacia un destino específico.

skustringrequired

SKU del producto.

destinationstring

Destino del feed. Default: walmart.

{ "sku": "CAM-ROJO-M", "destination": "walmart" }
200
{
  "feeds": [],
  "sku": "CAM-ROJO-M",
  "destination": "walmart"
}
400
{ "code": "bad-request", "message": "Missing required field: sku" }

Permiso requerido: products:read


Enriquecimiento T1 / Sears / Sanborns

Estos tres endpoints comparten el mismo propósito: dejarte auditar cómo está vinculado tu catálogo Fenicia contra el catálogo real del canal T1 (que también opera Sears y Sanborns). Fenicia invoca directo la integración de T1 para obtener estos datos — el mismo patrón de orquestador legítimo que el bypass de Shopify descrito arriba.

Productos del catálogo T1 enriquecidos con su estado de sincronización frente a Fenicia.

channelIdstringrequired

ID del canal T1/Sears/Sanborns (path).

pagenumber

Número de página. Default: 0.

limitnumber

Elementos por página. Default: 20.

searchstring

Término de búsqueda.

200Cada producto trae un campo syncStatus indicando su estado de vinculación.
{
  "products": []
}
404
{ "code": "channel-not-found", "message": "Channel not found" }

Permiso requerido: products:read

Productos de Fenicia que ya tienen un binding con T1.

channelIdstringrequired

ID del canal T1/Sears/Sanborns (path).

pagenumber

Número de página. Default: 0.

limitnumber

Elementos por página. Default: 20.

searchstring

Término de búsqueda.

200
{
  "products": []
}

Permiso requerido: products:read

Todos los productos de Fenicia elegibles para T1, indicando si ya tienen binding (hasBinding).

channelIdstringrequired

ID del canal T1/Sears/Sanborns (path).

pagenumber

Número de página. Default: 0.

limitnumber

Elementos por página. Default: 20.

searchstring

Término de búsqueda.

inStockboolean

Filtra solo productos con existencia.

200Cada producto trae hasBinding indicando si ya está vinculado a T1.
{
  "products": []
}

Permiso requerido: products:read

Wrapper de lista no confirmado línea por línea

Se muestra { "products": [...] } como forma orientativa consistente con otros listados paginados de este dominio (ver Consultar el catálogo); el reporte de auditoría no confirmó el nombre exacto de esta clave para los tres endpoints de T1 — verifícalo contra tu propia respuesta antes de depender de él.

curl "https://api.fenicia.io/products/channel/65f2a0b1c4d5e6f7a8b9c0aa/t1?limit=50" \
  -H "Authorization: Bearer fkapi_tu_api_key"

Errores

CódigoStatusDescripción
EXPORT_IN_PROGRESS409Ya hay una publicación en curso hacia este canal; espera a que termine antes de lanzar otra.
CHANNEL_NOT_FOUND404El canal indicado no existe o no pertenece a tu tenant.
CHANNEL_INACTIVE400El canal existe pero está inactivo/desconectado.
NO_PRODUCTS400La selección (selected/filtered) no resolvió ningún producto para publicar.
PRODUCT_EXPORT_FAILED400Falló la publicación de uno o más productos hacia el canal.
not-found404No existe un trabajo de publicación con ese jobId.
cancel-failed400El trabajo no se puede cancelar en su estado actual (ya terminó o ya fue cancelado).
bad-request400Falta un campo requerido o un valor de selectionMode/mode es inválido.
channel-not-found404(T1) El canal indicado no existe o no es de tipo T1/Sears/Sanborns.
invalid-channel-type400(T1) El canal indicado existe pero no es de tipo T1/Sears/Sanborns.
t1-integration-error / t1-api-error500(T1) Falló la comunicación con la integración o la API de T1.
auth:invalid_token401La API key es inválida o fue revocada.
auth:permission_denied403La API key no tiene el permiso requerido (products:read o products:export según el endpoint).

Consulta el catálogo completo de errores para el resto de los códigos posibles.

Formato de código de error no unificado en este dominio

A diferencia del dominio de Pedidos (que usa siempre namespace:snake_case), Productos mezcla convenciones heredadas: verás códigos SCREAMING_SNAKE_CASE (CHANNEL_NOT_FOUND), kebab-case (cancel-failed) y, en otras páginas de este dominio, bad-request/sub-code. Rama tu lógica por el valor exacto del campo code, no por su formato.

Siguientes pasos