NAV
go php python shell

Introducción

Bienvenida(o) al API del servicio de Logística de Fruno. Utilizando este API podrá accesar los diferentes "endpoints" permitiéndole a su empresa:

Debe tener en cuenta que para utilizar nuestros servicios su empresa debe establecer la relación comercial con Fruno y solicitar los usuarios que requieran, para esto la persona responsable de la relación comercial (en su empresa) debe comunicarse al correo: info@fruno.com.

Una vez que cuente con al menos un usuario para desarrollar su integración:

Ambientes/Hosts

Existen dos ambientes por lo que debe utilizar el host según corresponda:

Ambiente Host
Producción api.logistica.fruno.com
Pruebas pruebas.api.logistica.fruno.com

Sobre las fechas

Todas las fechas están en formato RFC 3339.

Ejemplo: 2021-01-14T09:06:03.601722-06:00

Sobre precio

Recordar que: el precio de los paquetes puede cambiar, por ejemplo si el peso neto enviado no corresponde con el peso real, este se corrige al recibir el paquete en nuestro centro de distribución lo que puede afectar el costo. Es importante enviar siempre el peso real de cada paquete.

La moneda indicada para los precios sigue el ISO 4217 y va a depender del contrato con su empresa (por ejemplo: USD o CRC).

Sobre las respuestas

Con excepción de la descarga de la guía (application/pdf) e imágenes (image/png o image/jpeg), todas las respuestas de las funciones se dan en JSON.

Sobre POST y PUT

Todo punto del API que requiere un cuerpo en un POST o PUT debe ser en JSON (como la creación de una dirección o paquete).

Content-Type: application/json; charset=utf-8

Sobre paginación

Algunos endpoints como la lista de direcciones o la lista de paquetes pueden retornar un subconjunto del total de ítems (dan una respuesta "paginada"), dependiendo de la cantidad de ítems el servicio puede incluir la lista completa (sin más "páginas").

Estas respuestas pueden incluir un valor llamado página. Si se quiere obtener el siguiente grupo ("página") de ítems se vuelve a invocar la función correspondiente pero agregando un campo (query string) pág_despues_de con el valor retornado por la ejecución anterior en el campo página.

Es decir, para avanzar en la lista de ítems el mismo API le da el valor para pág_despues_de que debe usar en la siguiente invocación para obtener la siguiente "página".

Ejemplo: GET /v1/direcciones?pág_despues_de=AAAAAAAA

¿Cómo obtener la primera "página"?

Simplemente omitir el campo pág_despues_de en el query string.

¿Cómo sé cuando ya no hay más ítems?

Sobre los ejemplos

Para cada función del API se da un ejemplo en 3 lenguajes y 1 shell:

Todos los ejemplos asumen que usted ha definido 3 variables de ambiente:

Variable Descripción
CUENTA_API Valor asignado por el sistema y se obtiene en la aplicación Web
LLAVE_API Valor asignado por el sistema y se obtiene en la aplicación Web
HOST_API Host correspondiente al ambiente (pruebas o producción)

El objetivo es ayudar a conocer el API rápidamente y con claridad, no mostrar la mejor implementación de clientes.

Sobre errores

Ejemplo de error en el encabezado X-Fr-Hora

{
 "type": "https://docs.api.logistica.fruno.com/?#encabezado-requerido-con-la-hora",
 "title": "X-Fr-Hora caducó",
}

Ejemplo de error al crear una dirección


{
 "type":"https://docs.api.logistica.fruno.com/?#crear",
 "title":"Errores de validación",
 "datos-inválidos":[
                    {"nombre":"nombre",
                     "error":"El nombre de la dirección debe ser mayor a 4 caracteres."},
                    {"nombre":"detalle",
                     "error":"El detalle de una dirección no puede estar vacío debe contener todas las indicaciones necesarias para encontrar el lugar."}
                   ],
}

Además del código de estado de la llamada HTTP (400, 404, 500), el sistema retorna en el cuerpo una estructura JSON según el RFC 7807 con información sobre el error. Como parte de esta estructura y para ciertos errores, se incluye un enlace a la sección en esta documentación correspondiente al problema.

HTTP 500

Ejemplo del cuerpo de un error interno (código de estado 500)

{
 "type": "https://docs.api.logistica.fruno.com/?#http-500",
 "title": "error interno",
 "incidente":"11e5a3131f124f900c1328ce142e3900"
}

En caso de que ocurra un error interno, la estructura JSON de error (en el cuerpo) incluirá un número de incidente por si fuera necesario darle seguimiento. Nuestro equipo de soporte recibe aviso inmediato del incidente.

Sobre "velocidad" de uso del API

Como una protección contra el abuso, el API aplica una restricción de un máximo posible de invocaciones por intervalo o "ventana" de tiempo.

Cada respuesta del API incluye 2 encabezados (headers) importantes que debe tener en cuenta porque estos contienen información sobre los límites (cuota) de la cantidad de solicitudes (requests) que se pueden hacer en cierto periodo de tiempo, a este periodo de tiempo le llamamos "ventana de tiempo". Estos encabezados son:

Ejemplo de respuesta con los encabezados:

< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< x-ratelimit-remaining: 30
< x-ratelimit-policy: 30;w=60

¿Cómo saber cuántas solicitudes puedo hacer por "ventana de tiempo" y cuánto dura esa "ventana"?

El encabezado llamado X-RateLimit-Policy le indica esa información con los datos separados por el caracter ";" (punto y coma) .

Tomando el ejemplo anterior vemos el valor 30;w=60, separando el contenido por ";" nos dice lo siguiente:

valor significado
30 cantidad máxima de solicitudes por ventana de tiempo
w=60 duración de la ventana de tiempo en segundos

Es decir, para los valores anteriores (de ejemplo) el API nos dice que podemos hacer un máximo de 30 solicitudes en 60 segundos (la "ventana de tiempo").

La política implementada se basa en el algoritmo llamado "Sliding Window", esto implica que en toda invocación al API se calcula la cantidad de llamadas que se han hecho en la ventana de tiempo (últimos X segundos) y en base a ese cálculo indica cuántas llamadas quedan disponibles en el encabezado X-Ratelimit-Remaining.

Estos encabezados están tanto en producción como en el ambiente de pruebas, aunque no necesariamente van a mostrar los mismo valores.

Es importante tomar en cuenta que si excede la cuota por ventana de tiempo el API estará retornando un código HTTP 429 (Too many requests) hasta que se vaya restaurando la cuota de nuevo con el paso del tiempo.

Una forma (no la única) de usar la información anterior es:

¿X-RateLimit-Policy nunca cambia?

Claro que si puede cambiar, igual que podría cambiar X-Ratelimit-Limit. Por ejemplo en un futuro podríamos aumentar la cuota máxima o durante circunstancias particulares reducir temporalmente la cuota máxima y/o ventana de tiempo.

Ese es precisamente el objetivo del encabezado X-RateLimit-Policy, permitirle al cliente del API automatizar la distribución de sus solicitudes en el tiempo.

Autenticación

