# Implementar la API de RM con IA

Esta página es para pegarla en un asistente de código (Claude, ChatGPT, Cursor, Copilot…). Trae un **contexto compacto**
de la API y **prompts listos para copiar**. El contrato completo es `openapi.json` (OpenAPI 3.1): descárgalo de
`/docs/openapi.json` y pégalo o adjúntalo junto con el prompt. Para las reglas de negocio con todo detalle está
`/docs/api.md`.

Nunca pegues una llave real en un chat o en un repositorio: usa una variable de entorno (`RM_API_KEY`) y un valor
falso en los ejemplos.

## Contexto compacto

```text
API REST v1 de RM (ecommerce multi-tenant). Base: {base_url} (p. ej. https://retail-s.online/api/v1). JSON UTF-8.

TENANT Y LLAVES
- Cada llave de API pertenece a UNA tienda: todo lo que lee o escribe es de esa tienda. No hay forma de elegir otra.
- Autenticación: cabecera "Authorization: Bearer rk_<id>_<secreto>". La sesión del back office no sirve.
- GET {base_url}/ devuelve la tienda, el nombre de la llave y sus scopes (prueba de conexión).

SCOPES (por área; GET pide read_<área>, POST/PUT/PATCH/DELETE piden write_<área>; escribir incluye leer)
- products: productos, variantes, imágenes, categorías, marcas, colecciones, atributos, plantillas, modificadores, traducciones, alta masiva, importación.
- inventory: inventario y ubicaciones. prices: precios y listas. customers: clientes y grupos.
- orders: pedidos, estados, pagos, devoluciones, reembolsos, paqueterías y medios de pago.
- integrations: conexiones, corridas de sincronización y referencias externas.
- Sin el permiso: 403 {"detail": "La llave no tiene el permiso «write_orders»."}.

IDENTIDAD DE LOS REGISTROS
- Con código (productos, categorías, marcas, colecciones, atributos, ubicaciones, listas de precios, grupos): el código, en mayúsculas (ply-001 = PLY-001).
- Variantes: por SKU. Clientes, pedidos, líneas, devoluciones, imágenes, conexiones y referencias externas: "id" público UUID (guárdalo como texto).
- PUT /<recurso>/<código>/ es upsert (201 crea, 200 reemplaza). PATCH actualiza solo lo enviado (404 si no existe). Los arreglos "options", "variants" (con variants_mode "exact"), "addresses" y "attributes" son DECLARATIVOS: lo que envías es lo que queda.
- Montos y cantidades: texto decimal ("199.0000"). Fechas: ISO 8601 con zona.

PAGINACIÓN Y SINCRONIZACIÓN
- Listados por cursor: ?limit= (1 a 500, por omisión 100). Respuesta {"next": url|null, "results": [...]}. Pide la URL "next" TAL CUAL hasta que sea null. No hay count, previous ni offset (offset da 400).
- Incremental: ?updated_since=<ISO 8601> en los listados por código y en clientes y pedidos; pedidos también ?placed_since=.
- Los resultados de trabajos masivos (bulk/import) se paginan con ?offset= y ?limit= en el trabajo, no por cursor.

IDEMPOTENCIA
- Cabecera opcional "Idempotency-Key" (hasta 100 caracteres) en POST /orders/, POST /orders/{id}/pay/, /returns/ y /refunds/.
- Misma llave y mismos datos: no repite la operación (201 la primera vez y 200 al repetir; pay siempre 200). Misma llave con otros datos: 409.
- Sin la cabecera, un reintento puede duplicar (pay repetido da 409 "ya está pagado").

CÓDIGOS DE RESPUESTA
- 200/201/204 correcto; 202 trabajo masivo encolado.
- 400 validación (detalle por campo; en bloques, {"errors": {"<índice>": [...]}}). 401 llave ausente, inválida o desactivada. 403 falta el scope. 404 no existe en tu tienda. 409 el paso no aplica al estado, o Idempotency-Key reutilizada con otros datos.
- 429: 600 peticiones por minuto por llave. Espera "Retry-After" segundos. Las respuestas autenticadas traen X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset (epoch), salvo en 401 y 403.
- Reintenta con espera creciente solo 429, 5xx y errores de red; nunca un 4xx.

OPERACIONES MASIVAS
- POST /prices/bulk/ y POST /inventory/bulk/: hasta 1000 filas. Por omisión todo o nada; ?partial=true aplica las válidas y responde 200 con "rows" por fila. inventory on_hand es ABSOLUTO (lo que dice el ERP), no un ajuste.
- POST /products/bulk/ (hasta 500 productos, upsert, 202 + id de trabajo; consulta GET /products/bulk/{id}/).
- POST /products/import/ (multipart, NDJSON, solo crea, 202; resultado en GET /products/import/{id}/rows/).

PEDIDOS
- Estados separados: status (open|cancelled|archived), payment_status, fulfillment_status; delivery_type (shipping|pickup|takeout|dine_in|delivery_app).
- POST /orders/ rechaza (400) sin existencia o sin precio. Cambios: POST /orders/{id}/{pay,prepare,ship,ready,deliver,cancel,archive,unarchive,returns,refunds}/ (409 si el paso no aplica).
- Inventario: cada pedido aparta al crearse; ship/ready descuenta; cancelar libera. /prepare/ solo deja el sello informativo erp_accepted_at.

WEBHOOKS SALIENTES (RM -> tu sistema; app "Webhooks" de la tienda)
- POST JSON a una URL https pública: {"id", "event", "store", "occurred_at", "data"}.
- Eventos: order.created, order.updated, order.paid, order.payment_failed, order.shipped, order.ready_for_pickup, order.delivered, order.cancelled, order.returned, order.refunded, inventory.changed.
- Cabeceras: X-RM-Event, X-RM-Event-Id (= id), X-RM-Signature = HMAC-SHA256 hexadecimal del CUERPO EXACTO (bytes sin reserializar) con el secreto de la app; User-Agent RM-Webhooks/1.
- Responde 2xx rápido (espera máxima 5 s). Reintentos con 5xx, 429, 408 o error de red: hasta 8 en ~2 h, mismo cuerpo, mismo id, misma firma. Otro 4xx no se reintenta.
- Al menos una vez y sin orden garantizado: deduplica por id y compara occurred_at (inventory.changed: ignora el más viejo que el último aplicado por SKU).
- Tras 5 fallos seguidos la app se pausa.
```

