Configuracao do MDS 2.0 Provider
O Levy Fleets implementa a Mobility Data Specification (MDS) 2.0 Provider API de ponta a ponta. Todos os endpoints vivem sob /api/mds/[subaccountId]/provider/v2/... e sao assinados com JWTs RS256 cujas chaves publicas sao publicadas em .well-known/jwks.json por subconta.
Versao da spec
Fixamos em MDS 2.0.1. O campo version no envelope de resposta reporta 2.0.1 e o header Content-Type e application/vnd.mds+json;version=2.0. Cidades executando um mds-provider-validator contra essa base devem passar sem avisos de esquema.
Catalogo de endpoints
Todas as rotas tem prefixo /api/mds/{subaccountId}/provider/v2/. O subaccountId e o UUID da subconta do operador (uma por cidade para a maioria dos operadores).
| Endpoint | Metodo | Proposito | Paginacao | Cache |
|---|---|---|---|---|
/vehicles | GET | Todos os veiculos registrados na subconta | nenhuma (pagina unica) | 60s edge |
/vehicles/status | GET | Estado atual por veiculo (uma linha cada) | cursor | 60s edge |
/vehicles/{device_id} | GET | Detalhe de veiculo unico | n/a | nenhum |
/trips | GET | Viagens terminando na janela ?end_time | cursor por ended_at | nenhum |
/events | GET | Eventos de mudanca de estado na janela | cursor por event_time | nenhum |
/telemetry/{vehicle_id} | GET | Amostras GPS/bateria para janela | nenhuma | nenhum |
/stops | GET | Estacionamentos + pontos de carga do operador | nenhuma | 60s edge |
/reports | GET | Agregados mensais pre-computados | nenhuma | 60s edge |
/.well-known/jwks.json | GET | JWK Set publico para verificar JWTs de resposta | n/a | 60s edge |
Lista de veiculos -- /vehicles
Retorna todo veiculo vinculado a subconta. Uma linha por device_id.
curl -H "Authorization: Bearer <token>" \
https://fleets.levyelectric.com/api/mds/<subaccountId>/provider/v2/vehicles
Envelope de resposta:
{
"version": "2.0.1",
"last_updated": "2026-05-18T12:00:00Z",
"ttl": 60000,
"vehicles": [ /* Objetos Vehicle MDS */ ]
}
Status de veiculos -- /vehicles/status
Paginado por cursor. Tamanho de pagina padrao 100, maximo 500. O cursor codifica o timestamp de atualizacao + ID do ultimo veiculo, de modo que paginas subsequentes sao estaveis em escritas.
curl -H "Authorization: Bearer <token>" \
"https://fleets.levyelectric.com/api/mds/<subaccountId>/provider/v2/vehicles/status?cursor=<opaque>&limit=200"
A resposta inclui next_cursor quando ha mais paginas:
{
"version": "2.0.1",
"last_updated": "2026-05-18T12:00:00Z",
"vehicles_status": [ /* objetos de status, inclui current_speed_limit_kph */ ],
"next_cursor": "eyJ0aW1lIjoiMjAyNi0wNS0xOFQxMjowMDowMFoiLCJpZCI6IjQyIn0="
}
O campo current_speed_limit_kph em cada linha de status reflete o menor limite de velocidade aplicavel na ultima posicao conhecida do veiculo, considerando tanto zonas do operador quanto geofences de politica ativos. Veja Aplicacao de Velocidade em Tempo Real.
Veiculo unico -- /vehicles/{device_id}
Retorna um objeto Vehicle MDS. Util para validadores que sondam registros individuais antes de varrer a lista completa.
Viagens -- /trips
Paginacao por cursor ordenada por ended_at ascendente. Query parameter obrigatorio end_time e uma janela de 1 hora em ISO 8601:
GET /trips?end_time=2026-05-18T00:00:00Z
O endpoint retorna viagens cujo ended_at cai em [end_time, end_time + 1h). Viagens abertas sao excluidas.
Eventos -- /events
Paginacao por cursor ordenada por event_time ascendente. Mesma semantica de janela end_time que /trips. Emite mudancas de estado (available, reserved, on_trip, non_operational, removed etc.).
Telemetria -- /telemetry/{vehicle_id}
Retorna amostras GPS + bateria para um unico veiculo em uma janela de tempo. Se a tabela vehicle_telemetry estiver ausente (algumas subcontas antigas), a rota volta a vehicle_events automaticamente.
Stops -- /stops
Estacionamentos e pontos de carga definidos pelo operador, expostos como objetos Stop MDS. Inclui vehicle_type_capacity conforme esquema MDS 2.0.
Reports -- /reports
Agregados mensais pre-computados: total de viagens, total de veiculos ativos, distancia media de viagem, duracao media de viagem. Util para revisao trimestral da cidade sem varrer /trips.
Autenticacao
O Levy Fleets aceita requisicoes MDS com token bearer. Cidades podem adicionalmente verificar o JWT assinado que retornamos em cada resposta.
Tokens bearer
Emitidos em Configuracoes -> API e Integracoes -> Tokens MDS. Um token por cidade por subconta e tipico. O token e verificado contra mds_city_tokens em toda requisicao.
Authorization: Bearer <opaque_token>
JWT assinado em cada resposta
Toda resposta bem-sucedida inclui um header MDS-JWT contendo um JWT assinado em RS256. O payload do JWT inclui o sha256 do corpo da resposta, o ID da subconta, o issuer (https://fleets.levyelectric.com) e o timestamp issued-at. Cidades que exigem prova de integridade podem verificar o JWT contra a URL JWKS.
A chave de assinatura e a linha ativa em mds_jwks_keys para a subconta. Gerada lazy na primeira requisicao. Veja Gerenciamento de Chaves JWKS para rotacao.
Assinatura HMAC opcional no corpo
Para cidades que exigem prova de integridade HMAC ao inves (ou alem) do JWT, defina o header MDS-Signature na resposta. O segredo compartilhado e configurado por linha de city-token.
Discovery JWKS
O conjunto JWK publico vive em:
GET /api/mds/{subaccountId}/.well-known/jwks.json
Forma da resposta:
{
"keys": [
{
"kty": "RSA",
"kid": "mds-2026-05",
"use": "sig",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}
O kid corresponde ao campo kid no header MDS-JWT para que cidades possam escolher a chave certa durante rotacao. Sempre publicamos a chave ativa mais quaisquer chaves ainda em sua janela de carencia de rotacao (padrao 7 dias).
Se uma cidade acessar a URL JWKS antes que a primeira requisicao tenha sido assinada, a rota gera lazy o primeiro par de chaves. Isso significa que novos operadores nao precisam tomar acao manual -- o primeiro ping do Populus e suficiente.
Envelope de resposta
Toda resposta MDS 2.0 usa a mesma forma externa:
{
"version": "2.0.1",
"last_updated": "2026-05-18T12:00:00Z",
"ttl": 60000,
"<data_key>": [ /* array de objetos MDS */ ],
"next_cursor": "..."
}
last_updatede sempre RFC3339 UTC (sem POSIX epoch -- MDS 2.0 exige RFC3339).ttlcorresponde ao headerCache-Control: max-age(em milissegundos -- spec MDS usa ms, HTTP usa s).next_cursoresta presente apenas quando ha mais paginas.
Paginacao por cursor
Cursores sao JSON { "time": <ISO>, "id": <last_id> } codificados em base64 opaco. Sao estaveis em escritas: uma nova linha inserida apos voce comecar o crawl aparecera em uma pagina futura, nunca no cursor atual.
Cidades devem:
- Fazer uma requisicao inicial sem
cursor. - Ler
next_cursorda resposta. - Passa-lo de volta na proxima requisicao:
?cursor=<value>. - Parar quando
next_cursorestiver ausente.
Tamanho de pagina padrao 100. Passe ?limit=<n> para sobrescrever (max 500).
Cache de borda
Cacheamos /vehicles, /vehicles/status, /stops, /reports e /.well-known/jwks.json no edge do Vercel por 60 segundos. Trips, events e telemetry nao sao cacheados -- sao delimitados por tempo via query, entao cada requisicao tem uma chave de cache unica de qualquer forma.
O header Cache-Control: public, s-maxage=60 e definido automaticamente em endpoints cacheados.
Rate limiting
Cada token bearer e limitado a 60 requisicoes por minuto com um burst de 600 requisicoes. Exceder o limite retorna HTTP 429 com Retry-After definido. Crawls de validador (10-15 endpoints consecutivos) ficam bem dentro do burst.
Se uma cidade consistentemente excede o limite, eleve via Configuracoes -> API e Integracoes -> Tokens MDS -> Override de Rate Limit.
Configuracao do validador
O mds-provider-validator do OMF (e o equivalente interno do Populus) espera:
| Verificacao | Valor |
|---|---|
| URL base | https://fleets.levyelectric.com/api/mds/<subaccountId>/provider/v2/ |
| Auth | Authorization: Bearer <token> |
| JWKS | https://fleets.levyelectric.com/api/mds/<subaccountId>/.well-known/jwks.json |
| Versao da spec | 2.0.1 |
| Content-Type | application/vnd.mds+json;version=2.0 |
Execute o validador voce mesmo antes de entregar a uma cidade:
mds-provider-validator \
--base "https://fleets.levyelectric.com/api/mds/<subaccountId>/provider/v2" \
--auth "Bearer <token>" \
--version 2.0.1 \
--jwks "https://fleets.levyelectric.com/api/mds/<subaccountId>/.well-known/jwks.json"
Um run limpo reporta Passed: all endpoints conform. Quaisquer falhas sao mostradas com o campo e endpoint problematicos e devem ser corrigidas antes de compartilhar a URL com uma cidade.
Solucao de problemas
- 401 em toda requisicao -- token bearer ausente ou revogado. Emita um novo em Configuracoes.
- Validador falha no campo
version-- confirme que o envelope de resposta reporta2.0.1, nao2.0.0. A versao e uma constante emsrc/lib/mds/v2/response.ts. - Header
MDS-JWTausente em uma resposta -- o par de chaves JWKS falhou em ser gerado. Visite/api/mds/<subaccountId>/.well-known/jwks.jsondiretamente para forcar um lazy mint, depois tente novamente. current_speed_limit_kphsempre 0 -- o veiculo nao tem posicao conhecida recente, ou nenhuma zona de operador / geofence de politica o contem. Verifique que uma amostra GPS recente existe emvehicle_events.
Veja Solucao de Problemas para a checklist completa.
Proximos passos
- Gerenciamento de Chaves JWKS -- rotacao, kid e janelas de carencia.
- Feeds GBFS 3.0 -- o feed publico ao lado da API MDS Provider.
- Ingestao de Politica de Cidades -- puxando regras do feed de politica da cidade.