Para autenticarse con el sistema, debe contar con el ID de su cuenta y la llave secreta asociada exclusivamente a su usuario.

Esta información la obtiene ingresando con su usuario y contraseña de desarrollador para el API en nuestro sistema de Logística.

La autenticación se realiza mediante autenticación básica de HTTP (RFC 7617).

Cálculo para el valor de la contraseña

Para generar el valor de la contraseña

func BasicAuthPwd(secreto, metodo, path, hora, cuerpo string) string {
    mac := hmac.New(sha256.New, []byte(secreto))
    mac.Write([]byte(metodo))
    mac.Write([]byte(path))
    mac.Write([]byte(hora))
    mac.Write([]byte(cuerpo))

    hash := mac.Sum(nil)

    return base64.StdEncoding.EncodeToString(hash)
}


func LlamarAPI(urlBase, cuenta, pwd, metodo, path, hora, cuerpo string) []byte {
    body := bytes.NewReader([]byte(cuerpo))
    req, err := http.NewRequest(metodo, urlBase+path, body)
    if err != nil {
        panic(err)
    }
    req.SetBasicAuth(cuenta, pwd)
    req.Header.Add("X-Fr-Hora", hora)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }

    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    return respBody
}
function basicAuthPwd($secreto, $metodo, $path, $hora, $cuerpo) {
    $s = hash_hmac('sha256', $metodo.$path.$hora.$cuerpo, $secreto, true);
    return base64_encode($s);
}

function llamarAPI($urlBase, $cuenta, $pwd, $metodo, $path, $hora, $cuerpo) {
    $ch = curl_init($urlBase.$path);
    curl_setopt($ch, CURLOPT_USERPWD, $cuenta.":".$pwd);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Fr-Hora:".$hora));
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $metodo);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    if (!empty($cuerpo)) {
     curl_setopt($ch, CURLOPT_POSTFIELDS, $cuerpo);
    }

    $response = curl_exec($ch);

    if(curl_errno($ch)){
        throw new Exception(curl_error($ch));
    }

    $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);
    return array($response, $contentType);
}
from datetime import datetime, timezone       
import hashlib
import base64
import hmac
import requests
import os

def basic_auth_pwd(secreto: str, metodo: str, path: str, hora: str, cuerpo: str) -> bytes:
    pwd = base64.b64encode(hmac.new(bytes(secreto, "utf-8"), bytes(metodo + path + hora + str(cuerpo), "utf-8"), digestmod=hashlib.sha256).digest())
    return pwd

def llamar_api(url_base: str, cuenta: str, pwd: str, metodo: str, path: str, hora: str, cuerpo):
    aut = base64.b64encode(bytes(cuenta+":","ascii")+pwd).decode("ascii")
    if metodo == "GET":
      r = requests.get(url_base + path, auth=(cuenta, pwd), headers={"X-Fr-Hora":hora})
    elif metodo == "POST":
      r = requests.post(url_base + path, auth=(cuenta, pwd), headers={"X-Fr-Hora":hora}, data=cuerpo.encode("utf-8"))
    elif metodo == "PUT":
      r = requests.put(url_base + path, auth=(cuenta, pwd), headers={"X-Fr-Hora":hora}, data=cuerpo.encode("utf-8"))
    elif metodo == "DELETE":
      r = requests.delete(url_base + path, auth=(cuenta, pwd), headers={"X-Fr-Hora":hora})
    return r
echo -n $METODO$API_PATH$HORA$CUERPO | \
 openssl dgst -sha256 -hmac $LLAVE_API -binary | \
 base64

La contraseña es la codificación en Base 64 del HMAC-SHA256 de la unión de:

Encabezado requerido con la hora

Cada request debe incluir el header X-Fr-Hora con la hora en la que se está realizando el request en formato RFC 3339, si este valor es considerado por el servidor como desviado de la hora actual el request será rechazado.

Ejemplo: X-Fr-Hora: 2021-03-16T14:22:20-06:00

Este valor deber ser el mismo utilizado en el cálculo del hash.

Lo anterior implica que no debe reutilizar el valor de este encabezado en múltiples llamadas.

Contratos

Su empresa puede contar con distintos contratos para los envíos, de ser así, al consultar un estimado de costo o crear un paquete deberá indicar el identificador del contrato según corresponda.

La estructura de un contrato es:

Campo Descripción
id Identificador del contrato
nombre Nombre del contrato

Lista

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/contratos"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/contratos";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/contratos"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/contratos" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

GET https://<HOST>/v1/contratos

Esta función le permite obtener la lista (no paginada) de contratos que tiene disponibles.

La lista puede estar vacía, en cuyo caso no será necesario incluir el campo id_contrato en las funciones de cálculo de costo y creación de paquetes.

CONTENIDO RESPUESTA


[{"id":"3e7f8e3c-da5c-493e-b774-b47f9223d30b"
  "nombre":"Especial encomiendas"},
  ...
]

División territorial

La división territorial se da en una jerarquía de 3 entidades: Provincia, Cantón, Distrito.

¿Por qué hay consultas de la división territorial?

Para poder crear un paquete hay que indicar la dirección de recolecta y la de entrega. Las direcciones deben crearse antes y para poder crear una dirección hay que indicar el distrito.

Provincias

GET https://<HOST>/v1/provincias

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/provincias"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/provincias";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/provincias"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/provincias" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA


[
  {"código":"2",
   "nombre":"ALAJUELA"},
  {"código":"3",
   "nombre":"CARTAGO"},
  ...
]

Esta función retorna la lista de provincias. Una provincia está definida como:

Campo Descripción
código Código de la provincia
nombre Nombre de la provincia

Cantones

GET https://<HOST>/v1/provincias/<CODIGO_PROVINCIA>/cantones

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/provincias/1/cantones"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/provincias/1/cantones";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/provincias/1/cantones"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/provincias/1/cantones" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA


[
 {"código":"112",
  "código_provincia":"1",
  "nombre":"ACOSTA"},
 {"código":"110",
  "código_provincia":"1",
  "nombre":"ALAJUELITA"},
 ...
]

Se obtiene la lista de cantones de la provincia indicada. Un cantón está definido como:

Campo Descripción
código Código del cantón
código_provincia Código de la provincia
nombre Nombre del cantón

Parámetros del URL

Parámetro Descripción
CODIGO_PROVINCIA El código de la provincia

Distritos

GET https://<HOST>/v1/provincias/<CODIGO_PROVINCIA>/cantones/<CODIGO_CANTON>/distritos

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/provincias/1/cantones/106/distritos"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/provincias/1/cantones/106/distritos";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/provincias/1/cantones/106/distritos"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/provincias/1/cantones/106/distritos" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA


[
 {"código":"10601",
  "código_cantón":"106",
  "código_provincia":"1",
  "nombre":"Aserrí",
  "con_servicio":true,
  "zonas_sin_servicio":[{"nombre":"1° de mayo"}],
  "es_gam":true},
 {"código":"10605",
  "código_cantón":"106",
  "código_provincia":"1",
  "nombre":"La Legua",
  "con_servicio":true,
  "zonas_sin_servicio":[],
  "es_gam":true},
 ...
]

