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:
- Mantener su catálogo de direcciones para recolectas y entregas
- Verificar zonas sin servicio
- Consultar división territorial
- Crear paquetes
- Cancelar paquetes
- Consultar estado de un paquete
- Descargar una guía (PDF)
- Descargar los comprobantes (imágenes) de recolecta y entrega asociados a un paquete
- Lista de paquetes, con filtros como: sólo los paquetes creados en un día en particular y/o con un estado en particular
- Obtener los datos completos asociados a un paquete, por ejemplo: detalle de las direcciones, peso, costo, comentarios, fechas, estado.
- Estimado de costo para un paquete
- Ambiente dedicado para pruebas
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:
- Podrá ingresar a logistica.fruno.com, en donde tendrá acceso a las "llaves" necesarias para autenticarse en el ambiente de pruebas y producción
- Invalidar su llave de acceso y generar una nueva
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?
- Cuando la lista está vacía
- No aparece el campo
páginaen el resultado
Sobre los ejemplos
Para cada función del API se da un ejemplo en 3 lenguajes y 1 shell:
- Go
- PHP
- Python (los ejemplos usan
requests, instalar conpython -m pip install requests) - 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:
- X-Ratelimit-Remaining: cuántos requests le quedan en la ventana de tiempo
- X-RateLimit-Policy: Política que aplica para la restricción (puede cambiar en el futuro).
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:
- Si en un momento dado tiene que hacer más de
X-Ratelimit-Limitsolicitudes continuas, puede utilizar los valores deX-Ratelimit-Policypara distribuir las solicitudes en el tiempo sin exceder la cuota. Por ejemplo para unX-Ratelimit-Policycon30;w=60: 30/60 (dividimos) implica que si hace 1 solicitud cada 2 segundos o menos agotaría la cuota por ventana de tiempo pero si hace una solicitud cada 2.5 segundos (por ejemplo) podría hacer todas las solicitudes que necesite sin problema. Por supuesto, usted puede definir cualquier variación en la descripción del ejemplo anterior, como decidir duplicarlo y distribuir las llamadas cada 4 segundos o sumarle el 50% y hacerlas cada 3 segundos, etc. - Si en un momento dado tiene que hacer menos de
X-Ratelimit-Limitsolicitudes continuas, puede hacerlas tan rápido como quiera.
¿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).
- Como usuario debe enviar el ID de la cuenta (de ahora en adelante CUENTA_API)
- Como contraseña debe realizar un cálculo utilizando su llave secreta (de ahora en adelante LLAVE_API)
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:
- Método HTTP de la llamada (GET, POST, PUT, DELETE)
- PATH de la función incluyendo el "/" inicial y sin parámetros (es decir, sin el query string)
- Hora en formato RFC 3339 (ejemplo:
2021-03-16T14:22:20-06:00) - Cuerpo de la llamada
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:
- Fuente: la dirección de recolecta
- Destino: la dirección de entrega
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:
- /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.
- 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:
- Crear la dirección de su nueva tienda, guardando el ID correspondiente.
- Crear dirección destino, guardando el ID retornado.
- 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. |