terminal Referencia técnica v1.0

Eventos de avance y coordenadas del móvil

Cómo AsistManager avisa a la compañía que un servicio avanzó, por qué el mensaje no trae un campo de evento y hay que deducir el paso, y en qué condiciones se envían las coordenadas de un móvil ocupado. Incluye la política de entrega, reintentos y caducidad.

updateActualizado: 17 de septiembre de 2026groupDirigido a: Equipos de sistemas e integradoresdeployed_codeAplica a: AsistManager · am-hermes-core · aplicación de prestadores

Descripción #

Cuando un móvil avanza un servicio desde la aplicación, AsistManager envía a la compañía una petición HTTP POST con el caso completo. Ese mensaje es el aviso de avance.

El cuerpo es siempre el mismo objeto, con todos los campos del caso. Nunca es una diferencia respecto del envío anterior: cada aviso es la fotografía entera del caso en ese momento.

La segunda mitad de este documento describe el otro mensaje que emite un móvil mientras trabaja: su posición. Los dos comparten la misma política de entrega y reintentos.

error
No existe un campo que indique qué pasó
No hay un campo evento, event, tipo ni equivalente. El paso se deduce mirando qué campos Hora* dejaron de tener la fecha nula. Una integración que espere un discriminador explícito no va a encontrarlo: el discriminador son las fechas.
info
Cómo solicitar el acceso
Para integrarse, escriba a soporte@dosalinfinito.com.ar y solicite el acceso. En ese trámite se registra el método de callback de su sistema, que es la dirección a la que la plataforma va a llamar. Las direcciones de los puntos de acceso y las credenciales se entregan de forma individual. Este documento describe el contrato: los campos, las variantes y la política de entrega.
warning
La fecha nula es 1899-12-30T00:00:00+00:00
Un hito que todavía no ocurrió no llega como null, ni como cadena vacía, ni con el campo ausente: llega con ese valor literal. Tratarlo como una fecha real produce demoras de más de cien años. La regla de lectura es simple: si el campo trae la fecha de 1899, ese paso no ocurrió.

Cómo se deduce el paso #

PasoCampos que pasan a tener valor<code>info.status</code>
Alta del casoHoraInicio · UsuarioCargapendiente
Asignación a un móvilHoraPasadoMovil · Movil · UsuarioAsignacion. En la primera asignación se completa además HoraActivacionInicial.asignado
En caminoHoraEnCamino · UsuarioEnCaminoasignado, sin cambio
ArriboHoraLlegada · UsuarioLlegada. Se completa también Patente, que el chofer confirma en el lugar.arribado
CierreHoraFinal · TipoServicioFinal · UsuarioFinalizacionfinalizado
BitácoraObservaciones · HistoricoNo cambia
error
«En camino» no cambia el estado
Cuando el chofer marca que salió, info.status sigue siendo asignado. No existe un estado en camino. La única manera de distinguir un móvil que ya salió de uno que todavía no es leer HoraEnCamino: si dejó de tener la fecha nula, el móvil está en camino. Quien muestre el estado al operador usando solo info.status va a perder ese paso.

Envoltorio del mensaje #

data_object JSON
json
{
  "source": "<clave de la compañía>",
  "identifier": "<id del caso, tal como lo creó la compañía>",
  "alias": "<prestador que atiende>",
  "date": "2026-09-17T11:42:08.310000-03:00",
  "task": { … }
}

El envoltorio es invariable: estas cinco claves en todos los avisos de avance, sin importar el paso.

Campos del envoltorio #

Campo Tipo Obligatorio Valores posibles Descripción
source string Obligatorio — La clave de la compañía de origen, la misma con la que se dio de alta el caso.
identifier string Obligatorio — El identificador del caso, tal como lo creó la compañía. Es la clave de correlación: es lo que permite saber a qué caso corresponde el aviso.
alias string Obligatorio — El prestador que está atendiendo el servicio.
date string Obligatorio ISO 8601 con desplazamiento <code>-03:00</code> Momento en que AsistManager emitió el aviso, en hora local. Sirve como reloj del emisor para ordenar mensajes que pudieron llegar desordenados.
task objeto Obligatorio — El caso completo, con todos sus campos. Está descripto en el documento de Consultas al prestador, en la sección El objeto task. Acá se detalla solo el bloque info, que es el que resume el avance.