Se obtiene la lista de distritos con servicio de logística, en donde cada distrito se define como:

Campo Descripción
código Código del distrito
código_cantón Código de cantón
código_provincia Código de provincia
nombre Nombre del distrito
con_servicio Si cuenta o no con servicio, valores posibles true o false
zonas_sin_servicio Opcional. Aunque un distrito cuente con servicio puede tener algunas zonas restringidas, de ser así, este campo contiene una lista de los nombres (strings) de las zonas sin servicio dentro del distrito.
es_gam Si pertenece al Gran Área Metropolitana (valor: true) o si se considera en área rural (valor: false)

Parámetros del URL

Parámetro Descripción
CODIGO_PROVINCIA El código de la provincia del cantón
CODIGO_CANTON El código del cantón

Query

Es posible filtrar en el query string (query component) la lista de distritos para incluir sólo aquellos con servicio (?incluir=con-servicio), sólo sin servicio (?incluir=sin-servicio) o todos los distritos sin importar si tienen servicio o no (?incluir=todos).

Si no se incluye el parámetro en el query se asume el valor por defecto: incluir sólo los distritos CON servicio.

Distritos sin servicio

GET https://<HOST>/v1/distritos-sin-servicio

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/distritos-sin-servicio"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/distritos-sin-servicio";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/distritos-sin-servicio"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/distritos-sin-servicio" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA


[
 {"código":"50102",
  "código_cantón":"501",
  "código_provincia":"5",
  "nombre":"Cañas Dulces",
  "con_servicio":false,
  "es_gam":false},
 {"código":"50206",
  "código_cantón":"502",
  "código_provincia":"5",
  "nombre":"Nosara",
  "con_servicio":false,
  "es_gam":false},
 ...
]

Esta función permite obtener solo la lista de distritos que no cuentan con servicio (sin importar el cantón al que pertenezcan).

Zonas sin servicio

GET https://<HOST>/v1/zonas-sin-servicio

Esta función retorna la lista de todas las zonas (áreas dentro de un distrito) sin servicio.

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/zonas-sin-servicio"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/zonas-sin-servicio";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/zonas-sin-servicio"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/zonas-sin-servicio" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA


[
  {"nombre":"1° de mayo",
   "código_distrito":"10601",
   "código_cantón":"106",
   "código_provincia":"1",
   "nombre_distrito":"Aserrí",
   "nombre_cantón":"ASERRI",
   "nombre_provincia":"SAN JOSÉ"},
   {"nombre":"20 de Noviembre",
    "código_distrito":"60115",
    "código_cantón":"601",
    "código_provincia":"6",
    "nombre_distrito":"El Roble",
    "nombre_cantón":"PUNTARENAS",
    "nombre_provincia":"PUNTARENAS"},
  ...
]

Direcciones

Una dirección tiene la siguiente estructura:

Campo Tipo Descripción
id UUID Identificador único de su dirección
código_distrito texto Distrito de la dirección
fecha_creación Fecha Fecha de creación (formato RFC3339)
creada_por_correo Correo electrónico Correo del usuario que creó la dirección.
nombre Texto Nombre único de la dirección (requerido).
detalle Texto Dirección exacta, con todo el detalle para poder llegar al lugar.
teléfono1 Texto Número telefónico
teléfono2 Texto Número telefónico
correo texto Correo electrónico asociado con la dirección.
referencia texto Cualquier valor que sea relevante para el cliente, por ejemplo para asociar la dirección con un ID dentro de sus sistemas.

Lista

GET https://<HOST>/v1/direcciones

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/direcciones"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/direcciones/";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/direcciones"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/direcciones" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA


{"página":"b14989ec99f34acb46a6b9aeed9fc6b3035",
 "direcciones":[{
                  "id": "12334017-2e6b-1c4f-a6c4-0f9c4c70ef43",
                  "código_distrito": "11501",
                  "fecha_creación": "2021-05-10T03:07:16.734974-06:00",
                  "creado_por_usuario_correo": "fulanita@fruno.com",
                  "nombre": "Tienda ABC",
                  "detalle": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
                  "teléfono1": "123456789",
                  "referencia": "REFINTERNO88859-358"
                },
                ...
              ]
}

Se obtiene la lista de direcciones y dependiendo de la cantidad de ítems puede tener múltiples "páginas".

Crear

POST https://<HOST>/v1/direcciones

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "POST"
path := "/v1/direcciones"
cuerpo := "{\"nombre\": \"Tienda ABC\", \"detalle\": \"de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.\", \"teléfono1\": \"123456789\",\"código_distrito\": \"11604\", \"referencia\":\"REFINTERNO88859-358\"}"
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "POST";
$path = "/v1/direcciones";
$cuerpo = '{"nombre": "Tienda ABC", "detalle": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.", "teléfono1": "123456789","código_distrito": "1604", "referencia":"REFINTERNO88859-358"}';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "POST"
path = "/v1/direcciones"
cuerpo = '{"nombre": "Tienda ABC", "detalle": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.", "teléfono1": "123456789","código_distrito": "11604", "referencia":"REFINTERNO88859-358"}'
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="POST" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/direcciones" \
CUERPO='{"nombre": "Tienda ABC", "detalle": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.", "teléfono1": "123456789","código_distrito": "11605", "referencia":"REFINTERNO88859-358"}' \
pwd=`echo -n  $METODO$API_PATH$HORA$CUERPO | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA --data-ascii "$CUERPO" -w "\n" -X $METODO

RESPUESTA

{
 "id":"a9fbbed2-9196-4995-aaf6-28effe6783ce",
 "código_distrito":"11605",
 "fecha_creación":"2021-04-03T08:32:01.260205-06:00",
 "creado_por_usuario_correo":"fulanito@fruno.com",
 "nombre":"Tienda ABC",
 "detalle":"de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
 "teléfono1":"123456789",
 "referencia":"REFINTERNO88859-358"}

Para crear una dirección, debe enviarse una solicitud POST cuyo cuerpo en JSON define los campos que debe tener la dirección:

Campo Descripción
nombre Cada dirección debe un nombre significativo y único. (Requerido, máximo: 40 caracteres)
detalle Todas las señas e indicaciones necesarias para encontrar el lugar. (Requerido, máximo: 200 caracteres)
código_distrito El código del distrito al que pertenece la dirección (Requerido)
teléfono1 Un teléfono asociado a la dirección. Este sirve en caso de que el mensajero o servicio al cliente deba contactar a la persona que recibe el paquete. (Opcional, máximo: 10 caracteres)
teléfono2 Otro teléfono asociado a la dirección. (Opcional, máximo: 10 caracteres)
correo Un correo electrónico asociado con la dirección.
referencia Este campo es completamente libre para el cliente que registra la dirección. Un uso frecuente es usar el identificador correspondiente a la dirección en los sistemas internos del cliente. (Opcional, máximo: 40 caracteres)

Como respuesta se recibe la estructura completa de la dirección.

Consultar

