Nuevo número: +51 948 997 674

32 resultados
Navegar Seleccionar ESC Cerrar

Documentación.

API REST para consultar tracking, agencias y ubicaciones de shalom.com.pe, y crear pedidos en pro.shalom.pe.

Auth

X-API-Key

Rate limit

60 / min

Formato

JSON

Base URL: https://api.shalom-api-peru.com

Quickstart

De cero a tu primera guía creada y rastreada, en cuatro llamadas. Cada paso usa lo que devolvió el anterior. Necesitas dos cosas antes de empezar: tu API key (la pides por WhatsApp) y las credenciales de Shalom Pro del cliente — email y password de pro.shalom.pe.

Paso 1 — Cambia email y password por un token

Todo lo que toca la cuenta del cliente (productos, órdenes, tracking) pide credenciales de Shalom Pro. Las canjeas una vez por un session_token que dura 2 horas, y de ahí en adelante mandas solo el token.

Esta llamada es la lenta: tarda entre 90 s y 2 min porque Shalom hace un login real, así que pon el timeout de tu cliente en ≥ 150 s o te vas a estrellar aquí. Las siguientes salen rápido. Detalle en Autenticación.

Request
curl -X POST https://api.shalom-api-peru.com/v1/shalom/sessions \
  -H "X-API-Key: tu-api-key" \
  -H "Content-Type: application/json" \
  --max-time 150 \
  -d '{
    "email": "cliente@empresa.com",
    "password": "tu-password-de-shalom-pro"
  }'

Te devuelve { "session_token": "ssk_a1b2c3d4e5f6...", "expires_at": "..." }. Ese ssk_... va en el header X-Shalom-Session de todos los pasos siguientes.

Paso 2 — Consigue los tres ids que pide la orden

Para crear una guía necesitas la agencia de origen, la de destino y el tipo de paquete. Las agencias salen de /v1/agencies/search, que solo pide X-API-Key:

Request — agencias
curl "https://api.shalom-api-peru.com/v1/agencies/search?q=arequipa" \
  -H "X-API-Key: tu-api-key"

El id de cada agencia es lo que mandas como origin_terminal_id y destiny_terminal_id. En el ejemplo: 404 y 7. El tipo de paquete sale de /v1/products, y aquí sí reusas el token:

Request — productos
curl https://api.shalom-api-peru.com/v1/products \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Session: ssk_a1b2c3d4e5f6..."

Toma el id del producto — 3 es Sobre — y pásalo como product_id.

Paso 3 — Crea la guía

Junta los tres ids, el destinatario y una clave de retiro de 4 dígitos (pickup_code, ni repetida ni consecutiva). El remitente no se manda: Shalom lo toma de la cuenta del token (salvo que pidas una guía empresarial).

Request
curl -X POST https://api.shalom-api-peru.com/v1/orders \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Session: ssk_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{
    "origin_terminal_id": 404,
    "destiny_terminal_id": 7,
    "product_id": 3,
    "quantity": 1,
    "payer": "sender",
    "declaracion_jurada": "docs",
    "receiver": {
      "document_type": "DNI",
      "document": "87654321",
      "name": "MARIA",
      "last_name": "GOMEZ",
      "sur_name": "TORRES",
      "phone": 998765432
    },
    "pickup_code": "2415"
  }'

Respuesta: { "guia": "80574902", "serie": "v872", "codigo": "CJTW", "ose_id": 584210 }. Guarda guia y codigo para rastrear, y el ose_id si vas a descargar el rótulo en PDF.

Esto crea una guía real y cobrable en la cuenta del cliente, visible en su panel. No hay sandbox y no hay idempotencia: si la llamada te da timeout, no la reintentes a ciegas — consulta primero GET /v1/orders para ver si la guía ya se creó, o la vas a duplicar. Si la creaste probando, bórrala con DELETE /v1/orders/{id} mientras no haya sido recibida en agencia.

Paso 4 — Rastréala

La guia del paso 3 va como numero, y el codigo tal cual. Mismo token del paso 1.

Request
curl -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Session: ssk_a1b2c3d4e5f6..." \
  "https://api.shalom-api-peru.com/v1/tracking?numero=80574902&codigo=CJTW"

Eso es la integración completa. Desde aquí: cotiza antes de crear, sube un lote por Excel, o rastrea hasta 50 guías de un tiro.

Autenticación

Hay dos niveles. El X-API-Key va en todas las rutas. Los endpoints que tocan la cuenta del cliente en Shalom Pro (tracking, órdenes, productos, personas, tarifas) exigen además sus credenciales. Agencias, ubicaciones y health solo necesitan la API key.

X-API-Key: tu-api-key

Credenciales de Shalom Pro — dos opciones

OpciónHeadersCuándo
A — token efímero (recomendada)X-Shalom-Session: ssk_...Pides el token una vez con POST /v1/shalom/sessions y dura 2 h. El password viaja una sola vez. Tiene precedencia si mandas ambas.
B — directoX-Shalom-Email
X-Shalom-Password
Sin paso previo, pero el password viaja en cada llamada. Para scripts cortos y pruebas.

El password no se loguea ni se persiste en nuestro servidor.

La primera llamada es lenta

La primera llamada de cada cuenta hace un login real contra Shalom y tarda ~90 s, hasta 2 min. Las siguientes son rápidas: la sesión queda caliente y se reutiliza. Configura el timeout de tu cliente en ≥ 150 s o la primera llamada se te cortará sola. Ver Latencia y reintentos.

Solicita tu API key por WhatsApp:

948 997 674

Identificadores

Un envío tiene varios ids y no son intercambiables. Los dos que más se confunden: ose_id sirve para rastrear y descargar PDFs; id sirve para borrar la orden.