Ejemplo: aviso de arribo #

data_object JSON
json
{
  "source": "<clave de la compañía>",
  "identifier": "20260917102415733",
  "alias": "prestador",
  "date": "2026-09-17T11:42:08.310000-03:00",
  "task": {
    "Numero": "20260917102415733",
    "Empresa": "<clave de la compañía>",
    "TipoServicioInicial": "Traslado",
    "Movil": "movil-12",
    "Patente": "AAA111",
    "HoraInicio": "2026-09-17T13:24:15.733+00:00",
    "HoraActivacionInicial": "2026-09-17T13:31:02.000+00:00",
    "HoraPasadoMovil": "2026-09-17T13:31:02.000+00:00",
    "HoraEnCamino": "2026-09-17T13:36:50.000+00:00",
    "HoraLlegada": "2026-09-17T14:42:05.000+00:00",
    "HoraFinal": "1899-12-30T00:00:00+00:00",
    "TipoServicioFinal": "",
    "UsuarioCarga": "USUARIOCARGA",
    "UsuarioAsignacion": "USUARIOASIGNACION",
    "UsuarioEnCamino": "USUARIOENCAMINO",
    "UsuarioLlegada": "USUARIOLLEGADA",
    "UsuarioFinalizacion": "",
    "Observaciones": "Cia.ObsOrigen: \r\nCia.ObsDestino: \r\nCia.Obs:\r\nDemora: 45\r\nCia.Copago: \r\n",
    "info": {
      "status": "arribado",
      "movil_id": "movil-12",
      "servicio_realizado": null,
      "servicio_final": null,
      "demora_prometida": 45,
      "demora_arribo": 77.82,
      "demora_trabajo": 0,
      "hora_inicio": "2026-09-17T13:24:15.733+00:00",
      "hora_asignado": "2026-09-17T13:31:02.000+00:00",
      "hora_arribo": "2026-09-17T14:42:05.000+00:00",
      "hora_final": "1899-12-30T00:00:00+00:00"
    }
  }
}

El objeto task va completo: acá se muestran solo los campos que hacen falta para entender el ejemplo. Los datos son ficticios. Observe que HoraFinal todavía tiene la fecha nula y que HoraEnCamino ya tiene valor, aunque el estado había pasado por asignado sin reflejarlo.

El bloque info #

AsistManager agrega dentro de task un objeto info que resume el avance ya interpretado, para que la compañía no tenga que recalcularlo. Es un resumen, no un reemplazo: la fuente de verdad de los hitos siguen siendo los campos Hora*.

Campos de task.info #

Campo Tipo Obligatorio Valores posibles Descripción
status string Obligatorio <code>pendiente</code> · <code>asignado</code> · <code>arribado</code> · <code>finalizado</code> Estado del caso. Son los cuatro únicos valores posibles. No hay un estado para «en camino».
movil_id string Obligatorio — Identificador del móvil asignado. Copia de Movil.
servicio_realizado booleano Obligatorio — Verdadero cuando el servicio se prestó. Es falso solamente si TipoServicioFinal es no reparo, cancelado con costo o cancelado sin costo. Llega en null antes del cierre, porque hasta entonces no hay respuesta posible.
servicio_final string Obligatorio — Copia de TipoServicioFinal. Llega en null antes del cierre.
demora_prometida entero Obligatorio Minutos Demora comprometida. Sale del campo Demora del caso o, si no está, de la línea Demora: N del bloque Observaciones.
demora_arribo decimal Obligatorio Minutos HoraLlegada menos HoraInicio, en minutos con decimales. Está limitada por abajo en cero: nunca llega negativa.
demora_trabajo decimal Obligatorio Minutos HoraFinal menos HoraLlegada, en minutos con decimales. También limitada por abajo en cero.
hora_inicio · hora_asignado · hora_arribo · hora_final string Obligatorio — Copias de los hitos correspondientes, para leerlos sin recorrer todo el caso.
warning
Campos reservados que hoy no traen información
El bloque info incluye además alert, demora_transcurrida, porcentaje_transcurrido y demora_restante. Están reservados y hoy no transportan información: alert viaja siempre vacío y los otros tres siempre en cero. No los use para calcular ni para decidir. Si más adelante se activan, se documenta acá.