GET https://<HOST>/v1/direcciones/<ID>

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/direcciones/6eb187e3-9e04-4047-90cd-2d36fd2bed04"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/direcciones/2e094b26-fc0e-44c4-8581-c2d587207a9e";
$cuerpo = '';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/direcciones/6eb187e3-9e04-4047-90cd-2d36fd2bed04"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/direcciones/ID_DIRECCION" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

Permite obtener la información de una dirección existente.

RESPUESTA

{
    "id": "12334017-2e6b-1c4f-a6c4-0f9c4c70ef43",
    "código_distrito": "11501",
    "fecha_creación": "2021-05-10T03:07:16.734974-06:00",
    "creado_por_usuario_correo": "fulanita@fruno.com",
    "nombre": "Tienda ABC",
    "detalle": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
    "teléfono1": "123456789",
    "referencia": "REFINTERNO88859-358"
}

Cambiar

PUT https://<HOST>/v1/direcciones/<ID_DIRECCION>

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "PUT"
path := "/v1/direcciones/6eb187e3-9e04-4047-90cd-2d36fd2bed04"
cuerpo := "{\"nombre\": \"Tienda XYZ\"}"
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "PUT";
$path = "/v1/direcciones/2e094b26-fc0e-44c4-8581-c2d587207a9e";
$cuerpo = '{\"nombre\": \"Tienda XYZ\"}';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "PUT"
path = "/v1/direcciones/6eb187e3-9e04-4047-90cd-2d36fd2bed04"
cuerpo = '{"nombre": "Tienda XYZ"}'
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="PUT" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/direcciones/ID_DIRECCION" \
CUERPO='{"nombre": "Tienda XYZ"}' \
pwd=`echo -n  $METODO$API_PATH$HORA$CUERPO | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA: CÓDIGO HTTP 204

Para cambiar una dirección debe hacer una solicitud PUT indicando el identificador de la dirección que se quiere modificar en el URL y enviando los datos en JSON en el cuerpo de la llamada que debe tener al menos uno de los siguientes campos con el nuevo valor:

Campo Descripción
nombre Cada dirección debe un nombre significativo y único.
detalle Todas las señas e indicaciones necesarias para encontrar el lugar.
teléfono1 Un teléfono asociado a la dirección. Este sirve en caso de que el mensajero o servicio al cliente deba contactar a la persona que recibe el paquete.
teléfono2 Otro teléfono asociado a la dirección.
correo Un correo electrónico asociado con la dirección.
referencia Este campo es completamente libre para el cliente que registra la dirección. Un uso frecuente es usar el identificador correspondiente a la dirección en los sistemas internos del cliente.

Borrar

DELETE https://<HOST>/v1/direcciones/<ID_DIRECCION>

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "DELETE"
path := "/v1/direcciones/6eb187e3-9e04-4047-90cd-2d36fd2bed04"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "DELETE";
$path = "/v1/direcciones/2e094b26-fc0e-44c4-8581-c2d587207a9e";
$cuerpo = '';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "DELETE"
path = "/v1/direcciones/6eb187e3-9e04-4047-90cd-2d36fd2bed04"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="DELETE" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/direcciones/ID_DIRECCION" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" -X $METODO --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

Para borrar una dirección, solo es necesario una solicitud DELETE con el ID de la dirección en el URL.

RESPUESTA

STATUS CODE: 200

Si la dirección fue utilizada en algún paquete, la consulta del paquete seguirá indicando el ID original (ya sea en id_dirección_fuente o id_dirección_destino) (en caso de que usted necesite mantener ese identificador) pero la dirección deja de existir al borrarse.

Paquetes

Cada paquete tiene un identificador único que llamaremos en los ejemplos: NUMERO_GUIA, este valor es asignado por el sistema al crear un paquete.

Estados posibles

En cualquier momento un paquete tiene un estado de un conjunto de 4 posibles, no se pueden agregar nuevos estados y tampoco existen sub-estados.

Tabla de transiciones

Estado actual\Siguiente porrecolectar entránsito cancelado entregado
se crea el paq.
porrecolectar se recolecta se cancela
entránsito se cancela se entrega
cancelado
entregado

Por recolectar

En el momento en que se crea un paquete el estado es porrecolectar.

Cancelado

Siempre y cuando un paquete no haya sido cancelado o entregado podrá ser cancelado, quedando en estado cancelado.

En tránsito

Una vez que un paquete es recolectado en la dirección fuente estará en estado entránsito hasta que haya sido entregado o cancelado.

Entregado

Cuando el paquete se entrega en la dirección destino pasará a estado entregado.

Lista de paquetes

GET https://<HOST>/v1/paquetes

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA


{"página":"051383a099f34acb46a6b9aeed9fc6baa8c",
 "paquetes":[{
        "guía":"GUIA",
        "estado":"porrecolectar",
        "descripción_estado":"Por recolectar",
        "urgente":false,
        "creado_por":"dev@example.com",
        "en_nombre_de_correo":"fulanita@example.com",
        "fecha_creación":"2021-03-08T14:19:11.186638-06:00",
        "fecha_recolecta": null,
        "fecha_cedi": null,
        "fecha_entrega": null,
        "recolecta_estimada_a": null,
        "recolecta_estimada_b": null,
        "entrega_estimada_a": null,
        "entrega_estimada_b": null,   
        "referencia":"ORDEN-88243567",
        "etiquetas": ["ABC123", "REF-987"],
        "descripción":"Pedido tienda virtual.",
        "observaciones":"SKU77356895 color blanco, SKU77356898 color negro",
        "alto":15,
        "ancho":20,
        "longitud":20,
        "peso_volumétrico":2000,
        "peso_neto":389,
        "peso_neto_original":389,
        "id_dirección_fuente":"62a73bf1-b84c-4fc3-afa0-eacc086794ef",
        "id_dirección_destino":"d6892ba6-5c09-4ca7-81f6-4d0626d0a986",
        "nombre_dirección_fuente":"Tienda ABC",
        "detalle_dirección_fuente":"de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
        "código_distrito_fuente":"XYZ",
        "referencia_fuente":"REFINTERNO88859-358",
        "nombre_dirección_destino":"Tienda ABC",
        "detalle_dirección_destino":"de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
        "código_distrito_destino":"XYZ",
        "referencia_destino":"REFINTERNO88859-358",
        "entregar_a":"Joshua Blanco",
        "teléfonos":"00001182, 00003188",
    },
    ...
        ]
}

Para obtener la lista de paquetes sólo debe hacer una solicitud GET y dependiendo de la cantidad de ítems puede tener múltiples páginas.

Filtros

Es posible filtrar la lista de paquetes por fecha de creación y/o estado, indicando el filtro en el query string (query component), como: paquetes?estado=porrecolectar, paquetes?fecha=2021-01-31).

Campo Descripción
fecha Fecha de creación en formato YYYY-MM-DD, ejemplo: 2021-01-31
estado Si se quiere filtrar por estado debe indicarse el parámetro con 1 de estos 4 valores posibles: entránsito porrecolectar entregado cancelado

Consultar estado