## Prompts

Cada prompt dice qué pegar junto con él. Pega siempre el **contexto compacto** de arriba y el `openapi.json`.

### 1. Conector de catálogo e inventario desde un ERP

Pega junto con: el contexto compacto, `openapi.json`, y un ejemplo de 3 a 5 artículos de tu ERP (JSON o CSV, sin datos reales de clientes).

```text
Eres un ingeniero de integraciones senior. Con el contexto y el OpenAPI de la API de RM que te adjunto, escribe un conector
que sincroniza el catálogo y el inventario desde mi ERP hacia RM. Lenguaje: <Python 3.12 | Node 20>. ERP de origen: <nombre>.

Requisitos:
1. Lee la llave desde la variable de entorno RM_API_KEY y la base desde RM_BASE_URL. Nada de secretos en el código.
2. Primera carga: productos por POST /products/bulk/ (lotes de 500) o, si son más de 20 000, POST /products/import/ con NDJSON. Consulta
   el trabajo hasta que termine y guarda en un archivo de errores las filas con status "error".
3. Carga incremental: usa updated_since del ERP; envía PUT /products/{code}/ solo de lo que cambió. Si el ERP ya tiene variantes, usa variants_mode "exact".
4. Precios: POST /prices/bulk/?partial=true en bloques de 1000, uno por lista de precios. Existencias: POST /inventory/bulk/?partial=true con
   on_hand ABSOLUTO. Reporta cada fila fallida sin detener el lote.
5. Guarda el mapa ERP <-> RM con POST /external-references/ (object_type product o variant) y reporta cada corrida con POST /connections/{id}/sync-runs/.
6. Reintentos: espera creciente en 429 (respeta Retry-After), 5xx y errores de red; nunca reintentes un 4xx. Respeta el límite de 600 peticiones por minuto.
7. Código claro, con registro (logging) sin datos personales, una prueba por cada función que transforma datos y un README con cómo ejecutarlo.

Entrega: estructura de archivos, el código completo y los comandos para probarlo contra una tienda de pruebas. Si el contrato no cubre algo, dilo en vez de inventar un endpoint.
```

### 2. Consumidor de webhooks de pedidos con verificación de firma

Pega junto con: el contexto compacto y, si quieres ejemplos reales, un cuerpo de evento de pruebas.

```text
Con el contexto de la API de RM que te adjunto, escribe un receptor HTTP de webhooks de pedidos. Framework: <FastAPI | Express | Django>.

Requisitos:
1. Verifica X-RM-Signature: HMAC-SHA256 en hexadecimal sobre el cuerpo EXACTO recibido (bytes crudos, sin parsear ni reserializar)
   con el secreto de RM_WEBHOOK_SECRET. Compara con una función de tiempo constante. Firma inválida: 401 sin procesar.
2. Responde 2xx en menos de 5 s: guarda el evento en una cola o tabla y procésalo aparte.
3. Deduplica por "id" (X-RM-Event-Id): el mismo evento puede llegar varias veces. Haz el procesamiento idempotente.
4. No asumas orden: compara occurred_at y descarta un evento más viejo que el último aplicado (en inventory.changed, por sku).
5. Maneja order.created, order.paid, order.shipped, order.cancelled y order.refunded; ignora con 2xx los eventos que no conoces.
6. Tras 2xx RM no reintenta; con 5xx, 429 o timeout sí. Distingue errores temporales (5xx) de datos inválidos (4xx).
7. Pruebas: firma válida, firma inválida, evento repetido y evento fuera de orden. Incluye un comando curl que firme un cuerpo de ejemplo con openssl.

Entrega código completo y las pruebas. No registres el secreto ni el cuerpo completo del pedido en los logs.
```