CampoQué esDónde lo obtienesDónde lo usas
guia8–10 dígitos. Va impresa en el comprobante físico.POST /v1/ordersGET /v1/tracking?numero=
codigoAlfanumérico de 4 caracteres. Lo asigna Shalom.POST /v1/ordersGET /v1/tracking?codigo=
ose_idID interno de Shalom del envío. No hace falta enviarlo para rastrear: te lo damos nosotros.POST /v1/orders · order.ose_id del tracking/events, /grt, /label, /voucher
idID de la orden dentro de la cuenta empresarial.GET /v1/ordersDELETE /v1/orders/{id}
pickup_codeClave de 4 dígitos para retirar en agencia. La eliges tú.La defines al crear la ordenLa presenta el destinatario en agencia
seriePrefijo del talonario (ej. "v872").POST /v1/ordersInformativo. No se usa en esta API.
internal_idID interno del panel de Shalom Pro.GET /v1/ordersInformativo. No se usa en esta API.
GET/v1/tracking

Busca una guía y retorna su estado, más la orden completa si autenticas. Tiene dos modos según qué mandes.

Modo estado (público). Con solo X-API-Key + numero, cualquier guía del sistema devuelve su línea de tiempo (status). No necesita credenciales Shalom. La respuesta trae detailed: false y omite order.

Modo detallado. Si además mandas credenciales Shalom (ver Autenticación), agregamos la orden completa (order: montos, fechas, contenido) y detailed: true. Si las credenciales fallan o Shalom rechaza la orden, la respuesta degrada al modo estado en vez de romper. Guía inexistente → 404.

Query params (al menos 1 requerido)

ParamTipoDescripción
numerostring8 a 10 dígitos (ej. "12345678"). Es la guia.
codigostringAlfanumérico de 4 caracteres (ej. "CJTW").
ose_idstringID interno de Shalom del envío. No lo consigues tú: lo devuelve POST /v1/orders y también viene en order.ose_id del rastreo.

Para el estado basta el numero (o el ose_id). El codigo por sí solo no resuelve el estado: úsalo junto al numero, o solo con credenciales Shalom. Ambos vienen impresos en el comprobante físico. Ver Identificadores.

Request — modo estado (solo API key)
curl -H "X-API-Key: mi-key" \
  "https://api.shalom-api-peru.com/v1/tracking?numero=82100156"
200 OK — modo estado (detailed: false)
{
  "detailed": false,
  "status": {
    "registrado": { "fecha": "2026-04-15 09:12:30" },
    "origen":     { "fecha": "2026-04-15 09:12:30" },
    "transito":   { "fecha": "2026-04-15 14:08:21", "completo": true,
                    "cargueros": ["964724", "966345"], "carguero": "966345" },
    "demora":     null,
    "destino":    { "fecha": "2026-04-16 01:01:03", "completo": true },
    "entregado":  { "fecha": "2026-04-16 11:40:45" },
    "reparto":    null
  }
}
Request — modo detallado (numero + codigo + credenciales)
curl -H "X-API-Key: mi-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  "https://api.shalom-api-peru.com/v1/tracking?numero=82100156&codigo=W79H"
Request — modo detallado por ose_id
curl -H "X-API-Key: mi-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  "https://api.shalom-api-peru.com/v1/tracking?ose_id=584210"
200 OK — modo detallado (detailed: true)
{
  "detailed": true,
  "order": {
    "ose_id": 584210,
    "numero_orden": "82100156",
    "codigo_orden": "W79H",
    "fecha_emision": "2026-04-14 09:12:30",
    "fecha_traslado": "2026-04-15 09:12:30",
    "tipo_pago": "Contra entrega",
    "estado_pago": "CA",
    "contenido": "1 MINI PAQUETERIA XS",
    "monto": "10.00",
    "entregado": true,
    "reparto": false,
    "aereo": false,
    "tiempo_llegada": "",
    "origen":       { "id": 0, "nombre": "", "abrebiatura": "", "ubigeo": 0, "departamento": "", "provincia": "", "distrito": "" },
    "destino":      { "id": 0, "nombre": "", "abrebiatura": "", "ubigeo": 0, "departamento": "", "provincia": "", "distrito": "" },
    "remitente":    { "documento": "", "nombre": "" },
    "destinatario": { "documento": "", "nombre": "" },
    "comprobante":  { "cop_id": 0, "serie": "", "numero": "", "tipo": "", "tipo_pago": "", "estado_pago": "", "fecha": "", "hora": "" }
  },
  "status": {
    "registrado": { "fecha": "2026-04-15 09:12:30" },
    "origen":     { "fecha": "2026-04-15 09:12:30" },
    "transito":   { "fecha": "2026-04-15 14:08:21", "completo": true,
                    "cargueros": ["964724", "964583", "966345"], "carguero": "966345" },
    "demora":     null,
    "destino":    { "fecha": "2026-04-16 01:01:03", "completo": true },
    "entregado":  { "fecha": "2026-04-16 11:40:45" },
    "reparto":    null
  }
}

El campo detailed te dice qué esperar. Cuando es false (modo estado) la respuesta trae solo status y omite order. Cuando es true aparece order, pero sus bloques origen, destino, remitente, destinatario y comprobante llegan vacíos: desde julio de 2026 Shalom dejó de incluirlos en su respuesta de rastreo, y los devolvemos en cero para no romper el shape de quienes ya los leen. No construyas sobre esos campos. De order lo que sí llega: los identificadores, las fechas, contenido, monto, tipo_pago, estado_pago y los flags (entregado, reparto, aereo). El objeto status completo llega en ambos modos.

Los hitos de status

status trae los siete hitos del envío. Los que todavía no ocurrieron llegan en null — es lo normal en un envío en curso, no un error.