GET https://<HOST>/v1/paquetes/<GUIA>/estado

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/NUMERO_GUIA/estado"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/NUMERO_GUIA/estado";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/NUMERO_GUIA/estado"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/NUMERO_GUIA/estado" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA

{"estado":"porrecolectar"}

Cuando sólo se quiere conocer el estado de un paquete, puede utilizarse esta función cuya estructura de retorno contiene únicamente el estado del paquete.

URL para consulta en la Web

GET https://<HOST>/v1/paquetes/<GUIA>/url_consulta_web

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/NUMERO_GUIA/url_consulta_web"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/NUMERO_GUIA/url_consulta_web";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/NUMERO_GUIA/url_consulta_web"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/NUMERO_GUIA/url_consulta_web" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA

{"url_consulta_web":"https://logistica.fruno.com/a/0123456789/consulta"}

Esta función retorna el URL en donde una persona interesada puede consultar el estado de un paquete.

Cancelar

PUT https://<HOST>/v1/paquetes/<GUIA>/cancelar

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "PUT"
path := "/v1/paquetes/NUMERO_GUIA/cancelar"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "PUT";
$path = "/v1/paquetes/NUMERO_GUIA/cancelar";
$cuerpo = '';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "PUT"
path = "/v1/paquetes/NUMERO_GUIA/cancelar"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.status_code)
METODO="PUT" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/NUMERO_GUIA/cancelar" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA

STATUS CODE: 204

Debe tomar en cuenta que un paquete solo puede ser cancelado antes de que sea recolectado.

Si se trata de cancelar un paquete que ya fue recolectado el sistema responderá con un código de estado 403.

Crear

POST https://<HOST>/v1/paquetes/

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "POST"
path := "/v1/paquetes"
cuerpo := "{\"etiquetas\":[\"ABC123\", \"REF-987\"], \"en_nombre_de_correo\":\"fulanita@example.com\", \"urgente\":true, \"id_dirección_fuente\": \"62a73bf1-b84c-4fc3-afa0-eacc086794ef\",\"id_dirección_destino\": \"d6892ba6-5c09-4ca7-81f6-4d0626d0a986\",\"alto\": 15,\"ancho\": 20,\"longitud\": 20,\"peso_neto\": 389,\"descripción\": \"Pedido tienda virtual, SKU77356895 color blanco\",\"referencia\": \"ORDEN-88243567\",\"entregar_a\": \"Joshua Blanco\"}"
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                      os.Getenv("CUENTA_API"), 
                      pwd, 
                      metodo, 
                      path, 
                      hora, 
                      cuerpo)))

}
$urlBase = "https://".getenv("HOST_API");
$metodo = "POST";
$path = "/v1/paquetes/";
$cuerpo = '{"etiquetas":["ABC123", "REF-987"], "en_nombre_de_correo":"fulanita@example.com", "urgente":true, "id_dirección_fuente": "1d86815d-7a10-4f93-8b80-adbf9ed785a0","id_dirección_destino": "a72266cc-ed3a-4042-926b-463ce1db545f","alto": 15,"ancho": 20,"longitud": 20,"peso_neto": 389,"descripción": "Pedido tienda virtual, SKU77356895 color blanco","referencia": "ORDEN-88243567","entregar_a": "Joshua Blanco"}';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "POST"
path = "/v1/paquetes/"
cuerpo = '{"etiquetas":["ABC123", "REF-987"], "en_nombre_de_correo":"fulanita@example.com", "urgente":true, "id_dirección_fuente": "1d86815d-7a10-4f93-8b80-adbf9ed785a0","id_dirección_destino": "a72266cc-ed3a-4042-926b-463ce1db545f","alto": 15,"ancho": 20,"longitud": 20,"peso_neto": 389,"descripción": "Pedido tienda virtual, SKU77356895 color blanco","referencia": "ORDEN-88243567","entregar_a": "Joshua Blanco"}'
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.status_code)
METODO="POST" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes" \
CUERPO='{"etiquetas":["ABC123", "REF-987"], "en_nombre_de_correo":"fulanita@example.com", "urgente":true, "id_dirección_fuente": "1d86815d-7a10-4f93-8b80-adbf9ed785a0","id_dirección_destino": "a72266cc-ed3a-4042-926b-463ce1db545f","alto": 15,"ancho": 20,"longitud": 20,"peso_neto": 389,"descripción": "Pedido tienda virtual, SKU77356895 color blanco","referencia": "ORDEN-88243567","entregar_a": "Joshua Blanco"}' \
pwd=`echo -n  $METODO$API_PATH$HORA$CUERPO | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA --data-ascii "$CUERPO" -w "\n" -X $METODO

RESPUESTA STATUS CODE: 201

{
    "guía": "GUIA123",
    "estado": "porrecolectar",
    "descripción_estado": "Por recolectar",
    "urgente":true,
    "recolecta_en_cedi": false,
    "creado_por": "example@fruno.com",
    "en_nombre_de_correo": "fulanita@example.com",
    "fecha_creación": "2021-05-10T09:14:01.302388Z",
    "fecha_recolecta": null,
    "fecha_cedi": null,
    "fecha_entrega": null,
    "referencia": "ORDEN-88243567",
    "etiquetas": ["ABC123", "REF-987"],
    "descripción": "Pedido tienda virtual, SKU77356895 color blanco",
    "observaciones": "",
    "alto": 15,
    "ancho": 20,
    "longitud": 20,
    "peso_volumétrico": 2000,
    "peso_neto": 389,
    "peso_neto_original": 389,
    "moneda": "CRC",
    "precio": 250000,
    "factura": "",
    "id_dirección_fuente": "e6634017-4e6b-4c4f-a6c4-0f9c4c70ef43",
    "id_dirección_destino": "a72266cc-ed3a-4042-926b-463ce1db545f",
    "nombre_dirección_fuente": "Tienda ABC",
    "detalle_dirección_fuente": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
    "referencia_fuente": "REFINTERNO88859-358",
    "nombre_dirección_destino": "Tienda XYZ",
    "detalle_dirección_destino": "de la esquina sur oeste del supermercado ABC,125 Sur, Residencial Margarita.",
    "referencia_destino": "REFINTERNO88859-358",
    "entregar_a": "Joshua Bloch",
    "teléfonos": "88961182",
    "eventos": [],
    "comentarios": [],
    "imágenes_recolecta": [],
    "imágenes_entrega": []
}

Para crear un paquete deberá realizar una solicitud POST en donde el cuerpo JSON de la misma contiene todos los campos y valores necesarios. Tan pronto un paquete se crea, entrará en el flujo de logística para ser recolectado.

Hay 2 direcciones que se requieren al crear un paquete:

Si alguna de las direcciones es nueva, es decir no se ha registrado previamente, deberá crear primero la(s) dirección(es).

Resultado Código estado HTTP
Éxito 201
Errores 400

La estructura JSON que debe enviarse consta de los siguientes campos:

