{"openapi":"3.1.0","jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","info":{"title":"Kroni Envíos — API pública","version":"1.0.0","description":"API de envíos de Kroni. Cotiza tarifas en tiempo real con varias paqueterías y\n—en la siguiente etapa— genera guías, descarga etiquetas y consulta el estado\ndel envío.\n\n## Autenticación\n\nEncabezado `Authorization: Bearer <API Key>`. Las llaves se generan desde\n**Ajustes → Desarrolladores** en el portal de Kroni y se muestran una sola vez.\n\n**Una sola URL base para producción y pruebas.** El entorno lo determina el\nprefijo de la llave, no el dominio (igual que Stripe):\n\n| Prefijo | Entorno | Comportamiento |\n| --- | --- | --- |\n| `kg_test_` | Pruebas | Tarifas de una paquetería simulada y determinista. No se cobra saldo ni se generan guías reales. |\n| `kg_live_` | Producción | Tarifas y guías reales. Cada guía descuenta del saldo prepago. |\n\nLos datos de ambos entornos están aislados: una llave de pruebas nunca ve\ninformación de producción, y viceversa.\n\n**La llave es de servidor.** No la pongas en código que corra en un navegador\nni en una aplicación móvil: la API no acepta peticiones con origen de navegador.\n\n## Límites de peticiones\n\nPor llave y por minuto: cotización 60, guías\n30, rastreo 120, resto\n300. Toda respuesta trae los encabezados `X-RateLimit-*`;\nal rebasar, un `429` con `Retry-After` en segundos.\n\n## Vigencia de la cotización\n\nCada cotización vive 60 minutos y devuelve un **folio**\n(`quote_id`) que conviene guardar: es lo que amarra la tarifa que le mostraste\na tu cliente con la guía que se genere después. Pasado ese plazo hay que volver\na cotizar.\n\n## Errores\n\nTodos comparten la misma forma: `{ \"error\": { \"code\", \"message\", \"details?\", \"request_id\" } }`.\nRamifica por `code`, que es estable; `message` puede cambiar de redacción.\n\n## Guías\n\n`POST /shipments` genera la guía a partir de un folio y una tarifa. Exige el\nencabezado `Idempotency-Key`: reenviar la misma llave con el mismo cuerpo\ndevuelve la misma guía sin cobrar dos veces; con un cuerpo distinto responde\n`409 idempotency_conflict`. Cada guía descuenta del saldo prepago; sin saldo,\n`402 insufficient_balance`.\n\nLa cancelación es una **solicitud con revisión**, no una cancelación inmediata:\n`POST /shipments/{id}/cancel` responde `202` con `status: pending_review`\ny el resultado llega por webhook o se consulta en la guía.\n\n## Webhooks\n\nRegistra una URL en tu aplicación (Ajustes → Desarrolladores) y te avisamos\ncuando una guía cambia de estado. Cada entrega lleva:\n\n| Encabezado | Contenido |\n| --- | --- |\n| `X-Kroni-Event` | `shipment.created`, `shipment.status_changed`, `shipment.delivered`, `shipment.cancellation_requested`, `shipment.cancelled` |\n| `X-Kroni-Event-Id` | UUID estable del evento — deduplica reenvíos con él |\n| `X-Kroni-Signature` | `t=<epoch>,v1=<HMAC-SHA256(t + \".\" + cuerpo, secreto)>` |\n\nVerifica la firma con el secreto de tu aplicación y rechaza si `|ahora − t|`\nsupera 300 segundos. Responde `2xx` en menos de 10 s;\nsi no, reintentamos con espera creciente hasta cinco veces.\n\n## Versionado\n\nLa ruta lleva la versión. Dentro de `v1` solo se hacen cambios compatibles\n(campos nuevos opcionales en la respuesta). Un cambio incompatible sale como\n`/v2` y `v1` se mantiene con aviso previo.","contact":{"name":"Soporte Kroni","url":"https://go.kroni.io/desarrolladores"}},"servers":[{"url":"https://go-api.kroni.io/v1","description":"Producción y pruebas — el prefijo de la llave determina el entorno"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Diagnóstico","description":"Validar credenciales y configuración."},{"name":"Cotización","description":"Tarifas en tiempo real y folios."},{"name":"Referencia","description":"Catálogos: paqueterías, límites, cobertura."},{"name":"Guías","description":"Generar, consultar, etiquetar y cancelar envíos."},{"name":"Rastreo","description":"Estado del envío y su historial."}],"paths":{"/ping":{"get":{"tags":["Diagnóstico"],"summary":"Validar la credencial","description":"Devuelve el entorno, la organización y los límites vigentes. Úsalo como prueba de humo al configurar la integración.","operationId":"ping","responses":{"200":{"description":"Credencial válida","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingResponse"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.\n\n`invalid_api_key` — La API Key no es válida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`app_suspended` — Tu aplicación está suspendida. Escríbenos para reactivarla.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rates":{"post":{"tags":["Cotización"],"summary":"Cotizar un envío","description":"Devuelve una tarifa por servicio disponible, con el costo total real que se cobrará.\n\nEl folio (`quote_id`) vive 60 minutos.\n\n`declared_value_mxn` por sí solo **no cobra nada**: viaja a la guía y sirve de base\npara la protección opcional, que se activa con `protect_declared_value`.","operationId":"createQuote","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesRequest"}}}},"responses":{"201":{"description":"Cotización generada","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesResponse"}}}},"400":{"description":"`invalid_json` — El cuerpo de la petición no es JSON válido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.\n\n`invalid_api_key` — La API Key no es válida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`no_coverage` — No encontramos paqueterías con cobertura para esa ruta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error` — La petición tiene campos inválidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`carrier_unavailable` — La paquetería no respondió. Inténtalo de nuevo o elige otra tarifa.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rates/{quote_id}":{"get":{"tags":["Cotización"],"summary":"Recuperar una cotización por folio","description":"Un folio vencido responde 200 con `expired: true`, para que puedas auditar históricos. El rechazo por vigencia ocurre al generar la guía.","operationId":"getQuote","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","pattern":"^qt_[0-9a-f]{32}$"}}],"responses":{"200":{"description":"Folio encontrado","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesResponse"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`quote_not_found` — No encontramos esa cotización.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reference/carriers":{"get":{"tags":["Referencia"],"summary":"Paqueterías, límites y conceptos de cargo","description":"Describe los conceptos y cómo se calculan. Los importes concretos solo aparecen dentro de una cotización, ya sumados al precio de esa ruta.","operationId":"listCarriers","responses":{"200":{"description":"Catálogo","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarrierCatalog"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reference/coverage":{"get":{"tags":["Referencia"],"summary":"Cobertura de un código postal","description":"Estado, municipio y qué paqueterías cubren el código postal. Úsalo para descartar rutas sin gastar una cotización.","operationId":"getCoverage","parameters":[{"name":"postal_code","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{5}$"}}],"responses":{"200":{"description":"Cobertura del código postal","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverageResponse"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`no_coverage` — No encontramos paqueterías con cobertura para esa ruta.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error` — La petición tiene campos inválidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments":{"post":{"tags":["Guías"],"summary":"Generar una guía a partir de una cotización","description":"Toma el folio (`quote_id`) y la tarifa (`rate_id`) de `POST /rates`, más las direcciones completas.\nLos códigos postales deben coincidir con los de la cotización.\n\nRequiere el encabezado `Idempotency-Key`. Misma llave + mismo cuerpo → la misma guía (200). Misma llave + cuerpo distinto → `409 idempotency_conflict`.\n\nEn producción descuenta del saldo prepago. En pruebas no cobra ni contacta a ninguna paquetería.","operationId":"createShipment","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":255},"description":"Identificador único de ESTE intento de generar la guía. Usa el mismo al reintentar."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShipmentRequest"}}}},"responses":{"200":{"description":"Reintento idempotente: la guía ya existía","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Shipment"}}}},"201":{"description":"Guía generada","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Shipment"}}}},"400":{"description":"`invalid_json` — El cuerpo de la petición no es JSON válido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"`insufficient_balance` — Saldo insuficiente para generar la guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`quote_not_found` — No encontramos esa cotización.\n\n`rate_not_found` — Esa tarifa no pertenece a la cotización indicada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`quote_expired` — La cotización venció. Vuelve a cotizar antes de generar la guía.\n\n`idempotency_conflict` — Ya usaste esa Idempotency-Key con un cuerpo distinto.\n\n`shipment_processing` — La guía con esa Idempotency-Key todavía se está generando. Reintenta en unos segundos con la misma llave.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`idempotency_key_required` — Falta el encabezado Idempotency-Key.\n\n`validation_error` — La petición tiene campos inválidos.\n\n`multi_package_unsupported` — La paquetería elegida no ampara varios bultos en una sola guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`carrier_unavailable` — La paquetería no respondió. Inténtalo de nuevo o elige otra tarifa.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_writes_disabled` — La generación de guías por API está temporalmente deshabilitada. La cotización sigue disponible.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Guías"],"summary":"Listar guías","description":"Paginación por cursor: manda `next_cursor` de la respuesta anterior en `cursor`. No hay `offset`.","operationId":"listShipments","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["created","picked_up","in_transit","out_for_delivery","delivered","incident","returned","cancelled","failed"]}},{"name":"reference","in":"query","schema":{"type":"string"},"description":"Tu folio, exacto."},{"name":"created_after","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"created_before","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Página de guías","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShipmentList"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error` — La petición tiene campos inválidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments/{shipment_id}":{"get":{"tags":["Guías"],"summary":"Consultar una guía","operationId":"getShipment","parameters":[{"name":"shipment_id","in":"path","required":true,"schema":{"type":"string","pattern":"^shp_[0-9a-f]{32}$"}}],"responses":{"200":{"description":"Guía","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Shipment"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`shipment_not_found` — No encontramos esa guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments/{shipment_id}/label":{"get":{"tags":["Guías"],"summary":"Descargar la etiqueta","description":"Devuelve el PDF. `format=pdf` es térmica de 4×6 pulgadas (por defecto); `format=pdf_letter` es tamaño carta.\nCon `redirect=true` responde `302` a una URL firmada temporal, para no proxear el binario.\n\nZPL no está disponible por ahora; pedirlo responde `415 label_format_unavailable`.","operationId":"getShipmentLabel","parameters":[{"name":"shipment_id","in":"path","required":true,"schema":{"type":"string","pattern":"^shp_[0-9a-f]{32}$"}},{"name":"format","in":"query","schema":{"type":"string","enum":["pdf","pdf_letter"],"default":"pdf"}},{"name":"redirect","in":"query","schema":{"type":"string","enum":["true","false"],"default":"false"}}],"responses":{"200":{"description":"PDF de la etiqueta","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"302":{"description":"Redirección a la URL firmada (solo con redirect=true)"},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`shipment_not_found` — No encontramos esa guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`shipment_cancelled` — Esta guía fue cancelada.\n\n`label_not_ready` — La etiqueta todavía se está generando. Inténtalo en unos segundos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`label_format_unavailable` — Ese formato de etiqueta no está disponible para esta paquetería.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/shipments/{shipment_id}/cancel":{"post":{"tags":["Guías"],"summary":"Solicitar la cancelación de una guía","description":"NO cancela de inmediato: registra una solicitud que Kroni revisa. Solo procede mientras la paquetería no haya recolectado. Refleja `pending_review` en tu sistema hasta recibir la resolución.","operationId":"cancelShipment","parameters":[{"name":"shipment_id","in":"path","required":true,"schema":{"type":"string","pattern":"^shp_[0-9a-f]{32}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelShipmentRequest"}}}},"responses":{"202":{"description":"Solicitud registrada","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelShipmentResponse"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`shipment_not_found` — No encontramos esa guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`shipment_not_cancellable` — Esta guía ya no se puede cancelar.\n\n`shipment_cancelled` — Esta guía fue cancelada.\n\n`cancellation_pending` — Ya hay una solicitud de cancelación en revisión para esta guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`validation_error` — La petición tiene campos inválidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`cancellation_quota_exceeded` — Alcanzaste el máximo de solicitudes de cancelación de este mes. Escríbenos si necesitas más.\n\n`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_writes_disabled` — La generación de guías por API está temporalmente deshabilitada. La cotización sigue disponible.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tracking/{tracking_number}":{"get":{"tags":["Rastreo"],"summary":"Estado de un envío","description":"Consulta a la paquetería en tiempo real y devuelve el estado normalizado y el historial. Solo guías de tu organización.","operationId":"trackShipment","parameters":[{"name":"tracking_number","in":"path","required":true,"schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Estado del envío","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Peticiones permitidas por minuto en este endpoint."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Peticiones que te quedan en la ventana actual."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Momento en que se reinicia la ventana (epoch en segundos)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tracking"}}}},"401":{"description":"`unauthorized` — Falta el encabezado Authorization con tu API Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`shipment_not_found` — No encontramos esa guía.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` — Rebasaste el límite de peticiones. Espera unos segundos e inténtalo de nuevo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`carrier_unavailable` — La paquetería no respondió. Inténtalo de nuevo o elige otra tarifa.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`api_disabled` — La API de Kroni está temporalmente deshabilitada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"shipment.status_changed":{"post":{"summary":"Una guía cambió de estado","description":"También se emite `shipment.created`, `shipment.delivered`, `shipment.cancellation_requested` y `shipment.cancelled` con la misma envoltura. Verifica `X-Kroni-Signature` antes de procesar.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"}}}},"responses":{"200":{"description":"Recibido. Cualquier 2xx detiene los reintentos."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"API Key generada en Ajustes → Desarrolladores."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","enum":["unauthorized","invalid_api_key","key_revoked","key_expired","app_suspended","ip_not_allowed","account_blocked","wrong_environment","api_disabled","api_writes_disabled","rate_limited","validation_error","not_found","invalid_json","quote_not_found","quote_expired","rate_not_found","no_coverage","weight_limit_exceeded","dimension_limit_exceeded","multi_package_unsupported","insufficient_balance","carrier_unavailable","idempotency_conflict","idempotency_key_required","shipment_not_found","shipment_processing","shipment_cancelled","cancellation_pending","cancellation_quota_exceeded","shipment_not_cancellable","label_not_ready","label_format_unavailable","internal_error"],"description":"Código estable. Ramifica por aquí, no por el texto del mensaje."},"message":{"type":"string","description":"Texto en español, apto para mostrarse a un operador."},"details":{"type":"array","description":"Solo en validation_error.","items":{"type":"object","required":["issue"],"properties":{"field":{"type":"string"},"issue":{"type":"string"}}}},"request_id":{"type":"string","description":"Repórtanoslo si necesitas soporte."}}}}},"RatesRequest":{"type":"object","properties":{"origin":{"type":"object","properties":{"postal_code":{"type":"string","pattern":"^\\d{5}$"},"city":{"type":"string","maxLength":120},"state":{"type":"string","maxLength":120},"neighborhood":{"type":"string","maxLength":120}},"required":["postal_code"],"additionalProperties":false},"destination":{"type":"object","properties":{"postal_code":{"type":"string","pattern":"^\\d{5}$"},"city":{"type":"string","maxLength":120},"state":{"type":"string","maxLength":120},"neighborhood":{"type":"string","maxLength":120}},"required":["postal_code"],"additionalProperties":false},"package":{"type":"object","properties":{"weight_kg":{"type":"number","exclusiveMinimum":0,"maximum":70},"length_cm":{"type":"number","exclusiveMinimum":0,"maximum":300},"width_cm":{"type":"number","exclusiveMinimum":0,"maximum":300},"height_cm":{"type":"number","exclusiveMinimum":0,"maximum":300}},"required":["weight_kg","length_cm","width_cm","height_cm"],"additionalProperties":false},"package_count":{"default":1,"type":"integer","exclusiveMinimum":0,"maximum":20},"declared_value_mxn":{"type":"number","exclusiveMinimum":0,"maximum":500000},"protect_declared_value":{"default":false,"type":"boolean"},"carriers":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1,"maxLength":50}}},"required":["origin","destination","package","package_count","protect_declared_value"],"additionalProperties":false},"RatesResponse":{"type":"object","properties":{"quote_id":{"type":"string","description":"Folio único. Consérvalo: amarra la tarifa mostrada con la guía que se genere."},"created_at":{"type":"string"},"expires_at":{"type":"string"},"expired":{"type":"boolean"},"currency":{"type":"string","const":"MXN"},"request":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"Eco de la petición ya normalizada."},"rates":{"type":"array","items":{"type":"object","properties":{"rate_id":{"type":"string","description":"Identificador de la tarifa dentro del folio. Se usa para generar la guía."},"carrier":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code","name"],"additionalProperties":false},"service":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code","name"],"additionalProperties":false},"total_amount":{"type":"number","description":"Costo total real del envío, IVA incluido."},"currency":{"type":"string","const":"MXN"},"taxes_included":{"type":"boolean","const":true},"amount_breakdown":{"type":"object","properties":{"subtotal":{"type":"number"},"surcharges":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Concepto del cargo. Ver GET /v1/reference/carriers."},"label":{"type":"string"},"amount":{"type":"number","description":"Monto ANTES de IVA, en MXN."}},"required":["code","label","amount"],"additionalProperties":false}},"taxes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","const":"IVA"},"rate":{"type":"number","description":"Tasa como fracción: 0.16 = 16%."},"amount":{"type":"number"}},"required":["code","rate","amount"],"additionalProperties":false}},"total":{"type":"number"}},"required":["subtotal","surcharges","taxes","total"],"additionalProperties":false,"description":"subtotal + Σ impuestos = total. Los recargos ya están dentro del subtotal."},"delivery":{"type":"object","properties":{"estimated_days":{"anyOf":[{"type":"number"},{"type":"null"}]},"estimated_date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"AAAA-MM-DD, hora local de México."},"eta_label":{"anyOf":[{"type":"string"},{"type":"null"}]},"periodicity":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["estimated_days","estimated_date","eta_label","periodicity"],"additionalProperties":false},"coverage":{"type":"object","properties":{"available":{"type":"boolean","const":true},"extended_zone":{"type":"boolean"}},"required":["available","extended_zone"],"additionalProperties":false},"package_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"multi_package":{"type":"string","enum":["combined","per_label","none"],"description":"combined = una guía ampara los N bultos; per_label = una llamada devuelve N guías; none = hay que generar N guías por separado."},"expires_at":{"type":"string"}},"required":["rate_id","carrier","service","total_amount","currency","taxes_included","amount_breakdown","delivery","coverage","package_count","multi_package","expires_at"],"additionalProperties":false}}},"required":["quote_id","created_at","expires_at","expired","currency","request","rates"],"additionalProperties":false},"PingResponse":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"api_version":{"type":"string","const":"v1"},"mode":{"type":"string","enum":["live","test"]},"organization":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name"],"additionalProperties":false},"app":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false},"rate_limits":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}}},"required":["ok","api_version","mode","organization","app","rate_limits"],"additionalProperties":false},"CarrierCatalog":{"type":"object","properties":{"carriers":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"cod_supported":{"type":"boolean"},"services":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"eta_label":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["code","name","eta_label"],"additionalProperties":false}},"limits":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Límites de la paquetería que tenemos registrados. Puede venir vacío."},"volumetric":{"type":"object","properties":{"divisor":{"anyOf":[{"type":"number"},{"type":"null"}]},"applies":{"type":"boolean"},"source":{"type":"string","enum":["carrier","kroni"]},"note":{"type":"string"}},"required":["divisor","applies","source","note"],"additionalProperties":false}},"required":["code","name","cod_supported","services","limits","volumetric"],"additionalProperties":false}},"surcharge_types":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"}},"required":["code","label","description"],"additionalProperties":false}},"limits":{"type":"object","properties":{"max_weight_kg":{"type":"number"},"max_dimension_cm":{"type":"number"},"max_packages_per_quote":{"type":"number"}},"required":["max_weight_kg","max_dimension_cm","max_packages_per_quote"],"additionalProperties":false},"vat":{"type":"object","properties":{"code":{"type":"string","const":"IVA"},"rate":{"type":"number"},"included_in_total":{"type":"boolean","const":true}},"required":["code","rate","included_in_total"],"additionalProperties":false}},"required":["carriers","surcharge_types","limits","vat"],"additionalProperties":false},"CoverageResponse":{"type":"object","properties":{"postal_code":{"type":"string"},"state":{"type":"string"},"state_code":{"type":"string"},"municipality":{"anyOf":[{"type":"string"},{"type":"null"}]},"neighborhoods":{"type":"array","items":{"type":"string"}},"carriers":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"available":{"type":"boolean"},"coverage_type":{"type":"string","enum":["standard","extended_zone","ocurre_only","blocked"]},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["code","name","available","coverage_type","notes"],"additionalProperties":false}}},"required":["postal_code","state","state_code","municipality","neighborhoods","carriers"],"additionalProperties":false},"CreateShipmentRequest":{"type":"object","properties":{"quote_id":{"type":"string","pattern":"^qt_[0-9a-f]{32}$"},"rate_id":{"type":"string","pattern":"^rt_[0-9a-f]{12}$"},"origin":{"type":"object","properties":{"contact_name":{"type":"string","minLength":1,"maxLength":100},"company_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"email":{"anyOf":[{"type":"string","maxLength":100,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"phone":{"type":"string","pattern":"^\\d{10}$"},"street":{"type":"string","minLength":1,"maxLength":200},"street_number":{"type":"string","minLength":1,"maxLength":20},"interior_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}]},"neighborhood":{"type":"string","minLength":1,"maxLength":100},"municipality":{"type":"string","minLength":1,"maxLength":100},"state":{"type":"string","minLength":1,"maxLength":100},"state_code":{"type":"string","minLength":2,"maxLength":5},"postal_code":{"type":"string","pattern":"^\\d{5}$"},"country":{"default":"MX","type":"string","minLength":2,"maxLength":2},"references":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["contact_name","phone","street","street_number","neighborhood","municipality","state","state_code","postal_code","country"],"additionalProperties":false},"destination":{"type":"object","properties":{"contact_name":{"type":"string","minLength":1,"maxLength":100},"company_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"email":{"anyOf":[{"type":"string","maxLength":100,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},{"type":"null"}]},"phone":{"type":"string","pattern":"^\\d{10}$"},"street":{"type":"string","minLength":1,"maxLength":200},"street_number":{"type":"string","minLength":1,"maxLength":20},"interior_number":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}]},"neighborhood":{"type":"string","minLength":1,"maxLength":100},"municipality":{"type":"string","minLength":1,"maxLength":100},"state":{"type":"string","minLength":1,"maxLength":100},"state_code":{"type":"string","minLength":2,"maxLength":5},"postal_code":{"type":"string","pattern":"^\\d{5}$"},"country":{"default":"MX","type":"string","minLength":2,"maxLength":2},"references":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"required":["contact_name","phone","street","street_number","neighborhood","municipality","state","state_code","postal_code","country"],"additionalProperties":false},"package":{"default":{},"type":"object","properties":{"declared_value_mxn":{"type":"number","exclusiveMinimum":0,"maximum":500000},"description":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}]}},"additionalProperties":false},"reference":{"type":"string","minLength":1,"maxLength":120},"label_format":{"default":"pdf","type":"string","enum":["pdf","pdf_letter"]}},"required":["quote_id","rate_id","origin","destination","package","label_format"],"additionalProperties":false},"Shipment":{"type":"object","properties":{"shipment_id":{"type":"string","description":"Identificador de la guía en Kroni."},"status":{"type":"string","enum":["created","picked_up","in_transit","out_for_delivery","delivered","incident","returned","cancelled","failed"]},"status_label":{"type":"string","description":"Etiqueta en español del estado."},"quote_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"rate_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Tu folio, tal como lo mandaste al crear la guía."},"carrier":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code","name"],"additionalProperties":false},"service":{"type":"object","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["code","name"],"additionalProperties":false},"tracking_number":{"anyOf":[{"type":"string"},{"type":"null"}]},"additional_tracking_numbers":{"type":"array","items":{"type":"string"},"description":"Guías hijas cuando la paquetería emite una por bulto."},"tracking_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"type":"object","properties":{"available":{"type":"boolean"},"formats":{"type":"array","items":{"type":"string","enum":["pdf","pdf_letter"]}},"url":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["available","formats","url"],"additionalProperties":false},"amount_charged":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Lo que se descontó del saldo, IVA incluido."},"currency":{"type":"string","const":"MXN"},"package_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cancellation":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["pending_review","approved","rejected"]},"requested_at":{"type":"string"},"note":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["status","requested_at","note"],"additionalProperties":false},{"type":"null"}]},"created_at":{"type":"string"}},"required":["shipment_id","status","status_label","quote_id","rate_id","reference","carrier","service","tracking_number","additional_tracking_numbers","tracking_url","label","amount_charged","currency","package_count","cancellation","created_at"],"additionalProperties":false},"ShipmentList":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"shipment_id":{"type":"string","description":"Identificador de la guía en Kroni."},"status":{"type":"string","enum":["created","picked_up","in_transit","out_for_delivery","delivered","incident","returned","cancelled","failed"]},"status_label":{"type":"string","description":"Etiqueta en español del estado."},"quote_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"rate_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"reference":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Tu folio, tal como lo mandaste al crear la guía."},"carrier":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}},"required":["code","name"],"additionalProperties":false},"service":{"type":"object","properties":{"code":{"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["code","name"],"additionalProperties":false},"tracking_number":{"anyOf":[{"type":"string"},{"type":"null"}]},"additional_tracking_numbers":{"type":"array","items":{"type":"string"},"description":"Guías hijas cuando la paquetería emite una por bulto."},"tracking_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"type":"object","properties":{"available":{"type":"boolean"},"formats":{"type":"array","items":{"type":"string","enum":["pdf","pdf_letter"]}},"url":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["available","formats","url"],"additionalProperties":false},"amount_charged":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Lo que se descontó del saldo, IVA incluido."},"currency":{"type":"string","const":"MXN"},"package_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cancellation":{"anyOf":[{"type":"object","properties":{"status":{"type":"string","enum":["pending_review","approved","rejected"]},"requested_at":{"type":"string"},"note":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["status","requested_at","note"],"additionalProperties":false},{"type":"null"}]},"created_at":{"type":"string"}},"required":["shipment_id","status","status_label","quote_id","rate_id","reference","carrier","service","tracking_number","additional_tracking_numbers","tracking_url","label","amount_charged","currency","package_count","cancellation","created_at"],"additionalProperties":false}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","next_cursor"],"additionalProperties":false},"CancelShipmentRequest":{"type":"object","properties":{"reason":{"type":"string","minLength":3,"maxLength":500}},"required":["reason"],"additionalProperties":false},"CancelShipmentResponse":{"type":"object","properties":{"shipment_id":{"type":"string"},"status":{"type":"string","const":"pending_review"},"requested_at":{"type":"string"},"message":{"type":"string"}},"required":["shipment_id","status","requested_at","message"],"additionalProperties":false},"Tracking":{"type":"object","properties":{"tracking_number":{"type":"string"},"carrier":{"type":"object","properties":{"code":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["code","name"],"additionalProperties":false},"status":{"type":"string","enum":["created","picked_up","in_transit","out_for_delivery","delivered","incident","returned","cancelled","failed"]},"status_label":{"type":"string"},"pickup_date":{"anyOf":[{"type":"string"},{"type":"null"}]},"delivery_date":{"anyOf":[{"type":"string"},{"type":"null"}]},"delivered_to":{"anyOf":[{"type":"string"},{"type":"null"}]},"incident":{"type":"object","properties":{"active":{"type":"boolean"},"description":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["active","description"],"additionalProperties":false},"courier_tracking_url":{"anyOf":[{"type":"string"},{"type":"null"}]},"events":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string"},"description":{"type":"string"},"location":{"anyOf":[{"type":"string"},{"type":"null"}]},"raw_status":{"anyOf":[{"type":"string"},{"type":"null"}]},"is_incident":{"type":"boolean"}},"required":["timestamp","description","location","raw_status","is_incident"],"additionalProperties":false}}},"required":["tracking_number","carrier","status","status_label","pickup_date","delivery_date","delivered_to","incident","courier_tracking_url","events"],"additionalProperties":false},"WebhookEnvelope":{"type":"object","required":["id","event","created_at","data"],"properties":{"id":{"type":"string","format":"uuid","description":"Mismo valor que X-Kroni-Event-Id."},"event":{"type":"string","enum":["shipment.created","shipment.status_changed","shipment.delivered","shipment.cancellation_requested","shipment.cancelled"]},"created_at":{"type":"string","format":"date-time"},"data":{"type":"object","description":"Para eventos de guía: shipment_id, reference, tracking_number, status, status_label y delivery_date."}}}}}}