HitoDescripción
registradoLa orden se registró en el sistema.
origenRecibido en la agencia de origen.
transitoEn viaje. Único hito que trae cargueros[] (todos los transportistas que movieron el envío) y carguero (el último). Esos ids son el cap_id que pide el GRT.
demoraIncidencia que retrasó el envío. Normalmente null.
destinoLlegó a la agencia de destino.
entregadoEntregado al destinatario.
repartoSalió a reparto a domicilio. null si la entrega es en agencia.

La fecha incluye la hora. Cada hito trae un solo campo fecha con formato "YYYY-MM-DD HH:MM:SS" — no hay un campo hora separado.

POST/v1/tracking/batch

Rastrea varias guías en una sola llamada (hasta 50 por request). Cada item usa los mismos identificadores que GET /v1/tracking (numero, codigo u ose_id — al menos uno) más un custom_id opcional. Ideal para refrescar el estado de muchos envíos sin hacer N requests.

Body

CampoTipoDescripción
items[]arrayLista de guías a rastrear. Requerido, de 1 a 50 elementos.
items[].custom_idstringEtiqueta libre tuya (ej. tu id de pedido). No la interpretamos: la devolvemos verbatim en el resultado para que correlaciones cada respuesta con tu sistema. Opcional.
items[].numero / codigo / ose_idstringIdentificadores de la guía. Al menos uno por item, igual que en GET /v1/tracking.

Errores por item, no por batch. Un item que falla (guía inexistente, sin identificador, etc.) no tumba al resto: ese item trae ok: false con su error (mismo code que daría GET /v1/tracking), y el status global sigue siendo 200. Solo devuelve 400 si el envelope está malformado (JSON inválido, items vacío, o más de 50).

Orden preservado. results sale en el mismo orden que mandaste items, y cada resultado hace echo de custom_id y de los identificadores enviados — puedes correlacionar por posición o por custom_id.

Request
curl -X POST https://api.shalom-api-peru.com/v1/tracking/batch \
  -H "X-API-Key: mi-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "custom_id": "pedido-001", "numero": "82100156", "codigo": "W79H" },
      { "custom_id": "pedido-002", "ose_id": "584210" },
      { "custom_id": "pedido-003", "numero": "00000000" }
    ]
  }'
200 OK — order y status recortados por brevedad
{
  "results": [
    {
      "custom_id": "pedido-001",
      "numero": "82100156",
      "codigo": "W79H",
      "ok": true,
      "tracking": {
        "detailed": true,
        "order": { "ose_id": 584210, "numero_orden": "82100156", "entregado": true },
        "status": { "entregado": { "fecha": "2026-04-16 11:40:45" } }
      }
    },
    {
      "custom_id": "pedido-002",
      "ose_id": "584210",
      "ok": true,
      "tracking": {
        "detailed": false,
        "status": { "entregado": { "fecha": "2026-04-16 11:40:45" } }
      }
    },
    {
      "custom_id": "pedido-003",
      "numero": "00000000",
      "ok": false,
      "error": { "code": "not_found", "message": "orden no existe" }
    }
  ],
  "summary": { "total": 3, "ok": 2, "failed": 1 }
}

Campos de la respuesta

CampoTipoDescripción
results[].okbooltrue si la guía se rastreó; false si falló.
results[].trackingobjectPresente cuando ok: true. Mismo shape que la respuesta de GET /v1/tracking (order + status).
results[].errorobjectPresente cuando ok: false. { code, message }, con el mismo code que devolvería el tracking individual.
summaryobject{ total, ok, failed } — conteos del batch.
GET/v1/tracking/{ose_id}/events

Solo los hitos del envío: registrado, origen, tránsito, demora, destino, entregado, reparto. Es el mismo objeto status que ya trae GET /v1/tracking — úsalo cuando solo necesitas refrescar el estado y ya tienes el ose_id.

PathTipoDescripción
ose_idstringID del sistema OSE (SUNAT) — el campo order.ose_id de la respuesta de GET /v1/tracking. Es el handle público para acceder a eventos, comprobante y GRT.
200 OK
{
  "registrado": { "fecha": "2026-04-15 09:12:30" },
  "origen":     { "fecha": "2026-04-15 09:12:30" },
  "transito":   { "fecha": "2026-04-15 14:08:21", "completo": true,
                  "cargueros": ["964724", "964583", "966345"], "carguero": "966345" },
  "demora":     null,
  "destino":    { "fecha": "2026-04-16 01:01:03", "completo": true },
  "entregado":  { "fecha": "2026-04-16 11:40:45" },
  "reparto":    null
}
GET/v1/tracking/{ose_id}/voucher

Comprobante de la orden.

Fuera de servicio temporalmente. Hoy este endpoint responde 404 not_found para cualquier orden. El comprobante se localiza con datos que Shalom dejó de enviar en su respuesta de rastreo (ver el aviso en GET /v1/tracking), así que no hay forma de resolverlo. Estamos migrando a la nueva ruta de comprobantes de Shalom; mientras tanto, no dependas de este endpoint.

GET/v1/tracking/{ose_id}/grt?cap_id={cap_id}

Enlace a la Guía de Remisión del Transportista. Un envío puede pasar por varios cargueros (transportistas) y cada uno emite su propio GRT — por eso el cap_id es obligatorio.

ParamEnDescripción
ose_idpathID del sistema OSE (SUNAT) — el campo order.ose_id de la respuesta de GET /v1/tracking.
cap_idqueryID del carguero (transportista). Los ids están en status.transito.cargueros[] de GET /v1/tracking. (requerido)
Request
curl -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  "https://api.shalom-api-peru.com/v1/tracking/584210/grt?cap_id=898441"
200 OK
{
  "enlace": "https://..."
}
GET/v1/agencies

Lista paginada de agencias.