Campo Descripción
alto Alto del paquete en centímetros (Requerido)
ancho Ancho del paquete en centímetros (Requerido)
longitud Longitud del paquete en centímetros (Requerido)
id_dirección_fuente ID de la dirección fuente (Requerido)
id_dirección_destino ID de la dirección destino (Requerido)
peso_neto Peso en gramos (Requerido)
id_contrato ID del contrato. Puede omitirse si la lista de contratos está vacía de lo contrario debe enviar el valor que corresponda.
entregar_a Nombre de la persona que recibe el paquete en la dirección destino (Requerido, máximo: 40 caracteres)
descripción Descripción del paquete (Opcional, Máximo: 150 caracteres), se incluye en el PDF a imprimir.
observaciones Cualquier texto que se quiera asociar al paquete (Opcional, máximo: 2000 caracteres), NO se incluye en el PDF que se imprime con cada paquete.
referencia Campo texto libre. Por ejemplo le permite asociar un valor de sus sistemas con el paquete que se crea. (Opcional, máximo: 40 caracteres)
etiquetas Arreglo de hileras (Opcional. Sin orden alguno. Máximo 10 elementos y cada elemento puede tener un máximo de 20 caracteres). Este campo le permite asociarle hasta 10 datos extra a su paquete, las etiquetas ("tags") pueden ser cualquier información extra que no esté cubierta con los otros campos y que tengan sentido para su empresa. NO se incluye en el PDF que se imprime con cada paquete.
urgente Si es (esto implica un recargo en el precio) o no urgente (Opcional, boolean)
en_nombre_de_correo Correo electrónico de la persona responsable en su empresa de resolver asuntos relacionados con el paquete (ver "Notificaciones de comentarios"). (Opcional, máximo: 60 caracteres)
notificaciones Si necesita que nuestro sistema envíe notificaciones por correo, deben incluirse este campo (ver detalle más adelante). (Opcional)

Notificaciones de comentarios

Cuando nuestro personal de operaciones agrega un comentario a un paquete, el sistema envía una notificación al correo de la persona que creó el paquete. Esta notificación no es optativa.

En el caso de paquetes creados vía API, la dirección de correo asociada a la creación del paquete es de la cuenta responsable por el token utilizado al hacer el POST, esto en la mayor parte de los casos será la dirección de correo de una persona en un equipo de desarrollo/TI y que no tiene relación con la operación de logística o tienda de su empresa.

Para que la persona responsable de resolver problemas con un paquete reciba las notificaciones de comentarios o pueda ser contactada por nuestro personal de operaciones, al crear un paquete se debe incluir el correo electrónico correspondiente en el campo en_nombre_de_correo.

Notificaciones por correo

Si su empresa quiere enviar notificaciones a sus clientes desde su propio dominio y/o personalizadas deberán ser enviadas por su sistema, revisando (polling) periódicamente el estado de los paquetes de interés mediante la consulta de estado.

Si no desea enviar sus propias notificaciones, nuestro sistema puede enviar avisos por correo electrónico para cada paquete cuando ocurren eventos como: creación del paquete, recolección, en tránsito, llegada a nuestro cedi, entrega y cancelación.

Cuáles notificaciones se deben enviar para un paquete y a qué correo, debe ser especificado en el momento de la creación del paquete bajo el campo notificaciones como parte de la estructura que se envía para crear el paquete.

Esta estructura debe contener un arreglo (máximo 3 elementos), en donde cada elemento es un diccionario con:

Campo Descripción
correo Correo electrónico (requerido)
creado Si el correo especificado debe recibir aviso al crearse el paquete (Opcional, boolean)
recolectado Si el correo especificado debe recibir aviso al recolectarse el paquete en la dirección fuente (Opcional, boolean)
en_cedi Si el correo especificado debe recibir aviso cuando el paquete llega a nuestro CEDI (Opcional, boolean)
en_tránsito Si el correo especificado debe recibir aviso cuando el paquete está en ruta, algo que sucede luego de recolectarse y cada vez que salga de nuestro CEDI. (Opcional, boolean)
entregado Si el correo especificado debe recibir aviso cuando el paquete se entrega en la dirección destino (Opcional, boolean)
cancelado Si el correo especificado debe recibir aviso cuando el paquete se cancela (Opcional, boolean)

Los campos anteriores son por defecto false por lo que debe especificar de forma explícita las notificaciones que deben enviarse.

Es importante tomar en cuenta quién va a recibir la notificación y especificar las notificaciones de forma deliberada de
acuerdo a quién las recibe. No se recomienda especificar todos los campos como true para el correo asociado con el destino del paquete. Por ejemplo, un correo diciendo que el paquete ABC123 se recibió en nuestro CEDI puede que no tenga ningún sentido para la persona que hizo una compra en una tienda en línea.

También debe tomar en consideración la cantidad de correo que puede recibir una persona por paquete, si activa todas las notificaciones para todos los paquetes para una persona en su empresa, implica que esa persona va a recibir al menos 5 correos por cada paquete lo que puede generar en cientos de correos por día.

Ejemplo de la estructura del campo notificaciones al crear un paquete:

... "notificaciones":[{"correo":"fulanita@example.com", "recolectado": true, "entregado":true}], ...

Información de un paquete

GET https://<HOST>/v1/paquetes/<GUIA>

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/NUMERO_GUIA"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/NUMERO_GUIA";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/NUMERO_GUIA"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/NUMERO_GUIA" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"

RESPUESTA

{
    "guía": "GUIA456",
    "estado": "entregado",
    "descripción_estado": "Entregado",
    "urgente": true,
    "recolecta_en_cedi": false,
    "devolución": false,
    "creado_por": "example@fruno.com",
    "en_nombre_de_correo": "fulanita@example.com",
    "fecha_creación": "2021-05-10T05:24:11.757647Z",
    "fecha_recolecta": "2021-05-10T08:30:01.391475Z",
    "fecha_cedi": "2021-05-10T16:30:01.391475Z",
    "fecha_entrega": "2021-05-11T05:35:01.632862Z",
    "recolecta_estimada_a": "2021-05-11T00:00:00Z",
    "recolecta_estimada_b": "2021-05-12T00:00:00Z",
    "entrega_estimada_a": "2021-05-13T00:00:00Z",
    "entrega_estimada_b": "2021-05-14T00:00:00Z",
    "referencia": "ORDEN-88243567",
    "etiquetas": ["Z01"],
    "descripción": "Pedido tienda virtual",
    "observaciones": ", SKU77356895 color blanco, , SKU77356898 color negro, , SKU77356891 color morado",
    "alto": 15,
    "ancho": 20,
    "longitud": 20,
    "peso_volumétrico": 2000,
    "peso_neto": 389,
    "peso_neto_original": 389,
    "moneda": "CRC",
    "precio": 250000,
    "factura": "",
    "id_dirección_fuente": "1d86815d-7a10-4f93-8b80-adbf9ed785a0",
    "id_dirección_destino": "a72266cc-ed3a-4042-926b-463ce1db545f",
    "nombre_dirección_fuente": "Tienda ABC",
    "detalle_dirección_fuente": "de la esquina sur oeste del supermercado ABC, 125 Sur, Residencial Margarita.",
    "distrito_fuente": "San Pedro",
    "cantón_fuente": "MONTES DE OCA",
    "provincia_fuente": "SAN JOSÉ",
    "referencia_fuente": "REFINTERNO88859-358",
    "nombre_dirección_destino": "Tienda XYZ",
    "detalle_dirección_destino": "de la escuela Gulliver,25 Sur. Residencial Lilliput.",
    "distrito_destino": "San Pedro",
    "cantón_destino": "MONTES DE OCA",
    "provincia_destino": "SAN JOSÉ",
    "referencia_destino": "REFINTERNO88859-358",
    "entregar_a": "Joshua Blanco",
    "teléfonos": "11223344,55667788",
    "eventos": [
        {
            "fecha": "2021-05-10T05:30:01.391475Z",
            "mensaje": "Paquete recibido en CEDI"
        },
        {
            "fecha": "2021-05-10T05:35:01.632862Z",
            "mensaje": "Avería vehicular"
        },
        {
            "fecha": "2021-05-10T05:35:01.632862Z",
            "mensaje": "Paquete entregado"
        }
    ],
    "comentarios": [
        {
            "fecha": "2021-05-10T05:30:01.391475Z",
            "mensaje": "Ejemplo comentario, Teléfono del cliente no contesta"
        },
        {
            "fecha": "2021-05-10T05:35:01.632862Z",
            "mensaje": "Ejemplo comentario"
        }
    ],
    "imágenes_recolecta": [
        {
            "id": "86c7a545-fa21-4540-a09a-e70760fb2771",
            "ContentType": "image/png"
        }
    ],
    "imágenes_entrega": [
        {
            "id": "e7704fe5-c379-4718-8869-47bf80da7820",
            "ContentType": "image/png"
        }
    ]
}

