# API y agentes — Antonysia Helados

Antonysia Helados publica su carta como datos, no solo como página. Si estás escribiendo
un agente, un bot o una integración, esto es todo lo que hace falta: dos endpoints
públicos, sin credenciales, de solo lectura.

Especificación: <https://www.antonysiahelados.com.ar/openapi.json>. Guía para agentes:
<https://www.antonysiahelados.com.ar/llms.txt>.

## Cuándo usar esta API

Sirve para contestar, con los números que cobra el sitio:

- Qué sabores hay y cuáles son veganos o sin lácteos.
- Cuánto sale un pote de 1/4, 1/2, 1 o 2 kilos, y cuánto un combo.
- Cuánto cuesta el envío, desde qué monto es sin cargo y cuál es el pedido mínimo.
- Si el local está abierto en este momento, en hora de Buenos Aires, y cuándo vuelve a abrir.
- Si una localidad o dirección entra en la zona de reparto (radio de 9 km).
- Cuánto sale un pedido armado, antes de hacerlo.

No sirve para crear pedidos ni para cobrar: **no hay endpoint público que escriba nada.**
Un pedido se arma en <https://www.antonysiahelados.com.ar/> y se confirma por WhatsApp con una
persona. Tampoco somos un marketplace, no vendemos otras marcas, no hacemos envíos fuera
del radio de 9 km y no hay venta mayorista publicada.

## Autenticación

Ninguna. No hay API keys, no hay tokens y no hay registro, porque no hay nada que
proteger: los dos endpoints devuelven la misma carta que cualquiera lee en la página, no
reciben datos personales y no escriben en ningún lado. Pedir una credencial para leer un
precio público sería un trámite sin contrapartida.

Por el mismo motivo no hay entorno de pruebas aparte: como ninguna llamada tiene efecto,
producción *es* el sandbox. Podés golpear los endpoints todo lo que necesites mientras
respetes la cuota de más abajo.

## Endpoints

| Método y ruta | Qué devuelve |
| --- | --- |
| `GET /api/v1/catalogo` | La carta entera en un solo JSON: negocio, sabores, potes, promos, adicionales, medios de pago, envío, zona y horario con la hora de Buenos Aires. |
| `GET /.well-known/mcp` | El manifiesto del servidor MCP: nombre, versión del protocolo, transporte y herramientas. |
| `POST /api/v1/mcp` | El servidor MCP: JSON-RPC 2.0 sobre Streamable HTTP. Acepta `initialize`, `ping`, `tools/list` y `tools/call`. |

Cualquier página del sitio acepta además `Accept: text/markdown` y contesta la misma URL
en Markdown en vez de HTML, con `Vary: Accept`.

## Empezar en un minuto

La carta completa:

```sh
curl -s https://www.antonysiahelados.com.ar/api/v1/catalogo
```

El handshake de MCP y la lista de herramientas:

```sh
curl -s https://www.antonysiahelados.com.ar/api/v1/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

Cotizar un pedido con las mismas reglas que cobra la página:

```sh
curl -s https://www.antonysiahelados.com.ar/api/v1/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
        "name":"cotizar_pedido",
        "arguments":{"potes":[{"tamano":"k050","sabores":["Rocher","Pistacho Italiano"]}],"entrega":"envio"}}}'