ParamDefaultDescripción
page1Página
per_page100Ítems por página (máx 500)
200 OK
{
  "page": 1,
  "per_page": 100,
  "items": [
    {
      "id": 1,
      "nombre": "LIMA CENTRO",
      "departamento": "LIMA",
      "aereo": true,
      "latitud": -12.046,
      "longitud": -77.030
    }
  ]
}
GET/v1/agencies/{id}

Detalle completo de una agencia por ID numérico.

GET/v1/locations/departments

Todos los departamentos del Perú.

{
  "items": [
    { "id": 1, "name": "AMAZONAS", "ubi_id": 101 }
  ]
}
GET/v1/locations/departments/{depId}/provinces

Provincias de un departamento.

PathDescripción
depIdID del departamento
GET/v1/locations/departments/{depId}/provinces/{provId}/districts

Distritos de una provincia.

PathDescripción
depIdID del departamento
provIdID de la provincia

Crear pedido en Shalom Pro

Crea una guía de envío real en la cuenta de Shalom Pro del cliente y devuelve los identificadores para tracking, descarga de PDF y operaciones posteriores. La resolución de remitente y destinatario, la validación del servicio de cobranza y la cotización se hacen automáticamente en una sola llamada.

Cada POST /v1/orders exitoso crea una preguía real en la cuenta de Shalom Pro, visible en el panel de pro.shalom.pe. Si la creaste por error, bórrala con DELETE /v1/orders/{id} mientras no haya sido despachada.

Estos endpoints necesitan X-API-Key + credenciales de Shalom Pro: ver Autenticación.

Latencia y reintentos

La primera llamada de cada cuenta hace un login real contra Shalom y tarda ~90 s, hasta 2 min. Las siguientes son rápidas: la sesión queda caliente y se reutiliza. Aplica a crear pedido, tracking, sesiones, bulk y productos.

Configura el timeout de tu cliente HTTP en al menos 150 s para estos endpoints. Con un timeout corto (20–30 s) la primera llamada se corta antes de terminar y te devuelve un error aunque la operación siga en curso del lado del servidor.

Si una llamada se corta por timeout

En las lecturas (tracking, productos, tarifa, listar órdenes, crear sesión) reintenta sin miedo: la sesión termina de calentarse en segundo plano y el reintento sale rápido.

No reintentes a ciegas POST /v1/orders ni /v1/orders/bulk. Un timeout de tu cliente no significa que la orden no se creó: puede haberse creado igual. Como la API no tiene clave de idempotencia, un reintento crearía una segunda guía real y cobrable. Ante un timeout al crear, consulta primero GET /v1/orders y revisa si la guía ya está ahí; reintenta solo si no aparece.

Usar la opción A concentra el arranque lento en una sola llamada: pides el token con POST /v1/shalom/sessions (una lectura, reintentable sin riesgo) y las órdenes posteriores ya salen rápidas.

POST/v1/shalom/sessions

Valida las credenciales contra pro.shalom.pe y emite un token efímero (formato ssk_<64-hex>) con TTL de 2 horas.

El password viaja en el body (no en headers, que suelen acabar en logs) y no se persiste: se usa para emitir el token y se descarta.

Request
curl -X POST https://api.shalom-api-peru.com/v1/shalom/sessions \
  -H "X-API-Key: tu-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "cliente@empresa.com",
    "password": "tu-password-de-shalom-pro"
  }'
200 OK
{
  "session_token": "ssk_a1b2c3d4e5f6...",
  "expires_at": "2026-05-08T18:23:01Z"
}

Body

CampoTipoDescripción
emailstringEmail de la cuenta en pro.shalom.pe.
passwordstringPassword de esa cuenta.

Cuándo expira el token

Dura 2 horas desde su emisión. Un token vencido devuelve 401 shalom_auth_failed: pide uno nuevo. Los tokens viven en memoria, así que un reinicio del servicio los invalida.

DELETE/v1/shalom/sessions

Revoca un token antes de que expire. Idempotente: si el token ya estaba revocado o nunca existió, igual responde 200. Útil para forzar logout cuando el cliente cambia de cuenta o detecta actividad sospechosa.

Request
curl -X DELETE https://api.shalom-api-peru.com/v1/shalom/sessions \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Session: ssk_a1b2c3d4e5f6..."
200 OK
{ "revoked": true }

El token a revocar va en el header X-Shalom-Session, no en el path ni en el body. Si el header falta, devuelve 400.

GET/v1/products

Lista los productos disponibles en la cuenta de Shalom Pro del cliente: Sobre, Caja Paquete XXS/XS/S/M/L y Otra Medida. Devuelve el id que usas en el body de POST /v1/orders y las dimensiones default de cada uno.

Mismo auth Shalom que el resto (ver Autenticación). La conexión con Shalom se reutiliza entre todos los endpoints: si ya creaste una orden con esas credenciales, esta llamada no vuelve a pagar el login.

Request
curl https://api.shalom-api-peru.com/v1/products \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro"
200 OK
{
  "products": [
    {
      "id": 3,
      "title": "Sobre",
      "img": "https://pro.shalom.pe/img/products/sobre.png",
      "content": "Hasta 0.5 kg",
      "sub_content": "Documentos y similares",
      "measurements": {
        "weight": 0.5,
        "width": 0.3,
        "height": 0.02,
        "length": 0.4
      }
    },
    {
      "id": 1098,
      "title": "Otra Medida",
      "img": "https://pro.shalom.pe/img/products/custom.png",
      "content": "Define las dimensiones",
      "sub_content": "Para bultos fuera de catálogo",
      "measurements": { "weight": 0, "width": 0, "height": 0, "length": 0 }
    }
  ]
}

Campos del producto