Cuando se quiere obtener todos los datos relacionados con un paquete, esta función retorna las fechas, datos de peso, estado, distritos, dirección, lista de imágenes relacionadas con la guía indicada.

Eventos

Cada paquete va a tener varios eventos relacionados, por ejemplo, cuando se recolecta, cuando ingresa en nuestro centro de distribución (CEDI).

Estos se ven reflejados en la lista del campo eventos.

Comentarios

Un miembro de nuestra operación de Logística puede agregar comentarios a un paquete, por ejemplo, un ejecutivo de servicio al cliente puede informar por medio de un comentario que se intentó la entrega pero la persona no se encontraba en la dirección. También, el usuario que crea el paquete puede agregar un comentario mediante la aplicación Web.

Todos estos comentarios se incluyen en el campo comentarios.

Imágenes

Cada paquete puede tener una serie de imágenes asociadas a la recolecta y/o entrega, por ejemplo, un cliente de Logística puede pedir que todas las entregas de sus paquetes lleven una foto del paquete al momento de la entrega.

Descargar PDF

GET https://<HOST>/v1/paquetes/<GUIA>

METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/GUIA123" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -H "Accept: application/pdf" -w "\n" -o guia.pdf

La guía es un PDF que contiene toda la información de la solicitud de transporte del paquete.

Para solicitar el PDF debe usar el mismo endpoint de información de paquete pero enviando el encabezado (header):

Accept: application/pdf

Imágenes

La lista de las imágenes relacionadas a un paquete se obtiene con la función información completa de la guía.

GET https://<HOST>/v1/paquetes/<GUIA>/imagenes/<ID>

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/GUIA123/imagenes/ID_IMAGEN"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

imagen := LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)

fmt.Println("Bytes:", len(imagen))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/GUIA123/imagenes/ID_IMAGEN";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

$resultado= llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo);
echo $resultado[1];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/GUIA123/imagenes/ID_IMAGEN"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.headers["Content-Type"])
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/GUIA123/imagenes/ID_IMAGEN" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -o imagen.jpg

Esta función retorna la imagen solicitada con un content-type image/jpeg o image/png.

Lista de correos

GET https://<HOST>/v1/paquetes/<GUIA>/notificaciones

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/NUMERO_GUIA/notificaciones"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/NUMERO_GUIA/notificaciones";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/NUMERO_GUIA/notificaciones"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/NUMERO_GUIA/notificaciones" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA

{
  "notificaciones": [
    {
      "correo": "fulanita@example.com",
      "cancelado": true,
      "entregado": true,
      "recolectado": true
    }
  ]
}

Se utiliza esta función si luego de crear un paquete (tanto por API como por la Web) se quiere obtener la lista de correos y las notificaciones configuradas.

En el caso que un paquete no tenga ningún correo para notificaciones el arreglo no tendrá elementos: { "notificaciones": [] }

Calcular estimado de costo

GET https://<HOST>/v1/paquetes/costo

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/costo"
cuerpo := ""
params := "?alto=10&ancho=10&longitud=10&id_dirección_fuente=00000000-0000-0000-0000-000000000000&id_dirección_destino=00000000-0000-0000-0000-000000000000&gr_peso=50"
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path+params, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/costo";
$cuerpo = "";
$params = '?alto=10&ancho=10&longitud=10&id_dirección_fuente=00000000-0000-0000-0000-000000000000&id_dirección_destino=00000000-0000-0000-0000-000000000000&gr_peso=50';
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path.$params, $hora, $cuerpo);
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/costo"
cuerpo = ""
params = "?alto=10&ancho=10&longitud=10&código_distrito_fuente=11601&código_distrito_destino=11601&gr_peso=50"
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path+params, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/costo" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH?alto=10&ancho=10&longitud=10&id_dirección_fuente=00000000-0000-0000-0000-000000000000&id_dirección_destino=00000000-0000-0000-0000-000000000000&gr_peso=50" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n"  -X $METODO

RESPUESTA

{
 "moneda":"CRC",
 "precio":250000,
 "peso_volumétrico":1000
}

Para una estimación de costo sin crear un paquete puede utilizar esta función con los siguientes campos en el query string:

Campo Descripción
alto Alto del paquete en centímetros (requerido)
ancho Ancho del paquete en centímetros (requerido)
longitud Longitud del paquete en centímetros (requerido)
id_dirección_fuente Identificador único de la dirección fuente (requerido)
id_dirección_destino Identificador único de la dirección destino (requerido)
gr_peso Peso en gramos (requerido)
id_contrato ID del contrato. Valor opcional. Debe verificar con la parte operativa de su empresa para enviar el valor que corresponda.

Eventos

Durante el "ciclo de vida" de un paquete le suceden una serie de eventos, por ejemplo si se intenta la recolecta pero el lugar está cerrado o si se intenta la entrega pero la persona que debe recibir el paquete no lo quiere recibir.

Algunos de los eventos posibles y la etapa (recolecta o entrega) en la que se pueden dar son:

Etapa Evento Descripción
recolecta Paquete recolectado se recolecta el paquete en la dirección fuente
recolecta Paquete recolectado en CEDI el cliente dejó el paquete directamente en el CEDI
recolecta Cancelado el paquete fue cancelado
recolecta El remitente no se encuentra Implica que no se pudo realizar la recolecta
recolecta Dirección de recolecta incorrecta Implica que no se pudo realizar la recolecta
entrega Cancelado el paquete fue cancelado
entrega Paquete en CEDI el paquete se encuentra en nuestro CEDI
entrega Paquete en ruta sucede cuando el paquete sale de nuestro CEDI hacia la dirección destino
entrega Paquete entregado cuando el paquete se entrega en la dirección destino
entrega Cambio de destino por devolución si un paquete debe devolverse a por devolución
entrega Rechazo de destinatario Implica que no se pudo realizar la entrega

