# Shopify

> Piloto privado para leer en vivo una tienda Shopify: datos de la tienda, pedidos, un pedido con su detalle, ventas sumadas de un período, productos e inventario. Solo lectura, sin datos de clientes y sin guardar pedidos en Connect.



**Piloto privado.** Emisso habilita Shopify por organización y la integración todavía no completa su validación con una tienda real. Solo las organizaciones invitadas ven el conector en el catálogo, pueden conectar una tienda y ejecutar sus tools. Esta guía describe cómo funciona.

`shopify` conecta **una tienda Shopify por conexión** y deja que tus agentes lean sus pedidos, sus ventas, sus productos y su inventario. Es una **herramienta en vivo**: cada llamada consulta la tienda en ese momento con la API de Shopify. Connect no sincroniza la tienda ni guarda pedidos, productos ni stock. Por eso este sistema no tiene `conexion.sincronizar` ni tools `.consultar`.

**Solo lectura.** Ninguna tool crea, cancela ni reembolsa pedidos, ni cambia precios, productos o stock. **Sin datos de clientes:** Connect no pide a Shopify nombres, correos, teléfonos ni direcciones de compradores, y no los publica.

## Qué puede hacer una conexión [#qué-puede-hacer-una-conexión]

| Tool                       | Permiso de la clave  | Qué hace                                                                                                                                                                                    |
| -------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shopify.tienda.leer`      | `shopify:pedidos`    | Datos de la tienda: dominio `.myshopify.com`, moneda, zona horaria, si los precios incluyen impuestos, los permisos concedidos y cuánto historial de pedidos alcanza.                       |
| `shopify.pedidos.listar`   | `shopify:pedidos`    | Lista pedidos con estado, cancelación y montos, hasta 25 por página. Filtra por rango de creación, estado de pago o nombre (`#1001`). Excluye pedidos de prueba salvo que pidas incluirlos. |
| `shopify.pedido.leer`      | `shopify:pedidos`    | Un pedido con códigos de descuento, impuestos, envíos, líneas (de a 25), transacciones de pago y reembolsos.                                                                                |
| `shopify.ventas.resumir`   | `shopify:pedidos`    | Suma exacta de los pedidos creados en un rango de hasta 366 días: subtotal, descuentos, impuestos, envío, bruto, reembolsado, neto y total actual. Excluye cancelados y pedidos de prueba.  |
| `shopify.productos.listar` | `shopify:productos`  | Productos con sus variantes, SKU y precios, del más recientemente actualizado al más antiguo. Filtra por estado o SKU.                                                                      |
| `shopify.inventario.leer`  | `shopify:inventario` | Stock de un ítem de inventario en cada ubicación: disponible, en mano, comprometido y entrante.                                                                                             |

Cada llamada lleva el `connectionId` de una conexión, incluso si tienes una sola: la conexión es la tienda.

## Qué necesitas para conectar [#qué-necesitas-para-conectar]

Para conectar una tienda **necesitas una cuenta en Shopify con acceso de administración a la tienda que quieres leer, y tu usuario debe tener permiso sobre ella para instalar apps.** Autorizas con ese usuario: Connect no crea tiendas ni las administra.

La conexión se crea **desde el panel**: Conexiones, Conectar, Shopify. El formulario pide **dos datos, y ninguno es un secreto**:

| Campo                                            | Qué es                                                                                                                                             |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| RUT de la empresa dueña de la tienda             | La empresa a la que pertenece la tienda. Lo declaras tú.                                                                                           |
| Tienda Shopify (nombre o dominio .myshopify.com) | El nombre de la tienda (`mi-tienda`), su dominio `mi-tienda.myshopify.com` o la dirección del administrador (`admin.shopify.com/store/mi-tienda`). |

**Un dominio propio no sirve.** `tienda.miempresa.cl` no identifica la tienda ante Shopify: usa el dominio `.myshopify.com`, que aparece en el administrador de Shopify, en Configuración, Dominios.

**Shopify no confirma ese RUT.** Su autorización no informa a qué contribuyente pertenece la tienda, así que Connect no tiene contra qué compararlo: el RUT queda como lo escribiste. Si conectas varias empresas, revisa que cada conexión lleve el RUT correcto.