CampoTipoDescripción
idintID del producto. Pásalo en product_id al crear la orden.
titlestringNombre legible: "Sobre", "Caja Paquete XS", "Otra Medida", etc.
contentstringDescripción corta del límite de peso.
sub_contentstringTexto auxiliar: ejemplos de uso.
imgstringURL del thumbnail para mostrar en tu UI.
measurementsobjectMedidas default { weight, width, height, length } en kg/m. Para Otra Medida vienen en cero — defínelas tú en el body de la orden.

Otra Medida — bulto fuera de catálogo

Si tu envío no entra en los tamaños estándar, usa el producto cuyo title sea "Otra Medida" y manda el campo opcional dimensions en el body de POST /v1/orders con { weight_kg, height_m, length_m, width_m }. Para los demás productos puedes omitirlo y se usan las medidas default.

POST/v1/tariff/calculate

Calcula el precio de un envío entre dos terminales sin comprometer la orden. Devuelve el precio para cada tipo de producto del catálogo (sobre, cajas XXS/XS/S/M/L y otra medida) para que puedas mostrar la cotización antes del checkout.

Request
curl -X POST https://api.shalom-api-peru.com/v1/tariff/calculate \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_terminal_id": 66,
    "destiny_terminal_id": 7,
    "dimensions": {
      "weight_kg": 1.0,
      "height_m": 0.1,
      "length_m": 0.2,
      "width_m": 0.15
    }
  }'
200 OK
{
  "currency": "PEN",
  "breakdown": {
    "sobre": 8.00,
    "caja_paquete_xxs": 8.00,
    "caja_paquete_xs": 10.00,
    "caja_paquete_s": 12.00,
    "caja_paquete_m": 15.00,
    "caja_paquete_l": 20.00,
    "otra_medida": 25.00
  }
}

Body

CampoTipoDescripción
origin_terminal_idintegerID del terminal de origen (lo obtienes de /v1/agencies) (requerido)
destiny_terminal_idintegerID del terminal de destino (requerido)
dimensionsobjectPeso y medidas del paquete (weight_kg, height_m, length_m, width_m). Afectan SOLO el precio de otra_medida. Si no se pasan, se usan defaults mínimos.
product_idintegerSi lo pasas, la respuesta incluye un campo product con el precio puntual para mostrar al usuario. Si el id no existe en el catálogo, el campo se omite (la respuesta sigue siendo 200 con el breakdown).

Con product_id — precio puntual

Si ya sabes qué producto va a comprar el usuario, manda product_id y te ahorras el mapeo manual del breakdown:

Request
curl -X POST https://api.shalom-api-peru.com/v1/tariff/calculate \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_terminal_id": 66,
    "destiny_terminal_id": 7,
    "product_id": 3
  }'
200 OK
{
  "currency": "PEN",
  "breakdown": {
    "sobre": 8.00,
    "caja_paquete_xxs": 8.00,
    "caja_paquete_xs": 10.00,
    "caja_paquete_s": 12.00,
    "caja_paquete_m": 15.00,
    "caja_paquete_l": 20.00,
    "otra_medida": 25.00
  },
  "product": {
    "id": 3,
    "title": "Sobre",
    "price": 8.00
  }
}

Errores: los generales. El 400 se da si faltan origin_terminal_id o destiny_terminal_id.

POST/v1/orders

Crea una guía de envío. Devuelve guia, serie y codigo — los identificadores con los que después puedes trackear o borrar la orden.

Request
curl -X POST https://api.shalom-api-peru.com/v1/orders \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  -H "Content-Type: application/json" \
  -d '{
    "origin_terminal_id": 404,
    "destiny_terminal_id": 7,
    "product_id": 3,
    "quantity": 1,
    "payer": "sender",
    "declaracion_jurada": "docs",
    "receiver": {
      "document_type": "DNI",
      "document": "87654321",
      "name": "MARIA",
      "last_name": "GOMEZ",
      "sur_name": "TORRES",
      "phone": 998765432
    },
    "pickup_code": "2415"
  }'
200 OK
{
  "guia": "80574902",
  "serie": "v872",
  "codigo": "CJTW",
  "ose_id": 584210
}

Guarda el ose_id: lo necesitas para descargar el rótulo y el comprobante. Qué es cada identificador de la respuesta, en Identificadores.

Todos los campos del body — requeridos y opcionales — están en Campos del body. Ojo con declaracion_jurada: es requerido ("docs", "ropa", "art" o "electro"); si lo omites, la API responde 400.

Errores propios de crear pedido

HTTPcodeCuándo se da
409conflictYa hay una persona registrada con ese documento: pasa su id en vez de los datos.
422upstream_rejectedRegla de negocio de Shalom: cuenta sin servicio de cobranza, producto restringido para esa ruta, etc.

El resto, en Errores.

401 — credenciales rechazadas
{
  "error": {
    "code": "shalom_auth_failed",
    "message": "X-Shalom-Email/Password rechazados por Shalom Pro",
    "request_id": "01KQ..."
  }
}
POST/v1/orders/bulk

Carga masiva de envíos desde la plantilla Excel de Shalom Pro (Formato-Pro-Masivo.xlsx). El wrapper importa el archivo, lo valida (todo-o-nada), asigna la misma clave de retiro a todo el lote y lo emite — en una sola llamada. Para crear un solo envío usa POST /v1/orders.

Es multipart/form-data, no JSON. Envía dos campos: file (el .xlsx) y pickup_code (clave de retiro de 4 dígitos aplicada a todos los envíos). Mismo auth que POST /v1/orders: X-API-Key + sesión Shalom (header X-Shalom-Session o X-Shalom-Email/X-Shalom-Password).

Campos (form-data)