```

## Herramientas MCP

| Herramienta | Para qué |
| --- | --- |
| `listar_sabores` | Los sabores con descripción, con filtro por categoría o solo veganos. |
| `consultar_precios` | Potes, combos, adicionales, envío, umbral de envío gratis y mínimo. |
| `estado_del_local` | Si está abierto ahora, en hora de Buenos Aires, y cuándo vuelve a abrir. |
| `zona_de_entrega` | Si una localidad o dirección entra en el radio de reparto. |
| `cotizar_pedido` | Subtotal, envío y total de un pedido armado. No lo crea ni lo reserva. |

## Versionado y deprecación

La versión va en la ruta: `/api/v1/…` es la forma canónica, y toda respuesta trae la
cabecera `API-Version: 1`.

- **Las rutas sin versión** —`/api/catalogo` y `/api/mcp`— son alias permanentes de la
  v1. Siguen funcionando y no tienen retiro previsto.
- **Un cambio compatible** (un campo nuevo, un sabor nuevo, un precio distinto) se hace
  sobre la v1 sin avisar. Tratá los objetos como extensibles: campos nuevos pueden
  aparecer en cualquier momento.
- **Un cambio incompatible** (sacar un campo, cambiar un tipo, renombrar una herramienta)
  estrena `/api/v2/…`. La v1 no se rompe el mismo día.
- **El retiro se avisa en las respuestas**, no solo en esta página: una ruta deprecada
  empieza a contestar `Deprecation` (RFC 9745) con la fecha en que quedó obsoleta,
  `Sunset` (RFC 8594) con la fecha en que deja de responder, y un `Link` con
  `rel="deprecation"` apuntando al aviso. Entre el primer `Deprecation` y el `Sunset` hay
  como mínimo **180 días**.

Hoy ninguna ruta está deprecada, así que ninguna respuesta trae esas cabeceras.

## Cuotas

60 pedidos por minuto y por IP, contados por endpoint. Cada respuesta declara cuánto
queda, en las dos formas del draft de la IETF, para que no haya que descubrir el límite
chocándolo:

| Cabecera | Qué dice |
| --- | --- |
| `RateLimit-Limit` | La cuota de la ventana: `60`. |
| `RateLimit-Remaining` | Cuántos pedidos quedan en la ventana en curso. |
| `RateLimit-Reset` | Segundos hasta que la ventana se renueva. |
| `RateLimit-Policy` | La política declarada, p. ej. `"catalogo";q=60;w=60`. |
| `RateLimit` | El estado en campo estructurado, p. ej. `"catalogo";r=59;t=60`. |
| `Retry-After` | Solo en el 429: cuántos segundos esperar. |

Una salvedad honesta: el contador vive en la memoria de la instancia que atiende y
`/api/v1/catalogo` se cachea cinco minutos en el CDN, así que los números son una guía
para autolimitarse, no un saldo exacto. El `Retry-After` del 429 sí es exacto.

## Errores

Todo error de la API se contesta en `application/problem+json` (RFC 9457) con el status
HTTP correcto —nunca un 200 con un error adentro— y nunca en HTML. Además de los campos
de la RFC (`type`, `title`, `status`, `detail`, `instance`), cada error trae `code`, que
es estable y está pensado para un `switch`, y `pista`, que dice cómo salir del error:

```json
{
  "type": "https://www.antonysiahelados.com.ar/developers#error-ruta-no-encontrada",
  "title": "La ruta no existe",
  "status": 404,
  "detail": "No hay ningún endpoint en /api/sabores.",
  "code": "ruta_no_encontrada",
  "pista": "Los endpoints publicados están en https://www.antonysiahelados.com.ar/openapi.json. El catálogo completo es GET /api/v1/catalogo.",
  "documentacion": "https://www.antonysiahelados.com.ar/developers",
  "instance": "/api/sabores"
}
```

| `code` | Status | Cuándo y qué hacer |
| --- | --- | --- |
| `ruta_no_encontrada` | 404 | Esa ruta no existe bajo `/api/`. El cuerpo lista los endpoints que sí existen: corregí la ruta, no reintentes. |
| `metodo_no_permitido` | 405 | La ruta existe pero no con ese método. La cabecera `Allow` de la misma respuesta dice cuáles acepta. |
| `cuerpo_invalido` | 400 | El cuerpo no es un JSON-RPC 2.0 válido, o llegó un lote (el protocolo ya no los admite). Arreglá el cuerpo, no reintentes igual. |
| `cuota_excedida` | 429 | Se pasó la cuota. Esperá los segundos de `Retry-After` y reintentá; mirá las cabeceras `RateLimit` para no volver a chocarla. |

El endpoint MCP es la excepción prevista por su propia especificación: un error de
protocolo se contesta como error JSON-RPC (`{"jsonrpc":"2.0","id":…,"error":{"code":…,"message":…}}`)
con status 200, porque es lo que un cliente MCP espera. Los errores que no son de
protocolo —cuota, método— salen igual en problem+json.

## Cosas que conviene saber

- **Los precios están en pesos argentinos**, como enteros, sin centavos. El campo
  `moneda` lo dice: `ARS`.
- **El horario se calcula en hora de Buenos Aires** (`America/Argentina/Buenos_Aires`),
  no en la del servidor ni en la tuya. `abiertoAhora` caduca: no lo caches más de unos
  minutos.
- **CORS abierto** (`Access-Control-Allow-Origin: *`) y sin cookies: podés llamar desde
  el navegador.
- **El nombre del sabor es la clave.** `cotizar_pedido` valida contra la carta: mandá los
  nombres exactos que devuelve `listar_sabores`.
- **Los precios cambian.** No los copies a tu código: leelos del catálogo, que sale de la
  misma fuente que cobra el sitio.

## Contacto técnico

Si algo de acá no funciona como está escrito, o necesitás un dato que la API no da, el
canal es el mismo de siempre: WhatsApp +54 9 11 6241-9013. No hay soporte por correo
electrónico.
