Tabla consolidada de las respuestas de error de la API de integración: código HTTP, message.code,
mensaje típico, causa y qué hacer. Los mensajes son contrato: puedes usarlos para mostrar ayuda en tu
sistema, pero decide por el código HTTP, no por el texto.
{
"message": {
"description": "texto explicativo",
"title": "Error | Unauthorized Error | Too Many Requests | Error Server",
"code": "90400 | 90401 | 90404 | 90409 | 90429 | 90500"
}
}
message.code = "90" + HTTP. Nunca hay data en un error. En 404 la descripción empieza con Error: . Cuando varios campos fallan a la vez en la validación del pedido, los mensajes se concatenan con ". ".
Un cuerpo JSON malformado (o demasiado grande) en cualquier endpoint de integrador responde 400 con este mismo envelope: { "message": { "description": "El cuerpo JSON no es válido", "title": "Error", "code": "90400" } }. Los 500 traen un texto fijo por endpoint y nunca el detalle interno del servidor.
| HTTP | code | Mensaje | Causa | Acción |
|---|---|---|---|---|
| 401 | 90401 | API key requerida (Authorization Bearer o X-API-Key) | No llegó ninguna cabecera con la key. | Envía X-API-Key o Authorization: Bearer. |
| 401 | 90401 | Formato de API key inválido | La key no tiene el formato gsg_<prefijo>_<secreto> (recortada, con espacios, mal copiada). | Copia la key completa desde el panel / canal seguro. |
| 401 | 90401 | API key no válida | La key no existe. | Verifica el ambiente (pruebas vs producción) y la key configurada. Compara el prefijo con el que muestra el panel. |
| 401 | 90401 | API key revocada o inactiva | La key fue rotada o revocada. | Despliega la key nueva generada en el panel o entregada por GSG. |
| 401 | 90401 | API key expirada; genere una nueva | La key tenía fecha de expiración y ya venció. | Genera/rota la key (panel o GSG) y despliégala. |
| 401 | 90401 | Integración pendiente de aprobación por GSG | Integración directa aún no aprobada. | Espera la aprobación (estado visible en el panel). |
| 401 | 90401 | Integración rechazada por GSG | GSG rechazó la solicitud. | Revisa el motivo en el panel y vuelve a solicitar. |
| 401 | 90401 | Cliente de integración inactivo | GSG desactivó la integración. | Contacta a GSG. |
| 401 | 90401 | store_token requerido (X-Store-Token o body.store_token) | Plataforma multi-tienda sin token en un endpoint con alcance de tienda. | Envía el store_token de la tienda. |
| 401 | 90401 | store_token no vinculado a esta API key | El token no existe o pertenece a otra plataforma. | Recupera el token correcto (panel del negocio o bindings/lookup). |
| 401 | 90401 | Vínculo de tienda inactivo o bloqueado (pendiente de aprobación por GSG) | El negocio se vinculó pero GSG aún no aprobó. | Espera la aprobación. |
| 401 | 90401 | Vínculo de tienda inactivo o bloqueado (rechazado por GSG) | GSG rechazó el vínculo. | El negocio puede volver a solicitarlo desde su panel. |
| 401 | 90401 | Vínculo de tienda inactivo o bloqueado (bloqueado por GSG) | GSG bloqueó la tienda. | Contacta a GSG. |
| 401 | 90401 | Vínculo de tienda inactivo o bloqueado (desactivado por el negocio) | El negocio se desvinculó. | El negocio debe volver a vincular desde su panel; obtendrás un token nuevo. |
| 401 | 90401 | La integración directa no tiene un negocio activo; contacte a GSG | Integración directa sin vínculo activo (bloqueado o desactivado). | Contacta a GSG. |
| 500 | 90500 | Error validando credenciales de integración | Fallo interno (base de datos) al validar; no significa que tus credenciales sean inválidas. | Reintenta con backoff. |
Los cuatro mensajes de vínculo comparten el prefijo histórico Vínculo de tienda inactivo o bloqueado; el detalle entre paréntesis indica el estado. Si tu sistema compara el texto para mostrar "tienda desconectada", compara por el prefijo.
Hay dos límites independientes; ambos responden 429 con el envelope estándar y las cabeceras RateLimit / RateLimit-Policy (draft-7).
| HTTP | code | Mensaje | Causa | Acción |
|---|---|---|---|---|
| 429 | 90429 | Demasiadas peticiones: máximo 300 por minuto por cliente de integración. Espere y reintente con backoff. | Más de 300 peticiones/min con la misma API key (sumando todas tus tiendas e IPs) a /api/v1/integration/* + POST /api/v1/orders/integration. Se evalúa después de autenticar y queda en tu bitácora de actividad. | Espera el valor reset de la cabecera RateLimit; reduce el polling; cachea catálogos. |
| 429 | 90429 | Demasiadas peticiones desde esta IP; espere y reintente con backoff | Más de 900 peticiones/min desde la misma IP (se evalúa antes de autenticar; protege contra ráfagas de credenciales inválidas). | Igual: backoff respetando reset. |
POST /api/v1/orders/integration| HTTP | code | Mensaje típico | Causa | Acción |
|---|---|---|---|---|
| 400 | 90400 | El cuerpo JSON no es válido | JSON malformado o cuerpo demasiado grande. | Revisa la serialización y el Content-Type. |
| 400 | 90400 | order_external_id es obligatorio (máximo 120 caracteres) | Falta o excede. | Envía tu id de pedido (≤ 120). La comparación no distingue mayúsculas (ORD-1 = ord-1): usa siempre la misma caja. |
| 400 | 90400 | route_type debe ser 1 (Entrega), 2 (Agencia), 3 (Cambio), 4 (Recojo en GSG) o 7 (Entrega Flex) | Tipo no soportado. | Usa uno de los cinco tipos. |
| 400 | 90400 | full_name es obligatorio (nombre completo del destinatario) · telephone es obligatorio (teléfono del destinatario) · product es obligatorio (descripción del producto a enviar) · code_pay_method es obligatorio (método de pago, ej. EFECTIVO, PAGADO, YAPE) | Falta un obligatorio común. | Completa el campo. telephone no se exige en tipo 2. |
| 400 | 90400 | date_route es obligatorio con fecha y hora; formato DD-MM-YYYY HH:mm en 24 horas (ej. 16-01-2026 14:30) | Falta la fecha. | Envía fecha y hora. |
| 400 | 90400 | date_route "…" no tiene un formato válido; use DD-MM-YYYY HH:mm en 24 horas (ej. 16-01-2026 14:30) o DD-MM-YYYY hh:mm:ss AM/PM (ej. 16-01-2026 02:30:00 PM) | Formato distinto (ISO, sin hora…). | Usa DD-MM-YYYY HH:mm. |
| 400 | 90400 | size "XXL" no es válido; valores permitidos: S (PEQUEÑO), M (MEDIANO), L (GRANDE), XL (con weight_kg, width_cm, height_cm y depth_cm por bulto: GSG clasifica), XL_GRANDE, XL_PLUS, XL_MAX (si lo omite se usa S/PEQUEÑO) | Tamaño fuera de catálogo. | Elige un código válido (para XL, una categoría o XL + peso y medidas). |
| 400 | 90400 | size XL requiere weight_kg, width_cm, height_cm y depth_cm por bulto (mayores a 0) para calcular la categoría; o envíe directamente XL_GRANDE, XL_PLUS o XL_MAX | "XL" a secas sin peso o sin alguna medida. | Envía las cuatro medidas por bulto o una categoría explícita. |
| 400 | 90400 | El bulto (… cm, … kg) supera XL Max: es Especial / Cargo y se cotiza con operaciones. | Las medidas o el peso exceden la categoría mayor. | Coordina el envío con GSG (no se registra por API). |
| 400 | 90400 | El tamaño M viaja en moto y admite hasta 3 kg por bulto (declarado 5 kg). Regístralo como XL con sus medidas. | Enviaste weight_kg con S/M y supera el máximo de moto. | Usa XL + medidas o una categoría XL. |
| 400 | 90400 | size: el servicio GSG XL no está disponible por integración en este momento; use S, M o L | GSG XL deshabilitado temporalmente. | Usa courier o coordina con GSG. Revisa features.xl_enabled. |
| 400 | 90400 | district es obligatorio (distrito de entrega; define la tarifa del servicio) | Falta el distrito en 1, 3, 7 o agencia por nombre / no partner. | Envía el distrito del catálogo. |
| 400 | 90400 | district "…" no existe en el catálogo de distritos GSG; verifique el nombre (ej. MIRAFLORES) o consulte GET /api/v1/integration/catalogs | Nombre fuera del catálogo. | Usa districts[].name exacto. |
| 400 | 90400 | El distrito "…" no está en la cobertura de GSG. Verifica el nombre del distrito. | Distrito sin tarifa vigente (detectado en la pre-cotización). | Usa un distrito con cobertura. |
| 400 | 90400 | El distrito "…" no tiene cobertura GSG XL; requiere cotización manual. | El distrito no tiene tarifa XL para esa categoría (districts[].xl). | Usa courier, otra categoría o coordina con GSG. |
| 400 | 90400 | pay_at_destination debe ser 0 o 1 (se acepta número o string numérico); se recibió "…" (ídem client_pays_delivery, package_registered, package_labeled) | Flag con valor no 0/1. | Envía 0 o 1. |
| 400 | 90400 | agency_id, agency_code o agency_name es requerido para ruta tipo Agencia · pay_at_destination es requerido para ruta tipo Agencia · number_document es requerido para ruta tipo Agencia | Faltan obligatorios del tipo 2. | Completa los campos. |
| 400 | 90400 | destination_address es requerido para ruta tipo Agencia | Agencia no partner ("otra agencia") sin dirección de la agencia. | Envía destination_address (y district). |
| 400 | 90400 | La agencia indicada por agency_id o agency_code no existe o está inactiva | Código/id fuera del catálogo. | Usa agencies[].code / id. |
| 400 | 90400 | agency_id y agency_code no corresponden a la misma agencia | Enviaste ambos y no coinciden. | Envía solo uno. |
| 400 | 90400 | La agencia indicada no está disponible para nuevos envíos | Agencia no seleccionable por clientes. | Elige otra del catálogo. |
| 400 | 90400 | El alias de agencia "…" es ambiguo | agency_name coincide con más de una agencia. | Usa agency_code. |
| 400 | 90400 | agency_name es requerido cuando se selecciona Otra agencia | agency_code: "OTHER" sin nombre. | Envía agency_name. |
| 400 | 90400 | La sucursal no pertenece a la agencia o está inactiva | agency_branch_id inválido. | Usa agencies[].branches[].id. |
| 400 | 90400 | La agencia … no tiene tarifa XL configurada para … | Partner sin tarifa XL para la categoría. | Coordina con GSG o usa courier. |
| 400 | 90400 | number_packages_in es requerido y debe ser mayor a 0 para ruta tipo Cambio | Tipo 3 sin bultos a recoger. | Envía entero ≥ 1. |
| 400 | 90400 | sede es requerido para ruta tipo Recojo en GSG · La sede GSG "…" no es válida. Sedes permitidas: ATE, LOS OLIVOS, LA VICTORIA. | Tipo 4 sin sede o sede desconocida. | Usa sedes[].name. |
| 400 | 90400 | El recargo XL de Recojo en GSG no está configurado para … | Tipo 4 + XL sin recargo configurado. | Coordina con GSG. |
| 400 | 90400 | <campo> supera el máximo de N caracteres · <campo> debe ser texto | Largo excedido u objeto donde va texto. | Recorta según limits. |
| 400 | 90400 | number_packages debe ser un entero mayor o igual a 1; se recibió "…" (ídem number_packages_in, agency_id, agency_branch_id) | Entero inválido. | Envía entero ≥ 1. |
| 400 | 90400 | cost debe ser un monto numérico no negativo; se recibió "…" (ídem advance, pay_amount_simple) | Monto inválido. | Número ≥ 0. |
| 400 | 90400 | latitude debe ser numérico; se recibió "…" (ídem longitude) | Coordenada no numérica. | Envía número o no envíes el campo. |
| 400 | 90400 | open_policy "…" no es válido; valores permitidos: SEALED, INSPECT, TRY_ON | Apertura del paquete fuera del catálogo. | Usa open_policy.options[].code del catálogo, o no envíes el campo (se aplica el predeterminado del negocio). |
| 400 | 90400 | pos_fee_included debe ser 0 o 1 (se acepta número o string numérico); se recibió "…" | Valor distinto de 0/1. | Envía 0 o 1, o no envíes el campo. |
| 400 | 90400 | url_maps debe ser un enlace http(s) de hasta 2000 caracteres (ej. https://maps.app.goo.gl/...) | Texto que no empieza con http:// o https://, con espacios o de más de 2000 caracteres. | Envía el enlace completo tal como lo compartió el destinatario, o no envíes el campo. |
| 409 | 90409 | order_external_id ya fue usado con otro contenido; use otro id o reenvíe el mismo cuerpo | Pedido ya procesado con ese id y el cuerpo cambió. | Consulta el pedido existente o usa otro id. |
| 409 | 90409 | Este pedido se está procesando; espere y reintente con el mismo payload | Petición concurrente en curso. | Reintenta en segundos con el mismo cuerpo. |
| 409 | 90409 | Idempotency-Key ya usada para otro order_external_id; use una clave nueva por pedido | Reutilizaste la clave con un pedido ya procesado o en proceso. (Si el intento anterior con esa clave falló, la clave se libera y no hay 409.) | Genera una clave por intento lógico. |
| 409 | 90409 | Petición duplicada (idempotencia); consulte el resultado previo · Conflicto de registro duplicado | Carrera de duplicados. | Consulta GET /orders/:id. |
| 500 | 90500 | Error validando el distrito del pedido · Error validando el pedido; reintente en unos segundos · Error de idempotencia · Error al registrar el pedido; reintente en unos segundos | Fallo interno (texto fijo; el detalle nunca se expone). | Reintenta con el mismo cuerpo y la misma Idempotency-Key: nada quedó a medias. |
POST /api/v1/integration/quoteMismos 400 que el registro para los campos que afectan el precio (route_type, district, size, sede, agencia, flags, largos). Si envías open_policy, pos_fee_included o url_maps, también se validan (no cambian el precio). No exige full_name, telephone, product, code_pay_method, date_route, pay_at_destination, number_document ni number_packages_in.
| HTTP | code | Mensaje típico | Causa | Acción |
|---|---|---|---|---|
| 400 | 90400 | Ver tabla de § 4 (validación y pre-cotización). | Datos inválidos. | Corrige y vuelve a cotizar. |
| 500 | 90500 | Error al cotizar el envío | Fallo interno. | Reintenta con backoff. |
GET /orders, GET /orders/:orderExternalId, GET /orders/tracking/:codeTracking| HTTP | code | Mensaje | Causa | Acción |
|---|---|---|---|---|
| 404 | 90404 | Error: No existe un pedido con ese identificador para esta tienda | El pedido no fue creado por API con esta tienda, el id/código está mal o el pedido fue eliminado. | Verifica el id y la tienda (token) usados al registrar. |
| 400 | 90400 | from debe tener formato YYYY-MM-DD · to debe tener formato YYYY-MM-DD | Fecha inválida en el listado. | Usa YYYY-MM-DD. |
| 400 | 90400 | from debe ser anterior o igual a to | Rango invertido. | Corrige el rango. |
| 400 | 90400 | El rango máximo es de 92 días | Rango demasiado amplio. | Divide en ventanas ≤ 92 días. |
| 400 | 90400 | state no existe en el catálogo; consulte GET /api/v1/integration/catalogs (states) | Filtro state desconocido. | Usa un code, nombre o id del catálogo. |
| 500 | 90500 | Error consultando el pedido · Error listando los pedidos | Fallo interno. | Reintenta con backoff. |
POST /orders/:orderExternalId/cancel| HTTP | code | Mensaje | Causa | Acción |
|---|---|---|---|---|
| 400 | 90400 | reason supera el máximo de 250 caracteres | Motivo demasiado largo. | Recorta el motivo. |
| 404 | 90404 | Error: No existe un pedido con ese identificador para esta tienda | Pedido ajeno o inexistente. | Verifica id y tienda. |
| 409 | 90409 | El pedido ya está en estado final (ENTREGADO) y no puede cancelarse por API | Último estado RECHAZADO o ENTREGADO. | No hay acción posible por API. |
| 409 | 90409 | El pedido ya está en proceso (RECOLECTADO) y no puede cancelarse por API; coordine con soporte GSG | Último estado distinto de REGISTRADO/RECOJO_PENDIENTE (recolectado, motorizado asignado, en ruta…). | Coordina con soporte GSG. |
| 409 | 90409 | El pedido ya fue recibido por GSG (o está en un estado que no admite cancelación automática); coordine la cancelación con soporte GSG | El paquete ya ingresó a GSG. | Contacta a soporte GSG para cancelar y coordinar la devolución. |
| 409 | 90409 | El pedido pertenece a un día ya liquidado por GSG y no admite cancelación automática; coordine con soporte | El pedido está en un día ya calculado/liquidado. | Solo soporte GSG puede modificarlo. |
| 409 | 90409 | El pedido ya está en un estado final y no puede cancelarse por API | El estado final se detectó al ejecutar la cancelación (carrera con un cambio de estado). | No hay acción posible por API. |
| 409 | 90409 | El pedido no admite cancelación automática; coordine con soporte GSG | Otra regla de negocio impidió cancelar. | Coordina con soporte GSG. |
| 500 | 90500 | Error al cancelar el pedido | Fallo interno. | Reintenta; la cancelación es idempotente. |
Regla completa: la cancelación por API procede solo si el último estado es REGISTRADO (3) o RECOJO_PENDIENTE (4), el movimiento de ingreso sigue pendiente y el día no está calculado/liquidado; cualquier otro caso responde 409 (90409). Los 409 nunca incluyen datos internos de GSG.
GET /me y GET /catalogs| HTTP | code | Mensaje | Causa | Acción |
|---|---|---|---|---|
| 401 | 90401 | Ver § 2 | API key inválida, o store_token enviado e inválido. | Corrige credenciales. |
| 500 | 90500 | Error consultando la integración · Error consultando los catálogos | Fallo interno. | Reintenta con backoff; usa tu caché del catálogo mientras tanto. |
POST /api/integration/bindings/lookupEste endpoint usa un validador genérico y un rate limit propio, por lo que dos de sus errores tienen un formato distinto.
| HTTP | Formato | Mensaje | Causa | Acción |
|---|---|---|---|---|
| 401 | Envelope (90401) | Ver § 2 | API key inválida. | Corrige la key. |
| 400 | Envelope (90400) | El cuerpo JSON no es válido | JSON malformado. | Revisa la serialización. |
| 400 | { "message": { "code": "90100", … }, "errors": [ { "path": "external_store_id", "msg": "…" } ] } | external_store_id es obligatorio · external_store_id máximo 120 caracteres | Cuerpo inválido. | Envía external_store_id (≤ 120). |
| 429 | { "ok": false, "error": { "code": "TOO_MANY_REQUESTS", "message": "…" } } | Demasiadas peticiones. Espera un momento. | Más de 100 llamadas/min por IP. | No hagas polling; cachea el token. |
| 500 | Envelope (90500) | Error consultando el binding por external_store_id | Fallo interno. | Reintenta con backoff. |
Un vínculo inexistente o no activo no es un error: responde 200 con exists: false o con store_token: null y reason (ver Guía de inicio § 6).
| HTTP | ¿Reintentar automáticamente? | Cómo |
|---|---|---|
| 400 | No | Corrige los datos. En el registro, el mismo order_external_id sigue disponible. |
| 401 | No | Revisa credenciales/estado del vínculo; alerta a tu equipo. En plataformas, refresca el store_token con bindings/lookup una vez. |
| 404 | No | Verifica id y tienda. |
| 409 "se está procesando" | Sí | Backoff corto (2–5 s), mismo cuerpo y misma Idempotency-Key. |
| 409 (otros) | No | Conflicto de negocio: consulta el pedido o cambia de id/clave. |
| 429 | Sí | Espera reset segundos (cabecera RateLimit) con backoff exponencial. Aplica tanto al límite por cliente (300/min) como al límite por IP (900/min). |
| 500 / timeout de red | Sí | Backoff exponencial (p. ej. 5 s, 15 s, 45 s), mismo cuerpo y misma Idempotency-Key. El registro nunca duplica pedidos. |
| Versión | Fecha | Cambios |
|---|---|---|
| 1.2 | 2026-09-25 | API 1.3: tres 400 nuevos en el registro (y en /quote si se envían los campos): open_policy fuera de catálogo, pos_fee_included distinto de 0/1 y url_maps que no es un enlace http(s) de hasta 2000 caracteres. |
| 1.1 | 2026-09-15 | Errores de tamaño con el modelo XL por zonas: mensaje de size inválido actualizado (XL + medidas, sin XL_STD), XL sin medidas, bulto que supera XL Max y peso que excede el máximo de moto en S/M. |
| 1.0 | 2026-09-03 | Revisión del mismo día: los 401 de vínculo no activo llevan el prefijo histórico Vínculo de tienda inactivo o bloqueado (…) con cuatro variantes; nuevo 401 API key expirada; genere una nueva; rate limit en dos niveles (300/min por cliente de integración + 900/min por IP) con sus mensajes; 400 El cuerpo JSON no es válido para JSON malformado; 500 con textos fijos (nunca el detalle interno); tabla completa de 409 de cancelación (en proceso, día liquidado, estado final, no admite cancelación automática); Idempotency-Key de un intento fallido se libera; number_packages_in no se exige en /quote; nota sobre order_external_id sin distinción de mayúsculas. |
| 1.0 | 2026-09-03 | Versión inicial: tabla consolidada de errores de autenticación, rate limit, registro de pedidos, cotización, consulta, cancelación, /me, /catalogs y lookup de tiendas; estrategia de reintentos. |