CampoTipoDescripción
filefile (.xlsx)La plantilla Formato-Pro-Masivo con una fila por envío. Máximo 5 MB. (requerido)
pickup_codestringClave de retiro de 4 dígitos que se asigna a todos los envíos del archivo (mismas reglas que en la orden única). (requerido)
Request
curl -X POST https://api.shalom-api-peru.com/v1/orders/bulk \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  -F "file=@Formato-Pro-Masivo.xlsx" \
  -F "pickup_code=2415"
200 OK
{
  "count": 12,
  "message": "Envíos registrados",
  "created": []
}

created siempre viene vacío: Shalom confirma la emisión pero no devuelve las guías. Para obtenerlas, consulta GET /v1/orders justo después — las del lote son las más recientes.

Validación todo-o-nada

Si cualquier fila tiene errores, no se crea ningún envío: la respuesta es 422 con el detalle por fila en rows para que corrijas el Excel y reintentes.

422 — filas con errores
{
  "error": {
    "code": "upstream_rejected",
    "message": "el archivo tiene filas con errores de validación",
    "request_id": "01KQ..."
  },
  "rows": [
    {
      "row": 3,
      "errors": ["El destinatario no tiene documento válido"]
    },
    {
      "row": 7,
      "errors": ["Terminal de destino no encontrado", "Peso requerido"]
    }
  ]
}

Errores propios de bulk

HTTPcodeCuándo se da
413payload_too_largeEl archivo supera los 5 MB.
415unsupported_media_typeEl body no es multipart/form-data.
422upstream_rejectedHay filas con errores (detalle en rows). No se creó ningún envío.

El resto, en Errores.

GET/v1/orders

Lista las órdenes de la cuenta: preguías y envíos despachados. Sin query params devuelve todas en una sola respuesta, sin campo meta. Al mandar page o per_page se activa la paginación y se agrega meta.

Cuidado con cuentas grandes. Cada orden pesa ~3 KB: una cuenta con 5.000 envíos devuelve ~15 MB si no paginas. Usa per_page.

La paginación y los filtros se aplican del lado de la API, no de Shalom: recortan la respuesta, pero no reducen la carga contra el upstream.

Query params (opcionales)

ParamDefaultDescripción
page1Número de página. Mandar page o per_page activa la paginación y agrega meta. Una página fuera de rango devuelve "orders": [] con el meta correcto.
per_page20Ítems por página. Tope máximo 200 (valores mayores se recortan a 200).
guiaFiltra por match exacto del campo guia.
statusFiltra por match exacto del código numérico de status (ej. 1).
fromFecha mínima (YYYY-MM-DD), inclusive. Filtra por la fecha de created_at.
toFecha máxima (YYYY-MM-DD), inclusive. Filtra por la fecha de created_at.

Los filtros (guia, status, from, to) se combinan con AND y se aplican antes de paginar, así que meta.total refleja el conteo ya filtrado. Filtrar sin paginar devuelve el mismo shape de siempre (sin meta).

Request — sin params (lista completa)
curl https://api.shalom-api-peru.com/v1/orders \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro"
200 OK
{
  "orders": [
    {
      "id": 87654321,
      "internal_id": 4567890,
      "guia": "12345678",
      "serie": "s001",
      "codigo": "ABCD",
      "pickup_code": "2415",
      "created_at": "2026-05-08 10:11:12",
      "updated_at": "2026-05-08 10:11:13",
      "shipping_date": "2026-05-08",
      "status": 1,
      "payer": "receiver",
      "aereo": false,
      "delivered": false,
      "paid": false,
      "origin": {
        "ter_id": 404,
        "name": "SALAS ICA",
        "address": "SUB LOTE 01 ZONA PANAMERICANA SUR KM. 293.350...",
        "abbreviation": "sls",
        "phone": "(01) 500 7878",
        "ubigeo": "ICA - ICA - SALAS"
      },
      "destination": {
        "ter_id": 7,
        "name": "AV PARRA 379 CO",
        "address": "AV. PARRA 379",
        "abbreviation": "AQP",
        "phone": "0",
        "ubigeo": "AREQUIPA - AREQUIPA - AREQUIPA"
      },
      "sender": {
        "id": 1234567,
        "document": "12345678",
        "name": "JUAN",
        "last_name": "PEREZ",
        "sur_name": "GARCIA",
        "full_name": "JUAN PEREZ GARCIA",
        "phone": 999888777
      },
      "receiver": { "id": 7654321, "document": "87654321", "...": "..." },
      "items": [
        {
          "id": 11223344,
          "quantity": 1,
          "weight": 0,
          "width": 0,
          "length": 0,
          "height": 0,
          "status": 1,
          "product_name": "Sobre",
          "product_detail": "Documentos simples en sobre manila / Tamaño A4",
          "product_price": 20
        }
      ],
      "warranty": { "active": false, "amount": 0, "total_cost": 0, "percent": 0.01 }
    }
  ]
}
Request — paginado + filtrado
# Paginar (agrega "meta") + filtrar por estado y rango de fechas
curl "https://api.shalom-api-peru.com/v1/orders?page=1&per_page=20&status=1&from=2026-05-01&to=2026-05-31" \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro"
200 OK (con meta)
{
  "orders": [
    { "id": 87654321, "guia": "12345678", "status": 1, "created_at": "2026-05-08 10:11:12" }
  ],
  "meta": {
    "total": 142,
    "page": 1,
    "per_page": 20,
    "last_page": 8
  }
}

Campos de la orden