Detalles de serialización #

Son particularidades del formato que conviene conocer antes de escribir el receptor, porque cada una rompió alguna integración alguna vez.

Método y cuerpo
El método es POST y el cuerpo es JSON. No hay firma del mensaje ni clave de idempotencia: el receptor no puede verificar el origen con el propio mensaje ni descartar repetidos por un identificador de envío.
Codificación
El cuerpo sale en ASCII con escapes \uXXXX, no en UTF-8 directo. Un acento viaja como \u00e1. Cualquier analizador de JSON conforme lo resuelve solo; un tratamiento del cuerpo como texto plano, no.
Orden de las claves
No está garantizado. No escriba lógica que dependa de la posición de un campo dentro del objeto.
Denunciante y Titular
Son campos distintos y pueden tener valores diferentes: quien llama no siempre es el titular del servicio.
Color
Puede llegar en null.
Observaciones
Usa \r\n como separador de líneas y mezcla líneas con formato Clave: valor con texto libre. Se analiza línea por línea, no con un formato fijo.
Campos Final*
Los campos de precios llegan vacíos salvo que el prestador haya valorizado el servicio.
error
Los desplazamientos de zona horaria no son uniformes dentro del mismo mensaje
El campo date del envoltorio viaja en -03:00 y los campos Hora* del caso, en +00:00. Conviven en el mismo cuerpo. Un analizador que asuma un único desplazamiento para todo el mensaje, o que recorte la zona horaria del texto, va a calcular mal las demoras. Convierta cada fecha respetando el desplazamiento que trae.

Qué no dispara un aviso #

Hay avances reales del servicio que no producen un aviso. Conocerlos evita reportar como faltante algo que nunca se emitió.

SituaciónComportamiento
El avance se carga desde los portales webNo se emite aviso. Solo notifica el avance registrado desde la aplicación del móvil. Un caso operado enteramente desde el escritorio no genera ningún aviso, aunque el estado del caso cambie.
El identificador del caso contiene manualNo se emite aviso. Son casos de carga manual.
El identificador del caso empieza con DEMO-No se emite aviso. Son casos de demostración.

Entrega y reintentos #

La entrega se considera exitosa cuando el receptor responde con un código HTTP 2xx. Cualquier otra respuesta, y también la falta de respuesta, se consideran error y habilitan el reintento.

Esta política es común a los avisos de avance, a los adjuntos, a las respuestas de consulta y a las posiciones. Lo que cambia entre ellos es cuánto tiempo se insiste.

Cadencia de reintentos #

TramoIntentosIntervalo
Primer envío1Inmediato
Tramo 110Cada 30 segundos
Tramo 210Cada 60 segundos
Tramo 36Cada 5 minutos
DespuésHasta que caduqueCada 5 minutos

Caducidad por tipo de mensaje #

MensajeCaduca a los
Aviso de avance de caso5 días desde que se generó
Adjunto5 días desde que se generó
Respuesta a una consulta15 minutos
Posición de un móvil15 minutos
error
Un 4xx apaga el reintento; un 5xx lo sostiene cinco días
Si su punto de acceso responde 400, 401 o 404, o cualquier otro 4xx que no sea 408 ni 429, después de dos respuestas de ese tipo el mensaje se descarta y no se vuelve a intentar. El criterio es que un 4xx describe un problema del mensaje, no del momento. En cambio, si responde 500, se insiste durante los cinco días completos. Consecuencia práctica: no devuelva 4xx por una falla temporal de su sistema, porque va a perder el mensaje. Ante una caída propia, responda 5xx.
warning
Los avisos pueden llegar desordenados, repetidos y hasta cinco días tarde
Hay hasta dos envíos en vuelo por destino, de modo que el orden de llegada no está garantizado, y los reintentos pueden producir entregas duplicadas. Como cada aviso es el caso completo y no una diferencia, aplicar un aviso viejo encima de uno nuevo hace retroceder el estado: un caso ya finalizado puede volver a mostrarse como asignado. Dos formas de protegerse, y conviene usar las dos: comparar los campos Hora* recibidos contra los que ya se conocen y descartar el mensaje si no aportan un hito nuevo, y usar date como reloj del emisor para no aplicar un mensaje anterior al último aplicado.
info
Un destino que falla sostenidamente recibe menos caudal
Si un destino acumula 100 fallos consecutivos, el caudal de envíos hacia ese destino baja a una petición por minuto hasta que vuelva a responder. Es una protección para que un receptor caído no acumule una avalancha de reintentos que lo vuelva a tumbar al recuperarse.