Al continuar, el navegador sale a Shopify para que instales la app de Emisso Connect en la tienda y apruebes sus permisos, y vuelve a Connect. **Connect no te pide tu contraseña de Shopify.** Guarda cifrado el permiso que emite Shopify, sin mostrarlo al agente.

**También puedes conectar con un enlace.** Si quien administra la tienda no tiene cuenta en tu organización, crea un enlace de conexión para Shopify (desde el panel o con `conexiones.enlace.crear`) y envíaselo. Esa persona escribe el RUT y la tienda, autoriza en Shopify y vuelve a una página de cierre; tú ves la conexión en el panel. El enlace es de un solo uso: queda consumido cuando la tienda se conecta, y no sirve vencido ni revocado. Un enlace para volver a autorizar usa la tienda que la conexión ya tiene y no permite cambiarla. Para desconectar, quien administra la tienda desinstala la app desde Shopify, o tú eliminas la conexión en el panel.

### Permisos que pide en Shopify [#permisos-que-pide-en-shopify]

Connect pide y exige **tres permisos de lectura**, y ninguno más:

| Permiso de Shopify | Para qué                                 |
| ------------------ | ---------------------------------------- |
| `read_orders`      | Pedidos, sus transacciones y reembolsos. |
| `read_products`    | Productos y variantes.                   |
| `read_inventory`   | Niveles de stock por ubicación.          |

Si la concesión no trae los tres, o trae cualquier otro (por ejemplo un permiso de escritura o de clientes), Connect la descarta, intenta retirar la app de la tienda y la conexión no se activa.

**Historial de 60 días.** Con `read_orders`, Shopify entrega solo los pedidos **creados en los últimos 60 días**. Ver pedidos más antiguos exige el permiso `read_all_orders`, que Shopify aprueba aparte para cada app. La app de Connect todavía no lo tiene, así que hoy toda conexión ve 60 días. Cada respuesta de pedidos lo informa en `historial`: `completo: false` y `diasDisponibles: 60`.

### Una tienda, una conexión [#una-tienda-una-conexión]

Una conexión lee **una sola tienda** y no cambia de tienda. Shopify mantiene una sola autorización vigente por app y tienda, así que una tienda puede estar en **una sola conexión activa** de Connect. Si autorizas una tienda que ya está conectada en otra conexión, Connect rechaza la autorización antes de completarla y la otra conexión sigue funcionando. Para mover la tienda, elimina primero la conexión anterior.

### Volver a autorizar [#volver-a-autorizar]

Si Shopify deja de aceptar la autorización, entra a **Ajustes** de la conexión y usa **Volver a autorizar**. Se autoriza **la misma tienda**: Connect la toma de la conexión y no la vuelve a pedir. Quien autoriza tiene que tener acceso a esa tienda.

### Desconectar [#desconectar]

* **Desde Connect:** elimina la conexión. Connect intenta además desinstalar su app de la tienda, lo que revoca el permiso del lado de Shopify. Si no lo consigue, desinstálala tú desde el administrador de Shopify, en Configuración, Apps y canales de venta.
* **Desde Shopify:** desinstalar la app de Emisso Connect corta el acceso de inmediato. Shopify avisa a Connect, que marca la conexión sin autorización; desde ese momento cada tool responde `connection_credential_required` hasta que vuelvas a autorizar.

## Permisos de tu clave [#permisos-de-tu-clave]

`shopify:pedidos`, `shopify:productos` y `shopify:inventario` se otorgan **solo nombrándolos**: ni `*` ni los perfiles de claves los incluyen. Una clave con `*` que llame a una tool de Shopify recibe `403 scope_not_granted`.