Se puede obtener la lista completa de los tipos de eventos que le pueden suceder a un paquete.

Lista completa de eventos posibles

GET https://<HOST>/v1/paquetes/tipo_eventos

Si se quiere obtener la lista completa de los eventos que pueden llegar a sucederle a un paquete, este endpoint retorna el catálogo completo indicando en cuál etapa se pueden dar y el identificador único para ese tipo de evento.

¿Para qué sirve el identificador único? Si necesita asociar en su integración -por ejemplo- una notificación en sus sistemas si el paquete no pudo entregarse porque el destinatario no se encontraba en la dirección tiene 2 opciones:

  1. /v1/paquetes/GUIA retorna en su estructura un item con la lista de eventos pero sólo el texto. Si lo desea puede utilizar el texto para reconocerlo y crear una acción específica en su sistema pero ese texto podría cambiar en actualizaciones futuras.
  2. Para no depender del mensaje del evento, el endpoint /v1/paquetes/GUIA/eventos le da la misma lista de eventos del paquete pero con el identificador único asociado al tipo del evento. (por razones "históricas" no se puede agregar a la lista de eventos en /v1/paquetes/GUIA en la versión 1 del API)
urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/tipo_eventos"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/tipo_eventos";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/tipo_eventos"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/tipo_eventos" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA


{
  "tipo_eventos": [
    {
      "id": "56ef0258-6ccb-437e-a33b-e56b2051cbaf",
      "nombre": "Paquete creado",
      "descripción": "",
      "etapa": "recolecta"
    },
    {
      "id": "8c98ae18-eeb4-45e7-abf5-7fd4ce006438",
      "nombre": "Paquete recolectado",
      "descripción": "",
      "etapa": "recolecta"
    },
    {
      "id": "06204837-1032-46db-b23f-3574144a9795",
      "nombre": "Paquete en CEDI",
      "descripción": "",
      "etapa": "recolecta"
    },
    ...
    ]
 }

Eventos de un paquete con su identificador único y el tipo de evento

Mediante este endpoint se pueden obtener los eventos de un paquete específico, incluyendo el tipo del evento.

Campo Descripción
id Identificador único para el evento del paquete consultado
fecha Fecha en la que ocurrió el evento
mensaje Texto del evento. Ejemplos: Paquete recolectado, Avería vehicular.
id_tipo_evento Indica el tipo de evento asociado a este evento en particular. Este identificador es el que corresponde a alguno en la lista obtenida en /v1/paquetes/tipo_eventos

GET https://<HOST>/v1/paquetes/GUIA/eventos

urlBase := "https://"+os.Getenv("HOST_API")
metodo := "GET"
path := "/v1/paquetes/GUIA/eventos"
cuerpo := ""
hora := time.Now().Format(time.RFC3339)
secreto := os.Getenv("LLAVE_API")
pwd := BasicAuthPwd(secreto, metodo, path, hora, cuerpo)

fmt.Println(string(LlamarAPI(urlBase, 
                       os.Getenv("CUENTA_API"), 
                       pwd, 
                       metodo, 
                       path, 
                       hora, 
                       cuerpo)))
$urlBase = "https://".getenv("HOST_API");
$metodo = "GET";
$path = "/v1/paquetes/GUIA/eventos";
$cuerpo = "";
$hora = (new DateTime('NOW'))->format(DateTimeInterface::RFC3339);
$pwd = basicAuthPwd(getenv("LLAVE_API"), $metodo, $path, $hora, $cuerpo);
$usuario = getenv("CUENTA_API");

echo llamarAPI($urlBase, $usuario, $pwd, $metodo, $path, $hora, $cuerpo)[0];
urlBase = "https://"+os.environ["HOST_API"]
metodo = "GET"
path = "/v1/paquetes/GUIA/eventos"
cuerpo = ""
hora = datetime.now(timezone.utc).astimezone().isoformat()
pwd = basic_auth_pwd(os.environ["LLAVE_API"], metodo, path, hora, cuerpo)
resultado = llamar_api(urlBase, os.environ["CUENTA_API"], pwd, metodo, path, hora, cuerpo)

print(resultado.content)
METODO="GET" HORA=`date --rfc-3339=ns | sed 's/ /T/'` \
API_PATH="/v1/paquetes/GUIA/eventos" \
pwd=`echo -n  $METODO$API_PATH$HORA | openssl dgst -sha256 -hmac $LLAVE_API -binary | base64`; \
curl "https://$HOST_API$API_PATH" --user $CUENTA_API:$pwd -H X-Fr-Hora:$HORA -w "\n" -X $METODO

RESPUESTA


{
  "eventos": [
    {
      "id": "67da48aa-f697-453e-909c-d393ada8ceed",
      "fecha": "2022-11-22T09:42:49.564Z",
      "mensaje": "Paquete recolectado",
      "id_tipo_evento": "8c98ae18-eeb4-45e7-abf5-7fd4ce006438"
    },
    ...
    {
      "id": "bbb0869d-a5c9-4f73-ba48-fae1c5fc5b61",
      "fecha": "2022-11-22T09:54:05.643053Z",
      "mensaje": "Paquete entregado",
      "id_tipo_evento": "43ce2ff6-c161-4faa-a1fb-ca5e98af0f02"
    }
  ]
}


Pasos

Supongamos que se necesita crear un paquete con la dirección de entrega nueva y la de recolecta es una nueva tienda de su empresa, usted debe:

  1. Crear la dirección de su nueva tienda, guardando el ID correspondiente.
  2. Crear dirección destino, guardando el ID retornado.
  3. Crear paquete con id_dirección_fuente (ID obtenido en el paso 1) y con id_dirección_destino (paso 2).

De ahí en adelante sólo necesita consultar periódicamente el estado de la guía retornada al crear el paquete para saber su estado.

Códigos de estado HTTP

Lista de estados retornados por el API.

Éxito

Método Código
GET 200
DELETE 200
POST 201
PUT 204

Error

Código Casos
400 Datos inválidos. Ejemplos: campo requerido no enviado, formato de valor erróneo.
401 No autorizado. Ejemplo: mal cálculo de la contraseña enviada, llave inválida, cuenta inválida.
404 Recurso no encontrado. Ejemplos: solicitar una dirección que no existe.
429 Demasiadas solicitudes en un corto tiempo.
500 Un error interno en el sistema, en cuyo caso se asigna un incidente y nuestro equipo de soporte es informado de inmediato.
502 Temporalmente fuera de servicio, intente más tarde.
503 Temporalmente fuera de servicio, intente más tarde.

Otros

Código Caso
102 El sistema está procesando el nuevo paquete y aún no puede retornar el PDF de la guía.