Coordenadas del móvil ocupado #

Mientras un móvil atiende un servicio de la compañía, AsistManager le reenvía las posiciones que el teléfono del chofer reporta. Es un mensaje distinto del aviso de avance y tiene su propio envoltorio.

El objetivo es que la compañía pueda mostrar el móvil en su mapa y estimar el arribo con datos propios, sin pedirle nada al prestador.

check_circle
Un móvil libre no reporta posición a nadie
Solo se envía la posición de un móvil que tiene un caso activo de esa compañía. Una compañía nunca ve dónde está un móvil que no está trabajando para ella, ni cuando está atendiendo a otra, ni cuando está libre. Es la garantía de privacidad del prestador y no es configurable.

Condiciones para que una posición se envíe #

  1. 1
    Pasaron al menos 30 segundos
    Desde la posición anterior de ese mismo móvil. Las posiciones más frecuentes que ese intervalo no se reenvían.
  2. 2
    Hay un caso activo de esa compañía con ese móvil
    Asignado dentro de las últimas 24 horas. Sin un caso que lo justifique, no hay envío.
  3. 3
    El identificador del caso no contiene <code>manual</code>
    Los casos de carga manual no producen reenvío de posiciones, igual que no producen avisos de avance.

Envoltorio de una posición #

data_object JSON
json
{
  "fecha": "2026-09-17T11:44:12.000000-03:00",
  "data": { … }
}

fecha es la hora local del reenvío. El objeto data es la medición del dispositivo, tal como la reportó el teléfono.

Ejemplo de posición #

data_object JSON
json
{
  "fecha": "2026-09-17T11:44:12.000000-03:00",
  "data": {
    "worker": "movil-12",
    "lat": -34.5401234,
    "lng": -58.4802345,
    "accuracy": 8.4,
    "timestamp": "2026-09-17T14:44:09.000Z",
    "local_timestamp": "2026-09-17T11:44:09.000-03:00",
    "speed": 11.2,
    "heading": 274.5,
    "altitude": 23.0,
    "battery": 0.62,
    "is_moving": true,
    "activity": "in_vehicle",
    "last_position": { … },
    "calculated": {
      "lat": "-34.5401234",
      "lng": "-58.4802345",
      "percent": "0.4000000",
      "lat_offset_1sec": "0.0000090",
      "lng_offset_1sec": "-0.0001080"
    },
    "address": { … },
    "prediction": {
      "model": "curve",
      "speed_mps": 11.2,
      "samples": 6,
      "confidence": 0.82,
      "heading": 274.5,
      "turn_rate_deg_s": -3.4,
      "radius_m": 188.6
    },
    "info": { … }
  }
}

Coordenadas y valores de ejemplo, no corresponden a un servicio real.

Campos de la medición #