CampoTipoDescripción
id / internal_id / guia / serie / codigo / pickup_codeIdentificadores del envío. Ver Identificadores.
created_at / updated_at / shipping_datestringTimestamps en formato Lima (YYYY-MM-DD HH:MM:SS y YYYY-MM-DD).
statusintCódigo numérico de estado de la orden (1 = creada, etc.). El significado lo define el flujo operativo de Shalom Pro.
payerstring"sender" o "receiver" (mapeado de REMITENTE/DESTINATARIO).
aereo / delivered / paidboolBooleanos que indican si el envío es aéreo, ya fue entregado y/o ya fue pagado.
origin / destinationobjectAgencia con ter_id, name, address, abbreviation, phone, ubigeo.
sender / receiverobjectPersona con id, document, name, last_name, sur_name, full_name, phone, email, address.
items[]arrayDetalle por bulto: quantity, weight, width, length, height, product_name, product_detail, product_price.
warrantyobject{ active, amount, total_cost, percent }. Si la orden no tiene garantía, active es false.
DELETE/v1/orders/{id}

Borra una orden de la cuenta del cliente. Funciona tanto para borradores (preguías sin guía empresarial real) como para guías ya emitidas que aún no fueron recibidas en agencia.

{id} es el campo id de GET /v1/orders, no el ose_id ni la guia (ver Identificadores).

Operación destructiva. La orden no se puede recuperar una vez borrada.

Request
curl -X DELETE https://api.shalom-api-peru.com/v1/orders/87654321 \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro"
200 OK
{
  "deleted": true,
  "id": 87654321
}

El error propio de este endpoint es el 422: Shalom rechaza el borrado cuando la guía ya fue recibida en agencia o despachada. El resto, en Errores.

422 — Shalom rechaza el borrado
{
  "error": {
    "code": "upstream_rejected",
    "message": "Shalom Pro rechazó el borrado: shalompro: delete /delete-guia: La guia ya fue recibida en agencia",
    "request_id": "01KQ..."
  }
}
GET/v1/orders/{ose_id}/label

Descarga el rótulo (la etiqueta que se pega en el paquete) como PDF binario: Content-Type: application/pdf. El ose_id lo devuelve POST /v1/orders (ver Identificadores).

Request
curl https://api.shalom-api-peru.com/v1/orders/1234567890/label \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  --output rotulo.pdf

Devuelve 404 si el ose_id no existe en la cuenta. Ver Errores.

GET/v1/orders/{ose_id}/voucher

Descarga el comprobante de la orden (resumen con datos de remitente, destinatario e items) como PDF. Mismo contrato que /label: Content-Type: application/pdf binario y mismo path param {ose_id}.

Request
curl https://api.shalom-api-peru.com/v1/orders/1234567890/voucher \
  -H "X-API-Key: tu-api-key" \
  -H "X-Shalom-Email: cliente@empresa.com" \
  -H "X-Shalom-Password: tu-password-de-shalom-pro" \
  --output comprobante.pdf

Errores específicos

Mismos códigos que /label (400, 404, 502).

Campos del body

El remitente lo toma Shalom de la cuenta autenticada (X-Shalom-Email / la sesión que uses). El body solo lleva al destinatario en receiver, y un campo sender suelto se ignora. Única excepción: si mandas shipment_type: "empresarial", la guía se emite a nombre de una empresa remitente y ahí sí el sender es obligatorio (ver más abajo).

Requeridos

CampoTipoDescripción
origin_terminal_idintID de la agencia de origen (campo id de GET /v1/agencies)
destiny_terminal_idintID de la agencia de destino (campo id de GET /v1/agencies)
product_idintID del producto (Sobre, Caja XXS/XS/S/M/L, Otra Medida). Listalo con GET /v1/products.
receiverobjectDestinatario. Siempre debe incluir document_type + document (ver subcampos abajo).
pickup_codestringClave de retiro de 4 dígitos. No puede ser repetido (1111..9999) ni consecutivo (1234..6789).
declaracion_juradastringTipo de contenido declarado — Shalom lo exige en toda orden. Acepta 4 alias cortos: "docs", "ropa", "art" (artículos de uso personal), "electro" (electrodomésticos); o los 4 textos literales que Shalom imprime en la guía ("Documentos", "Ropa", "Articulos de uso personal" sin tilde, "Electrodomésticos" con tilde). Si lo omites, la API responde 400 con los valores aceptados. (requerido)

Opcionales

CampoTipoDescripción
quantityintDefault 1.
shipment_typestringSi lo omites, la guía se crea como siempre (remitente = la cuenta autenticada). Con "empresarial" se emite una guía empresarial a nombre de una empresa remitente, y pasa a ser obligatorio el campo sender. Ver Guía empresarial.
senderobjectSolo se usa con shipment_type: "empresarial". Identifica a la empresa remitente: { "document_type": "RUC", "document": "20xxxxxxxxx" }. En el flujo normal se ignora.
payerstring"sender" (default, paga al despachar) o "receiver" (contra entrega).
dimensionsobjectDimensiones custom { weight_kg, height_m, length_m, width_m } — valores numéricos (peso en kg, medidas en metros, ej. { "weight_kg": 2.5, "height_m": 0.3, "length_m": 0.4, "width_m": 0.2 }). Solo para producto "Otra Medida".
aereoboolServicio aéreo. Solo si origen y destino lo soportan.
warrantyobject{ "amount": "100.00" } activa garantía con monto declarado.
collection_serviceobjectCobro contra entrega. Objeto { cost, data } — ver subcampos abajo. Requiere cuenta bancaria registrada en Shalom Pro previamente.
documentationarrayGuías adicionales: [{ "guide": "G123", "series": "S01" }].
contacto_docstringDocumento de la persona que recoge.

Guía empresarial (remitente empresa)

Por defecto la guía se emite a nombre del titular de la cuenta (una persona), y eso no se puede cambiar: el flujo estándar de Shalom no acepta otro remitente. Si necesitas que salga a nombre de tu empresa, agrega estos dos campos al body — el resto queda igual:

"shipment_type": "empresarial",
"sender": { "document_type": "RUC", "document": "20xxxxxxxxx" }

El document es el RUC de la empresa que quieres que figure como remitente. Sin shipment_type, todo se comporta exactamente como antes.