Si construyes sobre Connect para varios comercios, acota cada clave a las conexiones de su cliente ([conexiones de una clave](/docs/empezar/autenticacion#conexiones-de-una-clave)).

## Sumar las ventas de un período [#sumar-las-ventas-de-un-período]

```bash
curl -X POST https://connect.emisso.ai/api/v1/tools/shopify.ventas.resumir/execute \
  -H "Authorization: Bearer connect_sk_..." \
  -H "X-Connect-Connection: conn_9tKfR2mQx4Vb" \
  -H "Content-Type: application/json" \
  -d '{"input":{"desde":"2026-10-01T00:00:00-03:00","hasta":"2026-10-16T00:00:00-03:00"}}'
```

Respuesta (datos ficticios):

```json
{
  "data": {
    "desde": "2026-10-01T03:00:00.000Z",
    "hasta": "2026-10-16T03:00:00.000Z",
    "moneda": "CLP",
    "pedidos": 2,
    "cancelados": 1,
    "totales": {
      "subtotal": "41970.0",
      "descuentos": "2000.0",
      "impuestos": "6701.0",
      "envio": "3990.0",
      "bruto": "45960.0",
      "reembolsado": "12990.0",
      "neto": "32970.0",
      "actual": "32970.0"
    },
    "cobertura": { "completa": true, "motivos": [], "siguientePagina": null, "paginasLeidas": 1 },
    "historial": { "completo": false, "diasDisponibles": 60 }
  },
  "meta": { "request_id": "req_...", "tool_id": "shopify.ventas.resumir", "plane": "action" }
}
```

`desde` y `hasta` son instantes ISO 8601 con zona: `desde` incluido, `hasta` excluido. Para cortar por días de la tienda, usa la zona horaria que entrega `shopify.tienda.leer`.

## Desde un agente (MCP) [#desde-un-agente-mcp]

En MCP las seis tools se corren con la meta-tool `execute`, que es de solo lectura. Por ejemplo, para listar los pedidos pagados de octubre:

```json
{
  "tool": "shopify.pedidos.listar",
  "params": {
    "desde": "2026-10-01T00:00:00-03:00",
    "hasta": "2026-11-01T00:00:00-03:00",
    "estadoFinanciero": "paid",
    "maximo": 20
  },
  "connectionId": "conn_9tKfR2mQx4Vb"
}
```

Para seguir con la página siguiente, repite la llamada con los mismos filtros y pasa en `pagina` el valor `siguientePagina` de la respuesta. El detalle de un pedido se pide con `shopify.pedido.leer` y su `pedidoId`. Cómo conectar Claude, ChatGPT u otro cliente está en [MCP](/docs/agentes/mcp).

## Cómo leer los montos [#cómo-leer-los-montos]

### Decimales exactos, con su moneda [#decimales-exactos-con-su-moneda]

Cada monto viene como **decimal en texto** (`"12990.0"`), tal como lo entrega Shopify, junto con su `moneda` (ISO 4217). No lo conviertas a un número de coma flotante para sumarlo. Los montos de pedidos y de `shopify.ventas.resumir` están en la **moneda de la tienda**; `totalComprador` repite el total en la moneda en que pagó el comprador, que puede ser otra. **No sumes montos de monedas distintas.**

### Total, total actual, reembolsado y neto [#total-total-actual-reembolsado-y-neto]

En un pedido (`montos`):

* `total` es el total **antes de devoluciones**, con descuentos ya aplicados.
* `reembolsado` es lo devuelto hasta el momento de la consulta.
* `totalActual` es el total **después de devoluciones** y de las ediciones del pedido. Ya descuenta lo devuelto: no le restes `reembolsado`.
* `descuentos` es informativo: ya está restado del subtotal y del total.

En `shopify.ventas.resumir` (`totales`), `bruto` suma los `total`, `reembolsado` suma lo devuelto de esos mismos pedidos y `neto` es `bruto − reembolsado`, con cada devolución descontada una sola vez. `actual` suma los `totalActual`: es otra medida, que además descuenta líneas quitadas al editar un pedido, y puede no coincidir con `neto`. Elige una de las dos y no las combines.

Un reembolso de un pedido creado dentro del rango cuenta aunque haya ocurrido después: el resumen agrupa por **fecha de creación del pedido**, no por fecha del reembolso.

### Cobertura: cuándo una suma está completa [#cobertura-cuándo-una-suma-está-completa]

`shopify.ventas.resumir` lee hasta cinco páginas de 50 pedidos por llamada y se detiene antes si se acaba el tiempo o el cupo de consultas de Shopify. `cobertura.completa` es `true` solo si esa respuesta sumó **todos** los pedidos del rango que la conexión puede ver. Si es `false`, `cobertura.motivos` dice por qué:

| Motivo                                           | Qué significa                                                                                                                                                           |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mas_paginas`                                    | Quedan pedidos. Repite la llamada con el mismo rango y `pagina` igual a `cobertura.siguientePagina`, y suma tú los totales de cada respuesta.                           |
| `tope_paginas`, `tiempo_agotado`, `cupo_shopify` | Por qué esta llamada paró antes de terminar. Siempre van con `mas_paginas`.                                                                                             |
| `continuacion`                                   | La llamada se hizo con `pagina`: sus totales son solo de ese tramo. La serie está completa cuando la última respuesta ya no trae `mas_paginas` ni motivos de historial. |
| `historial_limitado`                             | El rango empieza antes de los 60 días que Shopify deja ver sin `read_all_orders`. Los pedidos más antiguos no están en la suma.                                         |
| `historial_desconocido`                          | Shopify no informó los permisos concedidos, así que no se puede afirmar el historial.                                                                                   |

**Con `completa` en `false`, no informes los totales como las ventas del período.**

En `shopify.pedido.leer`, las listas anidadas tienen tope: `transaccionesIncompletas`, `reembolsosIncompletos`, `enviosIncompletos` y los `lineasIncompletas` de cada reembolso dicen cuándo no vino todo. `montos.reembolsado` es exacto aunque la lista de reembolsos venga incompleta. En `shopify.productos.listar`, `variantesIncompletas` avisa cuando un producto tiene más de diez variantes.

## Lo que este conector no es [#lo-que-este-conector-no-es]

* **No es conciliación bancaria.** Un pedido pagado en Shopify no es un depósito en tu cuenta. Connect no lee los pagos de Shopify Payments, sus comisiones ni sus transferencias al banco. Para cuadrar con el banco, compara con los movimientos de tu [conexión bancaria](/docs/sistemas).
* **No emite documentos tributarios.** Leer un pedido no emite boletas ni facturas en el SII, y los impuestos que informa Shopify son los que calcula la tienda, no los declarados. Para emitir, mira [Emitir](/docs/emitir).
* **No guarda historia propia.** Lo que ves es lo que Shopify entrega en ese momento. Un pedido editado, reembolsado o archivado se ve como está hoy.
* **No ve más de 60 días de pedidos** mientras la app no tenga `read_all_orders`.

## Errores [#errores]

| Código                               | Qué significa                                                                                                                                             | Qué hacer                                                                                                    |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `428 connection_credential_required` | Shopify ya no acepta la autorización: se desinstaló la app, se revocó o venció; o la concesión guardada no trae exactamente los tres permisos de lectura. | En **Ajustes** de la conexión, usa **Volver a autorizar** con un usuario que tenga acceso a la misma tienda. |
| `403 connection_scope_not_enabled`   | Shopify rechazó la consulta por permisos.                                                                                                                 | Vuelve a autorizar. Si persiste, escríbenos: puede faltar una aprobación de Shopify para la app.             |
| `404 external_resource_not_found`    | El pedido o ítem de inventario no existe en la tienda de la conexión, o el pedido queda fuera de los 60 días visibles.                                    | Revisa el identificador y el historial de la conexión.                                                       |
| `409 connection_identity_mismatch`   | La tienda que respondió no es la fijada a la conexión.                                                                                                    | Avísanos: la conexión no se usará con otra tienda.                                                           |
| `429 rate_limited`                   | Shopify limitó las consultas de la tienda.                                                                                                                | Espera unos segundos y reintenta.                                                                            |
| `502 upstream_error`, `504 timeout`  | Shopify no respondió a tiempo o falló.                                                                                                                    | Reintenta en un momento. No invalida la autorización.                                                        |
| `502 upstream_unexpected_response`   | Shopify respondió con una forma que Connect no reconoce, o la tienda está pausada, congelada o bloqueada.                                                 | Revisa el estado de la tienda en Shopify. Si está activa, avísanos con el `request_id`.                      |

Cuando la autorización deja de valer, la tool responde `connection_credential_required`; una caída o un límite de Shopify responde `upstream_error` o `rate_limited` y no invalida la autorización. El envelope de cada código está en el [catálogo de errores](/docs/operar/errores).

## Próximos pasos [#próximos-pasos]

* Las seis tools, con contrato completo: [referencia de `shopify`](/docs/referencia/shopify).
* Cómo correr tools desde un agente: [MCP](/docs/agentes/mcp).