Campo Tipo Obligatorio Valores posibles Descripción
worker string Obligatorio — Identificador del móvil. Es el mismo valor que llega en Movil y en info.movil_id del aviso de avance, y es lo que permite asociar la posición con el caso.
lat · lng decimal Obligatorio Grados decimales Posición reportada por el dispositivo.
accuracy decimal Obligatorio Metros Precisión estimada de la medición. Un valor alto indica una posición poco confiable, típica de un túnel o de un estacionamiento cubierto.
timestamp string Obligatorio ISO 8601 en UTC Momento de la medición en el dispositivo.
local_timestamp string Obligatorio — El mismo momento expresado en la hora local del dispositivo.
speed decimal Opcional Metros por segundo · <code>-1</code> Velocidad instantánea. El valor -1 significa que se desconoce, no que el móvil esté detenido.
heading decimal Opcional Grados · <code>-1</code> Rumbo. El valor -1 significa que se desconoce.
altitude decimal Opcional Metros Altitud reportada por el dispositivo.
battery decimal Opcional De 0 a 1 Carga de la batería del teléfono, como fracción. 0.62 es 62 por ciento.
is_moving booleano Opcional — Indica si el dispositivo se considera en movimiento.
activity string Opcional <code>still</code> · <code>in_vehicle</code> y otros valores propios del sistema operativo Actividad detectada por el sistema operativo del teléfono. El conjunto de valores lo define el sistema operativo, no AsistManager, de modo que conviene tratar un valor desconocido como sin información en lugar de rechazarlo.
last_position objeto Opcional — La posición anterior del mismo móvil. Permite calcular un desplazamiento sin guardar estado del lado del receptor.
calculated objeto Opcional — Valores auxiliares para extrapolar la posición entre dos refrescos. Se detallan más abajo.
address objeto Opcional — La dirección resuelta por el servicio de mapas propio: calle, altura, calle transversal, distancia a la esquina y la cadena administrativa completa de la ubicación.
prediction objeto Opcional — Predicción de movimiento calculada en el teléfono. No siempre está presente. Se detalla más abajo.
info objeto Opcional — Datos del móvil que reporta: el nombre del chofer y la organización a la que pertenece.

El bloque calculated #

Campo Tipo Obligatorio Valores posibles Descripción
lat · lng string Obligatorio Grados decimales con 7 decimales Posición calculada sobre la que se aplican los desplazamientos.
percent string Obligatorio — Avance relativo entre la posición anterior y la actual.
lat_offset_1sec · lng_offset_1sec string Obligatorio Grados por segundo Cuánto se desplaza el móvil en un segundo, en cada eje. Sumándolos a la posición se anima el avance entre dos refrescos sin esperar la medición siguiente.

Los cinco valores llegan como cadenas de texto con siete decimales, no como números. Hay que convertirlos antes de operar con ellos.

La predicción de movimiento #

El bloque prediction describe cómo se está moviendo el móvil, para poder animarlo entre dos refrescos siguiendo el arco real de la calle en lugar de una recta entre dos puntos. En una curva, la diferencia entre ambas representaciones es visible: el ícono deja de cortar por la vereda.

Se calcula en el teléfono, sobre las últimas seis posiciones, con dos umbrales: se considera que el móvil está quieto por debajo de 1,5 metros por segundo, y que está en curva cuando el giro sostenido supera 1,5 grados por segundo de forma consistente.

error
prediction es opcional y hoy lo manda una minoría de los móviles
El bloque depende de la versión de la aplicación instalada en el teléfono, y hoy solo una minoría de los móviles la tiene. Una integración no puede depender de prediction: tiene que funcionar sin él y usarlo como mejora cuando llega. La proporción va a crecer con la actualización del parque de teléfonos, pero no hay una fecha a partir de la cual esté garantizado.

Campos de prediction #

Campo Tipo Obligatorio Valores posibles Descripción
model string Obligatorio <code>still</code> · <code>linear</code> · <code>curve</code> Modelo de movimiento detectado. Los tres valores son esos exactos, en inglés y en minúsculas.
speed_mps decimal Obligatorio Metros por segundo Velocidad estimada sobre la ventana de posiciones usada.
samples entero Obligatorio Hasta 6 Cuántas posiciones se usaron para el cálculo. Un valor bajo indica una estimación recién iniciada.
confidence decimal Obligatorio De 0 a 1 Confianza del modelo. Conviene fijar un umbral propio por debajo del cual se ignora la predicción y se anima en línea recta.
heading decimal Condicional Cuando el modelo lo determina Grados Rumbo estimado por la predicción.
turn_rate_deg_s decimal Condicional Cuando el modelo lo determina Grados por segundo, con signo Velocidad de giro. El signo indica el sentido del giro.
radius_m decimal Condicional Solo con model igual a curve Metros Radio del arco que describe el móvil.

Los tres modelos #

