Tradução por conveniência — a versão em inglês é a autoritativa.
Estado: Rascunho v0.1 · Uma especificação aberta e neutra em relação a fornecedores · Licença: aberta (ver §10)
OTEP é um protocolo aberto e neutro em relação a fornecedores para o ciclo de vida de qualquer sujeito rastreável. Ele define um modelo de evento e um vocabulário de estado para que os eventos de rastreamento de qualquer origem — entrega por frota própria, transportadoras terceirizadas, etiquetas de transportadoras e além — possam ser trocados, compreendidos e projetados para padrões internacionais sem reintegrar para cada parte.
Este documento é a especificação: a estrutura, os campos, as tabelas de estado, a máquina de estados, os mapeamentos para padrões externos e as regras de conformidade para construir uma implementação compatível. É independente da implementação — descreve o protocolo, não os componentes internos de qualquer fornecedor em particular. As palavras-chave RFC-2119 (MUST / SHOULD / MAY) são normativas.
O rastreamento é fragmentado: cada transportadora nomeia campos e códigos de estado de forma diferente, cada canal de entrega reporta no seu próprio formato, e conectar-se a padrões globais significa reintegrar repetidamente. O OTEP dá aos produtores e consumidores uma única linguagem comum: um produtor emite eventos OTEP uma vez, e todo consumidor OTEP os compreende e pode projetá-los para o padrão de que precisa.
Uma linha do tempo é um envelope que carrega o sujeito rastreado e uma lista ordenada de eventos.
{
"otep_version": "0.1",
"profile": "parcel", // domain profile (§8)
"subject": {
"tracking_number": "SR123...", // ≥1 identifier required
"order_id": 12345, // optional
"package_id": 67890, // optional
"external_tracking_number": "1Z...",// optional
"gs1_sscc": "00...", // optional — enables EPCIS epcList
"piece_id": "..." // optional — enables ONE Record linkage
},
"current_status": "delivered", // projection of the latest event
"delivered": true,
"events": [ /* OTEP events, §4 */ ]
}
{
// WHAT — carried once at the timeline level (subject above)
// WHEN
"occurred_at": "2026-06-10T09:30:00-04:00", // event instant, ISO-8601 with offset
"recorded_at": "2026-06-10T09:45:23-04:00", // ingestion instant (optional)
"time_type": "actual", // actual | estimated | scheduled
// WHERE
"location": {
"name": "Toronto Hub",
"code": "YYZ-2",
"gln": "0614141000005", // GS1 GLN → EPCIS SGLN
"lat": 43.6777, "lng": -79.6248,
"country": "CA" // ISO 3166-1 alpha-2
},
// WHY / WHAT HAPPENED
"status_code": "out_for_delivery", // an OTEP status code (§4)
"phase": "out_for_delivery", // derived from status_code
"incident_reason": null, // an OTEP incident reason (§4) when exception
// WHO
"actor": { "type": "driver", "name": "Jane D.", "phone": "+1..." },
// PROVENANCE
"source": {
"type": "self_delivery", // self_delivery | third_party_delivery | carrier_label
"provider_id": null,
"carrier_code": null,
"external_event_code": null, // your raw status code (preserve it)
"raw": { /* original payload */ }
},
// PROOF
"pod": {
"photos": [ { "url": "...", "content_base64": null } ],
"signature": [ { "url": "...", "content_base64": null } ],
"recipient": "John Smith"
}
}
Cada campo, exceto subject, occurred_at, status_code e source, é opcional —
as origens parciais preenchem o que têm. As leituras de nó/hub definem location para a instalação de leitura.
A telemetria GPS de alta frequência NÃO é um evento OTEP; um evento é emitido numa alteração de estado ou
numa leitura de nó.
20 códigos canónicos de ciclo de vida. phase é sempre derivável do código.
| código | phase | terminal | POD | significado |
|---|---|---|---|---|
information_submitted |
pre_shipment | informação do pedido recebida | ||
booking_confirmed |
pre_shipment | transportadora/reserva confirmada | ||
awaiting_pickup |
pre_shipment | pronto para recolha | ||
out_for_pickup |
pickup | a caminho da recolha | ||
picked_up |
pickup | ✓ | recolhido do expedidor | |
pickup_failed |
exception | tentativa de recolha falhou | ||
pickup_rescheduled |
exception | a recolha será repetida | ||
received |
inbound | recebido na instalação | ||
arrival_scan |
inbound | leitura de chegada no nó | ||
in_transit |
transit | em movimento | ||
package_outbound |
transit | saiu da instalação | ||
removed_from_route |
exception | retirado da rota | ||
route_cancelled |
exception | rota cancelada | ||
out_for_delivery |
out_for_delivery | no veículo | ||
delivered |
delivered | ✓ | ✓ | entregue ao destinatário |
delivery_failed |
exception | tentativa de entrega falhou | ||
delivery_rescheduled |
exception | será repetida / reentregue | ||
return_to_sender |
return | ✓ | a retornar à origem | |
rejected_by_recipient |
return | ✓ | destinatário recusou | |
cancelled |
return | ✓ | pedido cancelado |
pre_shipment · preparing · pickup · inbound · in_custody · transit ·
out_for_delivery · delivered · exception · return. (preparing e in_custody
estão reservados para perfis que não sejam de encomendas — §8.)
source.type: self_delivery · third_party_delivery · carrier_label.
time_type: actual · estimated · scheduled (padrão actual).
Quando um evento está na fase exception, ele SHOULD carregar um incident_reason deste
vocabulário normalizado:
carrier_damaged_parcel, carrier_sorting_error, carrier_address_not_found,
carrier_parcel_lost, carrier_not_enough_time, carrier_vehicle_issue,
carrier_capacity_exceeded, carrier_mechanical_delayretailer_cancelled, retailer_incorrect_data, retailer_not_ready,
retailer_incorrect_parcel, retailer_incorrect_dimensions, retailer_packaging_issueconsignee_refused, consignee_business_closed, consignee_not_available,
consignee_not_home, consignee_cancelled, consignee_verification_failed,
consignee_incorrect_address, consignee_access_restricted, consignee_safe_place_unavailablecustoms_delay, customs_documentation, customs_duties_unpaid,
customs_prohibited, customs_inspectionweather_delay, natural_disaster, force_majeureparcel_being_researched, security_issue, regulatory_hold, unknownAs fases avançam para a frente; as exceções interrompem e resolvem-se de volta. Os estados terminais (delivered,
return_to_sender, rejected_by_recipient, cancelled) encerram o sujeito.
pre_shipment → preparing → pickup → inbound → in_custody → transit → out_for_delivery → delivered ✓
└──────────── exception ──────────┘
└──────────── return / cancelled ✓
Regras:
occurred_at e MUST tolerar a chegada fora de ordem.As quatro dimensões do OTEP alinham-se campo a campo com os principais padrões, de modo que cada um é uma projeção de saída de um evento OTEP.
| OTEP | GS1 EPCIS 2.0 | IATA ONE Record | UN/CEFACT |
|---|---|---|---|
occurred_at (+offset) |
eventTime + eventTimeZoneOffset |
eventDate + eventTimeType=Actual |
Event Date/Time |
recorded_at |
recordTime |
recordedAt | — |
| subject (sscc/piece) | epcList (SSCC URN) |
linkedObject → Piece/Shipment |
Consignment |
location (gln/lat/lng) |
readPoint / bizLocation (SGLN) |
recordedAtLocation → Location |
Location |
status_code |
bizStep + disposition |
eventCode |
Transport status code |
actor |
sourceList / extension | Actor / Party | Party |
incident_reason |
disposition / ErrorDeclaration | event remark | Status reason code |
Os valores por código (EPCIS CBV bizStep/disposition, ONE Record eventCode, código UN/CEFACT)
são publicados no codebook legível por máquina. Para além destes padrões internacionais, uma
linha do tempo OTEP também pode ser projetada para traces de OpenTelemetry, observações de OGC SensorThings e
plataformas de comércio comuns (AfterShip, Shopify, Amazon, Walmart, BigCommerce, Magento,
WooCommerce, Etsy).
Confiança: os valores EPCIS CBV são URNs de padrão estáveis. Os valores de código de ONE Record e UN/CEFACT são a melhor aproximação para a última milha e devem ser validados contra as listas de códigos oficiais antes do uso externo. "Delivered to consignee" não tem um bizStep CBV exato — a aproximação mais próxima (
receiving+received) é utilizada, ou uma URN de extensão de vocabulário do utilizador.
O OTEP é consumido através de uma pequena superfície HTTP apenas de leitura; todos os endpoints são públicos.
| Verbo | Caminho | Retorna |
|---|---|---|
| GET | /api/v1/otep/trackings/{tracking_number} |
a linha do tempo para um número de rastreamento |
| GET | /api/v1/otep/trackings/{tracking_number}/events |
apenas eventos |
| POST | /api/v1/otep/trackings/batch |
vários números de rastreamento numa só chamada |
| POST | /api/v1/otep/validate |
verificação de conformidade para uma linha do tempo enviada (§9) |
Também está disponível uma consulta GraphQL que expõe a mesma linha do tempo.
A mesma linha do tempo é serializada na representação que solicitar, através de um parâmetro de
consulta ?format= ou de um perfil Accept:
| Solicitação | Representação |
|---|---|
?format=otep (padrão) |
linha do tempo OTEP nativa |
?format=epcis |
GS1 EPCIS 2.0 (JSON-LD ObjectEvents) |
?format=onerecord |
IATA ONE Record (LogisticsEvents) |
?format=uncefact |
estado de transporte UN/CEFACT |
?format=otlp |
traces de OpenTelemetry |
?format=sensorthings |
observações de OGC SensorThings |
?format=aftership | shopify | amazon | walmart | bigcommerce | magento | woocommerce | etsy |
o formato de rastreamento/fulfillment da plataforma |
Os eventos que não podem receber um código numa projeção são ignorados e contabilizados (nunca silenciosamente descartados).
O OTEP é um protocolo geral, não um protocolo de encomendas. A camada de protocolo (envelope de evento, espinha de
fases, máquina de estados) é universal; os códigos de estado concretos pertencem a um perfil declarado na
linha do tempo através de profile. Os códigos em §4 são o perfil parcel. Outros domínios —
mudanças, entrega de comida, armazenamento e além — adicionam os seus próprios conjuntos de códigos sob o seu perfil,
com namespace otep:<profile>:<code>, cada um mapeando para a mesma espinha de fases. Adicionar um perfil
é uma extensão, não uma alteração de protocolo.
Um produtor ou consumidor é conforme com OTEP quando cada evento que emite ou aceita satisfaz estas tabelas e regras.
| Campo | Tipo | Req. | Restrições |
|---|---|---|---|
otep_version |
string | MUST | semver, e.g. 0.1 |
profile |
string | MUST | um perfil registado |
subject |
object | MUST | §9.2 |
current_status |
string|null | SHOULD | um código de estado (§4) |
current_phase |
string|null | SHOULD | MUST ser igual à fase de current_status se ambos estiverem presentes |
delivered |
boolean | SHOULD | true se e só se current_status = delivered |
events |
array | MUST | objetos de evento (§9.3), ordenáveis por occurred_at |
subjectPelo menos UM de tracking_number / order_id / package_id MUST estar presente.
| Campo | Tipo | Restrições |
|---|---|---|
tracking_number |
string | não vazio |
order_id / package_id |
integer|null | |
external_tracking_number |
string|null | |
gs1_sscc |
string|null | 18 dígitos |
piece_id |
string|null |
| # | Campo | Tipo | Req. | Restrições |
|---|---|---|---|---|
| 1 | occurred_at |
string | MUST | ISO-8601 com offset |
| 2 | recorded_at |
string|null | SHOULD | ISO-8601 |
| 3 | time_type |
string | MAY (padrão actual) |
actual | estimated | scheduled |
| 4 | status_code |
string|null | MUST¹ | um código em §4.1 |
| 5 | phase |
string|null | SHOULD | MUST ser igual à fase de status_code |
| 6 | incident_reason |
string|null | SHOULD² | um motivo em §4.4 |
| 7 | description |
string|null | MAY | legível por humanos |
| 8 | location |
object|null | MAY | §9.4 |
| 9 | actor |
object|null | MAY | { type, name?, phone? }; type ∈ {driver, operator, carrier, system} |
| 10 | source |
object | MUST | §9.5 |
| 11 | pod |
object|null | MAY³ | { photos[], signature[], recipient? } |
¹ Um evento codificado MUST carregar um código de estado de §4.1. Uma leitura bruta que ainda não consegue classificar MAY definir
status_code = null, mas MUST preservar o código nativo em source.external_event_code e MUST
ser contabilizado, nunca descartado.
² Um evento na fase exception SHOULD carregar um incident_reason.
³ Os eventos com status_code ∈ {delivered, picked_up} SHOULD carregar um pod.
locationname (string) · code (string) · gln (GS1 GLN, 13 dígitos) · lat / lng (WGS-84) ·
country (ISO 3166-1 alpha-2). Todos opcionais.
source| Campo | Tipo | Req. | Restrições |
|---|---|---|---|
type |
string | MUST | self_delivery | third_party_delivery | carrier_label |
provider_id |
integer|null | MAY | |
carrier_code |
string|null | MAY | |
external_event_code |
string|null | SHOULD⁴ | o seu código de estado bruto |
raw |
object|null | MAY | payload original |
⁴ MUST estar presente quando status_code é null, para que o código nativo nunca se perca.
occurred_at MUST ser analisável como ISO-8601. Normalize epochs numéricos e .NET /Date(ms)/
na ingestão; não emita essas formas.occurred_at, tolerar a chegada fora de ordem,
e desduplicar por (subject, status_code, occurred_at).phase, current_status, current_phase, delivered são projeções — se
presentes, MUST ser consistentes com a linha do tempo de eventos.Verifique a sua saída enviando uma linha do tempo via POST para POST /api/v1/otep/validate. Trate quaisquer
errors como bloqueadores; resolva os warnings. Um JSON Schema legível por máquina e o codebook completo
(cada código de estado, fase e mapeamento externo) são publicados para validação offline.
O OTEP é uma especificação aberta, livre para qualquer parte implementar.
otep:<profile>:<code>, as fases como
otep:phase:<name>. Uma vez publicado numa versão lançada, o significado de um identificador é imutável.otep_version).x-
(otep:parcel:x-my_code) até serem registados.O OTEP convida outros fornecedores a trazerem o seu próprio padrão de evento de rastreamento para que o OTEP possa interoperar com ele, em qualquer direção:
source.external_event_code; os códigos não mapeados são contabilizados, nunca descartados.Mesmo que não consiga adotar o OTEP diretamente, é bem-vindo a adicionar um único otep_status normalizado
às respostas da sua própria API e a partilhar os seus códigos de evento de rastreamento para mapeamento de crosswalk. Proponha
os mapeamentos contra esta especificação (em vez de bifurcar) para que os implementadores convirjam; os novos formatos integram-se na
mesma negociação de conteúdo ?format= e nunca quebram um consumidor existente.