Referencia de la API
Base https://api.enviadores.com.mx/api/v1 · autentica con Authorization: Bearer ek_test_… (sandbox) o ek_live_…. Errores, reintentos, webhooks y modo de prueba están en la guía.
Versión 1.2.0 · OpenAPI 3.1.0 · openapi.json
rates
Cotización multi-paquetería
Cotizar en todas las paqueterías
post/rates
Cotiza con todas las paqueterías activas y devuelve tarifas con id (rate_id) listas para POST /shipments.
›Ver reglas
Requiere scope rates:read. Solo rutas domésticas MX (country ≠ MX → 422). Bultos: UN paquete (package) o VARIOS en un mismo envío (packages, 2–10 bultos; mandar ambos → 422). Con packages solo se devuelven ofertas reservables como envío multi-bulto (los servicios que no lo soportan no aparecen) y el peso facturable es la SUMA de los bultos. Códigos postales: deben existir en el catálogo SEPOMEX — un CP inexistente (p. ej. 99999) responde 422 nombrando from.postal_code/to.postal_code ANTES de consultar a las paqueterías (antes se cotizaba por zona a ciegas). Las tarifas de referencia de mostrador (market) NO se incluyen: todo rate_id devuelto es enviable. Seguro: insurance.insured_value (alias obsoleto declared_value) contrata cobertura para el envío: la póliza de la plataforma (prima incluida y firmada dentro de pricing.total_price) o, si esa póliza no está disponible, la cobertura de la propia paquetería — el precio mostrado ya la incluye y cada tarifa lo indica con insurance_included / insurance_not_supported. *Insurance: insured_value buys coverage; when the platform policy is unavailable the carrier's own coverage is used and the price shown already includes it.* Vista agrupada: ?view=grouped devuelve cada servicio distinto UNA sola vez (paquetería + nivel de servicio) con su rango de precios y sus opciones reservables (ver RatesGroupedResponse); la forma plana por defecto no cambia. La respuesta es la MISMA para toda llave (los costos de proveedor nunca se exponen). Plazo: la cotización responde en un máximo de ~15 s con las tarifas que llegaron a tiempo; si alguna cotización no llegó a tiempo, incomplete es true — las tarifas listadas siguen siendo reservables y una nueva cotización puede mostrar más. *Deadline: the quote answers within ~15 s with the rates that arrived; incomplete: true flags that more may exist.*
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header. - view"grouped"· query
grouped: colapsaservices[]en un elemento por servicio distinto (RatesGroupedResponse, conview: "grouped"). Omitido: la forma plana (RatesResponse).
›Campos del cuerpo (7)
Mostrar 5 camposOcultar campos
- postal_codestringrequerido
CP de 5 dígitos (MX).
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - statestring
- citystring
- neighborhoodstring
- from_idstring
Id de un remitente del directorio (
GET /senders). Mutuamente excluyente confrom. Mostrar 5 camposOcultar campos
- postal_codestringrequerido
CP de 5 dígitos (MX).
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - statestring
- citystring
- neighborhoodstring
- to_idstring
Id de un destinatario del directorio (
GET /recipients). Mutuamente excluyente conto. - packageobject
Mostrar 4 camposOcultar campos
- weight_kgnumberrequerido
Peso real en kg. El facturable es max(real, volumétrico L*W*H/5000); máximo 70 kg.
- length_cmnumber
Largo exterior de la caja ya cerrada (cm). Manda las tres medidas: sin ellas la cotización se calcula para una caja de 10×10×10 cm, llega con
size_estimated: trueyPOST /shipmentsla rechaza (422PACKAGE_DIMENSIONS_REQUIRED). *Outer size of the closed box; without all three the price is only an estimate and create is refused.* - width_cmnumber
Ancho exterior de la caja ya cerrada (cm).
- height_cmnumber
Alto exterior de la caja ya cerrada (cm).
- packagesPackagesList
Mostrar 4 camposOcultar campos
- weight_kgnumberrequerido
Peso real del bulto (kg).
- length_cmnumber
Largo exterior del bulto cerrado (cm). Obligatorio para
POST /shipments; enPOST /rates, sin las tres medidas el precio es estimado (size_estimated). - width_cmnumber
Ancho exterior del bulto cerrado (cm).
- height_cmnumber
Alto exterior del bulto cerrado (cm).
- insuranceInsuranceRequest
Mostrar 2 camposOcultar campos
- insured_valuenumber
Valor a asegurar (MXN). La prima queda incluida en
pricing.total_pricede cada tarifa. - declared_valuenumber
OBSOLETO — alias de
insured_value(compatibilidad con integradores previos). Si mandas ambos deben coincidir.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Cotización exitosa (forma autenticada multi-paquetería). | |
| 400 | INVALID_JSON | Cuerpo vacío/no-JSON |
MISSING_ROUTE | ||
MISSING_PACKAGE | ||
WEIGHT_EXCEEDED | Peso facturable > 70 kg | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 422 | VALIDATION_ERROR | Details.fields lista los campos — incluye country ≠ MX, package y packages juntos, packages fuera de 2–10, un CP inexistente en SEPOMEX (from.postal_code: el CP 99999 no existe en el catálogo SEPOMEX) y campos desconocidos a nivel raíz |
INSURANCE_VALUE_TOO_HIGH | Valor asegurado sobre el máximo asegurable | |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | RATES_ERROR | La cotización falló; reintenta. |
›Campos de la respuesta 200
curl -X POST "https://api.enviadores.com.mx/api/v1/rates" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": { "postal_code": "01000" }, "to": { "postal_code": "64000" }, "package": { "weight_kg": 1, "length_cm": 20, "width_cm": 15, "height_cm": 10 }}'{
"success": true,
"data": {
"services": [
{
"id": "e2etest_E2ETestCarrier_E2EStandard_ab12cd34",
"carrier": "Estafeta",
"service_name": "Terrestre",
"service_type": "standard_economy",
"tier": "standard_economy",
"delivery_window": "ground",
"pickup_included": false,
"address_delivery": true,
"pricing": {
"total_price": 145.5,
"currency": "MXN",
"iva_included": true,
"insurance_premium": 0,
"insured_value": null
},
"delivery": {
"estimated_days": "3-5",
"estimated_date": null,
"min_days": 3,
"max_days": 5
},
"insurance_included": false,
"insurance_not_supported": false,
"zona_extendida": false
},
{
"id": "e2etest_E2ETestCarrier_E2EExpress_7f0a16b2",
"carrier": "FedEx",
"service_name": "Express Nacional",
"service_type": "express",
"tier": "express",
"delivery_window": "next_day",
"pickup_included": true,
"address_delivery": true,
"pricing": {
"total_price": 289,
"currency": "MXN",
"iva_included": true,
"insurance_premium": 0,
"insured_value": null
},
"delivery": {
"estimated_days": "1-2",
"estimated_date": null,
"min_days": 1,
"max_days": 2
},
"insurance_included": false,
"insurance_not_supported": false,
"zona_extendida": false
}
],
"fetched_at": "2026-07-13T18:42:05Z",
"stale_after": "2026-07-13T18:47:05Z",
"meta": {
"billable_weight": 1,
"volumetric_weight": 0.6,
"zone": 5
},
"rate_id_expires_in_seconds": 1800,
"incomplete": false
},
"requestId": "req_a1b2c3d4e5"
}shipments
Creación y consulta de envíos
Listar envíos (paginado)
get/shipments
Por defecto: solo envíos creados por el usuario de la llave, para TODOS los roles — una llave admin ve únicamente sus propios envíos de cuenta de servicio.
›Ver reglas
Alcance por PDV (opt-in). Una llave admin que envía X-PDV-ID y tiene el scope shipments:read:pdv lista en cambio los envíos de ese punto de venta, sin importar qué operador los creó — el mismo fondo que ya responden /senders, /recipients y /balance bajo ese header. Sin el scope, el header no amplía nada y sigues viendo solo lo tuyo (los integradores que ya lo envían no cambian de resultados).
Cambio de contrato (2026-07): X-PDV-ID ahora se valida en esta ruta aunque no amplíe nada. Antes se ignoraba por completo, así que un PDV inexistente o fuera de la allowlist de la llave respondía 200 con lista vacía mientras /senders respondía 403; ahora responde 403 PDV_NOT_ALLOWED como todas las rutas hermanas.
Orden: más recientes primero. Requiere scope shipments:read (+ shipments:read:pdv para el alcance por PDV).
Parámetros
- pageinteger· query
- limitinteger· query
- guiastring· query
Búsqueda exacta por número de guía. más
Es la forma de llegar a un envío cuando solo tienes el número que ve el cliente (el
idinterno no es adivinable). Devuelve una página de 0 o 1 elementos, sujeta a la misma visibilidad que el resto del listado. - status"created" | "collected" | "in_transit" | "out_for_delivery" | "delivered" | "exception" | "returned" | "cancelled" | "unknown"· queryFiltra por estatus canónico — exactamente el mismo valor que devuelve el campo
statusde cada envío. Un valor fuera del enum es422 VALIDATION_ERROR, nunca una lista vacía silenciosa. - fromstring· queryDía calendario INCLUSIVO (zona horaria de negocio America/Mexico_City) desde el cual listar, por fecha de creación. Solo
YYYY-MM-DD. - tostring· queryDía calendario INCLUSIVO (America/Mexico_City) hasta el cual listar — el día completo, hasta las 23:59:59 locales.
fromposterior atoes422. - X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Página de envíos. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 422 | VALIDATION_ERROR | Filtro inválido (details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 17 camposOcultar campos
- idstringrequerido
- guiastring | nullrequerido
- carrierstring | nullrequerido
Nombre canónico de la paquetería física (SSOT de la plataforma).
- servicestring | nullrequerido
- status"created" | "collected" | "in_transit" | "out_for_delivery" | "delivered" | "exception" | "returned" | "cancelled" | "unknown"requerido
- status_labelstringrequerido
Etiqueta en español del estatus canónico.
- totalnumber | nullrequerido
- currency"MXN"requerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
- tracking_pendingbooleanrequerido
- label_urlstringrequerido
- packagesobject[]requerido
Lo que se registró al crear el envío, en el mismo vocabulario que usa
POST /shipments(weight_kg,length_cm, …). Para envíos multi-paquete es la lista completa por bulto, no el agregado. Lista vacía si el envío no tiene medidas registradas.Envíos anteriores al 2026-07-28: las dimensiones no se persistían (solo se usaban para calcular el peso volumétrico y se descartaban), así que
length_cm/width_cm/height_cmllegan ennullen todo envío creado antes de esa fecha.weight_kgsí está disponible en todo el histórico. Unnullsignifica "no registrado", nunca "cero": las dimensiones no declaradas tampoco se rellenan con un valor por defecto.Mostrar 4 camposOcultar campos
- weight_kgnumber | nullrequerido
- length_cmnumber | nullrequerido
- width_cmnumber | nullrequerido
- height_cmnumber | nullrequerido
- billable_weight_kgnumber | nullrequerido
Peso facturable con el que se cotizó —
max(peso real, peso volumétrico). Es el número contra el que hay que comparar cuando la paquetería aplica un sobrepeso; el peso real por sí solo no explica el cargo. El repeso de la paquetería NO vive en esta API (está en su reporte de facturación). - declared_valuenumber | nullrequerido
Valor declarado (MXN).
- contentstring | nullrequerido
Descripción del contenido capturada al crear el envío.
- senderobjectrequerido
Remitente, con la misma forma mínima que
recipient: solo nombre + ciudad/estado/CP — nunca la dirección completa ni teléfonos.Mostrar 4 camposOcultar campos
- namestring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- recipientobjectrequerido
Solo nombre + ciudad/estado/CP — nunca la dirección completa ni teléfonos.
Mostrar 4 camposOcultar campos
- namestring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
Mostrar 3 camposOcultar campos
- pageintegerrequerido
- limitintegerrequerido
- totalintegerrequerido
curl "https://api.enviadores.com.mx/api/v1/shipments" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"shipments": [
{
"id": "20260713-000123",
"guia": "1234567890",
"carrier": "Estafeta",
"service": "Terrestre",
"status": "in_transit",
"status_label": "En tránsito",
"total": 145.5,
"currency": "MXN",
"created_at": "2026-07-13T18:45:12Z",
"tracking_pending": false,
"label_url": "/api/v1/labels/20260713-000123",
"packages": [
{
"weight_kg": 1,
"length_cm": 20,
"width_cm": 15,
"height_cm": 10
}
],
"billable_weight_kg": 1,
"declared_value": null,
"content": "Ropa",
"sender": {
"name": "Juan Pérez",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000"
},
"recipient": {
"name": "María López",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000"
}
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1
}
},
"requestId": "req_e5f6a7b8c9"
}Crear un envío (genera la guía y cobra créditos)
post/shipments
Crea el envío contra un rate_id vigente (≤ 30 min) cotizado con la MISMA llave (y mismo PDV, si aplica).
›Ver reglas
Solo domésticos MX (customs/ocurre, country ≠ MX o campos desconocidos → 422). Bultos: los MISMOS que se cotizaron — package (uno) o packages (2–10 en un solo envío; ambos → 422); un conjunto distinto al cotizado responde 409 RATE_PACKAGE_MISMATCH. CP: origen/destino inline deben existir en SEPOMEX (422 nombrando from.postal_code/to.postal_code, antes de retener saldo). Seguro vs. valor declarado (no son lo mismo): insurance.insured_value es la póliza de plataforma que YA se cotizó — debe coincidir con la cotización (422 INSURANCE_MISMATCH si difiere o falta; el total firmado nunca se recalcula al crear); declared_value es el valor de la mercancía que se declara a la paquetería (MXN, informativo; algunos servicios lo exigen — DECLARED_VALUE_REQUIRED). Remitente/destinatario van inline (la plataforma crea o reutiliza los registros del directorio con dedupe exacto normalizado) o por referencia (from_id/to_id del directorio — la fila guardada se usa tal cual). El cargo sigue el ciclo autorizar→capturar/anular — un envío rechazado nunca deja fondos retenidos. Usa `Idempotency-Key` en todo intento.
Parámetros
- Idempotency-Keystring· header
UUID fresco por operación lógica (1–128 caracteres ASCII imprimibles; más
presente pero inválida →
400 IDEMPOTENCY_KEY_INVALID). Un reintento con la misma llave Y el mismo cuerpo repite la respuesta 2xx original (Idempotent-Replay: true) sin doble cargo, durante 15 minutos. La llave queda ligada al cuerpo exacto de la primera solicitud y alX-PDV-ID: cuerpo distinto →409 IDEMPOTENCY_KEY_PAYLOAD_MISMATCH; pasados los 15 min →409 IDEMPOTENCY_KEY_EXPIRED. Cambio 2026-08: ya NO se comparte espacio de nombres con el carril web del mismo usuario (antes una llave se repetía entre/v1/shipmentsy la app web; ahora eso responde409 IDEMPOTENCY_KEY_REUSED). Fuertemente recomendado enPOST /shipments. - X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
›Campos del cuerpo (12)
- rate_idstringrequerido
El
idde una tarifa de POST /rates cotizada con esta misma llave (y mismo X-PDV-ID, si se usó), con menos de 30 minutos. Trátalo como OPACO: cópialo tal cual; su formato no es parte del contrato (difiere entre el carril de prueba y el live) y no debe interpretarse ni validarse. - fromShipmentParty
Mostrar 14 camposOcultar campos
- namestringrequerido
- apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social (solo remitente; ignorado en destinatario).
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystringrequerido
- statestringrequerido
- postal_codestringrequerido
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - rfcstring
Solo remitente; usado por algunas paqueterías.
- from_idstring
Id de un remitente del directorio. Mutuamente excluyente con
from. Mostrar 14 camposOcultar campos
- namestringrequerido
- apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social (solo remitente; ignorado en destinatario).
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystringrequerido
- statestringrequerido
- postal_codestringrequerido
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - rfcstring
Solo remitente; usado por algunas paqueterías.
- to_idstring
Id de un destinatario del directorio (debe pertenecer al
from_idenviado). Mutuamente excluyente conto; requierefrom_id. - packageobject
La MISMA caja que se cotizó, con sus tres medidas: sin largo, ancho y alto → 422
PACKAGE_DIMENSIONS_REQUIRED(no se cobra nada).Mostrar 5 camposOcultar campos
- weight_kgnumberrequerido
- length_cmnumberrequerido
- width_cmnumberrequerido
- height_cmnumberrequerido
- type"paquete"
Opcional. Solo
paqueteen v1 (sin distinción de mayúsculas; vacío/null = paquete). Cualquier otro valor — p. ej.sobre— responde 422 tanto en /rates como en /shipments (envelope aún no soportado).
- packagesPackagesList
Mostrar 4 camposOcultar campos
- weight_kgnumberrequerido
Peso real del bulto (kg).
- length_cmnumber
Largo exterior del bulto cerrado (cm). Obligatorio para
POST /shipments; enPOST /rates, sin las tres medidas el precio es estimado (size_estimated). - width_cmnumber
Ancho exterior del bulto cerrado (cm).
- height_cmnumber
Alto exterior del bulto cerrado (cm).
- insuranceobject
La cobertura que YA se cotizó (
insured_valuebuys coverage; when the platform policy is unavailable the carrier's own coverage is used and the price shown already includes it). Omitirinsuranceacepta la cobertura cotizada; mandar uninsured_valueDISTINTO → 422INSURANCE_MISMATCH(el total firmado nunca se recalcula al crear). Una tarifa cuya paquetería no ofrece cobertura (insurance_not_supported: true) → 422INSURANCE_NOT_AVAILABLE.Mostrar 1 campoOcultar campos
- insured_valuenumberrequerido
Valor asegurado (MXN) — el mismo de la cotización.
- declared_valuenumber
Valor de la mercancía DECLARADO A LA PAQUETERÍA (MXN). NO es seguro, pero la paquetería puede cobrarlo: debe ser EXACTAMENTE el valor con el que se cotizó (el asegurado) — un valor que la cotización no llevaba responde 422
DECLARED_VALUE_MISMATCH, salvo en servicios que lo tratan como informativo (la recuperación deDECLARED_VALUE_REQUIRED). Si se omite se declara el valor cotizado; si la cotización no llevaba ninguno, no se inventa. *declared_value must equal the value the rate was quoted with; a carrier may price it.* - contenidostring
Descripción del contenido (aparece en la guía cuando la paquetería lo soporta).
- referencestring
RESERVADO — aceptado pero no persistido en v1.
- metadataobject
RESERVADO en el carril live (aceptado, no persistido). En el carril SANDBOX,
metadata.test_scenariofuerza una falla para rehearsal (verx-test-mode).Mostrar 1 campoOcultar campos
- test_scenario"insufficient_funds" | "rate_expired" | "vendor_error"
Solo llaves
ek_test_, y solo sobre ofertas SIMULADAS del catálogo: sobre una tarifa del carril FedEx de prueba (que va al entorno real de FedEx) responde422 TEST_SCENARIO_NOT_AVAILABLE.insufficient_funds→ 402 CREDIT_ERROR (details {required, available, shortfall}) sin importar el saldo;rate_expired→ 409 RATE_EXPIRED;vendor_error→ 422 VENDOR_ERROR (retryable=false).
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Envío creado (o repetido idempotentemente — header Idempotent-Replay: true; o deduplicado contra una guía existente — duplicate: true, en cuyo caso carrier/service pueden ser null). | |
| 400 | INVALID_JSON | |
VALIDATION_FAILED | Rechazo del núcleo, details.validationErrors | |
INVALID_VENDOR | ||
OCURRE_CARRIER_MISMATCH | ||
IDEMPOTENCY_KEY_INVALID | Header presente pero vacío, >128 chars o con caracteres no imprimibles; omitirlo es válido, mandarlo mal no | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 402 | CREDIT_ERROR | Créditos insuficientes. details: {required, available, shortfall}. Recarga y reintenta con el MISMO Idempotency-Key. |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 409 | RATE_NOT_FOUND | Cotiza de nuevo |
RATE_EXPIRED | >30 min | |
RATE_TAMPERED | ||
RATE_PDV_MISMATCH | Se cotizó bajo otro PDV | |
RATE_PACKAGE_MISMATCH | El package/packages enviado difiere del cotizado — medidas, peso o valor declarado — reenvía el MISMO conjunto que cotizaste, o cotiza de nuevo | |
GUIA_COLLISION | ||
IDEMPOTENCY_KEY_REUSED | Misma llave en otro endpoint — desde 2026-08 el carril web cuenta como otro endpoint | |
IDEMPOTENCY_KEY_PAYLOAD_MISMATCH | Misma llave, cuerpo o X-PDV-ID distintos: manda una llave nueva | |
IDEMPOTENCY_KEY_EXPIRED | La llave ya se completó hace más de 15 min: consulta GET /shipments o manda una llave nueva | |
RATE_ADDRESS_MISMATCH | La cotización corresponde a OTRA ruta: los CP de origen/destino con los que se está creando el envío no son los que se cotizaron — cotiza de nuevo con las direcciones definitivas; desde 2026-08 esto rechaza en lugar de solo registrarse | |
REQUOTE_REQUIRED | El precio del envío no coincide con el de la cotización; no se espera en este carril — el total se toma de la MISMA fila firmada que el ejecutor vuelve a verificar — y se documenta por completitud: cotiza de nuevo | |
| 422 | VALIDATION_ERROR | Contrato público, details.fields — incluye CP inexistente en SEPOMEX |
PACKAGE_DIMENSIONS_REQUIRED | Falta largo, ancho o alto de alguna caja — | |
INSURANCE_NOT_AVAILABLE | Se mandó insurance sobre una tarifa cuya paquetería no ofrece cobertura — venía con insurance_not_supported: true; cotiza de nuevo y elige una con insurance_included, o crea sin insurance | |
INSURANCE_MISMATCH | insurance.insured_value explícito distinto de la cobertura cotizada — details {quoted_insured_value, requested_insured_value}; omitirlo acepta la cobertura cotizada | |
DECLARED_VALUE_MISMATCH | declared_value distinto del que se cotizó, o presente cuando la cotización no llevaba ninguno y la paquetería lo cobra — details {quoted_declared_value, requested_declared_value}; cotiza de nuevo | |
DECLARED_VALUE_REQUIRED | El servicio exige declared_value; no se cobró | |
PRICING_CONFIG_ERROR | Margen mal configurado; contacta soporte | |
MISSING_PHONE | Remitente y/o destinatario sin teléfono válido de 10 dígitos; details.missing lista cuál | |
| 429 | RISK_LIMIT | Tope de guías/gasto diario de la cuenta, o tope de gasto de la LLAVE en ventana móvil de 24 h — este último con details {daily_cap_mxn, spent_24h_mxn, attempted_mxn} |
RATE_LIMITED | Límite por llave; ver Retry-After | |
| 500 | SHIPMENT_ERROR | Falla pre-commit; el cargo se revirtió — details.refunded |
ADDRESS_ERROR | No se pudo registrar remitente/destinatario; reintenta | |
CREDIT_ERROR | Falla interna de créditos | |
POST_COMMIT_ERROR | EL ENVÍO ES REAL y el cargo NO se revierte (details.committed: true). NO reintentes con llave nueva: consulta GET /shipments. | |
| 502 | VENDOR_ERROR | La paquetería rechazó/falló ANTES del commit; el cargo se revirtió ( |
›Campos de la respuesta 200
- shipmentobjectrequerido
Mostrar 10 camposOcultar campos
- idstringrequerido
- guiastring | null
Null cuando la paquetería publica la guía con retraso (
tracking_pending: true); consúltala después vía GET /shipments/{id}. - carrierstring | null
Null posible en respuestas
duplicate: true. - servicestring | null
- statusstringrequerido
Estatus canónico (created | collected | in_transit | out_for_delivery | ready_for_pickup | delivered | exception | returned | cancelled | unknown).
- totalnumber | null
Monto cobrado (MXN).
- currency"MXN"requerido
- label_urlstring | nullrequerido
Ruta de NUESTRO endpoint autenticado de etiquetas (/api/v1/labels/{id}) — nunca la URL cruda del proveedor.
- tracking_pendingbooleanrequerido
- created_atstring | null
ISO-8601 UTC (Z).
- duplicateboolean
Presente (true) cuando la paquetería devolvió una guía ya registrada y la plataforma deduplicó sin doble cargo.
- recoveredboolean
Presente (true) cuando la etiqueta se recuperó tras un error transitorio del proveedor.
curl -X POST "https://api.enviadores.com.mx/api/v1/shipments" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "rate_id": "e2etest_E2ETestCarrier_E2EStandard_ab12cd34", "from": { "name": "Juan Pérez", "phone": "5512345678", "street": "Av. Insurgentes Sur", "number": "600", "colonia": "Del Valle", "city": "Ciudad de México", "state": "CDMX", "postal_code": "01000" }, "to": { "name": "María López", "phone": "8187654321", "street": "Av. Constitución", "number": "400", "colonia": "Centro", "city": "Monterrey", "state": "Nuevo León", "postal_code": "64000" }, "package": { "weight_kg": 1, "length_cm": 20, "width_cm": 15, "height_cm": 10 }, "contenido": "Ropa"}'{
"success": true,
"data": {
"shipment": {
"id": "20260713-000123",
"guia": "1234567890",
"carrier": "Estafeta",
"service": "Terrestre",
"status": "created",
"total": 145.5,
"currency": "MXN",
"label_url": "/api/v1/labels/20260713-000123",
"tracking_pending": false,
"created_at": "2026-07-13T18:45:12Z"
}
},
"requestId": "req_b2c3d4e5f6"
}Consultar un envío
get/shipments/{id}
Misma visibilidad que el listado: propios por defecto, o los del PDV con X-PDV-ID + scope shipments:read:pdv.
›Ver reglas
Devuelve exactamente los mismos campos que cada elemento de GET /shipments (incluyendo packages, pesos y remitente) — el detalle no es más rico que la lista, por diseño. Si solo tienes el número de guía, usa GET /shipments?guia=….
Parámetros
- idstring· pathrequeridoId del envío (p. ej.
20260712-000123). - X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El envío. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe o no es visible para la llave (respuesta idéntica en ambos casos). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 17 camposOcultar campos
- idstringrequerido
- guiastring | nullrequerido
- carrierstring | nullrequerido
Nombre canónico de la paquetería física (SSOT de la plataforma).
- servicestring | nullrequerido
- status"created" | "collected" | "in_transit" | "out_for_delivery" | "delivered" | "exception" | "returned" | "cancelled" | "unknown"requerido
- status_labelstringrequerido
Etiqueta en español del estatus canónico.
- totalnumber | nullrequerido
- currency"MXN"requerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
- tracking_pendingbooleanrequerido
- label_urlstringrequerido
- packagesobject[]requerido
Lo que se registró al crear el envío, en el mismo vocabulario que usa
POST /shipments(weight_kg,length_cm, …). Para envíos multi-paquete es la lista completa por bulto, no el agregado. Lista vacía si el envío no tiene medidas registradas.Envíos anteriores al 2026-07-28: las dimensiones no se persistían (solo se usaban para calcular el peso volumétrico y se descartaban), así que
length_cm/width_cm/height_cmllegan ennullen todo envío creado antes de esa fecha.weight_kgsí está disponible en todo el histórico. Unnullsignifica "no registrado", nunca "cero": las dimensiones no declaradas tampoco se rellenan con un valor por defecto.Mostrar 4 camposOcultar campos
- weight_kgnumber | nullrequerido
- length_cmnumber | nullrequerido
- width_cmnumber | nullrequerido
- height_cmnumber | nullrequerido
- billable_weight_kgnumber | nullrequerido
Peso facturable con el que se cotizó —
max(peso real, peso volumétrico). Es el número contra el que hay que comparar cuando la paquetería aplica un sobrepeso; el peso real por sí solo no explica el cargo. El repeso de la paquetería NO vive en esta API (está en su reporte de facturación). - declared_valuenumber | nullrequerido
Valor declarado (MXN).
- contentstring | nullrequerido
Descripción del contenido capturada al crear el envío.
- senderobjectrequerido
Remitente, con la misma forma mínima que
recipient: solo nombre + ciudad/estado/CP — nunca la dirección completa ni teléfonos.Mostrar 4 camposOcultar campos
- namestring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- recipientobjectrequerido
Solo nombre + ciudad/estado/CP — nunca la dirección completa ni teléfonos.
Mostrar 4 camposOcultar campos
- namestring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
curl "https://api.enviadores.com.mx/api/v1/shipments/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"shipment": {
"id": "20260713-000123",
"guia": "1234567890",
"carrier": "Estafeta",
"service": "Terrestre",
"status": "in_transit",
"status_label": "En tránsito",
"total": 145.5,
"currency": "MXN",
"created_at": "2026-07-13T18:45:12Z",
"tracking_pending": false,
"label_url": "/api/v1/labels/20260713-000123",
"packages": [
{
"weight_kg": 1,
"length_cm": 20,
"width_cm": 15,
"height_cm": 10
}
],
"billable_weight_kg": 1,
"declared_value": null,
"content": "Ropa",
"sender": {
"name": "Juan Pérez",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000"
},
"recipient": {
"name": "María López",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000"
}
}
},
"requestId": "req_f6a7b8c9d0"
}tracking
Rastreo público
Rastrear cualquier guía
get/tracking/{guia}
Datos públicos de rastreo (mismo payload PII-mínimo de la página pública: estatus canónico, ciudad/estado de origen y destino, línea de tiempo con nombres de receptor depurados).
›Ver reglas
No requiere que la guía sea propia. Agnóstico al modo de la llave (funciona igual con ek_test_). Requiere scope tracking:read.
Parámetros
- guiastring· pathrequeridoNúmero de guía (coincidencia EXACTA).
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Estado de rastreo. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Genérico y de forma estable para toda guía desconocida/ inválida (sin oráculo de existencia). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
›Campos de la respuesta 200
- guiastringrequerido
- carrierstring | nullrequerido
- statusstringrequerido
- status_labelstringrequerido
- originobjectrequerido
Mostrar 2 camposOcultar campos
- citystring | null
- statestring | null
- destinationobjectrequerido
Mostrar 2 camposOcultar campos
- citystring | null
- statestring | null
- created_atstring | nullrequerido
- eventsobject[]requerido
Mostrar 3 camposOcultar campos
- timestampstring | null
- descriptionstring | null
Nombres de receptor (POD) depurados.
- locationstring | null
curl "https://api.enviadores.com.mx/api/v1/tracking/GUIA" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"guia": "1234567890",
"carrier": "Estafeta",
"status": "in_transit",
"status_label": "En tránsito",
"origin": {
"city": "Ciudad de México",
"state": "CDMX"
},
"destination": {
"city": "Monterrey",
"state": "Nuevo León"
},
"created_at": "2026-07-13T18:45:12Z",
"events": [
{
"timestamp": "2026-07-13T20:10:00Z",
"description": "Recolectado",
"location": "Ciudad de México, CDMX"
},
{
"timestamp": "2026-07-14T09:30:00Z",
"description": "En tránsito al destino",
"location": "Querétaro, QRO"
}
]
},
"requestId": "req_c3d4e5f6a7"
}cancellations
Solicitudes de cancelación
Solicitar la cancelación de un envío propio
post/cancellations
Crea una solicitud de cancelación (estado inicial pending).
›Ver reglas
El avance del estado es operado por el staff/los procesos de la plataforma — consulta GET /cancellations/{id}. El reembolso esperado es lo que se cobró por el envío. Requiere scope cancellations:write.
›Campos del cuerpo (3)
- shipment_idstringrequerido
Id del envío propio a cancelar.
- reason_code"wrong_address" | "duplicate" | "customer_changed_mind" | "damaged" | "never_shipped" | "not_picked_up" | "other"
- reason_textstring
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 201 | Solicitud creada. | |
| 400 | INVALID_JSON | |
REQUEST_FAILED | Rechazo del servicio | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | FORBIDDEN | Rechazo de permiso del servicio |
INSUFFICIENT_SCOPE | ||
PDV_NOT_ALLOWED | . | |
| 404 | NOT_FOUND | El envío no existe o no pertenece al usuario de la llave (respuesta idéntica). |
| 409 | CONFLICT | El envío ya está cancelado o ya existe una solicitud abierta (details.existing_request_id). |
| 422 | VALIDATION_ERROR | Details.fields |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | REQUEST_ERROR | . |
›Campos de la respuesta 201
- idintegerrequerido
- display_idstringrequerido
Live: formato
CR%08d. Sandbox:SBX-CR-<id>. - status"pending" | "cancelled"requerido
Live devuelve
pending(la solicitud entra a la cola de staff). Sandbox devuelvecancelled(cancelación síncrona y terminal — verx-test-mode). - refund_status"not_applicable" | "refunded"requerido
Live:
not_applicable(reembolso lo resuelve el staff). Sandbox:refunded(reembolso inmediato). - refund_amount_expectednumber | null
- cancellation_deadlinestring | null
ISO-8601 UTC (Z). Siempre null en sandbox.
curl -X POST "https://api.enviadores.com.mx/api/v1/cancellations" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "shipment_id": "20260713-000123", "reason_code": "customer_changed_mind"}'{
"success": true,
"data": {
"id": 42,
"display_id": "CR00000042",
"status": "pending",
"refund_status": "not_applicable",
"refund_amount_expected": 145.5,
"cancellation_deadline": "2026-07-14T18:45:12Z"
},
"requestId": "req_a7b8c9d0e1"
}Consultar una solicitud de cancelación
get/cancellations/{id}
Visibilidad idéntica a la del envío que cancela (propios por defecto;
›Ver reglas
los del PDV con X-PDV-ID + scope shipments:read:pdv) — deliberadamente en paralelo, para que un envío que puedes leer nunca tenga una cancelación que no.
Máquina de estados: pending → approved → in_progress → cancelled | rejected_by_carrier; otros estados: not_cancellable (la guía no admite cancelación vía API — terminal), reported_to_carrier (reporte manual ante la paquetería, sigue abierta), rejected, failed, expired. Reembolso: not_applicable → refund_in_progress → refunded | refund_denied (los reembolsos son pass-through de la paquetería; refunded cubre también el reembolso manual por override administrativo). Requiere scope cancellations:read.
Parámetros
- idinteger· pathrequerido
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | La solicitud. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe o el envío subyacente no pertenece al usuario de la llave (respuesta idéntica). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | GET_ERROR | . |
›Campos de la respuesta 200
Mostrar 13 camposOcultar campos
- idintegerrequerido
- display_idstringrequerido
- shipment_idstring | nullrequerido
- status"pending" | "approved" | "in_progress" | "cancelled" | "rejected_by_carrier" | "not_cancellable" | "reported_to_carrier" | "rejected" | "failed" | "expired"requerido
- refund_status"not_applicable" | "refund_in_progress" | "refunded" | "refund_denied"requerido
- reason_codestring | nullrequerido
- reason_textstring | nullrequerido
- refund_amount_expectednumber | nullrequerido
- refund_amount_actualnumber | nullrequerido
- cancellation_deadlinestring | nullrequerido
- created_atstring | nullrequerido
- updated_atstring | nullrequerido
- resolved_atstring | nullrequerido
curl "https://api.enviadores.com.mx/api/v1/cancellations/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"cancellation": {
"id": 42,
"display_id": "CR00000042",
"shipment_id": "20260713-000123",
"status": "cancelled",
"refund_status": "refunded",
"reason_code": "customer_changed_mind",
"reason_text": null,
"refund_amount_expected": 145.5,
"refund_amount_actual": 145.5,
"cancellation_deadline": "2026-07-14T18:45:12Z",
"created_at": "2026-07-13T19:02:33Z",
"updated_at": "2026-07-15T10:12:05Z",
"resolved_at": "2026-07-15T10:12:05Z"
}
},
"requestId": "req_b8c9d0e1f2"
}pickups
Recolecciones a domicilio (solicitudes; las confirma nuestro equipo)
Listar tus solicitudes de recolección
get/pickups
Las solicitudes de recolección de la cuenta, de la más reciente a la más antigua — qué pediste, para cuándo, y si nuestro equipo ya la gestionó.
›Ver reglas
Visibilidad idéntica a la del listado de envíos: por omisión las recolecciones de los envíos que creó el usuario de la llave; con X-PDV-ID validado y el scope ampliado shipments:read:pdv, las de ese punto de venta (las dos ramas son EXCLUYENTES). Mientras la programación de recolecciones no esté habilitada en la cuenta, este listado responde 200 con una página vacía — un listado de algo que no puedes usar no tiene nada que reportar. Requiere scope pickups:read.
Parámetros
- pageinteger· query
- limitinteger· query
- status"pending" | "scheduled" | "awaiting_confirmation" | "failed" | "cancelled"· query
Filtra por estatus público de la recolección — el mismo valor que devuelve el campo
status. másawaiting_confirmation= la registramos y nuestro equipo aún la está gestionando;scheduled= ya quedó con la paquetería. Un valor fuera del enum es422 VALIDATION_ERROR. - X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Página de solicitudes de recolección. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 422 | VALIDATION_ERROR | status fuera del enum o paginación inválida (campos en details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | GET_ERROR | . |
›Campos de la respuesta 200
Mostrar 12 camposOcultar campos
- idintegerrequerido
Id numérico de la recolección.
- display_idstringrequerido
Live: formato
PR%08d. Sandbox:SBX-PU-<id>. - shipment_idstring | nullrequerido
- status"pending" | "scheduled" | "awaiting_confirmation" | "failed" | "cancelled"requerido
Una solicitud SIEMPRE nace en
awaiting_confirmation(se está gestionando; NO la presentes como confirmada). Pasa ascheduledcuando el equipo la confirma con la paquetería — ahí lleganconfirmed_window_*yconfirmation_number, y se envía el correo al solicitante.failed= no se pudo agendar (entregar en sucursal).cancelled= la canceló quien la pidió.pendingno se usa. El sandbox devuelveawaiting_confirmation, igual que producción. - carrierstring | null
Paquetería física que hará la recolección.
- requested_window_startstring | null
ISO-8601 UTC (Z). Inicio de la ventana solicitada.
- requested_window_endstring | null
ISO-8601 UTC (Z). Fin de la ventana solicitada.
- confirmed_window_startstring | null
ISO-8601 UTC (Z). Ventana confirmada por la paquetería, cuando la devuelve.
- confirmed_window_endstring | null
ISO-8601 UTC (Z).
- confirmation_numberstring | null
Folio/referencia de recolección de la paquetería.
- created_atstring | null
- updated_atstring | null
Mostrar 3 camposOcultar campos
- pageintegerrequerido
- limitintegerrequerido
- totalintegerrequerido
curl "https://api.enviadores.com.mx/api/v1/pickups" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"pickups": [
{
"id": 42,
"display_id": "PR00000042",
"shipment_id": "20260830-000123",
"status": "awaiting_confirmation",
"carrier": "Estafeta",
"requested_window_start": "2026-08-31T16:00:00Z",
"requested_window_end": "2026-08-31T23:00:00Z",
"confirmed_window_start": null,
"confirmed_window_end": null,
"confirmation_number": null,
"created_at": "2026-08-30T10:00:00Z",
"updated_at": "2026-08-30T10:00:02Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1
}
},
"requestId": "req_a7b8c9d0e1"
}Solicitar la recolección de un envío propio
post/pickups
Solicita que la paquetería pase por el paquete al domicilio del remitente, en lugar de que el remitente lo lleve a sucursal.
›Ver reglas
Esto es una solicitud, no una reserva. No agendamos con la paquetería de forma automática: el equipo de Enviadores la gestiona y avisa por correo al solicitante cuando queda. Por eso la respuesta siempre regresa status: awaiting_confirmation — nunca reportes al usuario que ya hay mensajero confirmado. Consulta GET /pickups/{id} para el estado final, o espera el correo.
Por qué. Un 200 de una paquetería no es prueba de que el mensajero vaya a llegar, y el peor error posible aquí es decirle a alguien que su recolección está confirmada y dejarlo esperando. Preferimos ser lentos y ciertos.
Sólo se puede pedir para Estafeta, FedEx, DHL, UPS y Paquetexpress (422 PICKUP_CARRIER_NOT_ELIGIBLE para el resto; con cualquier otra, el remitente lleva el paquete a un punto de esa empresa). Una sola recolección abierta por envío.
pickup_date, ready_time y close_time son hora local de México (America/Mexico_City); la respuesta devuelve las ventanas en UTC (Z).
La recolección no cobra saldo; cualquier cargo de la paquetería llega después por la vía de sobrecargos, igual que en el carril web.
La dirección de recolección es la del propio envío — este endpoint no la cambia. Requiere scope pickups:write.
›Campos del cuerpo (5)
- shipment_idstringrequerido
Id de un envío propio con guía, no entregado ni cancelado.
- pickup_datestringrequerido
Fecha local (America/Mexico_City). Hoy o después.
- ready_timestringrequerido
HH:MM 24h local — desde cuándo está listo el paquete.
- close_timestringrequerido
HH:MM 24h local — hasta cuándo puede pasar el mensajero. Debe ser posterior a ready_time.
- notesstring
Indicación para el mensajero.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 201 | Solicitud registrada — status siempre es awaiting_confirmation. Un 201 significa que recibimos la solicitud, NO que haya mensajero confirmado. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | PICKUP_UNAVAILABLE | La programación de recolecciones aún no está habilitada en esta cuenta (el paquete puede entregarse en sucursal; escríbenos para agendarla manualmente). |
PICKUP_UNLOCK_REQUIRED | La cuenta todavía sólo puede usar iMile Terrestre y Estafeta Terrestre y Día Siguiente sin recolección (estas se dejan en una sucursal Estafeta); se desbloquea al verificar identidad (INE) en Mi cuenta o al acumular $2,500 MXN en recargas. | |
FORBIDDEN | ||
INSUFFICIENT_SCOPE | ||
PDV_NOT_ALLOWED | . | |
| 404 | NOT_FOUND | El envío no existe o no es de esta llave (respuesta idéntica, sin oráculo de existencia). |
| 409 | CONFLICT | Ya existe una recolección abierta para este envío (details.existing_request_id). |
| 422 | VALIDATION_ERROR | Campos en details.fields |
PICKUP_CARRIER_NOT_ELIGIBLE | La paquetería del envío no está en la lista (details.eligible_carriers); el paquete puede entregarse en sucursal | |
REQUEST_FAILED | El envío no admite recolección (sin guía, entregado, cancelado, o fecha/hora imposible). | |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | REQUEST_ERROR | . |
›Campos de la respuesta 201
Mostrar 12 camposOcultar campos
- idintegerrequerido
Id numérico de la recolección.
- display_idstringrequerido
Live: formato
PR%08d. Sandbox:SBX-PU-<id>. - shipment_idstring | nullrequerido
- status"pending" | "scheduled" | "awaiting_confirmation" | "failed" | "cancelled"requerido
Una solicitud SIEMPRE nace en
awaiting_confirmation(se está gestionando; NO la presentes como confirmada). Pasa ascheduledcuando el equipo la confirma con la paquetería — ahí lleganconfirmed_window_*yconfirmation_number, y se envía el correo al solicitante.failed= no se pudo agendar (entregar en sucursal).cancelled= la canceló quien la pidió.pendingno se usa. El sandbox devuelveawaiting_confirmation, igual que producción. - carrierstring | null
Paquetería física que hará la recolección.
- requested_window_startstring | null
ISO-8601 UTC (Z). Inicio de la ventana solicitada.
- requested_window_endstring | null
ISO-8601 UTC (Z). Fin de la ventana solicitada.
- confirmed_window_startstring | null
ISO-8601 UTC (Z). Ventana confirmada por la paquetería, cuando la devuelve.
- confirmed_window_endstring | null
ISO-8601 UTC (Z).
- confirmation_numberstring | null
Folio/referencia de recolección de la paquetería.
- created_atstring | null
- updated_atstring | null
curl -X POST "https://api.enviadores.com.mx/api/v1/pickups" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "shipment_id": "20260830-000123", "pickup_date": "2026-08-31", "ready_time": "10:00", "close_time": "17:00", "notes": "Timbre azul, preguntar por Ana"}'{
"success": true,
"data": {
"pickup": {
"id": 42,
"display_id": "PR00000042",
"shipment_id": "20260830-000123",
"status": "awaiting_confirmation",
"carrier": "Estafeta",
"requested_window_start": "2026-08-31T16:00:00Z",
"requested_window_end": "2026-08-31T23:00:00Z",
"confirmed_window_start": null,
"confirmed_window_end": null,
"confirmation_number": null,
"created_at": "2026-08-30T10:00:00Z",
"updated_at": "2026-08-30T10:00:02Z"
}
},
"requestId": "req_a7b8c9d0e1"
}Consultar una recolección
get/pickups/{id}
Estado de una solicitud de recolección propia — úsalo para dar seguimiento mientras está en awaiting_confirmation (el solicitante también recibe un correo cuando se resuelve). Visibilidad idéntica a la del envío al que pertenece, en paralelo deliberado con GET /cancellations/{id}. Requiere scope pickups:read.
Parámetros
- idinteger· pathrequerido
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | La recolección. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe o el envío subyacente no pertenece al usuario de la llave (respuesta idéntica). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | GET_ERROR | . |
›Campos de la respuesta 200
Mostrar 12 camposOcultar campos
- idintegerrequerido
Id numérico de la recolección.
- display_idstringrequerido
Live: formato
PR%08d. Sandbox:SBX-PU-<id>. - shipment_idstring | nullrequerido
- status"pending" | "scheduled" | "awaiting_confirmation" | "failed" | "cancelled"requerido
Una solicitud SIEMPRE nace en
awaiting_confirmation(se está gestionando; NO la presentes como confirmada). Pasa ascheduledcuando el equipo la confirma con la paquetería — ahí lleganconfirmed_window_*yconfirmation_number, y se envía el correo al solicitante.failed= no se pudo agendar (entregar en sucursal).cancelled= la canceló quien la pidió.pendingno se usa. El sandbox devuelveawaiting_confirmation, igual que producción. - carrierstring | null
Paquetería física que hará la recolección.
- requested_window_startstring | null
ISO-8601 UTC (Z). Inicio de la ventana solicitada.
- requested_window_endstring | null
ISO-8601 UTC (Z). Fin de la ventana solicitada.
- confirmed_window_startstring | null
ISO-8601 UTC (Z). Ventana confirmada por la paquetería, cuando la devuelve.
- confirmed_window_endstring | null
ISO-8601 UTC (Z).
- confirmation_numberstring | null
Folio/referencia de recolección de la paquetería.
- created_atstring | null
- updated_atstring | null
curl "https://api.enviadores.com.mx/api/v1/pickups/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"pickup": {
"id": 42,
"display_id": "PR00000042",
"shipment_id": "20260830-000123",
"status": "awaiting_confirmation",
"carrier": "Estafeta",
"requested_window_start": "2026-08-31T16:00:00Z",
"requested_window_end": "2026-08-31T23:00:00Z",
"confirmed_window_start": null,
"confirmed_window_end": null,
"confirmation_number": null,
"created_at": "2026-08-30T10:00:00Z",
"updated_at": "2026-08-30T10:00:02Z"
}
},
"requestId": "req_a7b8c9d0e1"
}balance
Saldo de créditos
Consultar el saldo que cargarían tus envíos
get/balance
Sin X-PDV-ID: el saldo del usuario de la llave.
›Ver reglas
Con X-PDV-ID (llaves admin, misma allowlist que POST /shipments): el fondo del punto de venta — exactamente el principal que un envío bajo ese header cargaría. available es lo gastable; held son autorizaciones pendientes; balance = available + held. Requiere scope balance:read.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Saldo del principal. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
- balancenumberrequerido
available + held (total del principal en el libro mayor).
- heldnumberrequerido
Autorizaciones pendientes (holds).
- availablenumberrequerido
Lo gastable por un envío nuevo.
- currency"MXN"requerido
- scopeobjectrequerido
Mostrar 2 camposOcultar campos
- type"user" | "pdv"requerido
- idstringrequerido
curl "https://api.enviadores.com.mx/api/v1/balance" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"balance": 4820.5,
"held": 145.5,
"available": 4675,
"currency": "MXN",
"scope": {
"type": "user",
"id": "U0000001"
}
},
"requestId": "req_d4e5f6a7b8"
}labels
Etiquetas (guías)
Descargar la etiqueta de un envío propio
get/labels/{envio_id}
Transmite la copia de la etiqueta que guarda la plataforma (PDF/ZPL);
›Ver reglas
si aún no existe, la obtiene de la paquetería, la guarda y la transmite. Nunca redirige a la URL de la paquetería. Con ?format=json responde JSON (LabelLink) en lugar de bytes. Solo envíos creados por el usuario de la llave (todos los roles). Requiere scope labels:read.
Parámetros
- envio_idstring· pathrequerido
- format"json"· query
json= en vez de transmitir bytes responde JSON con un enlace firmado y con vencimiento a la copia de Enviadores (LabelLink). másEs la vía para agentes (la herramienta MCP
get_label): un agente no puede consumir el PDF transmitido, así que un enlace que se abre sin llave es la única respuesta útil.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Bytes de la etiqueta (copia de la plataforma). Con ?format=json: application/json con LabelLink. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe, no es del usuario de la llave, o no hay etiqueta disponible (respuesta idéntica en todos los casos). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
›Campos de la respuesta 200
- shipment_idstringrequerido
- guiastring
Solo en sandbox (la vía en vivo omite el campo).
- label_urlstring | nullrequerido
En vivo: enlace firmado a la copia de Enviadores (
https://api.enviadores.com.mx/api/v1/label-files/{envio_id}/{exp}/{firma}.pdf). Se abre sin llave, vence enexpires_at(30 días; pide uno nuevo con esta misma llamada) y nunca es la URL de la paquetería. En sandbox: enlace a la etiqueta de prueba.nullcuando la etiqueta aún no está disponible;notelo explica. - expires_atstring
Solo en vivo: cuándo deja de funcionar
label_url(UTC). - format"pdf"requerido
- sandboxbooleanrequerido
truecon llavesek_test_: etiqueta de prueba sin validez. - notestring
Presente solo cuando hay algo que aclarar (etiqueta sandbox, o sin URL directa).
curl "https://api.enviadores.com.mx/api/v1/labels/ENVIO_ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -o guia.pdfaddresses
Directorio: remitentes y destinatarios
Listar remitentes del directorio
get/senders
Remitentes visibles para la llave.
›Ver reglas
Visibilidad: llaves de cliente y llaves admin SIN X-PDV-ID ven solo los registros creados por el usuario de la llave; una llave admin con X-PDV-ID validado ve exactamente el directorio de ese punto de venta (el mismo que usa el personal de mostrador). Nunca hay lectura global en este carril. Requiere scope addresses:read. Este endpoint SÍ devuelve dirección completa y teléfono — el guardián es la visibilidad, no el recorte de campos.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header. - pageinteger· query
- limitinteger· query
- postal_codestring· queryFiltro exacto por CP (5 dígitos; otro formato → 422).
- qstring· query
Búsqueda de texto sobre nombre, razón social, RFC, teléfono, email y dirección (calle/colonia/municipio/estado/CP). más
Cada palabra debe aparecer en ALGÚN campo, no necesariamente en el mismo:
arturo meijueiroencuentra un registro cuyo nombre esARTUROy cuyo apellido solo aparece en el email. Insensible a acentos y mayúsculas; se consideran las primeras 5 palabras.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Página de remitentes. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 422 | VALIDATION_ERROR | Filtro inválido (details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- namestring | nullrequerido
- apellido_paternonullrequerido
Siempre
null: el nombre completo está enname. - apellido_maternonullrequerido
Siempre
null: el nombre completo está enname. - companystring | nullrequerido
- rfcstring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
Mostrar 3 camposOcultar campos
- pageintegerrequerido
- limitintegerrequerido
- totalintegerrequerido
curl "https://api.enviadores.com.mx/api/v1/senders" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"senders": [
{
"id": "C0012345",
"name": "Juan Pérez",
"apellido_paterno": null,
"apellido_materno": null,
"company": null,
"rfc": null,
"phone": "5512345678",
"email": "[email protected]",
"street": "Av. Insurgentes Sur",
"number": "600",
"colonia": "Del Valle",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000",
"created_at": "2026-07-13T18:40:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1
}
},
"requestId": "req_c9d0e1f2a3"
}Crear (o reutilizar) un remitente
post/senders
Create-or-reuse con el MISMO dedupe exacto normalizado que usa POST /shipments al registrar direcciones inline, evaluado sobre las filas visibles para la llave: si ya existe un remitente idéntico visible, se devuelve ese (created: false, 200);
›Ver reglas
si no, se crea (created: true, 201). El postal_code debe tener 5 dígitos y EXISTIR en el catálogo postal (SEPOMEX); si city/state vienen, deben ser coherentes con el CP (un desajuste responde 422 nombrando el valor esperado) y si se omiten se AUTOCOMPLETAN del catálogo — la misma validación de exactitud que POST /contacts/import. Requiere scope addresses:write. Con X-PDV-ID (llave admin), la fila creada pertenece al directorio de ese PDV y queda visible para su personal.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
›Campos del cuerpo (14)
- namestringrequerido
Nombre de pila (
nombre). - apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social.
- rfcstring
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando la ciudad esperada).
- statestring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando el estado esperado).
- postal_codestringrequerido
5 dígitos; debe EXISTIR en el catálogo postal (SEPOMEX).
- country"MX"
Opcional. Solo
MX— cualquier otro valor responde 422.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Remitente idéntico ya existente reutilizado (created: false). | |
| 201 | Remitente creado (created: true). | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 422 | VALIDATION_ERROR | Campos faltantes/inválidos o desconocidos (details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | ADDRESS_ERROR | No se pudo registrar; reintenta. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- namestring | nullrequerido
- apellido_paternonullrequerido
Siempre
null: el nombre completo está enname. - apellido_maternonullrequerido
Siempre
null: el nombre completo está enname. - companystring | nullrequerido
- rfcstring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
- createdbooleanrequerido
curl -X POST "https://api.enviadores.com.mx/api/v1/senders" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Juan Pérez García", "phone": "5512345678", "email": "[email protected]", "street": "Av. Insurgentes Sur", "number": "600", "colonia": "Del Valle", "city": "Ciudad de México", "state": "CDMX", "postal_code": "01000"}'{
"success": true,
"data": {
"sender": {
"id": "C0012345",
"name": "Juan Pérez",
"apellido_paterno": null,
"apellido_materno": null,
"company": null,
"rfc": null,
"phone": "5512345678",
"email": "[email protected]",
"street": "Av. Insurgentes Sur",
"number": "600",
"colonia": "Del Valle",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000",
"created_at": "2026-07-13T18:40:00Z"
},
"created": false
},
"requestId": "req_e1f2a3b4c5"
}{
"success": true,
"data": {
"sender": {
"id": "C0012345",
"name": "Juan Pérez",
"apellido_paterno": null,
"apellido_materno": null,
"company": null,
"rfc": null,
"phone": "5512345678",
"email": "[email protected]",
"street": "Av. Insurgentes Sur",
"number": "600",
"colonia": "Del Valle",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000",
"created_at": "2026-07-13T18:40:00Z"
},
"created": true
},
"requestId": "req_d0e1f2a3b4"
}Consultar un remitente
get/senders/{id}
Requiere scope addresses:read. 404 idéntico para inexistente y no-visible (sin oráculo).
Parámetros
- idstring· pathrequerido
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El remitente. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- namestring | nullrequerido
- apellido_paternonullrequerido
Siempre
null: el nombre completo está enname. - apellido_maternonullrequerido
Siempre
null: el nombre completo está enname. - companystring | nullrequerido
- rfcstring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
curl "https://api.enviadores.com.mx/api/v1/senders/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"sender": {
"id": "C0012345",
"name": "Juan Pérez",
"apellido_paterno": null,
"apellido_materno": null,
"company": null,
"rfc": null,
"phone": "5512345678",
"email": "[email protected]",
"street": "Av. Insurgentes Sur",
"number": "600",
"colonia": "Del Valle",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000",
"created_at": "2026-07-13T18:40:00Z"
}
},
"requestId": "req_f2a3b4c5d6"
}Editar un remitente
patch/senders/{id}
Actualización PARCIAL de un remitente visible.
›Ver reglas
Requiere scope addresses:write. Solo los campos presentes cambian; se debe enviar al menos uno. Los requeridos pueden cambiarse pero no vaciarse; los opcionales aceptan null para limpiarse. Los apellidos NO forman parte del dedupe, así que PATCH es la forma de completarlos en una fila creada sin ellos. 404 idéntico para inexistente y no-visible (sin oráculo).
Parámetros
- idstring· pathrequerido
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
›Campos del cuerpo (14)
- namestring
- apellido_paternostring | null
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring | null
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring | null
- rfcstring | null
- phonestring
- emailstring | null
- streetstring
- numberstring | null
- coloniastring
- citystring
- statestring
- postal_codestring
- country"MX"
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El remitente actualizado. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 409 | DUPLICATE_ADDRESS | La edición dejaría este remitente idéntico a OTRO ya visible para la llave. La fila no se modifica. |
| 422 | VALIDATION_ERROR | (details.fields) — campo desconocido, requerido vaciado, country ≠ MX, apellido (deprecated) > 50, o cuerpo sin campos editables. |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- namestring | nullrequerido
- apellido_paternonullrequerido
Siempre
null: el nombre completo está enname. - apellido_maternonullrequerido
Siempre
null: el nombre completo está enname. - companystring | nullrequerido
- rfcstring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
curl -X PATCH "https://api.enviadores.com.mx/api/v1/senders/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Juan Pérez García", "email": null}'{
"success": true,
"data": {
"sender": {
"id": "C0012345",
"name": "Juan Pérez García",
"apellido_paterno": null,
"apellido_materno": null,
"company": null,
"rfc": null,
"phone": "5512345678",
"email": "[email protected]",
"street": "Av. Insurgentes Sur",
"number": "600",
"colonia": "Del Valle",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "01000",
"created_at": "2026-07-13T18:40:00Z"
}
},
"requestId": "req_a3b4c5d6e7"
}Listar destinatarios del directorio
get/recipients
Misma visibilidad que GET /senders. Un destinatario siempre pertenece a un remitente (sender_id). Requiere scope addresses:read.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header. - pageinteger· query
- limitinteger· query
- sender_idstring· querySolo destinatarios de este remitente.
- postal_codestring· query
- qstring· query
Búsqueda de texto sobre nombre, alias, teléfono, email y dirección (calle/colonia/ciudad/estado/CP). más
Cada palabra debe aparecer en ALGÚN campo, no necesariamente en el mismo. Insensible a acentos y mayúsculas; se consideran las primeras 5 palabras.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Página de destinatarios. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 422 | VALIDATION_ERROR | Filtro inválido (details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- sender_idstringrequerido
- aliasstring | nullrequerido
- namestring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- referenciastring | nullrequerido
- delivery_instructionsstring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
Mostrar 3 camposOcultar campos
- pageintegerrequerido
- limitintegerrequerido
- totalintegerrequerido
curl "https://api.enviadores.com.mx/api/v1/recipients" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"recipients": [
{
"id": "D0067890",
"sender_id": "C0012345",
"alias": "Oficina",
"name": "María López",
"phone": "8187654321",
"email": null,
"street": "Av. Constitución",
"number": "400",
"colonia": "Centro",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000",
"referencia": "Edificio azul, junto a la farmacia",
"delivery_instructions": "Entregar en recepción",
"created_at": "2026-07-13T18:41:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1
}
},
"requestId": "req_b4c5d6e7f8"
}Crear (o reutilizar) un destinatario
post/recipients
Create-or-reuse bajo el remitente sender_id (debe ser visible para la llave;
›Ver reglas
si no → 404 ciego). Mismo dedupe normalizado que el carril de envíos; en una reutilización, alias/referencia/delivery_instructions NO se aplican (no forman parte de la identidad — la fila existente se devuelve intacta). El postal_code debe tener 5 dígitos y EXISTIR en el catálogo postal (SEPOMEX); si city/state vienen, deben ser coherentes con el CP (un desajuste responde 422 nombrando el valor esperado) y si se omiten se AUTOCOMPLETAN del catálogo — la misma validación de exactitud que POST /contacts/import. Requiere scope addresses:write.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
›Campos del cuerpo (14)
- sender_idstringrequerido
Remitente dueño (visible para la llave; si no → 404 ciego).
- aliasstring
Nombre descriptivo ("Casa", "Oficina").
- namestringrequerido
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
- coloniastringrequerido
- citystring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando la ciudad esperada).
- statestring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando el estado esperado).
- postal_codestringrequerido
5 dígitos; debe EXISTIR en el catálogo postal (SEPOMEX).
- country"MX"
- referenciastring
Referencias del domicilio.
- delivery_instructionsstring
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Destinatario idéntico ya existente reutilizado (created: false). | |
| 201 | Destinatario creado (created: true). | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | sender_id inexistente o no visible (respuesta idéntica). |
| 422 | VALIDATION_ERROR | Campos faltantes/inválidos o desconocidos (details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | ADDRESS_ERROR | No se pudo registrar; reintenta. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- sender_idstringrequerido
- aliasstring | nullrequerido
- namestring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- referenciastring | nullrequerido
- delivery_instructionsstring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
- createdbooleanrequerido
curl -X POST "https://api.enviadores.com.mx/api/v1/recipients" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender_id": "C0012345", "alias": "Oficina", "name": "María López", "phone": "8187654321", "street": "Av. Constitución", "number": "400", "colonia": "Centro", "city": "Monterrey", "state": "Nuevo León", "postal_code": "64000", "referencia": "Edificio azul, junto a la farmacia", "delivery_instructions": "Entregar en recepción"}'{
"success": true,
"data": {
"recipient": {
"id": "D0067890",
"sender_id": "C0012345",
"alias": "Oficina",
"name": "María López",
"phone": "8187654321",
"email": null,
"street": "Av. Constitución",
"number": "400",
"colonia": "Centro",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000",
"referencia": "Edificio azul, junto a la farmacia",
"delivery_instructions": "Entregar en recepción",
"created_at": "2026-07-13T18:41:00Z"
},
"created": false
},
"requestId": "req_d6e7f8a9b0"
}{
"success": true,
"data": {
"recipient": {
"id": "D0067890",
"sender_id": "C0012345",
"alias": "Oficina",
"name": "María López",
"phone": "8187654321",
"email": null,
"street": "Av. Constitución",
"number": "400",
"colonia": "Centro",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000",
"referencia": "Edificio azul, junto a la farmacia",
"delivery_instructions": "Entregar en recepción",
"created_at": "2026-07-13T18:41:00Z"
},
"created": true
},
"requestId": "req_c5d6e7f8a9"
}Consultar un destinatario
get/recipients/{id}
Requiere scope addresses:read. 404 idéntico para inexistente y no-visible (sin oráculo).
Parámetros
- idstring· pathrequerido
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El destinatario. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- sender_idstringrequerido
- aliasstring | nullrequerido
- namestring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- referenciastring | nullrequerido
- delivery_instructionsstring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
curl "https://api.enviadores.com.mx/api/v1/recipients/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"recipient": {
"id": "D0067890",
"sender_id": "C0012345",
"alias": "Oficina",
"name": "María López",
"phone": "8187654321",
"email": null,
"street": "Av. Constitución",
"number": "400",
"colonia": "Centro",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000",
"referencia": "Edificio azul, junto a la farmacia",
"delivery_instructions": "Entregar en recepción",
"created_at": "2026-07-13T18:41:00Z"
}
},
"requestId": "req_e7f8a9b0c1"
}Editar un destinatario
patch/recipients/{id}
Actualización PARCIAL de un destinatario visible.
›Ver reglas
Requiere scope addresses:write. sender_id es inmutable (no se re-asigna de remitente). Solo los campos presentes cambian; al menos uno. Los requeridos pueden cambiarse pero no vaciarse; los opcionales aceptan null. 404 idéntico para inexistente y no-visible.
Parámetros
- idstring· pathrequerido
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
›Campos del cuerpo (13)
- aliasstring | null
- namestring
- phonestring
- emailstring | null
- streetstring
- numberstring | null
- coloniastring
- citystring
- statestring
- postal_codestring
- country"MX"
- referenciastring | null
- delivery_instructionsstring | null
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El destinatario actualizado. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 409 | DUPLICATE_ADDRESS | La edición dejaría este destinatario idéntico a OTRO del MISMO remitente. La fila no se modifica. |
| 422 | VALIDATION_ERROR | (details.fields) — campo desconocido, sender_id presente (inmutable), requerido vaciado, country ≠ MX, o cuerpo sin campos editables. |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 15 camposOcultar campos
- idstringrequerido
- sender_idstringrequerido
- aliasstring | nullrequerido
- namestring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- referenciastring | nullrequerido
- delivery_instructionsstring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
curl -X PATCH "https://api.enviadores.com.mx/api/v1/recipients/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone": "8187654322", "delivery_instructions": null}'{
"success": true,
"data": {
"recipient": {
"id": "D0067890",
"sender_id": "C0012345",
"alias": "Oficina",
"name": "María López",
"phone": "8187654322",
"email": null,
"street": "Av. Constitución",
"number": "400",
"colonia": "Centro",
"city": "Monterrey",
"state": "Nuevo León",
"postal_code": "64000",
"referencia": "Edificio azul, junto a la farmacia",
"delivery_instructions": "Entregar en recepción",
"created_at": "2026-07-13T18:41:00Z"
}
},
"requestId": "req_f8a9b0c1d2"
}Validar un código postal y listar sus colonias
get/postal-codes/{postal_code}
Resuelve un código postal mexicano (catálogo SEPOMEX) en su estado, municipio y el conjunto de colonias/asentamientos — justo lo que se necesita para llenar una dirección antes de POST /senders o POST /shipments.
›Ver reglas
Requiere scope addresses:read. Datos de referencia públicos y de solo lectura: responde IDÉNTICo con llaves ek_live_ y ek_test_ (no hay nada que simular). 422 si el CP no son 5 dígitos; 404 si no existe en el catálogo; 200 con los datos si es válido.
Parámetros
- postal_codestring· pathrequeridoCódigo postal de 5 dígitos.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El código postal existe. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | El código postal (bien formado) no está en el catálogo SEPOMEX. más
|
| 422 | VALIDATION_ERROR | El código postal debe ser exactamente 5 dígitos. |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
- postal_codestringrequerido
El CP consultado (5 dígitos).
- statestring | nullrequerido
Estado (
d_estado). Úsalo comostate. - state_codestring | nullrequerido
Código de estado normalizado (mismo esquema que
state_codedel directorio; p. ej.DF,JAL,NL). - municipalitystring | nullrequerido
Municipio/alcaldía (
d_mnpio). Suele ser el mejor valor paracity. - citystring | nullrequerido
Ciudad (
d_ciudad) cuando SEPOMEX la distingue del municipio;nullen la mayoría de los CP. Asentamientos únicos del CP, ordenados por nombre. Un CP mapea a un estado/municipio pero a muchas colonias.
Mostrar 2 camposOcultar campos
- namestringrequerido
Nombre del asentamiento (
d_asentade SEPOMEX). Úsalo comocolonia/neighborhood. - settlement_typestring | nullrequerido
Tipo de asentamiento SEPOMEX:
Colonia,Fraccionamiento,Pueblo,Barrio, etc.Gran usuario= CP propio de una institución (vergran_usuario).
- gran_usuarioobject | nullrequerido
Presente cuando el CP es de «gran usuario»: un código que Correos de México asignó a una sola institución (p. ej. 80129, Palacio de Gobierno del Estado de Sinaloa) y que ya no publica en su catálogo, aunque la institución lo sigue usando. Es válido, pero varias paqueterías no lo atienden y cotizarlo trae menos tarifas; cotiza y envía con
parent_postal_codeyparent_settlementcomo colonia.nullen un CP ordinario.Mostrar 3 camposOcultar campos
- institutionstringrequerido
La institución dueña del CP.
- parent_postal_codestring | nullrequerido
CP oficial SEPOMEX de la colonia donde está la institución (80129 → 80000). Úsalo en lugar del CP especial.
nullsi no se conoce. - parent_settlementstring | nullrequerido
La colonia de
parent_postal_codedonde está la institución (p. ej.Centro Sinaloa).nullsi no se conoce.
curl "https://api.enviadores.com.mx/api/v1/postal-codes/POSTAL_CODE" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"postal_code": "64000",
"state": "Nuevo León",
"state_code": "NL",
"municipality": "Monterrey",
"city": "Monterrey",
"colonias": [
{
"name": "Centro",
"settlement_type": "Colonia"
}
],
"gran_usuario": null
},
"requestId": "req_a9b0c1d2e3"
}{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Postal code not found",
"details": {
"suggestions": [
{
"postal_code": "72776",
"state": "Puebla",
"state_code": "PU",
"municipality": "San Pedro Cholula",
"city": "San Pedro Cholula"
}
]
}
},
"requestId": "req_c1d2e3f4a5"
}Importar contactos al directorio en bloque (remitentes y destinatarios)
post/contacts/import
Importación masiva del directorio — pensada para migrar contactos desde una hoja de cálculo, otra plataforma o una lista fotografiada, típicamente a través de un asistente de IA. Requiere scope addresses:write (es el mismo alcance de POST /senders / POST /recipients: una importación son esos dos creates en bloque).
›Ver reglas
Dos carriles de entrada, mutuamente excluyentes:
- contacts: arreglo de filas {type: 'sender'|'recipient', name, phone, street, number?, colonia, city?, state?, postal_code, email?, company?, rfc?} — el mismo vocabulario de campos que POST /senders / POST /recipients (los destinatarios además aceptan sender_id?, alias?, referencia?, delivery_instructions?). El cliente (p. ej. el modelo) debe mapear los datos del usuario TAL CUAL, nunca inventar valores.
- raw_csv: el contenido del CSV del usuario, VERBATIM (UTF-8, BOM opcional, máx. 256KB). El servidor lo interpreta de forma determinista: delimitador coma/punto y coma/tabulador (detectado), fila de encabezado obligatoria con las columnas de la plantilla tipo,nombre,telefono,calle,numero,colonia,ciudad,estado,codigo_postal,email,empresa,rfc (coincidencia insensible a mayúsculas y acentos, cualquier orden; columnas desconocidas → 422, nunca adivinadas). tipo acepta remitente/destinatario (o sender/recipient).
Dos fases sin estado: dry_run: true (el DEFAULT — el modo seguro) valida todo y devuelve la vista previa normalizada sin escribir nada; dry_run: false re-valida e inserta. La seguridad ante reintentos viene del DEDUPE POR CONTENIDO, no de claims: una fila idéntica (tupla normalizada nombre+teléfono+dirección+CP — el MISMO dedupe de POST /senders) a un contacto existente visible se reporta duplicate y se omite, así que repetir un commit jamás duplica el directorio.
Validación por fila (nunca aborta el lote completo): codigo_postal de 5 dígitos y existente en el catálogo SEPOMEX; si ciudad/estado vienen, deben ser coherentes con el CP (un desajuste nombra el valor esperado) y si faltan se AUTOCOMPLETAN del catálogo; telefono se normaliza a 10 dígitos (se toleran espacios, guiones y el prefijo +52/521); email con formato válido cuando venga. Cada fila responde status: ok|duplicate|invalid con issues[] en español nombrando campo y motivo.
Padre de los destinatarios: un destino siempre pertenece a un remitente. Orden de resolución: sender_id explícito de la fila → el PRIMER remitente válido del mismo lote → el remitente más reciente del directorio. Sin candidato, la fila es invalid con instrucciones.
Topes: máximo 200 filas por llamada (más → 422 pidiendo trocear el lote) y 256KB de raw_csv.
Conteos (invariantes): received = valid + invalid, valid = filas ok + duplicates; en commit además created y skipped con received = created + skipped + invalid — imposible perder filas en silencio.
Con una llave ek_test_ la importación escribe en el directorio del sandbox (mismas reglas, mismos formatos de respuesta). La visibilidad/destino de las filas es la misma de POST /senders: llaves de cliente y llaves admin sin X-PDV-ID escriben como el usuario de la llave; una llave admin con X-PDV-ID validado escribe en el directorio de ese punto de venta.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
›Campos del cuerpo (3)
- contactsobject[]
Filas mapeadas tal cual de los datos del usuario. Mutuamente excluyente con raw_csv.
Mostrar 19 camposOcultar campos
- type"sender" | "recipient"requerido
- namestringrequerido
- phonestringrequerido
10 dígitos MX; se normalizan espacios/guiones/+52.
- emailstring
- companystring
Solo remitentes (razón social).
- rfcstring
Solo remitentes.
- apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - streetstringrequerido
- numberstring
- coloniastringrequerido
- citystring
Opcional — se autocompleta desde el CP; si viene, se valida contra él.
- statestring
Opcional — se autocompleta desde el CP; si viene, se valida contra él.
- postal_codestringrequerido
- countrystring
Opcional; solo MX.
- sender_idstring
Solo destinatarios: remitente dueño; default = primer remitente del lote o el más reciente del directorio.
- aliasstring
Solo destinatarios.
- referenciastring
Solo destinatarios.
- delivery_instructionsstring
Solo destinatarios.
- raw_csvstring
CSV del usuario, verbatim (UTF-8, máx. 256KB). Mutuamente excluyente con contacts.
- dry_runboolean
true (default): valida y previsualiza sin escribir. false: re-valida e inserta.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Resultado por fila + resumen. En dry_run nada se escribió; en commit, created/skipped reportan lo insertado/omitido. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 422 | VALIDATION_ERROR | Error de contrato (ambos carriles a la vez, ninguno, campo desconocido, más de 200 filas → trocear el lote, CSV >256KB) o de forma del CSV (encabezado con columnas desconocidas/faltantes, no-UTF-8), con los motivos en details.fields. Los problemas POR FILA nunca son 422: viajan como status: invalid dentro de rows. |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
- dry_runbooleanrequerido
- rowsobject[]requerido
Mostrar 8 camposOcultar campos
- indexintegerrequerido
Posición 0-based de la fila en la solicitud.
- lineinteger | null
Línea del archivo en el carril raw_csv (encabezado = línea 1); null en el carril contacts.
- type"sender" | "recipient" | nullrequerido
- namestring | nullrequerido
- status"ok" | "duplicate" | "invalid"requerido
- issuesstring[]requerido
Motivos en español, nombrando el campo.
- contactobject | null
La fila normalizada (teléfono a 10 dígitos, ciudad/estado autocompletados) cuando status=ok.
- idstring | null
Id creado (commit) o del contacto existente (duplicate).
- summaryobjectrequerido
Mostrar 6 camposOcultar campos
- receivedintegerrequerido
- validintegerrequerido
Filas ok + duplicates (received = valid + invalid).
- invalidintegerrequerido
- duplicatesintegerrequerido
- createdinteger
Solo en commit. received = created + skipped + invalid.
- skippedinteger
Solo en commit; = duplicates.
curl -X POST "https://api.enviadores.com.mx/api/v1/contacts/import" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"dry_run": false,
"rows": [
{
"index": 0,
"line": 2,
"type": "sender",
"name": "Juan Pérez",
"status": "ok",
"issues": [],
"contact": {
"type": "sender",
"name": "Juan Pérez",
"phone": "5512345678",
"street": "Av. Insurgentes Sur",
"number": "600",
"colonia": "San Ángel",
"city": "Ciudad de México",
"state": "Ciudad de México",
"postal_code": "01000"
},
"id": "C0012399"
},
{
"index": 1,
"line": 3,
"type": "recipient",
"name": "Ana López",
"status": "duplicate",
"issues": [
"ya existe un contacto idéntico en tu directorio (se omite)"
],
"contact": null,
"id": "D0045012"
},
{
"index": 2,
"line": 4,
"type": "sender",
"name": "Mal Teléfono",
"status": "invalid",
"issues": [
"telefono: debe tener 10 dígitos después de quitar espacios, guiones y el prefijo +52 (recibido «123»)"
],
"contact": null,
"id": null
}
],
"summary": {
"received": 3,
"valid": 2,
"invalid": 1,
"duplicates": 1,
"created": 1,
"skipped": 1
}
},
"requestId": "req_f1e2d3c4b5"
}webhooks
Webhooks de eventos (envíos y recolecciones) — alternativa al polling
Listar webhooks
get/webhooks
Todos los webhooks de la cuenta en el modo de la llave (live o sandbox), incluidos los desactivados automáticamente — status, failure_count (fallos consecutivos) y last_delivery_* explican por qué dejaron de llegar eventos. Nunca incluye secretos. Requiere scope webhooks:read.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Lista + el catálogo de eventos y el tope activo. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 11 camposOcultar campos
- idstringrequerido
wh_+ 24 hex. - urlstringrequerido
- mode"live" | "test"requerido
Lane de la llave que lo registró.
liverecibe eventos reales;testsoloshipment.created/shipment.cancelleddel sandbox y el ping. - status"active" | "disabled"requerido
disabled= desactivado automáticamente: tras 20 fallos consecutivos, o porque la llave API que lo registró fue revocada (disabled_reason: key_revoked— un webhook muere con su llave). Al desactivarse, sus eventos pendientes se descartan. No cuenta para el tope; elimínalo y regístralo de nuevo. - failure_countintegerrequerido
Fallos CONSECUTIVOS de entrega (cualquier respuesta no 2xx o error de transporte). Vuelve a 0 con cada 2xx. Los pings de prueba no lo afectan.
- last_delivery_atstring | null
ISO-8601 UTC (Z). Último intento (éxito o fallo), pings incluidos.
- last_delivery_statusinteger | null
Status HTTP del último intento;
0= fallo de transporte (timeout/TLS/DNS). - created_atstring | null
ISO-8601 UTC (Z).
- disabled_atstring | null
ISO-8601 UTC (Z).
- disabled_reasonstring | null
key_revokedcuando la llave que lo registró fue revocada; texto explicativo cuando fue por fallos consecutivos.
- max_activeintegerrequerido
Tope de webhooks activos por cuenta y modo.
- eventsstring[]requerido
Catálogo completo de eventos suscribibles.
curl "https://api.enviadores.com.mx/api/v1/webhooks" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"webhooks": [
{
"id": "wh_0f3a9c1d2e4b5a6978c0d1e2",
"url": "https://hooks.mitienda.com/enviadores",
"events": [
"shipment.created",
"shipment.delivered"
],
"mode": "live",
"status": "active",
"failure_count": 0,
"last_delivery_at": "2026-09-04T16:05:12Z",
"last_delivery_status": 200,
"created_at": "2026-09-04T16:00:00Z",
"disabled_at": null,
"disabled_reason": null
}
],
"max_active": 5,
"events": [
"shipment.created",
"shipment.collected",
"shipment.in_transit",
"shipment.out_for_delivery",
"shipment.delivered",
"shipment.exception",
"shipment.returned",
"shipment.cancelled",
"pickup.resolved"
]
},
"requestId": "req_a7b8c9d0e1"
}Registrar un webhook
post/webhooks
Registra un endpoint https:// público que recibirá un POST firmado por cada evento suscrito.
›Ver reglas
El `secret` se devuelve UNA sola vez en esta respuesta: no se almacena (se deriva bajo una llave del servidor) y no vuelve a mostrarse; guárdalo al recibirlo. Máximo 5 webhooks activos por cuenta y modo (409 WEBHOOK_LIMIT_REACHED). La URL se valida contra SSRF: solo https://, hostname público (se rechazan localhost, .local, .internal, literales IP privadas/loopback/link-local y hosts que resuelvan a ellas) y sin credenciales embebidas; en cada entrega se vuelve a resolver el host y la conexión se fija a la IP pública verificada. Con llave ek_test_ el webhook es de sandbox (solo shipment.created/shipment.cancelled del carril de pruebas + ping). Requiere scope webhooks:write.
›Campos del cuerpo (2)
- urlstringrequerido
Endpoint
https://público en el puerto 443 u 8443 (cualquier otro puerto se rechaza). Se rechazan http, credenciales embebidas, fragmentos,localhost/.local/.internal, literales IP privadas, hosts que resuelvan a rangos privados y hosts que apunten al propio API de Enviadores. Eventos a recibir (duplicados se descartan).
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 201 | Webhook registrado. secret aparece SOLO aquí. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Los webhooks no están habilitados en este despliegue (respuesta idéntica a una ruta inexistente). |
| 409 | WEBHOOK_LIMIT_REACHED | Ya hay 5 webhooks activos en este modo (details {max_active, mode}). Elimina uno con DELETE /webhooks/{id}. |
| 422 | VALIDATION_ERROR | details.fields nombra el problema: URL no https://, puerto distinto de 443/8443, host privado/no resoluble/propio del API, credenciales embebidas, events vacío o con un evento desconocido, campo desconocido. |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 201
Mostrar 11 camposOcultar campos
- idstringrequerido
wh_+ 24 hex. - urlstringrequerido
- mode"live" | "test"requerido
Lane de la llave que lo registró.
liverecibe eventos reales;testsoloshipment.created/shipment.cancelleddel sandbox y el ping. - status"active" | "disabled"requerido
disabled= desactivado automáticamente: tras 20 fallos consecutivos, o porque la llave API que lo registró fue revocada (disabled_reason: key_revoked— un webhook muere con su llave). Al desactivarse, sus eventos pendientes se descartan. No cuenta para el tope; elimínalo y regístralo de nuevo. - failure_countintegerrequerido
Fallos CONSECUTIVOS de entrega (cualquier respuesta no 2xx o error de transporte). Vuelve a 0 con cada 2xx. Los pings de prueba no lo afectan.
- last_delivery_atstring | null
ISO-8601 UTC (Z). Último intento (éxito o fallo), pings incluidos.
- last_delivery_statusinteger | null
Status HTTP del último intento;
0= fallo de transporte (timeout/TLS/DNS). - created_atstring | null
ISO-8601 UTC (Z).
- disabled_atstring | null
ISO-8601 UTC (Z).
- disabled_reasonstring | null
key_revokedcuando la llave que lo registró fue revocada; texto explicativo cuando fue por fallos consecutivos.
- secretstringrequerido
Secreto de firma — se muestra SOLO aquí. Guárdalo: no se almacena y no se puede recuperar; para obtener otro hay que eliminar el webhook y registrarlo de nuevo.
curl -X POST "https://api.enviadores.com.mx/api/v1/webhooks" \ -H "Authorization: Bearer $ENVIADORES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://hooks.mitienda.com/enviadores", "events": [ "shipment.created", "shipment.delivered", "shipment.exception" ]}'{
"success": true,
"data": {
"webhook": {
"id": "wh_0f3a9c1d2e4b5a6978c0d1e2",
"url": "https://hooks.mitienda.com/enviadores",
"events": [
"shipment.created",
"shipment.delivered",
"shipment.exception"
],
"mode": "live",
"status": "active",
"failure_count": 0,
"last_delivery_at": null,
"last_delivery_status": null,
"created_at": "2026-09-04T16:00:00Z",
"disabled_at": null,
"disabled_reason": null
},
"secret": "whsec_9f2c…(64 hex)"
},
"requestId": "req_a7b8c9d0e1"
}Consultar un webhook
get/webhooks/{id}
Un webhook propio (misma cuenta y mismo modo que la llave). Requiere scope webhooks:read.
Parámetros
- idstring· pathrequeridoId del webhook (
wh_+ 24 hex), devuelto porPOST /webhooks.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | El webhook. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe, es de otra cuenta o de otro modo (respuesta idéntica). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 11 camposOcultar campos
- idstringrequerido
wh_+ 24 hex. - urlstringrequerido
- mode"live" | "test"requerido
Lane de la llave que lo registró.
liverecibe eventos reales;testsoloshipment.created/shipment.cancelleddel sandbox y el ping. - status"active" | "disabled"requerido
disabled= desactivado automáticamente: tras 20 fallos consecutivos, o porque la llave API que lo registró fue revocada (disabled_reason: key_revoked— un webhook muere con su llave). Al desactivarse, sus eventos pendientes se descartan. No cuenta para el tope; elimínalo y regístralo de nuevo. - failure_countintegerrequerido
Fallos CONSECUTIVOS de entrega (cualquier respuesta no 2xx o error de transporte). Vuelve a 0 con cada 2xx. Los pings de prueba no lo afectan.
- last_delivery_atstring | null
ISO-8601 UTC (Z). Último intento (éxito o fallo), pings incluidos.
- last_delivery_statusinteger | null
Status HTTP del último intento;
0= fallo de transporte (timeout/TLS/DNS). - created_atstring | null
ISO-8601 UTC (Z).
- disabled_atstring | null
ISO-8601 UTC (Z).
- disabled_reasonstring | null
key_revokedcuando la llave que lo registró fue revocada; texto explicativo cuando fue por fallos consecutivos.
curl "https://api.enviadores.com.mx/api/v1/webhooks/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"Eliminar un webhook
delete/webhooks/{id}
Elimina el webhook. Las entregas se detienen de inmediato y los eventos pendientes en cola para él se descartan. Volver a registrar la misma URL genera un id y un secret NUEVOS. Requiere scope webhooks:write.
Parámetros
- idstring· pathrequeridoId del webhook (
wh_+ 24 hex), devuelto porPOST /webhooks.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Eliminado. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe, es de otra cuenta o de otro modo (respuesta idéntica). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
- deletedtruerequerido
- idstringrequerido
curl -X DELETE "https://api.enviadores.com.mx/api/v1/webhooks/ID" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"deleted": true,
"id": "wh_0f3a9c1d2e4b5a6978c0d1e2"
},
"requestId": "req_a7b8c9d0e1"
}Probar un webhook (ping)
post/webhooks/{id}/test
Entrega un {"event":"ping"} firmado de forma síncrona (mismos headers y misma firma que un evento real;
›Ver reglas
data = {webhook_id}) y reporta el resultado. Un ping fallido NO cuenta para la desactivación automática — repítelo con libertad mientras ajustas el endpoint. Sin cuerpo. Requiere scope webhooks:write.
Parámetros
- idstring· pathrequeridoId del webhook (
wh_+ 24 hex), devuelto porPOST /webhooks.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Resultado del ping (200 aunque el endpoint haya fallado — mira delivered). | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | No existe, es de otra cuenta o de otro modo (respuesta idéntica). |
| 409 | WEBHOOK_DISABLED | El webhook fue desactivado automáticamente (details.disabled_reason); elimínalo y regístralo de nuevo. |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
- deliveredbooleanrequerido
truesi el endpoint respondió 2xx dentro de 10 s. - response_statusinteger | nullrequerido
Status HTTP recibido;
nullsi no hubo respuesta (timeout, TLS, DNS). - error"timeout" | "connection failed" | "non-2xx status" | "endpoint rejected" | nullrequerido
Clase del fallo, deliberadamente gruesa (el detalle de red no se expone para que el ping no sirva como sonda de puertos):
timeout,connection failed(rechazo/reset/TLS/DNS),non-2xx status(verresponse_status),endpoint rejected(la URL ya no pasa la política);nullsi se entregó. - event_idstringrequerido
evt_+ 24 hex — eliddel cuerpo enviado y el headerX-Enviadores-Delivery.
curl -X POST "https://api.enviadores.com.mx/api/v1/webhooks/ID/test" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"delivered": false,
"response_status": 500,
"error": "HTTP 500",
"event_id": "evt_4a5b6c7d8e9f0a1b2c3d4e5f"
},
"requestId": "req_a7b8c9d0e1"
}account
La cuenta y la llave: identidad, capacidades y movimientos de saldo
Identificar la llave y la cuenta (whoami)
get/me
Quién eres desde el punto de vista de la API: la cuenta, si la conexión es de prueba (ek_test_) o de producción (ek_live_), los scopes que la llave realmente porta en ESTA petición, el saldo disponible y las capacidades de la superficie.
›Ver reglas
Es el endpoint de autodiagnóstico: cuando algo responde 403 INSUFFICIENT_SCOPE, compara key.scopes con el scope que nombró el error; cuando dudes si una guía es real, mira account.mode.
Cualquier llave válida puede llamarlo, sin importar sus scopes — un whoami que pudiera responder 403 por falta de permiso no serviría para su único propósito. Aun así no expone nada nuevo: balance.available es el mismo número de GET /balance (misma lectura), y el resto son hechos sobre tu propia llave. Nunca devuelve el secreto ni su hash ni su prefijo, ni la allowlist de PDVs, ni correo/teléfono/rol.
key.scopes es el conjunto EFECTIVO: una llave creada con "los permisos por omisión" (scopes_mode: default) se re-resuelve contra los scopes por omisión vigentes en cada petición, así que aquí ves lo que el gate acaba de aplicar. Una llave que alguien restringió a mano (scopes_mode: explicit) queda congelada como se creó.
account.tier_label es el escalón de verificación de las cuentas de registro propio (T1…T4); es null para cuentas creadas por nuestro equipo, que no están en esa escalera, y también si la consulta de verificación no está disponible.
capabilities describe la superficie, no la cuenta: pickups sigue el interruptor de recolecciones (siempre true en sandbox), y international/multi_package se derivan de las mismas validaciones que aplica POST /shipments, así que no pueden desincronizarse de la realidad.
En modo de prueba, balance.available es el saldo COMPUTADO del sandbox.
Parámetros
- X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Identidad de la llave y la cuenta. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
- accountobjectrequerido
Mostrar 5 camposOcultar campos
- idstringrequerido
Id del usuario dueño de la llave.
- namestring | nullrequerido
Nombre visible de la cuenta (razón social, nombre de la persona, o el usuario). Nunca correo ni teléfono.
- mode"test" | "live"requerido
test= sandbox (nada es real);live= producción. - account_type"customer" | "staff"requerido
Dos valores públicos: cuenta de cliente, o cuenta de nuestro mostrador. El rol interno no se expone.
- tier_label"T0" | "T1" | "T2" | "T3" | "T4" | nullrequerido
Escalón de verificación de una cuenta de registro propio.
nullsi la cuenta no está en la escalera (creada por nuestro equipo) o si la consulta no está disponible.
- keyobjectrequerido
Mostrar 7 camposOcultar campos
- idstringrequerido
Id público de la llave (no es el secreto).
- labelstring | nullrequerido
Etiqueta que le puso su dueño.
- mode"test" | "live"requerido
- scopesstring[]requerido
Los scopes EFECTIVOS de esta petición — lo que el gate acaba de aplicar.
- scopes_mode"default" | "explicit"requerido
default: la llave se creó con los permisos por omisión y se actualiza sola cuando aparecen nuevos.explicit: alguien la restringió al crearla; queda congelada así. - spend_cap_daily_mxnnumber | nullrequerido
Tope de gasto en ventana móvil de 24 h, o
nullsi no tiene. - rate_limit_tier"standard" | "elevated"requerido
- balanceobjectrequerido
Mostrar 2 camposOcultar campos
- availablenumberrequerido
Lo gastable ahora mismo — el mismo número que
GET /balance. Para el desglose (held, total,scope) usa ese endpoint. - currency"MXN"requerido
- capabilitiesobjectrequerido
Mostrar 4 camposOcultar campos
- pickupsbooleanrequerido
Si esta conexión puede solicitar recolecciones. Siempre
trueen sandbox. - internationalbooleanrequerido
Si la API acepta destinos fuera de México. Hoy
false(v1 es doméstico MX). - multi_packagebooleanrequerido
Si una solicitud puede llevar más de un paquete. Se deriva de la misma validación que aplica
POST /shipments. - webhooksbooleanrequerido
Si existen webhooks salientes en esta superficie. Hoy
false: consulta conGET /shipmentsoGET /tracking/{guia}.
curl "https://api.enviadores.com.mx/api/v1/me" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"account": {
"id": "U0000001",
"name": "Comercializadora Ejemplo SA de CV",
"mode": "live",
"account_type": "customer",
"tier_label": "T3"
},
"key": {
"id": "AK0123456789abcdef",
"label": "Servidor de producción",
"mode": "live",
"scopes": [
"rates:read",
"shipments:write",
"shipments:read",
"tracking:read",
"cancellations:write",
"cancellations:read",
"balance:read",
"labels:read",
"addresses:read",
"addresses:write",
"pickups:write",
"pickups:read"
],
"scopes_mode": "default",
"spend_cap_daily_mxn": 5000,
"rate_limit_tier": "standard"
},
"balance": {
"available": 4675,
"currency": "MXN"
},
"capabilities": {
"pickups": true,
"international": false,
"multi_package": false,
"webhooks": false
}
},
"requestId": "req_d4e5f6a7b8"
}Listar los movimientos de saldo
get/transactions
El libro mayor detrás de GET /balance: cargos por guías, reembolsos por cancelación, recargas y ajustes manuales, del más reciente al más antiguo.
›Ver reglas
Es la respuesta a "¿en qué se fue el saldo?".
Mismo principal que `GET /balance`, por construcción. Sin header: el saldo PERSONAL del usuario de la llave. Con X-PDV-ID validado (llaves admin, misma allowlist): el libro mayor de ese fondo de punto de venta. Las consultas cruzadas por user_id del endpoint interno NO existen aquí: una llave lee exactamente el fondo que cargarían sus envíos.
amount viene con SIGNO en MXN (negativo = salió dinero) y balance_after es el saldo tras ese movimiento. type usa un vocabulario público de cuatro palabras (charge, refund, topup, adjustment) que resume el enum interno; los detalles internos (qué reventa, qué pierna de una corrección, qué programa) no se exponen.
En modo de prueba el listado se DERIVA de los envíos sandbox — un cargo al crear, un reembolso al cancelar — de modo que la suma cuadra exactamente con el saldo computado del sandbox. Requiere scope balance:read.
Parámetros
- pageinteger· query
- limitinteger· query
- type"charge" | "refund" | "topup" | "adjustment"· query
Filtra por tipo público de movimiento — exactamente el mismo valor que devuelve el campo
typede cada fila. máscharge= cargo (salió saldo),refund= devolución,topup= recarga en línea,adjustment= cualquier otro abono (alta manual, promoción, liquidación). Un valor fuera del enum es422 VALIDATION_ERROR, nunca una lista vacía silenciosa. - fromstring· queryDía calendario INCLUSIVO (zona horaria de negocio America/Mexico_City) desde el cual listar movimientos. Solo
YYYY-MM-DD. - tostring· queryDía calendario INCLUSIVO (America/Mexico_City) hasta el cual listar — el día completo.
fromposterior atoes422. - X-PDV-IDstring· header
Solo llaves de cuentas admin: el punto de venta cuyo fondo se carga/consulta. más
Requiere que la llave tenga una allowlist de PDVs configurada por un administrador (
pdv_allowlist); sin allowlist, todo PDV real es denegado por defecto. Validado además contra los PDV activos. Cualquier violación →403 PDV_NOT_ALLOWED. El valorpersonal_accountequivale a omitir el header.
Respuestas
| HTTP | Código | Qué significa |
|---|---|---|
| 200 | Página de movimientos. | |
| 401 | UNAUTHORIZED | Credencial Bearer ausente/mal formada o llave desconocida/revocada (respuesta idéntica en ambos casos). |
| 403 | INSUFFICIENT_SCOPE | A la llave le falta el scope requerido |
PDV_NOT_ALLOWED | Violación del carril X-PDV-ID. másNota de orden: el limitador por llave corre ANTES del gate de scope, así que una petición con scope incorrecto y el bucket agotado responde | |
| 404 | NOT_FOUND | Ruta desconocida, recurso ajeno/inexistente, o la API pública no está habilitada (respuestas indistinguibles por diseño). |
| 422 | VALIDATION_ERROR | type fuera del enum, fecha mal formada o from posterior a to (campos en details.fields). |
| 429 | RATE_LIMITED | Bucket de la llave agotado. másEspera |
| 500 | SERVER_ERROR | Falla interna (p. ej. lectura de base de datos). Es transitoria; reintenta con backoff. |
›Campos de la respuesta 200
Mostrar 7 camposOcultar campos
- idstringrequerido
Id del movimiento (estable; el que conviene citar a soporte).
- type"charge" | "refund" | "topup" | "adjustment"requerido
Vocabulario público: cargo, devolución, recarga en línea, o cualquier otro abono.
- amountnumberrequerido
Importe en MXN CON SIGNO: negativo si salió saldo, positivo si entró.
- balance_afternumber | nullrequerido
Saldo del principal después de este movimiento.
- shipment_idstring | nullrequerido
Envío relacionado, cuando el movimiento nació de uno.
- descriptionstring | nullrequerido
Texto del movimiento. Las correcciones de cargo (cuando reasignamos un cobro al fondo correcto) se reportan siempre como
Ajuste: el detalle interno describe nuestra propia contabilidad y nombra al operador que la hizo, y nada de eso viaja a esta superficie. - created_atstring | nullrequerido
ISO-8601 en UTC (Z).
Mostrar 3 camposOcultar campos
- pageintegerrequerido
- limitintegerrequerido
- totalintegerrequerido
curl "https://api.enviadores.com.mx/api/v1/transactions" \ -H "Authorization: Bearer $ENVIADORES_API_KEY"{
"success": true,
"data": {
"transactions": [
{
"id": "TX00012345",
"type": "charge",
"amount": -184.5,
"balance_after": 4675,
"shipment_id": "20260830-000123",
"description": "Guía Estafeta Terrestre",
"created_at": "2026-08-30T10:00:02Z"
},
{
"id": "TX00012344",
"type": "topup",
"amount": 2000,
"balance_after": 4859.5,
"shipment_id": null,
"description": "Recarga en línea",
"created_at": "2026-08-29T18:12:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 2
}
},
"requestId": "req_b1c2d3e4f5"
}Modelos
Esquemas que usan las peticiones y respuestas. Abre uno para ver sus campos.
›SuccessEnvelope
- successtruerequerido
- dataobjectrequerido
- requestIdstringrequerido
Correlación de logs; también en el header
X-Request-Id.
›ErrorEnvelope
- successfalserequerido
- errorobjectrequerido
Mostrar 3 camposOcultar campos
- codestringrequerido
Código de máquina estable (p. ej. RATE_EXPIRED, CREDIT_ERROR).
409 REQUEST_IN_PROGRESS: la mismaIdempotency-Keysigue procesándose — espera y reintenta con la MISMA llave. - messagestringrequerido
- detailsobject
Contexto adicional por código (p. ej. {required, available, shortfall} en CREDIT_ERROR; {fields} en VALIDATION_ERROR; {committed: true} en POST_COMMIT_ERROR; {suggestions} en el NOT_FOUND de /postal-codes).
- requestIdstringrequerido
›RatesRequestOrigen: exactamente UNO de from (inline) o from_id (remitente del directorio).
Origen: exactamente UNO de from (inline) o from_id (remitente del directorio). Destino: exactamente UNO de to o to_id. Mandar ambos miembros de un par → 422; un id no visible para la llave → 404 (respuesta ciega). Cotizar por id usa los datos completos guardados (ciudad/estado/colonia), así que la resolución de zona es igual o mejor que con CP suelto. Bultos: exactamente UNO de package (una caja) o packages (2–10 cajas en un mismo envío).
Mostrar 5 camposOcultar campos
- postal_codestringrequerido
CP de 5 dígitos (MX).
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - statestring
- citystring
- neighborhoodstring
- from_idstring
Id de un remitente del directorio (
GET /senders). Mutuamente excluyente confrom. Mostrar 5 camposOcultar campos
- postal_codestringrequerido
CP de 5 dígitos (MX).
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - statestring
- citystring
- neighborhoodstring
- to_idstring
Id de un destinatario del directorio (
GET /recipients). Mutuamente excluyente conto. - packageobject
Mostrar 4 camposOcultar campos
- weight_kgnumberrequerido
Peso real en kg. El facturable es max(real, volumétrico L*W*H/5000); máximo 70 kg.
- length_cmnumber
Largo exterior de la caja ya cerrada (cm). Manda las tres medidas: sin ellas la cotización se calcula para una caja de 10×10×10 cm, llega con
size_estimated: trueyPOST /shipmentsla rechaza (422PACKAGE_DIMENSIONS_REQUIRED). *Outer size of the closed box; without all three the price is only an estimate and create is refused.* - width_cmnumber
Ancho exterior de la caja ya cerrada (cm).
- height_cmnumber
Alto exterior de la caja ya cerrada (cm).
- packagesPackagesList
Mostrar 4 camposOcultar campos
- weight_kgnumberrequerido
Peso real del bulto (kg).
- length_cmnumber
Largo exterior del bulto cerrado (cm). Obligatorio para
POST /shipments; enPOST /rates, sin las tres medidas el precio es estimado (size_estimated). - width_cmnumber
Ancho exterior del bulto cerrado (cm).
- height_cmnumber
Alto exterior del bulto cerrado (cm).
- insuranceInsuranceRequest
Mostrar 2 camposOcultar campos
- insured_valuenumber
Valor a asegurar (MXN). La prima queda incluida en
pricing.total_pricede cada tarifa. - declared_valuenumber
OBSOLETO — alias de
insured_value(compatibilidad con integradores previos). Si mandas ambos deben coincidir.
›PackagesListVARIOS bultos que viajan como UN solo envío (un cargo, una cotización).
VARIOS bultos que viajan como UN solo envío (un cargo, una cotización). Mutuamente excluyente con package. La lista cotizada y la reservada deben ser IDÉNTICAS (mismo orden, mismas medidas) o POST /shipments responde 409 RATE_PACKAGE_MISMATCH. Solo se cotizan/reservan servicios que soportan multi-bulto.
›InsuranceRequestCobertura para el envío.
Cobertura para el envío. insured_value contrata la póliza de la PLATAFORMA (prima incluida en pricing.total_price) o, cuando esa póliza no está disponible, la cobertura de la propia paquetería — el precio mostrado ya la incluye; cada tarifa indica insurance_included / insurance_not_supported. NO es el valor declarado a la paquetería (declared_value en POST /shipments). *Insurance: insured_value buys coverage for the shipment; when the platform policy is unavailable the carrier's own coverage is used and the price shown already includes it.*
- insured_valuenumber
Valor a asegurar (MXN). La prima queda incluida en
pricing.total_pricede cada tarifa. - declared_valuenumber
OBSOLETO — alias de
insured_value(compatibilidad con integradores previos). Si mandas ambos deben coincidir.
›RatesGroupedResponseRespuesta de POST /rates?view=grouped: cada servicio distinto (paquetería + nivel de servicio; para priority también la ventana horaria) aparece UNA vez con su rango de precios y sus opciones reservables.
Respuesta de POST /rates?view=grouped: cada servicio distinto (paquetería + nivel de servicio; para priority también la ventana horaria) aparece UNA vez con su rango de precios y sus opciones reservables. Mismo unlock_notice/fetched_at/stale_after/meta/rate_id_expires_in_seconds/incomplete/size_estimated/size_notice que la forma plana.
Ordenados por
price_fromascendente.Mostrar 14 camposOcultar campos
- service_keystringrequerido
Clave estable del servicio:
carrier|tier(+|ventanapara priority). - carrierstringrequerido
Nombre canónico de la paquetería.
- tier"priority" | "express" | "standard_economy" | "international"requerido
- delivery_windowstring | nullrequerido
- service_labelstringrequerido
Etiqueta legible, p. ej.
DHL · Express,Estafeta · Prioritario 10:30 AM. - estimated_daysstring | number | nullrequerido
De
options[0](la opción reservable más barata). - guaranteed_window"2_days" | "ground" | null
De
options[0]; veroptions[].guaranteed_window. - guaranteed_window_daysstring | null
De
options[0]. - pickup_availablebooleanrequerido
true si ALGUNA opción incluye recolección.
- booking_lockedbooleanrequerido
true solo si TODAS las opciones están bloqueadas para esta cuenta.
- price_fromnumber | nullrequerido
- price_tonumber | nullrequerido
- currency"MXN"requerido
- optionsobject[]requerido
Precios de este servicio: primero los que esta cuenta puede reservar (
booking_locked: false), cada grupo de menor a mayor, así queoptions[0]es la opción reservable más barata. Reservaoptions[0].rate_idsalvo que el usuario pida otra.price_from/price_toabarcan todas las opciones.Mostrar 12 camposOcultar campos
- rate_idstringrequerido
Para
POST /shipments. OPACO. - service_namestring | nullrequerido
Nombre comercial del servicio tal como lo publica la paquetería.
- total_pricenumber | nullrequerido
- estimated_daysstring | number | nullrequerido
- guaranteed_window"2_days" | "ground" | null
Tiempo de entrega esperado según la cobertura oficial de Estafeta en el destino, cuando la tarifa vende un servicio más rápido de lo que Estafeta garantiza ahí:
2_days(normalmente 2 días) oground(terrestre, normalmenteguaranteed_window_daysdías). null si no aplica o no hay datos de cobertura. La tarifa sigue siendo válida y reservable; preséntalo al cliente final como el tiempo de entrega esperado, no como una advertencia. - guaranteed_window_daysstring | null
Días de esa garantía:
2para2_days,3-5paraground; null cuando guaranteed_window es null. - pickup_includedbooleanrequerido
- booking_lockedbooleanrequerido
- insurance_includedbooleanrequerido
La opción incluye cobertura (ya dentro de total_price).
- insurance_not_supportedbooleanrequerido
La paquetería no ofrece cobertura para esta opción.
- insurance_premiumnumberrequerido
Prima de plataforma itemizada (0 sin póliza).
- insured_valuenumber | nullrequerido
- view"grouped"requerido
- unlock_noticestring | null
- fetched_atstring | nullrequerido
- stale_afterstring | nullrequerido
- metaobjectrequerido
- rate_id_expires_in_seconds1800requerido
- incompletebooleanrequerido
truecuando algunas cotizaciones de paqueterías no llegaron dentro del plazo de cotización (15 s): las opciones listadas siguen siendo válidas y reservables; cotizar de nuevo puede mostrar más. *Some carrier quotes did not arrive within the 15 s quote window; listed options remain bookable, a new quote may list more.* - size_estimatedbooleanrequerido
truecuando alguna caja llegó sin largo, ancho o alto: todos los precios son para una caja de 10×10×10 cm, no el precio real, yPOST /shipmentsrechaza la cotización (422PACKAGE_DIMENSIONS_REQUIRED). Pide las medidas y cotiza de nuevo. *Some box had no length/width/height: prices are for a 10×10×10 cm box and create is refused.* - size_noticestring | nullrequerido
Aviso bilingüe para mostrar cuando
size_estimatedestrue;nullen otro caso.
›RateGroup
- service_keystringrequerido
Clave estable del servicio:
carrier|tier(+|ventanapara priority). - carrierstringrequerido
Nombre canónico de la paquetería.
- tier"priority" | "express" | "standard_economy" | "international"requerido
- delivery_windowstring | nullrequerido
- service_labelstringrequerido
Etiqueta legible, p. ej.
DHL · Express,Estafeta · Prioritario 10:30 AM. - estimated_daysstring | number | nullrequerido
De
options[0](la opción reservable más barata). - guaranteed_window"2_days" | "ground" | null
De
options[0]; veroptions[].guaranteed_window. - guaranteed_window_daysstring | null
De
options[0]. - pickup_availablebooleanrequerido
true si ALGUNA opción incluye recolección.
- booking_lockedbooleanrequerido
true solo si TODAS las opciones están bloqueadas para esta cuenta.
- price_fromnumber | nullrequerido
- price_tonumber | nullrequerido
- currency"MXN"requerido
- optionsobject[]requerido
Precios de este servicio: primero los que esta cuenta puede reservar (
booking_locked: false), cada grupo de menor a mayor, así queoptions[0]es la opción reservable más barata. Reservaoptions[0].rate_idsalvo que el usuario pida otra.price_from/price_toabarcan todas las opciones.Mostrar 12 camposOcultar campos
- rate_idstringrequerido
Para
POST /shipments. OPACO. - service_namestring | nullrequerido
Nombre comercial del servicio tal como lo publica la paquetería.
- total_pricenumber | nullrequerido
- estimated_daysstring | number | nullrequerido
- guaranteed_window"2_days" | "ground" | null
Tiempo de entrega esperado según la cobertura oficial de Estafeta en el destino, cuando la tarifa vende un servicio más rápido de lo que Estafeta garantiza ahí:
2_days(normalmente 2 días) oground(terrestre, normalmenteguaranteed_window_daysdías). null si no aplica o no hay datos de cobertura. La tarifa sigue siendo válida y reservable; preséntalo al cliente final como el tiempo de entrega esperado, no como una advertencia. - guaranteed_window_daysstring | null
Días de esa garantía:
2para2_days,3-5paraground; null cuando guaranteed_window es null. - pickup_includedbooleanrequerido
- booking_lockedbooleanrequerido
- insurance_includedbooleanrequerido
La opción incluye cobertura (ya dentro de total_price).
- insurance_not_supportedbooleanrequerido
La paquetería no ofrece cobertura para esta opción.
- insurance_premiumnumberrequerido
Prima de plataforma itemizada (0 sin póliza).
- insured_valuenumber | nullrequerido
›RatesEndpointParty
- postal_codestringrequerido
CP de 5 dígitos (MX).
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - statestring
- citystring
- neighborhoodstring
›RatesResponse
Mostrar 16 camposOcultar campos
- idstringrequerido
rate_id — pásalo tal cual a POST /shipments. OPACO: su formato no es parte del contrato (difiere entre el carril de prueba y el live); no lo interpretes ni lo valides.
- carrierstring | nullrequerido
- service_namestring | nullrequerido
Nombre comercial del servicio tal como lo publica la paquetería.
- service_typestring | null
Clasificación por niveles: priority | express | standard_economy…
- tierstring | null
Igual a service_type (alias de compatibilidad).
- delivery_windowstring | null
Ventana de entrega normalizada (p. ej. next_day, ground).
- pickup_includedboolean
La tarifa incluye recolección.
- address_deliveryboolean
Entrega a domicilio (false = entrega en sucursal/ocurre).
- pricingobjectrequerido
Mostrar 5 camposOcultar campos
- total_pricenumber | nullrequerido
Total a cobrar en MXN, 2 decimales (IVA incluido; incluye la prima de seguro de plataforma cuando aplique).
- currency"MXN"requerido
- iva_includedboolean
- insurance_premiumnumber
Prima itemizada ya incluida en total_price (0 sin póliza).
- insured_valuenumber | null
Valor declarado asegurado (null sin póliza).
- deliveryobject
Mostrar 4 camposOcultar campos
- estimated_daysstring | number | null
- estimated_datestring | null
- min_daysinteger | null
- max_daysinteger | null
- insurance_includedboolean
- insurance_not_supportedboolean
La paquetería no acepta seguro para esta tarifa.
- guaranteed_window"2_days" | "ground" | null
Tiempo de entrega esperado según la cobertura oficial de Estafeta en el destino, cuando la tarifa vende un servicio más rápido de lo que Estafeta garantiza ahí:
2_days(normalmente 2 días) oground(terrestre, normalmenteguaranteed_window_daysdías). null si no aplica o no hay datos de cobertura. La tarifa sigue siendo válida y reservable; preséntalo al cliente final como el tiempo de entrega esperado, no como una advertencia. - guaranteed_window_daysstring | null
Días de esa garantía:
2para2_days,3-5paraground; null cuando guaranteed_window es null. - zona_extendidaboolean
Destino en zona extendida: puede implicar cargos de reexpedición. Cuando la paquetería los cotiza por adelantado ya vienen dentro de total_price; si no, pueden facturarse después como cargo de paquetería.
- booking_lockedboolean
La tarifa es VISIBLE pero aún no reservable para esta cuenta:
POST /shipmentsresponde403 SERVICE_UNLOCK_REQUIRED. Solo afecta a cuentas de registro propio sin identidad verificada (INE) y con menos de $2,500 MXN en recargas acumuladas. Para esas cuentas solo quedan abiertos iMile Terrestre y Estafeta Terrestre y Estafeta Día Siguiente sin recolección (estas se dejan en una sucursal Estafeta y los cargos extra se pagan ahí); todo lo demás llega bloqueado, incluidos los servicios sin recolección de otros transportistas. Las rutas de desbloqueo vienen enunlock_notice.
- unlock_noticestring | null
Mensaje es-MX para mostrar al usuario cuando su catálogo está limitado (hay tarifas con
booking_locked) o cuando se desbloqueó por recargas acumuladas y la verificación de identidad sigue pendiente.nullpara cuentas sin restricción. - fetched_atstring | nullrequerido
ISO-8601.
- stale_afterstring | nullrequerido
ISO-8601 (fetched_at + 5 min): re-cotiza para PRECIOS frescos; el rate_id sigue siendo enviable hasta los 30 min.
- metaobjectrequerido
Mostrar 3 camposOcultar campos
- billable_weightnumber | null
Peso facturable (kg): max(real, volumétrico).
- volumetric_weightnumber | null
L×W×H / 5000 (kg).
- zoneinteger | null
- rate_id_expires_in_seconds1800requerido
TTL del rate_id: crea el envío dentro de esta ventana o recibirás 409 RATE_EXPIRED.
- incompletebooleanrequerido
truecuando algunas cotizaciones de paqueterías no llegaron dentro del plazo de 15 s y la respuesta se envió con las que sí llegaron. Cada tarifa listada es completa y reservable; una nueva cotización puede incluir más paqueterías o servicios. *The quote answers within 15 s with the carrier quotes that arrived; listed rates remain bookable, a new quote may list more.* - size_estimatedbooleanrequerido
truecuando alguna caja llegó sin largo, ancho o alto: todos los precios son para una caja de 10×10×10 cm, no el precio real, yPOST /shipmentsrechaza la cotización (422PACKAGE_DIMENSIONS_REQUIRED). Pide las medidas y cotiza de nuevo. *Some box had no length/width/height: prices are for a 10×10×10 cm box and create is refused.* - size_noticestring | nullrequerido
Aviso bilingüe para mostrar cuando
size_estimatedestrue;nullen otro caso.
›RateUna tarifa enviable.
Una tarifa enviable. id es el rate_id para POST /shipments. La forma es IDÉNTICA para toda llave (los costos internos de proveedor nunca se incluyen) y es una whitelist estricta: no aparecerán campos no documentados.
- idstringrequerido
rate_id — pásalo tal cual a POST /shipments. OPACO: su formato no es parte del contrato (difiere entre el carril de prueba y el live); no lo interpretes ni lo valides.
- carrierstring | nullrequerido
- service_namestring | nullrequerido
Nombre comercial del servicio tal como lo publica la paquetería.
- service_typestring | null
Clasificación por niveles: priority | express | standard_economy…
- tierstring | null
Igual a service_type (alias de compatibilidad).
- delivery_windowstring | null
Ventana de entrega normalizada (p. ej. next_day, ground).
- pickup_includedboolean
La tarifa incluye recolección.
- address_deliveryboolean
Entrega a domicilio (false = entrega en sucursal/ocurre).
- pricingobjectrequerido
Mostrar 5 camposOcultar campos
- total_pricenumber | nullrequerido
Total a cobrar en MXN, 2 decimales (IVA incluido; incluye la prima de seguro de plataforma cuando aplique).
- currency"MXN"requerido
- iva_includedboolean
- insurance_premiumnumber
Prima itemizada ya incluida en total_price (0 sin póliza).
- insured_valuenumber | null
Valor declarado asegurado (null sin póliza).
- deliveryobject
Mostrar 4 camposOcultar campos
- estimated_daysstring | number | null
- estimated_datestring | null
- min_daysinteger | null
- max_daysinteger | null
- insurance_includedboolean
- insurance_not_supportedboolean
La paquetería no acepta seguro para esta tarifa.
- guaranteed_window"2_days" | "ground" | null
Tiempo de entrega esperado según la cobertura oficial de Estafeta en el destino, cuando la tarifa vende un servicio más rápido de lo que Estafeta garantiza ahí:
2_days(normalmente 2 días) oground(terrestre, normalmenteguaranteed_window_daysdías). null si no aplica o no hay datos de cobertura. La tarifa sigue siendo válida y reservable; preséntalo al cliente final como el tiempo de entrega esperado, no como una advertencia. - guaranteed_window_daysstring | null
Días de esa garantía:
2para2_days,3-5paraground; null cuando guaranteed_window es null. - zona_extendidaboolean
Destino en zona extendida: puede implicar cargos de reexpedición. Cuando la paquetería los cotiza por adelantado ya vienen dentro de total_price; si no, pueden facturarse después como cargo de paquetería.
- booking_lockedboolean
La tarifa es VISIBLE pero aún no reservable para esta cuenta:
POST /shipmentsresponde403 SERVICE_UNLOCK_REQUIRED. Solo afecta a cuentas de registro propio sin identidad verificada (INE) y con menos de $2,500 MXN en recargas acumuladas. Para esas cuentas solo quedan abiertos iMile Terrestre y Estafeta Terrestre y Estafeta Día Siguiente sin recolección (estas se dejan en una sucursal Estafeta y los cargos extra se pagan ahí); todo lo demás llega bloqueado, incluidos los servicios sin recolección de otros transportistas. Las rutas de desbloqueo vienen enunlock_notice.
›ShipmentCreateRequestRemitente: exactamente UNO de from (inline) o from_id (directorio).
Remitente: exactamente UNO de from (inline) o from_id (directorio). Destinatario: exactamente UNO de to o to_id. Ambos miembros de un par → 422; id no visible → 404 ciego. to_id requiere from_id y el destinatario debe pertenecer a ese remitente (un destinatario de otro remitente responde 404). Un lado referenciado usa la fila guardada tal cual (sin create-or-reuse). Bultos: exactamente UNO de package o packages — los MISMOS que se cotizaron.
- rate_idstringrequerido
El
idde una tarifa de POST /rates cotizada con esta misma llave (y mismo X-PDV-ID, si se usó), con menos de 30 minutos. Trátalo como OPACO: cópialo tal cual; su formato no es parte del contrato (difiere entre el carril de prueba y el live) y no debe interpretarse ni validarse. - fromShipmentParty
Mostrar 14 camposOcultar campos
- namestringrequerido
- apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social (solo remitente; ignorado en destinatario).
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystringrequerido
- statestringrequerido
- postal_codestringrequerido
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - rfcstring
Solo remitente; usado por algunas paqueterías.
- from_idstring
Id de un remitente del directorio. Mutuamente excluyente con
from. Mostrar 14 camposOcultar campos
- namestringrequerido
- apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social (solo remitente; ignorado en destinatario).
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystringrequerido
- statestringrequerido
- postal_codestringrequerido
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - rfcstring
Solo remitente; usado por algunas paqueterías.
- to_idstring
Id de un destinatario del directorio (debe pertenecer al
from_idenviado). Mutuamente excluyente conto; requierefrom_id. - packageobject
La MISMA caja que se cotizó, con sus tres medidas: sin largo, ancho y alto → 422
PACKAGE_DIMENSIONS_REQUIRED(no se cobra nada).Mostrar 5 camposOcultar campos
- weight_kgnumberrequerido
- length_cmnumberrequerido
- width_cmnumberrequerido
- height_cmnumberrequerido
- type"paquete"
Opcional. Solo
paqueteen v1 (sin distinción de mayúsculas; vacío/null = paquete). Cualquier otro valor — p. ej.sobre— responde 422 tanto en /rates como en /shipments (envelope aún no soportado).
- packagesPackagesList
Mostrar 4 camposOcultar campos
- weight_kgnumberrequerido
Peso real del bulto (kg).
- length_cmnumber
Largo exterior del bulto cerrado (cm). Obligatorio para
POST /shipments; enPOST /rates, sin las tres medidas el precio es estimado (size_estimated). - width_cmnumber
Ancho exterior del bulto cerrado (cm).
- height_cmnumber
Alto exterior del bulto cerrado (cm).
- insuranceobject
La cobertura que YA se cotizó (
insured_valuebuys coverage; when the platform policy is unavailable the carrier's own coverage is used and the price shown already includes it). Omitirinsuranceacepta la cobertura cotizada; mandar uninsured_valueDISTINTO → 422INSURANCE_MISMATCH(el total firmado nunca se recalcula al crear). Una tarifa cuya paquetería no ofrece cobertura (insurance_not_supported: true) → 422INSURANCE_NOT_AVAILABLE.Mostrar 1 campoOcultar campos
- insured_valuenumberrequerido
Valor asegurado (MXN) — el mismo de la cotización.
- declared_valuenumber
Valor de la mercancía DECLARADO A LA PAQUETERÍA (MXN). NO es seguro, pero la paquetería puede cobrarlo: debe ser EXACTAMENTE el valor con el que se cotizó (el asegurado) — un valor que la cotización no llevaba responde 422
DECLARED_VALUE_MISMATCH, salvo en servicios que lo tratan como informativo (la recuperación deDECLARED_VALUE_REQUIRED). Si se omite se declara el valor cotizado; si la cotización no llevaba ninguno, no se inventa. *declared_value must equal the value the rate was quoted with; a carrier may price it.* - contenidostring
Descripción del contenido (aparece en la guía cuando la paquetería lo soporta).
- referencestring
RESERVADO — aceptado pero no persistido en v1.
- metadataobject
RESERVADO en el carril live (aceptado, no persistido). En el carril SANDBOX,
metadata.test_scenariofuerza una falla para rehearsal (verx-test-mode).Mostrar 1 campoOcultar campos
- test_scenario"insufficient_funds" | "rate_expired" | "vendor_error"
Solo llaves
ek_test_, y solo sobre ofertas SIMULADAS del catálogo: sobre una tarifa del carril FedEx de prueba (que va al entorno real de FedEx) responde422 TEST_SCENARIO_NOT_AVAILABLE.insufficient_funds→ 402 CREDIT_ERROR (details {required, available, shortfall}) sin importar el saldo;rate_expired→ 409 RATE_EXPIRED;vendor_error→ 422 VENDOR_ERROR (retryable=false).
›ShipmentPartyLos datos se registran en el directorio del usuario de la llave con dedupe exacto (normalizado por espacios/mayúsculas), así que repetir el mismo remitente/destinatario no crea filas nuevas.
Los datos se registran en el directorio del usuario de la llave con dedupe exacto (normalizado por espacios/mayúsculas), así que repetir el mismo remitente/destinatario no crea filas nuevas. Los campos exceden-longitud se recortan al límite de columna.
- namestringrequerido
- apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social (solo remitente; ignorado en destinatario).
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystringrequerido
- statestringrequerido
- postal_codestringrequerido
- country"MX"
Opcional. Solo
MX(sin distinción de mayúsculas/minúsculas; vacío = MX) — cualquier otro valor responde 422 (v1 es doméstico MX). - rfcstring
Solo remitente; usado por algunas paqueterías.
›ShipmentCreateResponse
- shipmentobjectrequerido
Mostrar 10 camposOcultar campos
- idstringrequerido
- guiastring | null
Null cuando la paquetería publica la guía con retraso (
tracking_pending: true); consúltala después vía GET /shipments/{id}. - carrierstring | null
Null posible en respuestas
duplicate: true. - servicestring | null
- statusstringrequerido
Estatus canónico (created | collected | in_transit | out_for_delivery | ready_for_pickup | delivered | exception | returned | cancelled | unknown).
- totalnumber | null
Monto cobrado (MXN).
- currency"MXN"requerido
- label_urlstring | nullrequerido
Ruta de NUESTRO endpoint autenticado de etiquetas (/api/v1/labels/{id}) — nunca la URL cruda del proveedor.
- tracking_pendingbooleanrequerido
- created_atstring | null
ISO-8601 UTC (Z).
- duplicateboolean
Presente (true) cuando la paquetería devolvió una guía ya registrada y la plataforma deduplicó sin doble cargo.
- recoveredboolean
Presente (true) cuando la etiqueta se recuperó tras un error transitorio del proveedor.
›Shipment
- idstringrequerido
- guiastring | nullrequerido
- carrierstring | nullrequerido
Nombre canónico de la paquetería física (SSOT de la plataforma).
- servicestring | nullrequerido
- status"created" | "collected" | "in_transit" | "out_for_delivery" | "delivered" | "exception" | "returned" | "cancelled" | "unknown"requerido
- status_labelstringrequerido
Etiqueta en español del estatus canónico.
- totalnumber | nullrequerido
- currency"MXN"requerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
- tracking_pendingbooleanrequerido
- label_urlstringrequerido
- packagesobject[]requerido
Lo que se registró al crear el envío, en el mismo vocabulario que usa
POST /shipments(weight_kg,length_cm, …). Para envíos multi-paquete es la lista completa por bulto, no el agregado. Lista vacía si el envío no tiene medidas registradas.Envíos anteriores al 2026-07-28: las dimensiones no se persistían (solo se usaban para calcular el peso volumétrico y se descartaban), así que
length_cm/width_cm/height_cmllegan ennullen todo envío creado antes de esa fecha.weight_kgsí está disponible en todo el histórico. Unnullsignifica "no registrado", nunca "cero": las dimensiones no declaradas tampoco se rellenan con un valor por defecto.Mostrar 4 camposOcultar campos
- weight_kgnumber | nullrequerido
- length_cmnumber | nullrequerido
- width_cmnumber | nullrequerido
- height_cmnumber | nullrequerido
- billable_weight_kgnumber | nullrequerido
Peso facturable con el que se cotizó —
max(peso real, peso volumétrico). Es el número contra el que hay que comparar cuando la paquetería aplica un sobrepeso; el peso real por sí solo no explica el cargo. El repeso de la paquetería NO vive en esta API (está en su reporte de facturación). - declared_valuenumber | nullrequerido
Valor declarado (MXN).
- contentstring | nullrequerido
Descripción del contenido capturada al crear el envío.
- senderobjectrequerido
Remitente, con la misma forma mínima que
recipient: solo nombre + ciudad/estado/CP — nunca la dirección completa ni teléfonos.Mostrar 4 camposOcultar campos
- namestring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- recipientobjectrequerido
Solo nombre + ciudad/estado/CP — nunca la dirección completa ni teléfonos.
Mostrar 4 camposOcultar campos
- namestring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
›Pagination
- pageintegerrequerido
- limitintegerrequerido
- totalintegerrequerido
›Tracking
- guiastringrequerido
- carrierstring | nullrequerido
- statusstringrequerido
- status_labelstringrequerido
- originobjectrequerido
Mostrar 2 camposOcultar campos
- citystring | null
- statestring | null
- destinationobjectrequerido
Mostrar 2 camposOcultar campos
- citystring | null
- statestring | null
- created_atstring | nullrequerido
- eventsobject[]requerido
Mostrar 3 camposOcultar campos
- timestampstring | null
- descriptionstring | null
Nombres de receptor (POD) depurados.
- locationstring | null
›CancellationCreated
- idintegerrequerido
- display_idstringrequerido
Live: formato
CR%08d. Sandbox:SBX-CR-<id>. - status"pending" | "cancelled"requerido
Live devuelve
pending(la solicitud entra a la cola de staff). Sandbox devuelvecancelled(cancelación síncrona y terminal — verx-test-mode). - refund_status"not_applicable" | "refunded"requerido
Live:
not_applicable(reembolso lo resuelve el staff). Sandbox:refunded(reembolso inmediato). - refund_amount_expectednumber | null
- cancellation_deadlinestring | null
ISO-8601 UTC (Z). Siempre null en sandbox.
›Pickup
- idintegerrequerido
Id numérico de la recolección.
- display_idstringrequerido
Live: formato
PR%08d. Sandbox:SBX-PU-<id>. - shipment_idstring | nullrequerido
- status"pending" | "scheduled" | "awaiting_confirmation" | "failed" | "cancelled"requerido
Una solicitud SIEMPRE nace en
awaiting_confirmation(se está gestionando; NO la presentes como confirmada). Pasa ascheduledcuando el equipo la confirma con la paquetería — ahí lleganconfirmed_window_*yconfirmation_number, y se envía el correo al solicitante.failed= no se pudo agendar (entregar en sucursal).cancelled= la canceló quien la pidió.pendingno se usa. El sandbox devuelveawaiting_confirmation, igual que producción. - carrierstring | null
Paquetería física que hará la recolección.
- requested_window_startstring | null
ISO-8601 UTC (Z). Inicio de la ventana solicitada.
- requested_window_endstring | null
ISO-8601 UTC (Z). Fin de la ventana solicitada.
- confirmed_window_startstring | null
ISO-8601 UTC (Z). Ventana confirmada por la paquetería, cuando la devuelve.
- confirmed_window_endstring | null
ISO-8601 UTC (Z).
- confirmation_numberstring | null
Folio/referencia de recolección de la paquetería.
- created_atstring | null
- updated_atstring | null
›Cancellation
- idintegerrequerido
- display_idstringrequerido
- shipment_idstring | nullrequerido
- status"pending" | "approved" | "in_progress" | "cancelled" | "rejected_by_carrier" | "not_cancellable" | "reported_to_carrier" | "rejected" | "failed" | "expired"requerido
- refund_status"not_applicable" | "refund_in_progress" | "refunded" | "refund_denied"requerido
- reason_codestring | nullrequerido
- reason_textstring | nullrequerido
- refund_amount_expectednumber | nullrequerido
- refund_amount_actualnumber | nullrequerido
- cancellation_deadlinestring | nullrequerido
- created_atstring | nullrequerido
- updated_atstring | nullrequerido
- resolved_atstring | nullrequerido
›Balance
- balancenumberrequerido
available + held (total del principal en el libro mayor).
- heldnumberrequerido
Autorizaciones pendientes (holds).
- availablenumberrequerido
Lo gastable por un envío nuevo.
- currency"MXN"requerido
- scopeobjectrequerido
Mostrar 2 camposOcultar campos
- type"user" | "pdv"requerido
- idstringrequerido
›SenderCreateRequestEl mismo vocabulario de campos que el from de POST /shipments, con city/state opcionales aquí (se autocompletan del catálogo SEPOMEX según el CP — la misma validación de exactitud que POST /contacts/import).
El mismo vocabulario de campos que el from de POST /shipments, con city/state opcionales aquí (se autocompletan del catálogo SEPOMEX según el CP — la misma validación de exactitud que POST /contacts/import). name es el nombre completo; apellido_paterno/apellido_materno están deprecados (se aceptan y se integran a name). Campos desconocidos → 422. Los campos exceden-longitud se recortan al límite de columna.
- namestringrequerido
Nombre de pila (
nombre). - apellido_paternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring
Razón social.
- rfcstring
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
Número exterior.
- coloniastringrequerido
- citystring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando la ciudad esperada).
- statestring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando el estado esperado).
- postal_codestringrequerido
5 dígitos; debe EXISTIR en el catálogo postal (SEPOMEX).
- country"MX"
Opcional. Solo
MX— cualquier otro valor responde 422.
›SenderUpdateRequestActualización PARCIAL: solo los campos presentes cambian.
Actualización PARCIAL: solo los campos presentes cambian. Se debe enviar al menos uno. Un campo requerido (name/phone/street/colonia/city/state/postal_code) puede cambiarse pero NO vaciarse. Campos opcionales aceptan null para limpiarse. Campos desconocidos → 422. Si la edición dejaría la dirección idéntica a OTRA visible → 409 DUPLICATE_ADDRESS.
- namestring
- apellido_paternostring | null
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - apellido_maternostring | null
DEPRECATED (2026-09-21): el nombre completo va en
name. Aún se acepta y se integra anameal guardar. - companystring | null
- rfcstring | null
- phonestring
- emailstring | null
- streetstring
- numberstring | null
- coloniastring
- citystring
- statestring
- postal_codestring
- country"MX"
›RecipientCreateRequestEl mismo vocabulario de campos que el to de POST /shipments + sender_id, con city/state opcionales aquí (se autocompletan del catálogo SEPOMEX según el CP — la misma validación de exactitud que POST /contacts/import).
El mismo vocabulario de campos que el to de POST /shipments + sender_id, con city/state opcionales aquí (se autocompletan del catálogo SEPOMEX según el CP — la misma validación de exactitud que POST /contacts/import). alias/referencia/delivery_instructions solo se aplican al CREAR (una reutilización devuelve la fila existente intacta).
- sender_idstringrequerido
Remitente dueño (visible para la llave; si no → 404 ciego).
- aliasstring
Nombre descriptivo ("Casa", "Oficina").
- namestringrequerido
- phonestringrequerido
- emailstring
- streetstringrequerido
- numberstring
- coloniastringrequerido
- citystring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando la ciudad esperada).
- statestring
Opcional: si se omite se autocompleta del catálogo SEPOMEX según el CP; si viene, debe coincidir con el CP (si no → 422 nombrando el estado esperado).
- postal_codestringrequerido
5 dígitos; debe EXISTIR en el catálogo postal (SEPOMEX).
- country"MX"
- referenciastring
Referencias del domicilio.
- delivery_instructionsstring
›RecipientUpdateRequestActualización PARCIAL.
Actualización PARCIAL. sender_id NO puede cambiarse (los destinatarios no se re-asignan de remitente) → 422 si se envía. Misma disciplina que SenderUpdateRequest: al menos un campo, no vaciar requeridos, null limpia opcionales, unificar con OTRO destinatario del MISMO remitente → 409 DUPLICATE_ADDRESS.
- aliasstring | null
- namestring
- phonestring
- emailstring | null
- streetstring
- numberstring | null
- coloniastring
- citystring
- statestring
- postal_codestring
- country"MX"
- referenciastring | null
- delivery_instructionsstring | null
›SenderRecurso remitente.
Recurso remitente. Su id sirve como from_id en POST /rates y POST /shipments.
- idstringrequerido
- namestring | nullrequerido
- apellido_paternonullrequerido
Siempre
null: el nombre completo está enname. - apellido_maternonullrequerido
Siempre
null: el nombre completo está enname. - companystring | nullrequerido
- rfcstring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
›RecipientRecurso destinatario.
Recurso destinatario. Su id sirve como to_id en POST /rates y POST /shipments (requiere from_id del remitente dueño).
- idstringrequerido
- sender_idstringrequerido
- aliasstring | nullrequerido
- namestring | nullrequerido
- phonestring | nullrequerido
- emailstring | nullrequerido
- streetstring | nullrequerido
- numberstring | nullrequerido
- coloniastring | nullrequerido
- citystring | nullrequerido
- statestring | nullrequerido
- postal_codestring | nullrequerido
- referenciastring | nullrequerido
- delivery_instructionsstring | nullrequerido
- created_atstring | nullrequerido
ISO-8601 UTC (Z).
›Colonia
- namestringrequerido
Nombre del asentamiento (
d_asentade SEPOMEX). Úsalo comocolonia/neighborhood. - settlement_typestring | nullrequerido
Tipo de asentamiento SEPOMEX:
Colonia,Fraccionamiento,Pueblo,Barrio, etc.Gran usuario= CP propio de una institución (vergran_usuario).
›PostalCodeResultado de GET /postal-codes/{postal_code}.
Resultado de GET /postal-codes/{postal_code}. Datos de referencia SEPOMEX (públicos, solo lectura).
- postal_codestringrequerido
El CP consultado (5 dígitos).
- statestring | nullrequerido
Estado (
d_estado). Úsalo comostate. - state_codestring | nullrequerido
Código de estado normalizado (mismo esquema que
state_codedel directorio; p. ej.DF,JAL,NL). - municipalitystring | nullrequerido
Municipio/alcaldía (
d_mnpio). Suele ser el mejor valor paracity. - citystring | nullrequerido
Ciudad (
d_ciudad) cuando SEPOMEX la distingue del municipio;nullen la mayoría de los CP. Asentamientos únicos del CP, ordenados por nombre. Un CP mapea a un estado/municipio pero a muchas colonias.
Mostrar 2 camposOcultar campos
- namestringrequerido
Nombre del asentamiento (
d_asentade SEPOMEX). Úsalo comocolonia/neighborhood. - settlement_typestring | nullrequerido
Tipo de asentamiento SEPOMEX:
Colonia,Fraccionamiento,Pueblo,Barrio, etc.Gran usuario= CP propio de una institución (vergran_usuario).
- gran_usuarioobject | nullrequerido
Presente cuando el CP es de «gran usuario»: un código que Correos de México asignó a una sola institución (p. ej. 80129, Palacio de Gobierno del Estado de Sinaloa) y que ya no publica en su catálogo, aunque la institución lo sigue usando. Es válido, pero varias paqueterías no lo atienden y cotizarlo trae menos tarifas; cotiza y envía con
parent_postal_codeyparent_settlementcomo colonia.nullen un CP ordinario.Mostrar 3 camposOcultar campos
- institutionstringrequerido
La institución dueña del CP.
- parent_postal_codestring | nullrequerido
CP oficial SEPOMEX de la colonia donde está la institución (80129 → 80000). Úsalo en lugar del CP especial.
nullsi no se conoce. - parent_settlementstring | nullrequerido
La colonia de
parent_postal_codedonde está la institución (p. ej.Centro Sinaloa).nullsi no se conoce.
›LabelLinkRespuesta de GET /labels/{envio_id}?format=json: enlace a la etiqueta en vez de los bytes.
Respuesta de GET /labels/{envio_id}?format=json: enlace a la etiqueta en vez de los bytes.
- shipment_idstringrequerido
- guiastring
Solo en sandbox (la vía en vivo omite el campo).
- label_urlstring | nullrequerido
En vivo: enlace firmado a la copia de Enviadores (
https://api.enviadores.com.mx/api/v1/label-files/{envio_id}/{exp}/{firma}.pdf). Se abre sin llave, vence enexpires_at(30 días; pide uno nuevo con esta misma llamada) y nunca es la URL de la paquetería. En sandbox: enlace a la etiqueta de prueba.nullcuando la etiqueta aún no está disponible;notelo explica. - expires_atstring
Solo en vivo: cuándo deja de funcionar
label_url(UTC). - format"pdf"requerido
- sandboxbooleanrequerido
truecon llavesek_test_: etiqueta de prueba sin validez. - notestring
Presente solo cuando hay algo que aclarar (etiqueta sandbox, o sin URL directa).
›WebhookEventEventos suscribibles.
Eventos suscribibles. shipment.created se emite al comprar la guía; shipment.collected…shipment.exception cuando el rastreo observa una transición REAL del estatus canónico — un estatus no terminal puede repetirse legítimamente (exception → in_transit → exception produce dos shipment.exception), pero la misma transición observada dos veces se entrega una sola vez; shipment.delivered, shipment.returned y shipment.cancelled son terminales y se entregan a lo sumo una vez por envío (cancelled también cuando la cancelación queda en firme); pickup.resolved cuando nuestro equipo cierra una solicitud de recolección (scheduled o failed). El evento ping solo existe para POST /webhooks/{id}/test y no es suscribible.
›WebhookCreateRequest
- urlstringrequerido
Endpoint
https://público en el puerto 443 u 8443 (cualquier otro puerto se rechaza). Se rechazan http, credenciales embebidas, fragmentos,localhost/.local/.internal, literales IP privadas, hosts que resuelvan a rangos privados y hosts que apunten al propio API de Enviadores. Eventos a recibir (duplicados se descartan).
›WebhookRecurso webhook.
Recurso webhook. NUNCA incluye el secreto: solo existe en la respuesta de POST /webhooks.
- idstringrequerido
wh_+ 24 hex. - urlstringrequerido
- mode"live" | "test"requerido
Lane de la llave que lo registró.
liverecibe eventos reales;testsoloshipment.created/shipment.cancelleddel sandbox y el ping. - status"active" | "disabled"requerido
disabled= desactivado automáticamente: tras 20 fallos consecutivos, o porque la llave API que lo registró fue revocada (disabled_reason: key_revoked— un webhook muere con su llave). Al desactivarse, sus eventos pendientes se descartan. No cuenta para el tope; elimínalo y regístralo de nuevo. - failure_countintegerrequerido
Fallos CONSECUTIVOS de entrega (cualquier respuesta no 2xx o error de transporte). Vuelve a 0 con cada 2xx. Los pings de prueba no lo afectan.
- last_delivery_atstring | null
ISO-8601 UTC (Z). Último intento (éxito o fallo), pings incluidos.
- last_delivery_statusinteger | null
Status HTTP del último intento;
0= fallo de transporte (timeout/TLS/DNS). - created_atstring | null
ISO-8601 UTC (Z).
- disabled_atstring | null
ISO-8601 UTC (Z).
- disabled_reasonstring | null
key_revokedcuando la llave que lo registró fue revocada; texto explicativo cuando fue por fallos consecutivos.
›WebhookCreated
Mostrar 11 camposOcultar campos
- idstringrequerido
wh_+ 24 hex. - urlstringrequerido
- mode"live" | "test"requerido
Lane de la llave que lo registró.
liverecibe eventos reales;testsoloshipment.created/shipment.cancelleddel sandbox y el ping. - status"active" | "disabled"requerido
disabled= desactivado automáticamente: tras 20 fallos consecutivos, o porque la llave API que lo registró fue revocada (disabled_reason: key_revoked— un webhook muere con su llave). Al desactivarse, sus eventos pendientes se descartan. No cuenta para el tope; elimínalo y regístralo de nuevo. - failure_countintegerrequerido
Fallos CONSECUTIVOS de entrega (cualquier respuesta no 2xx o error de transporte). Vuelve a 0 con cada 2xx. Los pings de prueba no lo afectan.
- last_delivery_atstring | null
ISO-8601 UTC (Z). Último intento (éxito o fallo), pings incluidos.
- last_delivery_statusinteger | null
Status HTTP del último intento;
0= fallo de transporte (timeout/TLS/DNS). - created_atstring | null
ISO-8601 UTC (Z).
- disabled_atstring | null
ISO-8601 UTC (Z).
- disabled_reasonstring | null
key_revokedcuando la llave que lo registró fue revocada; texto explicativo cuando fue por fallos consecutivos.
- secretstringrequerido
Secreto de firma — se muestra SOLO aquí. Guárdalo: no se almacena y no se puede recuperar; para obtener otro hay que eliminar el webhook y registrarlo de nuevo.
›WebhookTestResult
- deliveredbooleanrequerido
truesi el endpoint respondió 2xx dentro de 10 s. - response_statusinteger | nullrequerido
Status HTTP recibido;
nullsi no hubo respuesta (timeout, TLS, DNS). - error"timeout" | "connection failed" | "non-2xx status" | "endpoint rejected" | nullrequerido
Clase del fallo, deliberadamente gruesa (el detalle de red no se expone para que el ping no sirva como sonda de puertos):
timeout,connection failed(rechazo/reset/TLS/DNS),non-2xx status(verresponse_status),endpoint rejected(la URL ya no pasa la política);nullsi se entregó. - event_idstringrequerido
evt_+ 24 hex — eliddel cuerpo enviado y el headerX-Enviadores-Delivery.
›WebhookPayloadCuerpo de cada POST a tu endpoint.
Cuerpo de cada POST a tu endpoint. Headers: Content-Type: application/json, User-Agent: enviadores-webhooks/1.0, X-Enviadores-Signature: t=<unix>,v1=<hex> (v1 = HMAC-SHA256(secret, "<t>.<cuerpo crudo>")), X-Enviadores-Event, X-Enviadores-Delivery (= id). No se siguen redirecciones; responde 2xx en <10 s.
- idstringrequerido
evt_+ 24 hex. Estable entre reintentos de la misma entrega — deduplica con él. - eventstringrequerido
Nombre del evento (
WebhookEvent, oping). - mode"live" | "test"requerido
- created_atstringrequerido
ISO-8601 UTC (Z). Momento en que se emitió el evento.
- dataobjectrequerido
{shipment: Shipment}parashipment.*(misma forma queGET /shipments/{id}),{pickup: Pickup}parapickup.resolved(misma forma queGET /pickups/{id}),{webhook_id}paraping.
›Me
- accountobjectrequerido
Mostrar 5 camposOcultar campos
- idstringrequerido
Id del usuario dueño de la llave.
- namestring | nullrequerido
Nombre visible de la cuenta (razón social, nombre de la persona, o el usuario). Nunca correo ni teléfono.
- mode"test" | "live"requerido
test= sandbox (nada es real);live= producción. - account_type"customer" | "staff"requerido
Dos valores públicos: cuenta de cliente, o cuenta de nuestro mostrador. El rol interno no se expone.
- tier_label"T0" | "T1" | "T2" | "T3" | "T4" | nullrequerido
Escalón de verificación de una cuenta de registro propio.
nullsi la cuenta no está en la escalera (creada por nuestro equipo) o si la consulta no está disponible.
- keyobjectrequerido
Mostrar 7 camposOcultar campos
- idstringrequerido
Id público de la llave (no es el secreto).
- labelstring | nullrequerido
Etiqueta que le puso su dueño.
- mode"test" | "live"requerido
- scopesstring[]requerido
Los scopes EFECTIVOS de esta petición — lo que el gate acaba de aplicar.
- scopes_mode"default" | "explicit"requerido
default: la llave se creó con los permisos por omisión y se actualiza sola cuando aparecen nuevos.explicit: alguien la restringió al crearla; queda congelada así. - spend_cap_daily_mxnnumber | nullrequerido
Tope de gasto en ventana móvil de 24 h, o
nullsi no tiene. - rate_limit_tier"standard" | "elevated"requerido
- balanceobjectrequerido
Mostrar 2 camposOcultar campos
- availablenumberrequerido
Lo gastable ahora mismo — el mismo número que
GET /balance. Para el desglose (held, total,scope) usa ese endpoint. - currency"MXN"requerido
- capabilitiesobjectrequerido
Mostrar 4 camposOcultar campos
- pickupsbooleanrequerido
Si esta conexión puede solicitar recolecciones. Siempre
trueen sandbox. - internationalbooleanrequerido
Si la API acepta destinos fuera de México. Hoy
false(v1 es doméstico MX). - multi_packagebooleanrequerido
Si una solicitud puede llevar más de un paquete. Se deriva de la misma validación que aplica
POST /shipments. - webhooksbooleanrequerido
Si existen webhooks salientes en esta superficie. Hoy
false: consulta conGET /shipmentsoGET /tracking/{guia}.
›Transaction
- idstringrequerido
Id del movimiento (estable; el que conviene citar a soporte).
- type"charge" | "refund" | "topup" | "adjustment"requerido
Vocabulario público: cargo, devolución, recarga en línea, o cualquier otro abono.
- amountnumberrequerido
Importe en MXN CON SIGNO: negativo si salió saldo, positivo si entró.
- balance_afternumber | nullrequerido
Saldo del principal después de este movimiento.
- shipment_idstring | nullrequerido
Envío relacionado, cuando el movimiento nació de uno.
- descriptionstring | nullrequerido
Texto del movimiento. Las correcciones de cargo (cuando reasignamos un cobro al fondo correcto) se reportan siempre como
Ajuste: el detalle interno describe nuestra propia contabilidad y nombra al operador que la hizo, y nada de eso viaja a esta superficie. - created_atstring | nullrequerido
ISO-8601 en UTC (Z).