Esta guía te lleva desde cero hasta tu primera llamada exitosa: cómo obtener credenciales según tu escenario, cómo autenticarte, qué forma tienen las respuestas y cómo comprobar la conexión.
| Credencial | Quién la entrega | Alcance |
|---|---|---|
API key (gsg_<prefijo>_<secreto>) |
GSG, al dar de alta la plataforma. Se muestra una sola vez. | Identifica a la plataforma. Una por plataforma. |
store_token (st_…) |
Cada negocio, desde su panel GSG (Integraciones → Plataformas → Vincular). Si la plataforma exige aprobación manual, el token se habilita cuando GSG aprueba el vínculo. | Identifica la tienda. Uno por negocio vinculado. |
La plataforma obtiene el store_token de dos formas: el negocio lo copia de su panel GSG y lo pega en
la configuración de tu producto, o tu backend lo recupera automáticamente con
POST /api/integration/bindings/lookup usando el external_store_id
que el negocio declaró al vincularse.
api_key y el store_token. El webhook_secret se mostró una sola vez al enviar la solicitud (si indicó URL de webhook); si no lo guardó, rótelo desde Integraciones → Integración directa → Webhook (rotate_secret: true) o marque "rotar el secret" al regenerar la API key.Mientras la solicitud está pendiente, el negocio puede retirarla desde el mismo panel (queda inactiva y puede volver a solicitarse más adelante).
X-Store-Token en todas las llamadas. Si lo envías, debe ser el de tu propio vínculo.
En toda la documentación las rutas se muestran relativas a {BASE_URL}. Sustitúyela por
la URL base que te indique GSG. La URL base de producción es la que se muestra
en el panel de negocios GSG (sección Integraciones); GSG puede facilitarte además un ambiente de pruebas.
| Grupo | Prefijo | Autenticación |
|---|---|---|
| Registro de pedidos | {BASE_URL}/api/v1/integration/orders (alias idéntico: {BASE_URL}/api/v1/orders/integration) | API key + tienda |
| API de integrador | {BASE_URL}/api/v1/integration/… | API key (+ tienda donde aplica) |
| Lookup de tiendas (plataformas) | {BASE_URL}/api/integration/bindings/lookup | Solo API key |
Content-Type: application/json en todas las peticiones con cuerpo.| Cabecera | Obligatoria | Descripción |
|---|---|---|
X-API-Key |
Sí* | La API key completa, tal como se entregó (gsg_<prefijo_hex>_<secreto>). |
Authorization |
Sí* | Bearer <API_KEY>. Alternativa a X-API-Key; si llegan ambas, prevalece X-API-Key. |
X-Store-Token |
Según escenario | Token de la tienda (st_…). Obligatorio para plataformas en los endpoints con alcance de tienda (registro, cotización, consulta, listado, cancelación). Opcional para integraciones directas. También se acepta en el cuerpo JSON como store_token. |
Idempotency-Key |
No | Solo en el registro de pedidos. Identificador único por intento (máx. 100 caracteres). Alias: X-Idempotency-Key. Ver idempotencia. |
* Debes enviar una de las dos formas de API key.
| Endpoint | API key | X-Store-Token |
|---|---|---|
GET /api/v1/integration/me | Sí | Opcional (si lo envías, se valida y la respuesta incluye la tienda) |
GET /api/v1/integration/catalogs | Sí | Opcional (con tienda: your_courier_cost con tu descuento y business) |
POST /api/v1/integration/quote | Sí | Plataformas: obligatorio · Directas: opcional |
POST /api/v1/integration/orders (o /api/v1/orders/integration) | Sí | Plataformas: obligatorio · Directas: opcional |
GET /api/v1/integration/orders… y POST …/cancel | Sí | Plataformas: obligatorio · Directas: opcional |
POST /api/integration/bindings/lookup | Sí | No aplica |
Todas las respuestas de la API de integrador usan el mismo envelope. En éxito hay
message y data; en error solo message. El campo
message.code es "90" + código HTTP.
{
"message": { "description": "Ok", "title": "OK OK", "code": "90200" },
"data": { ... }
}
{
"message": {
"description": "texto explicativo del error",
"title": "Error",
"code": "90400"
}
}
| HTTP | message.code | message.title | Significado |
|---|---|---|---|
| 200 | 90200 | OK OK | Operación correcta. Incluye data. |
| 400 | 90400 | Error | Validación de datos o regla de negocio (distrito sin cobertura, agencia inexistente, tamaño inválido…). Corrige y reenvía. Un JSON malformado responde El cuerpo JSON no es válido con este mismo envelope. |
| 401 | 90401 | Unauthorized Error | Credenciales inválidas: API key ausente, mal formada, revocada o expirada; cliente inactivo/pendiente/rechazado; store_token ausente, ajeno o vínculo no activo (Vínculo de tienda inactivo o bloqueado (…)). |
| 404 | 90404 | Error | El pedido no existe para esta tienda (solo se ven pedidos creados por API con la misma tienda). La descripción empieza con Error: . |
| 409 | 90409 | Error | Conflicto: idempotencia (order_external_id reutilizado con otro contenido, pedido en proceso, Idempotency-Key reutilizada) o el estado del pedido no admite la acción (cancelación). |
| 429 | 90429 | Too Many Requests | Rate limit superado. Espera y reintenta con backoff. |
| 500 | 90500 | Error Server | Error interno de GSG con un texto fijo por endpoint (nunca el detalle interno). Puedes reintentar; en el registro de pedidos el reintento con el mismo cuerpo es seguro (nada quedó a medias). |
POST /api/integration/bindings/lookup valida el cuerpo con un
validador genérico. Si external_store_id falta o supera 120 caracteres responde HTTP 400 con
{ "message": { "code": "90100", ... }, "errors": [ { "path": "external_store_id", "msg": "..." } ] }
(un JSON malformado sí usa el envelope estándar: El cuerpo JSON no es válido, 90400).
Su rate limit (100/min) responde 429 con { "ok": false, "error": { "code": "TOO_MANY_REQUESTS", "message": "..." } }.
GET /api/v1/integration/me
Devuelve quién eres para GSG: tu cliente de integración, el prefijo de la API key en uso, la tienda
(si enviaste store_token o eres integración directa), el estado de tu webhook (solo integración directa) y el rate limit.
No tiene efectos secundarios: úsalo como health check de credenciales.
curl "https://{BASE_URL}/api/v1/integration/me" \
-H "X-API-Key: gsg_PREFIJO_SECRETO" \
-H "X-Store-Token: st_TOKEN_DE_TIENDA"
{
"message": { "description": "Ok", "title": "OK OK", "code": "90200" },
"data": {
"api_version": "1.4",
"server_time": "2026-09-03T15:00:00.000Z",
"integration_client": {
"id": 1,
"code": "stocky_360",
"name": "Stocky360",
"type": "PLATFORM",
"status": "active"
},
"api_key": {
"prefix": "2d35df56256315f7",
"created_at": "2026-08-01T14:02:11.000Z",
"last_used_at": "2026-09-03T14:59:40.000Z"
},
"store": {
"binding_id": 12,
"business_id": 2120,
"business_name": "Ctrl go",
"external_store_id": "flujos_ctrlgo",
"status": "active"
},
"webhook": null,
"rate_limit": { "per_minute": 300 },
"docs_url": "https://business.gsgcorp.pe/documentacion_publica/"
}
}
| Campo | Descripción |
|---|---|
integration_client.type | PLATFORM (multi-tienda) o DIRECT (integración directa). |
api_key.prefix | Prefijo hexadecimal de la key en uso; sirve para confirmar qué key está configurada sin exponer el secreto. |
store | null si no enviaste store_token (plataformas). Para integraciones directas se resuelve automáticamente el negocio dueño. |
webhook | Solo para integraciones directas: { configured, is_active, url_masked, last_delivery_at } (url_masked = host y último segmento del path, null si no hay webhook). Para plataformas es null (el webhook de la plataforma no se describe a la tienda). |
rate_limit.per_minute | Techo vigente de peticiones por minuto por cliente de integración (tu API key). |
Respuesta con Cache-Control: no-store.
store_token de una tienda (plataformas)
Servidor a servidor. Devuelve el store_token de una tienda ya vinculada a tu plataforma
a partir del external_store_id que el negocio declaró al vincularse en su panel GSG.
No es para polling: consúltalo al conectar una tienda o al recibir 401 por token inválido, y cachea el resultado.
POST {BASE_URL}/api/integration/bindings/lookup
X-API-Key: gsg_PREFIJO_SECRETO
Content-Type: application/json
{ "external_store_id": "flujos_ctrlgo" }
{
"message": { "description": "Ok", "title": "OK OK", "code": "90200" },
"data": {
"exists": true,
"binding": {
"id": 12,
"business_id": 2120,
"user_id": 3310,
"store_token": "st_a1b2c3…",
"external_store_id": "flujos_ctrlgo",
"status": "active",
"created_at": "2026-08-20T16:45:00.000Z"
}
}
}
{
"message": { "description": "Ok", "title": "OK OK", "code": "90200" },
"data": {
"exists": true,
"binding": { "id": 12, "business_id": 2120, "user_id": 3310, "store_token": null,
"external_store_id": "flujos_ctrlgo", "status": "pending", "created_at": "…" },
"reason": "El vínculo existe pero está pendiente de aprobación por GSG."
}
}
{ "message": { "description": "Ok", "title": "OK OK", "code": "90200" }, "data": { "exists": false } }
binding.status | reason | Qué hacer |
|---|---|---|
active | — | Guarda el store_token y úsalo en las llamadas de esa tienda. |
pending | El vínculo existe pero está pendiente de aprobación por GSG. | Espera la aprobación; reintenta más tarde. |
rejected | El vínculo fue rechazado por GSG; el negocio puede volver a solicitarlo desde su panel. | Indica al negocio que revise el motivo en su panel. |
blocked | El vínculo existe pero está bloqueado por administración; contacte a soporte. | Contacta a GSG. |
inactive | El vínculo existe pero está inactivo; reactívelo desde el flujo de registro. | El negocio debe volver a vincular desde su panel. |
Todos los endpoints de integrador (/api/v1/integration/* y POST /api/v1/orders/integration)
aplican dos límites independientes:
| Límite | Techo | Cuándo se evalúa | Mensaje (message.description) |
|---|---|---|---|
| Por cliente de integración (tu API key, sumando todas tus tiendas e IPs) | 300/min (el valor vigente lo devuelve /me → rate_limit.per_minute) | Después de autenticar; el 429 queda registrado en tu actividad | Demasiadas peticiones: máximo 300 por minuto por cliente de integración. Espere y reintente con backoff. |
| Por dirección IP | 900/min | Antes de autenticar (frena ráfagas con credenciales inválidas) | Demasiadas peticiones desde esta IP; espere y reintente con backoff |
Al superar cualquiera de los dos, la respuesta es 429 con el envelope estándar:
{
"message": {
"description": "Demasiadas peticiones: máximo 300 por minuto por cliente de integración. Espere y reintente con backoff.",
"title": "Too Many Requests",
"code": "90429"
}
}
Cada respuesta incluye las cabeceras estándar RateLimit-* (borrador IETF draft-7):
RateLimit-Policy: 300;w=60 y RateLimit: limit=300, remaining=<n>, reset=<segundos>
(las del límite que esté más cerca de agotarse).
Úsalas para regular tus ráfagas. Recomendaciones:
reset; no reintentes en bucle.GET /catalogs (TTL 5 min) y usa webhooks para estados; deja el polling como respaldo.store_token como credencial: quien lo tenga junto con tu API key puede registrar pedidos a nombre de esa tienda. Guárdalo y envíalo exactamente como lo recibiste (la comparación no distingue mayúsculas, pero usa siempre la misma caja).webhook_secret igual que la key y verifica siempre la firma de los eventos.api_key.prefix que devuelve /me: te permite saber qué key está desplegada en cada ambiente sin exponer el secreto.base_url, api_key y store_token.GET /catalogs y guarda distritos, tamaños, agencias y sedes (Catálogos y tarifas).POST /quote y registra tu primer pedido con POST /api/v1/integration/orders (API de pedidos). El ejemplo mínimo está en la portada.GET /orders/:orderExternalId y configura tu webhook (Seguimiento, Webhooks).| Versión | Fecha | Cambios |
|---|---|---|
| 1.2 | 2026-09-29 | El ejemplo de GET /me muestra api_version "1.4" (hora límite de registro del día; ver API de pedidos 1.4). |
| 1.1 | 2026-09-15 | Registro de pedidos también en POST /api/v1/integration/orders (alias idéntico de /api/v1/orders/integration); enlace al quickstart de la portada. |
| 1.0 | 2026-09-03 | Revisión del mismo día: en integración directa el webhook_secret se muestra al enviar la solicitud (no al generar la API key) y la solicitud pendiente puede retirarse desde el panel; GET /me devuelve webhook: null para plataformas; 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; 401 de vínculo con prefijo Vínculo de tienda inactivo o bloqueado (…) y 401 por API key expirada; regla "misma caja" para store_token. |
| 1.0 | 2026-09-03 | Versión inicial: credenciales por escenario (plataforma / directa), cabeceras, envelope y códigos,
prueba con GET /api/v1/integration/me, lookup de tiendas, rate limit 300/min y seguridad. |