Requisito previo: la empresa tiene que estar registrada y aprobada en tu cuenta de Shalom Pro (panel: Tu Negocio → Crear negocio — RUC, representante legal y documentos de respaldo; Shalom la aprueba manualmente). Si el RUC no está registrado, la API responde 404 con el mensaje "la empresa (RUC) no está registrada en la cuenta Shalom Pro".

Ten en cuenta que una guía empresarial es un producto distinto dentro de Shalom, no la misma guía con otro nombre impreso: tiene su propia tarifa y su propio flujo de emisión. El remitente empresa aparece en el comprobante (/voucher); el rótulo solo muestra destinatario y destino.

receiver (destinatario)

El destinatario (persona natural o empresa) siempre se identifica por documento: document_type + document son obligatorios, aunque pases también el id. Según lo que envíes:

  1. Solo document_type + document: si el destinatario ya existe en la cuenta, se reusa. Si aún no existe, agrega también name y — solo para DNI/CE — last_name + sur_name para registrarlo. Opcionales: phone, email, address.
  2. Con id: si ya conoces su id (de GET /v1/persons/search o de un envío anterior), pásalo junto con document_type + document para evitar la búsqueda.

El remitente no va en el body: Shalom lo toma de la cuenta autenticada.

Empresas (RUC): la razón social va completa en name. last_name y sur_name no aplican.

Ejemplo: persona (DNI)

{
  "document_type": "DNI",
  "document": "12345678",
  "name": "JUAN",
  "last_name": "PEREZ",
  "sur_name": "GARCIA",
  "phone": 999888777
}

Ejemplo: empresa (RUC)

{
  "document_type": "RUC",
  "document": "20123456789",
  "name": "MI EMPRESA S.A.C.",
  "phone": 999888777
}
CampoTipoDescripción
idintperson_id de Shalom Pro. Si lo pasas, los demás campos no son necesarios.
document_typestring"DNI" (8 dígitos), "RUC" (11 dígitos, empresas) o "CE" (carnet de extranjería).
documentstringSolo dígitos para DNI/RUC; alfanumérico ≥4 chars para CE.
namestringNombre (persona) o razón social completa (empresa). Obligatorio.
last_namestringPrimer apellido. Obligatorio para DNI/CE, no aplica a RUC.
sur_namestringSegundo apellido. No aplica a RUC.
phoneintTeléfono numérico (sin código de país, ej. 999888777).
emailstringOpcional.
addressstringOpcional.

Servicio de cobranza (collection_service)

Cobro contra entrega: Shalom cobra al destinatario y deposita en la cuenta bancaria que indiques. Es un objeto con cost (el monto a cobrar) y data (los datos de la cuenta donde se deposita).

Requiere que la cuenta tenga el servicio de cobranza habilitado en pro.shalom.pe. Si no lo está, la orden falla con 422 upstream_rejected.

CampoTipoDescripción
costnumberMonto a cobrar al destinatario (soles). Numérico, sin comillas.
data.documentstringDocumento del titular de la cuenta.
data.namestringNombre del titular de la cuenta.
data.bankstringBanco: "BCP", "BBVA", "Interbank" o "Scotiabank".
data.type_accountstringTipo de cuenta: "Ahorro" o "Corriente".
data.account_numberstringNúmero de cuenta.
data.ccistringCódigo de Cuenta Interbancario (CCI), 20 dígitos.

Ejemplo

{
  "cost": 150.00,
  "data": {
    "document": "12345678",
    "name": "JUAN PEREZ",
    "bank": "BCP",
    "type_account": "Ahorro",
    "account_number": "1234567890",
    "cci": "00212312345678901234"
  }
}

Errores

Estructura consistente en todos los errores:

{
  "error": {
    "code": "not_found",
    "message": "orden no existe",
    "request_id": "01JXZ..."
  }
}
HTTPcodeDescripción
400bad_requestParámetros faltantes o inválidos
401unauthorizedAPI key inválida o ausente
401shalom_auth_failedCredenciales de Shalom Pro ausentes o rechazadas (endpoints de tracking y /v1/orders)
403forbiddenSin permisos
404not_foundRecurso no existe
409conflictConflicto de estado (ej. persona duplicada en /v1/orders)
413payload_too_largeArchivo demasiado grande (solo /v1/orders/bulk)
415unsupported_media_typeContent-Type no soportado (ej. /v1/orders/bulk espera multipart/form-data)
422upstream_rejectedShalom Pro rechazó la solicitud por una regla de negocio
429rate_limitedRate limit excedido
502upstream_unavailableShalom Pro no respondió o devolvió algo inesperado
502shalom_unavailableShalom Pro no disponible (solo /v1/orders)
504upstream_timeoutShalom Pro tardó más de 30s en responder
500internalError interno

Rate Limits

Limite por defecto: 60 requests por minuto por API key.

Toda respuesta (no solo el 429) incluye los headers X-RateLimit-*, así sabes tu límite y cuánto te queda sin tener que chocar contra el error. Los exponemos vía Access-Control-Expose-Headers, así que también son legibles desde el browser.

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 55
X-RateLimit-Reset: 1713200060

Headers

HeaderDescripción
X-RateLimit-LimitRequests por minuto de tu plan.
X-RateLimit-RemainingRequests disponibles ahora mismo (capacidad de ráfaga restante). Llega a 0 cuando estás topeado.
X-RateLimit-ResetEpoch en segundos (UTC) en que el cupo vuelve a estar lleno.
Retry-AfterSolo en el 429: segundos a esperar antes de reintentar (mínimo 1).

Health Checks

Públicos, sin autenticación.

GET/healthz

Liveness. 200 si el servidor está vivo.

GET/readyz

Readiness. 200 cuando está listo para recibir tráfico.