Catalogación por API: las APIs y el flujo, con JSON de ejemplo
Para darse idea de cómo se ve cada llamada · propuesta del 16-oct-2026 · alineación con Mercado Libre, 17-oct-2026 por la tarde
Propuesta sin ratificar · ejemplos ilustrativos
Los ejemplos salen de la propuesta funcional y de la propuesta de arquitectura, que todavía no se ratifican. El JSON del producto (atributo, variante, búsqueda y categoría) es la alineación con la guía de producto de Mercado Libre, del 17-oct por la tarde: todavía no está en decisiones.md. Los datos son inventados. El contrato final sale de la spec de FT-PRD-10 y FT-USR-03. Las decisiones, los componentes, los estados y lo que falta decidir están en la propuesta completa.
1. En una mirada: cuántas APIs se ocupan
No. Para catalogar un producto no se ocupan las 45 que contaba el inventario de la mañana. Se ocupan 8 llamadas, y dos de ellas se hacen una sola vez (quién soy, y dónde llegan los avisos). Las otras 33 sí se ocupan, para lo que viene después del alta y que un ítem de marketplace no resuelve: lotes, correcciones, variantes que se suman después, fotos sin URL pública, historial, entrega por canal, modificaciones con permiso y la operación de los avisos. 4 dejan de ser ruta porque el dato ya viene en otra respuesta. Quedan 41.
8llamadas para catalogar un producto
33rutas del ciclo, después del alta
4de las 45 se funden en otra respuesta
41rutas que quedan bajo /partner/v1
Para catalogar
Cuándo
Ruta
1
Una vez por sesión
GET /me
2
Una vez
POST /webhooks
3
Por producto
GET /categories/discovery?q=
4
Por categoría
GET /categories/{categoryId}
5
Opcional, antes de crear
POST /validations, con el mismo JSON del alta
6
Por producto
GET /products/search
7
Por producto, o un lote
POST /proposals o POST /batches
8
Cuando quiera el estado
GET /proposals/{proposalId}
Si la búsqueda dice que el producto ya existe, no hay novena llamada: la respuesta trae el id de catálogo, el SKU y si Mirakl ya acusó. La oferta se crea en Mirakl. Si aun así se manda el alta, lleva catalogProductId y no reenvía la ficha.
Deja de ser ruta
Dónde queda el dato
GET /value-lists/{listCode}/values
values del atributo, en GET /categories/{categoryId}
GET /brands
el atributo BRAND, con sus valores
GET /offer-targets/{sku}
sku, allowedAction y offer.miraklStatus, en la búsqueda
GET /products/{id}/media
pictures, en GET /products/{id}
Cómo conté. El 17-oct por la mañana había 45: cada método con su ruta, una vez, como en las tablas del §5 de la propuesta funcional (POST /batches sale dos veces y cuenta una). No entran el token, que lo da el proveedor de identidad, ni los 17 avisos, que son eventos y no rutas. Tampoco las tres rutas para rotar credenciales, que la arquitectura saca de la versión 1. Por la tarde se restan las 4 que se funden: 41. De esas, 8 son el camino de arriba y 33 el ciclo. Cinco rutas no se borran, cambian de nombre: POST /suggestions/classification pasa a GET /categories/discovery, GET /templates/{templateId} a GET /categories/{categoryId}, su /schema y GET /templates/changes igual, y POST /products/lookup a GET /products/search. El id de categoría es el de la plantilla: por dentro sigue siendo la vista efectiva del negocio.
Grupo
Endpoints
Para qué
§5.1 Autenticación · 1
GET /me (el token, POST /oauth/token, lo da el proveedor de identidad)
Saber quién es el consumidor, qué puede hacer y sus límites.
§5.2 Catálogos de referencia · 5
GET /categories · GET /categories/{categoryId} · GET /categories/{categoryId}/schema · POST /brand-requests · GET /categories/changes
Recorrer el árbol, bajar atributos y valores de una categoría, pedir una marca que no está y ver qué cambió. El esquema JSON se queda: Mercado Libre no lo da y el integrador valida en su casa.
§5.3 Sugerencias con IA · 1
GET /categories/discovery?q=
Predecir la categoría a partir del título. La IA sigue detrás; la puerta es un GET, como el predictor de Mercado Libre.
§5.4 Validación previa · 1
POST /validations
Probar hasta 100 productos sin crear nada.
§5.5 Propuestas y lotes · 10
POST /proposals · POST /batches · GET /batches/{batchId} · GET /batches/{batchId}/items · GET /proposals/{proposalId} · GET /proposals · PATCH /proposals/{proposalId} · POST /proposals/{proposalId}/variants · DELETE /proposals/{proposalId}/variants/{variantId} · POST /proposals/{proposalId}/cancel
Mandar uno o un lote, seguirlo, corregirlo, sumar o quitar variantes y cancelar.
§5.6 Identificación · 1
GET /products/search
Saber si ya existe, por GTIN o por texto, y si toca crearlo o ofertar. Trae el SKU y el acuse de Mirakl.
§5.8 Imágenes · 4
POST /media/uploads · PUT /proposals/{id}/media · PUT /products/{id}/media · POST /media/batches
Subir un archivo cuando no hay URL pública. Si la hay, va en pictures.source del alta y estas rutas no se llaman.
§5.9 Estado e historial · 6
GET /proposals/{id}/history · GET /products/{id}/history · GET /products/{id} · GET /products/{id}/channels · GET /events · GET /receipts/{requestId}
Consultar la ficha, la historia, la entrega por canal, los avisos y los recibos.
§5.10 Modificaciones · 4
GET /products/{id}/permissions · POST /products/{id}/modifications · POST /modifications/batches · GET /modifications/{id}
Cambiar un producto publicado y seguir el cambio.
§5.11 Webhooks · 8
POST /webhooks · GET /webhooks · PATCH /webhooks/{id} · DELETE /webhooks/{id} · POST /webhooks/{id}/test · POST /webhooks/{id}/secret · GET /webhooks/{id}/deliveries · POST /webhooks/{id}/deliveries/{deliveryId}/retry
Suscribirse a los avisos, probarlos, rotar el secreto y reintentar entregas.
§5.12 Sandbox · 0
Sin rutas propias
Probar de punta a punta con las mismas rutas y credenciales de prueba.
Los 17 avisos:proposal.received, proposal.validated, proposal.correction_required, proposal.rejected, proposal.already_exists, proposal.accepted, proposal.cancelled, product.sku_created, product.in_preparation, product.publication_sent, product.published, product.publication_failed, product.updated, product.modification_rejected, product.retired, batch.completed y template.changed. El aviso de que cambió la categoría sigue llamándose template.changed hasta cerrar el catálogo de eventos.
2. Solo para catalogar
Catalogar es dar de alta la ficha de un producto que no existe. Hay dos flujos. Empiezan igual y se separan al mandar: uno, o un lote. Si la búsqueda encuentra el producto, aquí se acaba: no se cataloga.
Solo el alta. El visto bueno, SAP, los canales y las modificaciones están en el flujo completo, más abajo.
POST /proposals con la ficha completa: atributos, attribute_combinations y, si hay URL, pictures.source. Responde 201 con proposalId y estado IN_APPROVAL. Para ver cómo va, GET /proposals/{proposalId}.
Flujo 2. Se cataloga un lote
Las mismas llamadas, y en el paso 7 se manda POST /batches con hasta 1,000 productos. Cada renglón es el JSON del flujo 1. Responde 202 con batchId. Saber si terminó ya es el ciclo: el aviso batch.completed y GET /batches/{batchId}/items.
Estos no catalogan
La búsqueda responde allowedAction. Solo CREATE_PROPOSAL entra a los dos flujos de arriba. Las otras cinco acciones están en la propuesta funcional, §5.6, y no crean ficha:
allowedAction
Qué sigue
CREATE_PROPOSAL
Flujo 1 o flujo 2. Es el único que cataloga.
CREATE_OFFER
La oferta se crea en Mirakl. La ficha ya existe.
MULTI_OFFER
La oferta se crea en Mirakl. La ficha ya existe.
STOCK_OUT_OFFER
La oferta se crea en Mirakl, sobre un producto sin stock.
REQUEST_EAN_RELEASE
Pedir que liberen el EAN. No se manda la ficha.
NOT_ALLOWED
No se crea nada. En Mercaderías, un producto que ya existe no se duplica.
Si aun así se manda el alta de uno que ya existe, el cuerpo solo lleva catalogProductId y no la ficha. El ejemplo está en mandar uno.
3. El flujo completo
Las flechas llenas son llamadas y las punteadas, respuestas; las de punta abierta son avisos (webhooks) que el PIM manda al sistema del tercero. Las rutas de la API de terceros van bajo /partner/v1; el token lo da el proveedor de identidad. Desde la fase 4 todo pasa en segundo plano. Mercaderías y Marketplace sólo se separan en el visto bueno: Compras o la IA.
Según la arquitectura: la ruta la fija el proveedor de identidad que salga de PD-26, y el token sólo identifica al cliente y sus alcances; negocios, tiendas y estado se leen en cada llamada del registro de terceros (la propuesta funcional los metía en el token).
Paso 2GET/partner/v1/me
Saber quién es, qué negocios, tiendas y alcances tiene, en qué ambiente está, cuáles son sus límites y cuánto lleva consumido.
Request
GET /partner/v1/me
Authorization: Bearer eyJ...ejemplo
X-Request-Id: REQ-DEMO-0001
⚠️ Ilustrativo: usage. Los límites son los de arranque del sandbox y el piloto; el tope diario se fija a mano al dar el permiso y el 100 es de ejemplo. Según la arquitectura, un límite sin configurar vale null, nunca cero ni ilimitado, y con él no se puede escribir.
Paso 3POST/partner/v1/webhooks
Decir a qué dirección HTTPS quiere recibir los avisos y cuáles. La respuesta trae, una sola vez, el secreto para verificar la firma de cada aviso.
Request
POST /partner/v1/webhooks
Authorization: Bearer eyJ...ejemplo
Idempotency-Key: 00000000-0000-4000-8000-000000000001
Content-Type: application/json
{
"url": "https://{host-del-seller}/avisos-pim",
"events": [
"proposal.received",
"proposal.correction_required",
"proposal.accepted",
"product.sku_created",
"product.publication_sent",
"product.published",
"product.updated",
"batch.completed"
],
"description": "Avisos para el orquestador de pruebas"
}
Predecir de una a tres categorías a partir del título, como el predictor de Mercado Libre (domain_discovery). La IA sigue detrás de esta puerta. categoryId es el id de la plantilla.
Request
GET /partner/v1/categories/discovery?business=MKP&q=bateria%20portatil%2020000%20mAh
Authorization: Bearer eyJ...ejemplo
El atributo va como en Mercado Libre: id, value_id y value_name. confidence es de Liverpool; Mercado Libre ordena por probabilidad y no manda un número. ⚠️ Ilustrativo: itemGroup y webCategory. La categoría web es informativa: la asigna Liverpool y el tercero nunca la manda.
Bajar la categoría, que es la plantilla del negocio: atributos, tipo, valores cerrados, cuáles son obligatorios y en qué versión. Los valores vienen aquí; no hay una llamada aparte por lista ni por marca.
Request
GET /partner/v1/categories/CAT-DEMO-0412?business=MKP
Authorization: Bearer eyJ...ejemplo
id, value_type, tags (required, allow_variations, variation_attribute) y values siguen a Mercado Libre. filledBy, acceptedOnlyAtCreation, version y recommended son de Liverpool. ⚠️ Ilustrativo: imageRules y el código de calculatedFields. La plantilla trae cinco secciones; aquí los atributos van en una.
Paso 6POST/partner/v1/validations
Probar hasta 100 productos con las mismas reglas del portal, sin crear nada. Cada elemento de items es el cuerpo de POST /proposals. Validar uno es ese cuerpo, sin envoltura. Por producto dice qué está mal, campo por campo, y qué pasaría al mandarlo.
Request
POST /partner/v1/validations
Authorization: Bearer eyJ...ejemplo
Content-Type: application/json
{
"success": true,
"data": {
"results": [
{
"externalRef": "DEMO-PB20",
"valid": false,
"outcome": "WOULD_CREATE",
"completeness": 0.82,
"photoResponsibility": "SUPPLIER",
"categoryVersion": 7,
"calculated": { "TITULO_WEB": "Batería externa Marca Demo PB-DEMO-20" },
"errors": [
{
"variantRef": "DEMO-PB20-BLA",
"path": "/items/0/variations/1/attributes/0/value_name",
"attribute": "GTIN",
"code": "EAN_CHECK_DIGIT",
"message": "El dígito verificador no corresponde a un EAN-13",
"value": "2000000000023"
},
{
"path": "/items/0/attributes/3/value_id",
"attribute": "TIPO_CONECTOR",
"code": "VALUE_NOT_IN_LIST",
"message": "«USB C» no está en la lista; manda el id del valor",
"value": "USB C",
"allowedValues": "values de TIPO_CONECTOR en GET /categories/CAT-DEMO-0412"
}
],
"warnings": [
{ "attribute": "MATERIAL", "code": "RECOMMENDED_MISSING", "message": "Recomendado para esta plantilla" }
]
}
]
}
}
Según la arquitectura: hasta 100 por llamada (la propuesta funcional decía 50) y sin lote de sólo validación en la v1. Los errores de negocio vienen dentro de cada producto, con 200; no reserva el EAN. ⚠️ Ilustrativo: el código de calculated.
Paso 7GET/partner/v1/products/search
Saber, antes de mandar, si el producto ya existe y qué le toca: crearlo, ofertar o nada. Se busca por GTIN (product_identifier) o por texto (q). Nunca dice qué seller es el dueño. Si existe, la misma respuesta trae el SKU y si Mirakl ya acusó: no hay otra ruta para «sobre qué ofertar».
Request
GET /partner/v1/products/search?business=MKP&status=active&product_identifier=2000000000039
Authorization: Bearer eyJ...ejemplo
Si no hay resultado, results viene vacío y toca crear. Con q puede haber varios. miraklStatus es ACKNOWLEDGED, PENDING o FAILED, y esta consulta no reenvía nada. ⚠️ ACKNOWLEDGED dice que Mirakl recibió el envío, no que ya acepte ofertas. ⚠️ Ilustrativo: la envoltura query.
Paso 9POST/partner/v1/proposals
Mandar un producto con sus variantes. El atributo y la variante viajan como en Mercado Libre; el sobre (business, externalRef, logística) es de Liverpool. Responde en el momento: la propuesta creada y en aprobación, o el motivo por el que no se creó nada. Precio e inventario no entran: la oferta es de Mirakl.
Con catalogProductId no se manda la ficha. No se crea propuesta: la respuesta trae el SKU y allowedAction = MULTI_OFFER, y la oferta se crea en Mirakl. Es el mismo papel que catalog_product_id en Mercado Libre.
Según la arquitectura: 201 en línea, con IN_APPROVAL (la propuesta funcional decía 202 con VALIDATING); ⚠️ si la prueba de carga da tiempos altos, pasaría a 202. Precio, inventario y fecha de disponibilidad no entran por esta API.
Paso 10POST/partner/v1/batches
Mandar hasta 1,000 productos de un negocio en una llamada. Acusa en el momento y procesa en segundo plano; un producto malo no detiene a los demás.
Paso 11AVISOproposal.receivedel PIM llama al webhook del tercero
El PIM avisa que recibió el producto del lote. Todo aviso llega firmado (HMAC-SHA256 de «timestamp.cuerpo» con el secreto de la suscripción) y al menos una vez: el repetido se descarta por eventId y el orden lo da version.
Response que espera el PIM, en 10 segundos o menos
HTTP/1.1 200 OK
⚠️ Ilustrativo: batchId y links.batch. El catálogo no dice si este aviso sale uno por producto o uno por lote.
Paso 13GET/partner/v1/batches/{batchId}/items
Ver el resultado de cada producto del lote, ligado a su referencia y nunca a su posición. Lo que requiere corrección no creó nada y se reenvía con la misma referencia.
Request
GET /partner/v1/batches/BATCH-000045/items?page=1&size=50
Authorization: Bearer eyJ...ejemplo
{
"eventId": "EVT-000104",
"type": "proposal.correction_required",
"occurredAt": "2026-11-02T17:05:12Z",
"partnerId": "SELLER-DEMO-01",
"business": "MKP",
"proposalId": "PROP-000123",
"externalRef": "DEMO-PB20",
"status": "CORRECTION_REQUIRED",
"previousStatus": "IN_APPROVAL",
"version": 4,
"correctionDueAt": null,
"reasons": [
{
"attribute": "TITULO",
"code": "SCORE_BELOW_THRESHOLD",
"area": "IA_MARKETPLACE",
"message": "El título no dice qué es el producto; incluye el tipo y la capacidad"
}
],
"requiredActions": ["FIX_FIELDS"],
"links": { "proposal": "/partner/v1/proposals/PROP-000123" }
}
Response que espera el PIM
HTTP/1.1 200 OK
Según la arquitectura: correctionDueAt vale null mientras el negocio no tenga plazo configurado. ⚠️ Ilustrativo: el valor de area, que fija el motor de estados.
Paso 15PATCH/partner/v1/proposals/{proposalId}
Corregir sólo lo que se pidió, sobre la versión que leyó; si alguien la cambió antes, responde 409.
⚠️ Ilustrativo: el valor de If-Match, version y el estado de la respuesta: la propuesta funcional dice VALIDATING, pero la arquitectura deja ese estado sólo para los lotes.
Paso 17AVISOproduct.sku_createdel PIM llama al webhook del tercero
SAP asignó el SKU: trae el SKU y el EAN de cada variante.
Pedir una dirección de subida por archivo, cuando la foto no tiene URL pública. Si la tiene, va en pictures.source del alta y esta llamada no se hace. El archivo se sube con PUT antes de 15 minutos, y después se usa su mediaId.
Request
POST /partner/v1/media/uploads
Authorization: Bearer eyJ...ejemplo
Idempotency-Key: 00000000-0000-4000-8000-000000000005
Content-Type: application/json
⚠️ Ilustrativo: la forma de la respuesta; el catálogo dice «juego guardado y validado».
Paso 22AVISOproduct.publishedel PIM llama al webhook del tercero
Un canal confirmó; aquí fue el último, así que la propuesta queda publicada. Trae el detalle por canal; el acuse dice que el canal recibió el producto, no que ya se vea.
⚠️ Ilustrativo: channel, channels, los códigos de canal y publishedAt. Los estados por canal sí son del catálogo: PENDING, SENT, PUBLISHED, FAILED y RETIRED.
Paso 23GET/partner/v1/events?cursor=
Recuperar en orden, desde donde se quedó, los avisos de los últimos 30 días. Es el respaldo de los webhooks.
Request
GET /partner/v1/events?cursor=EVT-000104&types=proposal.accepted,product.sku_created
Authorization: Bearer eyJ...ejemplo
⚠️ Ilustrativo: events, nextCursor y el formato de types. Los 30 días son un supuesto de arranque.
Paso 24GET/partner/v1/products/{id}/permissions
Saber qué puede cambiar de un producto publicado: qué es editable, qué pide aprobación y qué es de sólo lectura. Lo que no esté definido sale de sólo lectura.
Request
GET /partner/v1/products/PROP-000123/permissions
Authorization: Bearer eyJ...ejemplo
⚠️ Ilustrativo: la forma de cada cambio en changes y el valor de If-Match.
5. ¿Está robusta? Qué le falta para quedar al centavo
Veredicto. La base es sólida y ya está escrita: una sola puerta para sistemas, límites por consumidor que fallan cerrado, idempotencia en toda escritura, resultado por producto ligado a la referencia del tercero, avisos firmados con reintentos y un feed de respaldo, versionado con convivencia y un sandbox con el mismo contrato. Lo que falta no es de diseño sino de cierre: el contrato todavía describe en palabras muchos campos (varias fichas de arriba llevan alguno ilustrativo), no tiene un catálogo único de errores, deja abiertos algunos casos de borde y casi ningún número está medido. Siete recomendaciones bloquean el piloto, once deben quedar antes de producción y una es mejora.
Lo que ya está cubierto
Tema
Lo que ya tiene
Dónde
Seguridad y alcances del token
OAuth 2.0 con credenciales de cliente, token de 15 minutos y siete alcances. Cada servicio vuelve a validar firma, emisor y audiencia, y el estado del consumidor se lee en cada llamada, así que una suspensión vale en la siguiente.
Propuesta funcional §5.1; decisiones PD-26 y T-4
Idempotencia y reintentos
Idempotency-Key en toda escritura: la misma llave da el mismo recibo, y con otro contenido, 409 IDEMPOTENCY_MISMATCH. externalRef único por tercero, If-Match al corregir y modificar, y recibos que se consultan.
Propuesta funcional §5.0 y §5.9; modelo de datos
Lotes y fallas parciales
Un producto malo no tumba el lote; el resultado va por producto, ligado a la referencia y nunca a la posición; un lote que pasa el límite se rechaza completo; un negocio por lote.
Propuesta funcional §5.0 y §5.5; contratos §2.2; decisiones T-2
Webhooks
Firma HMAC-SHA256 de «timestamp.cuerpo» que vence a los 5 minutos, respuesta en 10 segundos, reintentos de 1 minuto a 24 horas, entrega al menos una vez con eventId y version, historial de entregas con reintento manual, aviso de prueba, rotación del secreto con 24 horas de traslape y feed de respaldo.
Propuesta funcional §5.11; decisiones T-6 y Alertas
Versionado
/v1 en la ruta; agregar no rompe; lo que rompe va a /v2 con 6 meses de convivencia y las cabeceras Deprecation y Sunset. Plantillas con versión, fecha de vigencia, aviso template.changed y error TEMPLATE_VERSION_OUTDATED.
Propuesta funcional §5.0 y §5.2; contratos §1
Límites de uso
Por consumidor y sin valor por omisión: un límite sin configurar vale null y no deja escribir. Cabeceras RateLimit-* y Retry-After, y los errores RATE_LIMITED y DAILY_QUOTA_EXCEEDED.
page, size de 1 a 200 y total; cursor en el feed; filtros por estado, fecha de cambio y referencia.
Propuesta funcional §5.0, §5.5 y §5.9
Catálogo de errores
Un solo sobre con código, mensaje, correlationId y detalle por producto, variante y campo, con su ruta, el valor recibido y los valores permitidos; nueve códigos nuevos.
Propuesta funcional §5.0; contratos §1
Ambiente de pruebas y pruebas de contrato
Sandbox con el mismo contrato, plantillas reales y SAP, Compras y canales simulados; OpenAPI 3.1, JSON Schema por plantilla y colección de Postman; pruebas de contrato de la tabla de estados.
Propuesta funcional §5.12; decisiones T-7 y T-3
Observabilidad
correlationId en cada error, recibo por cada escritura, entrega por canal e historial de entregas de avisos.
Propuesta funcional §5.0, §5.9 y §5.11
Lo que no está medido
Se dice «sin dato» en lugar de inventar, y cada número tiene cómo medirse.
Decisiones RF-GEN-08
Lo que falta
#
Prioridad
Qué hacer
Por qué y fuente
1
Bloquea el piloto
Cerrar el contrato en un OpenAPI 3.1, con el nombre exacto de cada campo, un ejemplo por operación y por aviso, el alcance que pide cada operación y el identificador que lleva /products/{id}.
El catálogo describe en palabras muchas salidas y sólo un aviso tiene cuerpo de ejemplo; arriba, varias fichas llevan campos ilustrativos. Quedan abiertos el estado tras PATCH (la propuesta funcional dice VALIDATING, que la arquitectura deja sólo para lotes) y el {id} de /products. Fuente: propuesta funcional §5.1 a §5.11 (columna «Salida») y §5.12, que ya promete el OpenAPI; contratos §1.
2
Bloquea el piloto
Un catálogo único de errores: código, HTTP, cuándo sale, si se reintenta y qué hacer, con los códigos por campo.
Los códigos están repartidos entre §5.0, §5.4, §5.5 y §5.10, los contratos de la arquitectura y los contratos internos; los de campo sólo aparecen en ejemplos, y varios 4xx no tienen código (imágenes, URL de webhook, sugerencias y lote demasiado grande). Que el error diga dónde está el problema es uno de los pedidos del seller. Fuente: propuesta funcional §5.0 y principio 6 (§2); contratos §1.
3
Bloquea el piloto
Decir de dónde sale el valor de If-Match: devolver ETag o version en cada lectura de propuesta y de producto.
Corregir y modificar exigen If-Match y responden 409 VERSION_CONFLICT, pero ninguna lectura documenta ETag ni version; sólo los avisos traen version. Fuente: propuesta funcional §5.0 (cabeceras), §5.5 y §5.10.
4
Bloquea el piloto
Fijar 201 o 202 para el envío individual antes de publicar el contrato: correr antes la prueba de carga, o documentar desde el primer día que el cliente acepta las dos respuestas.
La arquitectura responde 201 en línea, pero si los tiempos salen altos pasa a 202; cambiarlo después rompe a quien ya integró. Fuente: contratos §2.1 ⚠️; decisiones T-2; DUD-38.
5
Bloquea el piloto
Una tabla de escenarios del sandbox: qué mandar para provocar cada desenlace (Compras acepta o rechaza, marca bloqueada, ya existe, falla de SAP, faltan imágenes, publicación fallida) y qué aviso llega.
El sandbox simula SAP, Compras y canales, pero la marca en externalRef que decide a Compras no está definida, ni cómo provocar los demás avisos. Fuente: propuesta funcional §5.12; decisiones T-7.
6
Bloquea el piloto
Medir desde el primer día del piloto, por consumidor y operación: llamadas, errores por código, latencia p95, respuestas 429, entregas de avisos y su demora; y ligar X-Request-Id, correlationId y el aviso que resulte.
La arquitectura no compromete límites ni nivel de servicio hasta medir, y esa medición sale del piloto y de la prueba de carga. Tampoco dice si correlationId es el mismo X-Request-Id. Fuente: decisiones RF-GEN-08 («Cómo medirlo»); propuesta funcional §5.0.
7
Bloquea el piloto
Elegir el proveedor de identidad y su plan para dar las credenciales del piloto.
Sin él no hay token. Ya está registrado como duda crítica. Fuente: dudas de la arquitectura, 1; PD-26.
8
Antes de producción
El tope diario dentro de un lote: rechazar el lote completo al recibirlo si no cabe en lo que queda del día, o sumar un resultado por producto para lo que no entró.
El resultado por producto tiene cuatro valores y ninguno dice «pasó el tope»; DAILY_QUOTA_EXCEEDED sólo existe como error de una llamada. Rechazar completo es coherente con «nunca se trunca». Fuente: contratos §1 y §2.2; decisiones T-5; propuesta funcional §5.0.
9
Antes de producción
Cerrar los casos de borde del lote: si proposal.received sale uno por producto o uno por lote (recomiendo uno por lote, o ninguno, porque el 202 ya acusa), qué quiere decir que un lote quede FAILED y qué responde una referencia repetida dentro del mismo lote.
Con 1,000 productos por llamada serían 1,000 avisos de recibido. El modelo de datos ya tiene el estado FAILED y una referencia única por lote, pero el contrato no dice qué ve el tercero. Fuente: propuesta funcional §5.11 (tabla de eventos); modelo de datos, lotes.
10
Antes de producción
Idempotencia completa: publicar cuánto tiempo vale una llave y qué recibe un reintento mientras la primera petición sigue en proceso.
La ventana de 24 horas es un supuesto ⚠️ y el contrato no cubre el reintento simultáneo. Fuente: modelo de datos, recibos de envío; propuesta funcional §5.0.
11
Antes de producción
Suscripciones que fallan siempre: cuándo se desactiva una suscripción, cómo se avisa y cómo se reactiva.
Tras unos 10 intentos en 24 horas la entrega queda FAILED y el evento sigue en el feed, pero no hay regla para la suscripción, aunque el modelo ya tiene ACTIVE y DISABLED. Fuente: propuesta funcional §5.11; modelo de datos, suscripciones.
12
Antes de producción
Paginación estable para sincronizar: fijar el orden de cada lista y usar cursor también en GET /proposals?updatedSince=.
Las listas por página no fijan orden; si los datos cambian entre páginas, una sincronización puede saltar o repetir registros. El feed ya usa cursor. Fuente: propuesta funcional §5.0 (paginación) y §5.5.
13
Antes de producción
Qué versión de plantilla vale para lo que ya está en curso, la del alta o la vigente, y avisar los cambios con anticipación con su fecha de vigencia.
Sin plazo de gracia en la v1, una corrección contra la versión anterior puede fallar con TEMPLATE_VERSION_OUTDATED; la propuesta funcional decía que un obligatorio nuevo aplica a las propuestas nuevas después del aviso. Fuente: decisiones T-8; contratos §1; propuesta funcional, principio 11 (§2); dudas de la arquitectura, 12.
14
Antes de producción
Cabeceras de límites por tipo de límite: decir qué límite reporta cada RateLimit-* (lecturas, escrituras, sugerencias o tope diario) y probar que un cliente que las respeta no recibe 429.
Hay tres límites por minuto y un tope diario, y un solo juego de cabeceras. Fuente: propuesta funcional §5.0 (cabeceras); decisiones RF-GEN-08.
15
Antes de producción
Pruebas de contrato contra el OpenAPI en el CI, y los ejemplos públicos generados desde ese OpenAPI.
Hoy sólo la tabla de estados tiene prueba de contrato, y estos ejemplos son una copia a mano que nadie compara. Fuente: decisiones T-3; propuesta funcional §5.12.
16
Antes de producción
Revisión de Seguridad de la API completa antes de abrirla: alcances mínimos por operación, validación de las URL de webhook al registrarlas y en cada entrega, secretos cifrados que se muestran una sola vez y su rotación.
El diseño ya prevé estas protecciones y falta verificarlas; PD-26 y las alertas los ratifica Arquitectura con Seguridad. Fuente: decisiones PD-26 y Alertas; modelo de datos, suscripciones.
17
Antes de producción
Medir y publicar lo que hoy es supuesto: límites, retención del feed, de las entregas y de los recibos, plazo de corrección, máximo diario de SAP y umbral del score.
Son «sin dato» o valores de arranque ⚠️; correctionDueAt vale null mientras el plazo no se decida. Fuente: decisiones RF-GEN-08; dudas de la arquitectura 2, 5, 11 y 14.
18
Antes de producción
Descarga de archivos por URL: quién baja el fileUrl de los lotes y las URL de imágenes, con qué reintentos, tamaño máximo y vigencia, y cómo se reporta una URL caída.
Ya está abierta para las imágenes, y el lote por archivo, de hasta 20,000 productos, tiene el mismo hueco. Fuente: propuesta funcional §5.5 y §5.8 («Abierto»); dudas de la arquitectura, 13.
19
Mejora
Quitar de la v1 lo que la v1 no trae y alinear las fuentes: el alcance credentials.manage sin rutas, el mode=VALIDATE_ONLY de los lotes, el tope de 50 de la validación (la arquitectura dice 100) y la notación {id} frente a {proposalId}.
La propuesta funcional los sigue listando, y un integrador lee las dos. Fuente: propuesta funcional §5.1, §5.4, §5.5 y §5.8 a §5.10; decisiones T-8; contratos §1.
«Propuesta funcional» es docs/catalogacion-por-api/propuesta-flujo-catalogacion-api.md; «contratos», «decisiones», «modelo de datos» y «dudas de la arquitectura» son los archivos de docs/arquitectura/catalogacion-por-api/, en el repositorio del discovery.