<code>model</code>Qué significaCómo conviene representarlo
stillEl móvil está quieto: la velocidad está por debajo de 1,5 metros por segundo.No animar. Mantener el ícono en su lugar evita el temblor por error de medición.
linearEl móvil avanza en línea recta.Interpolar entre la posición actual y la proyección del rumbo.
curveEl móvil describe un arco: el giro sostenido supera 1,5 grados por segundo.Animar sobre el arco usando radius_m y turn_rate_deg_s.
info
activity y prediction.model pueden contradecirse
No miden lo mismo. activity es la actividad que reconoce el sistema operativo del teléfono, con su propia lógica y su propia inercia; prediction.model es geometría calculada sobre las últimas seis posiciones. Un móvil detenido en un semáforo puede reportar in_vehicle y still al mismo tiempo, y eso no es un error. Si tiene que elegir uno para mover el ícono en el mapa, use prediction.model; activity describe al chofer, no al desplazamiento.
info
La posición incluye datos del chofer
El bloque info de la medición trae el nombre del chofer y la organización a la que pertenece. Lo decimos de forma explícita para que la compañía sepa qué dato está recibiendo y lo trate en consecuencia dentro de su propio sistema.

Errores frecuentes #

La mayoría de los reclamos de integración sobre este mensaje se explican por alguno de estos puntos.

Código Qué se ve Causa Cómo se resuelve
Sin avisos El caso avanza y la compañía no recibe nada El avance se está cargando desde los portales web, o el identificador contiene manual o empieza con DEMO-. Verificar por qué vía se está operando el caso. Solo el avance registrado desde la aplicación del móvil genera aviso.
Estado que retrocede Un caso finalizado vuelve a verse como asignado Se aplicó un aviso viejo, llegado tarde o repetido, encima de uno más nuevo. El cuerpo es el caso completo, así que sobrescribe. Descartar el mensaje si no aporta un hito Hora* nuevo, o comparar date contra el del último mensaje aplicado.
Se pierde «en camino» El operador nunca ve que el móvil salió Se está leyendo solo info.status, que sigue en asignado durante ese paso. Leer HoraEnCamino: si dejó de tener la fecha nula, el móvil salió.
Demoras absurdas Aparecen tiempos de más de cien años Se tomó la fecha nula 1899-12-30T00:00:00+00:00 como una fecha real. Tratar ese valor como el paso no ocurrió antes de cualquier cálculo.
Demoras corridas 3 horas Los tiempos calculados no coinciden con la operación Se asumió un único desplazamiento horario para todo el mensaje. date viaja en -03:00 y los Hora* en +00:00. Analizar cada fecha con el desplazamiento que trae, sin recortarlo ni normalizarlo a mano.
Mensajes perdidos Después de una caída propia faltan avisos que nunca se recuperan El receptor respondió 4xx durante la caída. Con dos respuestas 4xx que no sean 408 ni 429, el mensaje se descarta. Responder 5xx ante una falla temporal. Con 5xx se reintenta durante cinco días.
Sin posiciones No llegan coordenadas de un móvil El móvil no tiene un caso activo de esa compañía asignado en las últimas 24 horas, o no pasaron 30 segundos desde su posición anterior. Es el comportamiento esperado. Un móvil libre no reporta su posición a ninguna compañía.
Animación entrecortada El ícono del móvil salta en lugar de moverse Se está esperando prediction, que solo envía una minoría de los móviles. Usar calculated como base de la animación y prediction como mejora cuando está presente.

Compatibilidad #

Componente Desde Estado Nota
Aviso de avance con el caso completo En producción vigente Uso masivo. El formato del envoltorio no cambió.
Bloque <code>task.info</code> En producción vigente Resumen del avance ya interpretado.
<code>alert</code> · <code>demora_transcurrida</code> · <code>porcentaje_transcurrido</code> · <code>demora_restante</code> Reservados Planificado Presentes en el mensaje, sin información: alert vacío y los otros tres en cero.
Reenvío de posiciones del móvil ocupado En producción vigente Sujeto a las tres condiciones de envío.
Bloque <code>prediction</code> Según la versión de la aplicación vigente Opcional. Hoy lo envía una minoría de los móviles. No se puede depender de él.
← Volver a la documentación de AsistManager Última actualización: 17 de septiembre de 2026