### 3. Script de importación masiva

Pega junto con: el contexto compacto, `openapi.json` y una muestra de 5 filas de tu archivo (CSV o Excel) con las columnas reales.

```text
Con el contexto y el OpenAPI de la API de RM que te adjunto, escribe un script de línea de comandos que importe mi archivo de productos <CSV | XLSX> a RM.
Lenguaje: <Python 3.12 | Node 20>. Te paso una muestra de 5 filas.

Requisitos:
1. Mapea las columnas a la estructura de PUT /products/{code}/ (code, name, status, category, options, variants, attributes...). Pide confirmar el mapeo antes de enviar.
2. Modo --dry-run: valida y muestra cuántos productos se crearían y qué filas son inválidas, sin llamar a la API.
3. Envío: POST /products/import/ (NDJSON) para cargas grandes de productos nuevos; POST /products/bulk/ en lotes de 500 si hay que actualizar existentes.
4. Consulta el trabajo hasta que termine (con espera creciente) y escribe un reporte con las filas "error" y su mensaje. Código de salida distinto de 0 si hubo errores.
5. Llave en RM_API_KEY y base en RM_BASE_URL; jamás en el código ni en el archivo de reporte.
6. Es reanudable: puede volver a correr sin duplicar (usa upsert por código o salta los ya creados).
7. Después, opcionalmente, imágenes por URL (POST /products/{code}/images/ con "url" https) y existencias con /inventory/bulk/.

Entrega el script completo, un ejemplo de archivo de entrada y el comando para ejecutarlo.
```

### 4. Cliente de la API en Python o Node

Pega junto con: el contexto compacto y `openapi.json`.

```text
Con el OpenAPI y el contexto de la API de RM que te adjunto, genera un cliente pequeño y tipado para <Python 3.12 (httpx) | Node 20 (fetch) con TypeScript>.

Requisitos:
1. Una clase RMClient(base_url, api_key) con la llave en la cabecera Authorization: Bearer.
2. Un método por operación de los recursos: products, variants, inventory, prices, customers y orders (incluidas las acciones pay, ship, cancel, returns y refunds).
3. Los listados son iteradores/generadores que siguen "next" tal cual hasta que sea null; acepta limit y updated_since.
4. Reintentos con espera creciente en 429 (respeta Retry-After), 5xx y errores de red; ningún reintento en 4xx. Errores tipados: ValidationError (400, con el detalle por campo),
   AuthError (401/403), NotFound (404) y ConflictError (409).
5. Idempotency-Key opcional en create_order, pay_order, create_return y refund_order; genera un UUID si el llamador no lo da y reutiliza ESE MISMO UUID en todos los reintentos de la misma operación (uno nuevo por operación, nunca uno nuevo por reintento).
6. Decimales como texto (no float); fechas ISO 8601.
7. Pruebas con un servidor simulado para: paginación, 429 con Retry-After y 409 por Idempotency-Key reutilizada.

Entrega el cliente, las pruebas y un ejemplo de uso de 20 líneas. No incluyas ninguna llave real.
```

### 5. Revisión de seguridad del conector

Pega junto con: el contexto compacto y el código completo del conector (sin el archivo `.env`).

```text
Actúa como revisor de seguridad. Te paso el contexto de la API de RM y el código de mi conector. Revísalo y dame una lista priorizada (crítico, alto, medio, bajo)
con archivo y línea, el riesgo en una frase y el arreglo concreto.

Busca en particular:
1. Secretos: llave de API (rk_...), secreto de webhooks o credenciales del ERP en el código, en logs, en mensajes de error, en el historial de git o en archivos de ejemplo.
2. Receptor de webhooks: firma HMAC-SHA256 sobre el cuerpo crudo, comparación en tiempo constante, rechazo sin firma, deduplicación por id, límite de tamaño del cuerpo y respuesta rápida.
3. Mínimo privilegio: la llave debe tener solo los scopes que el conector usa (read_orders sin write si solo lee pedidos, etc.).
4. Reintentos: ninguno en 4xx, espera creciente en 429/5xx, respeto de Retry-After, Idempotency-Key en las operaciones que crean o cobran.
5. Validación de datos del ERP antes de enviarlos; manejo de errores que no pierda datos ni los duplique; verificación TLS activa (nunca verify=False).
6. Datos personales de clientes en logs, archivos temporales o reportes.
7. Dependencias desactualizadas o sin versión fija.

No arregles nada sin mostrarme antes el hallazgo. Si algo del contrato de RM no te consta, dilo en vez de suponer.
```
