Inbound API
Registrar solicitações no DirectLuz vindas de sistemas externos.
Endpoint
POST /api/external/service-ordersURL completa em produção: https://app-directluz-v2-backend-prod.azurewebsites.net/api/api/external/service-orders
Cada solicitação recebida vira um Alerta no DirectLuz. Dependendo da política da cidade, esse alerta pode virar uma Ordem de Serviço (OS) imediatamente ou aguardar triagem manual da equipe operacional. O sistema externo sempre recebe 201 quando o alerta é aceito — a existência (ou não) de OS é refletida na resposta.
Cabeçalhos
| Cabeçalho | Obrigatório | Descrição |
|---|---|---|
Content-Type | Sim | application/json |
X-Api-Key | Sim | Chave de API emitida pela DirectLuz |
Idempotency-Key | Recomendado | UUID v4 único por solicitação. Permite reentrega segura |
Accept-Language | Não | pt-BR por padrão |
Estrutura do payload
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
protocolNumber | string | Sim | Identificador único da solicitação no sistema externo. Reenvios com o mesmo protocolNumber atualizam o alerta existente em vez de duplicar |
assetType | string (enum) | Sim | Tipo de ativo sobre o qual a OS atuará. Valor aceito nesta versão: POLE. Requisições com valor não reconhecido recebem 422 |
requestedAt | string (ISO 8601) | Sim | Data e hora em que a solicitação foi registrada na origem |
requester | object | Sim | Dados do solicitante |
requester.name | string | Sim | Nome do solicitante |
requester.phone | string (E.164) | Não | Telefone ou WhatsApp de contato |
lampType | string | Não | Tipo de lâmpada relatado (ex.: LED, Sodio, VaporDeMercurio). Aplicável quando assetType = POLE |
reasonCode | string | Não | Código do motivo do alerta no sistema do parceiro. O DirectLuz mantém uma tabela de tradução por parceiro (DE/PARA) para mapear esse código ao motivo interno apropriado. Se vier ausente, vazio ou desconhecido, o alerta é criado com o motivo padrão EXTERNAL_OTHER (prioridade Média, SLA 24h) e o operador classifica na triagem |
address | object | Sim | Localização da solicitação. Pode conter desde um único campo até o conjunto completo |
address.street | string | Sim | Logradouro. Único campo de endereço obrigatório |
address.number | string | Não | Número da residência mais próxima. Aceita S/N ou variantes |
address.neighborhood | string | Não | Bairro |
address.reference | string | Não | Ponto de referência informado pelo cidadão |
address.latitude | number | Não | Latitude WGS-84. Quando ausente, a DirectLuz realiza geocodificação a partir dos demais campos |
address.longitude | number | Não | Longitude WGS-84. Quando ausente, a DirectLuz realiza geocodificação a partir dos demais campos |
problem | object | Sim | Descrição do problema |
problem.description | string | Sim | Texto descrevendo o problema reportado |
problem.additionalNotes | string | Não | Observação adicional |
attachments | array | Não | Lista de anexos (fotografias do logradouro/ativo). Ver seção de Anexos |
Sobre o motivo do alerta (reasonCode)
O parceiro envia o código que ele já usa internamente (ex.: LAMP_OUT, POLE_DAMAGED). A operação da cidade cadastra no painel administrativo um mapeamento DE/PARA entre esses códigos e os motivos internos do DirectLuz — assim, o parceiro não precisa conhecer ou conformar-se à taxonomia interna. Quando um código novo (ainda não mapeado) chega, o alerta é criado com o motivo padrão EXTERNAL_OTHER para não bloquear a integração; o operador pode reclassificar manualmente e o administrador cadastra o mapeamento para os próximos.
Sobre o nível de detalhe do endereço
O sistema externo recebe solicitações de cidadãos com nível de detalhe variável. A DirectLuz aceita ambos os extremos: o único campo de endereço obrigatório é address.street.
Sobre coordenadas geográficas
Quando o payload não contém address.latitude e address.longitude, a DirectLuz realiza geocodificação interna a partir dos campos textuais. Se a geocodificação falhar (provider indisponível, endereço ambíguo), a solicitação continua sendo aceita (201) — o alerta fica visível na caixa de entrada operacional do DirectLuz com a marcação "endereço não geolocalizado", e um operador resolve manualmente, ou um job periódico re-tenta a geocodificação automaticamente. O sistema externo não precisa fazer nada diferente nesse cenário.
Exemplo de requisição — payload completo
POST /api/external/service-orders HTTP/1.1
Host: api.directluz.com.br
Content-Type: application/json
X-Api-Key: dlz_live_8f2c4a1b9e7d3a5c6b8e1f9d2c4a7b5e
Idempotency-Key: 9f4d2c1a-7b6e-4a8d-9c1f-2e5b8d3a4c7f
{
"protocolNumber": "EXT-2026-001234",
"assetType": "POLE",
"requestedAt": "2026-05-20T13:42:00Z",
"requester": {
"name": "Maria da Silva",
"phone": "+5583999998888"
},
"lampType": "LED",
"reasonCode": "LAMP_OUT",
"address": {
"street": "Av. Exemplo",
"number": "1320",
"neighborhood": "Centro",
"reference": "Em frente ao mercadinho",
"latitude": -7.114,
"longitude": -34.831
},
"problem": {
"description": "Poste apagado há três dias",
"additionalNotes": "À noite a rua fica muito escura"
},
"attachments": [
{
"type": "photo",
"url": "https://parceiro.exemplo.gov.br/files/abc123.jpg",
"filename": "logradouro.jpg",
"contentType": "image/jpeg"
}
]
}Exemplo de payload com endereço parcial
{
"protocolNumber": "EXT-2026-001235",
"assetType": "POLE",
"requestedAt": "2026-05-20T14:10:00Z",
"requester": { "name": "João Pereira" },
"address": { "street": "Rua das Trincheiras" },
"problem": { "description": "Lâmpada queimada" }
}Resposta de sucesso
201 Created — solicitação registrada. O conteúdo da resposta depende da política de intake configurada pela cidade.
A resposta sempre inclui um objeto alert com os dados do alerta criado a partir das informações da requisição.
Modo automático (OS gerada imediatamente)
Quando a cidade está configurada para abrir OS automaticamente, o alerta é criado e já vinculado a uma OS. O campo internalProtocol traz o número humano-legível da OS gerada. O id raiz refere-se à OS; o alerta fica em alert.
{
"id": "8c2b4a1f-3d6e-4b9a-bc2f-5a7d8e1c9b3a",
"internalProtocol": "OS-00042",
"protocolNumber": "EXT-2026-001234",
"assetType": "POLE",
"status": "OPEN",
"priority": "Medium",
"createdAt": "2026-05-20T13:42:05Z",
"alert": {
"id": "3e7f1a2b-9c4d-4e8a-b1f2-6d5c8a9b0e1f",
"alertNumber": 42,
"alertReasonCode": "LAMP_OUT",
"alertReasonName": "Lâmpada apagada",
"requesterName": "Maria da Silva",
"requesterPhone": "+5583999998888",
"description": "Poste apagado há três dias",
"additionalNotes": "À noite a rua fica muito escura",
"lampType": "LED",
"externalRequestedAt": "2026-05-20T13:42:00Z",
"latitude": -7.114,
"longitude": -34.831,
"status": "Linked",
"attachments": [
{
"success": true,
"blobPath": "service-orders-external/cidade/3e7f1a2b.../f9d8e7c6...jpg",
"filename": "logradouro.jpg",
"contentType": "image/jpeg",
"sizeBytes": 124500,
"error": null
}
]
}
}Modo manual (alerta aguardando triagem)
Quando a cidade exige triagem manual, o alerta é registrado e fica aguardando classificação operacional. A OS só é gerada depois — quando isso acontecer, o sistema externo recebe um webhook ServiceOrder.Created. O id raiz refere-se ao alerta nesse cenário; internalProtocol é omitido até o alerta virar OS.
{
"id": "5a3d1c9f-2b4e-4a8d-9c1f-7e6b5d8a3c4f",
"protocolNumber": "EXT-2026-001234",
"assetType": "POLE",
"status": "RECEIVED",
"createdAt": "2026-05-20T13:42:05Z",
"alert": {
"id": "5a3d1c9f-2b4e-4a8d-9c1f-7e6b5d8a3c4f",
"alertNumber": 42,
"alertReasonCode": "LAMP_OUT",
"alertReasonName": "Lâmpada apagada",
"requesterName": "Maria da Silva",
"requesterPhone": "+5583999998888",
"description": "Poste apagado há três dias",
"additionalNotes": "À noite a rua fica muito escura",
"lampType": "LED",
"externalRequestedAt": "2026-05-20T13:42:00Z",
"latitude": -7.114,
"longitude": -34.831,
"status": "Pending",
"attachments": [
{
"success": true,
"blobPath": "service-orders-external/cidade/5a3d1c9f.../a1b2c3d4...jpg",
"filename": "logradouro.jpg",
"contentType": "image/jpeg",
"sizeBytes": 124500,
"error": null
}
]
}
}200 OK — solicitação já havia sido registrada (idempotência ou reenvio com mesmo protocolNumber). Retorna o resultado original sem efeito colateral.
Campos da resposta
| Campo | Origem | Descrição |
|---|---|---|
id | DirectLuz (interno) | UUID v4 da entidade criada — OS quando modo automático, alerta quando triagem manual |
internalProtocol | DirectLuz (interno) | OS-NNNNN. Presente apenas no modo automático |
protocolNumber | Sistema externo | Eco do protocolo informado na requisição |
assetType | Eco do payload | Tipo de ativo (POLE nesta versão) |
status | DirectLuz | OPEN (OS criada) ou RECEIVED (alerta aguardando triagem) |
priority | DirectLuz | Presente apenas no modo automático |
createdAt | DirectLuz | Data/hora UTC de criação da entidade principal |
alert.id | DirectLuz (interno) | UUID v4 do alerta criado |
alert.alertNumber | DirectLuz (interno) | Número sequencial interno do alerta |
alert.alertReasonCode | DirectLuz (interno) | Código do motivo interno aplicado ao alerta (ex.: LAMP_OUT, EXTERNAL_OTHER) |
alert.alertReasonName | DirectLuz (interno) | Nome legível do motivo interno |
alert.requesterName | Eco do payload | Nome do solicitante |
alert.requesterPhone | Eco do payload | Telefone do solicitante (E.164) |
alert.description | Eco do payload | Descrição do problema (problem.description) |
alert.additionalNotes | Eco do payload | Observações adicionais (problem.additionalNotes) |
alert.lampType | Eco do payload | Tipo de lâmpada informado |
alert.externalRequestedAt | Eco do payload | Data/hora UTC da solicitação original (requestedAt) |
alert.latitude | DirectLuz | Latitude WGS-84 resolvida (vinda do payload ou geocodificação). null se não foi possível geolocalizar |
alert.longitude | DirectLuz | Longitude WGS-84 resolvida. null se não foi possível geolocalizar |
alert.status | DirectLuz | Status interno do alerta no DirectLuz. Ver tabela de valores abaixo |
alert.attachments | DirectLuz | Array com o resultado do processamento de cada anexo enviado. Sempre presente (pode ser []) |
alert.attachments[].success | DirectLuz | true se o anexo foi armazenado com sucesso; false em caso de falha |
alert.attachments[].blobPath | DirectLuz | Caminho interno no storage da DirectLuz. null se falhou |
alert.attachments[].filename | Eco do payload | Nome do arquivo |
alert.attachments[].contentType | Eco do payload | Tipo MIME do anexo |
alert.attachments[].sizeBytes | DirectLuz | Tamanho em bytes do arquivo armazenado. null se falhou |
alert.attachments[].error | DirectLuz | Descrição do erro quando success é false; null quando bem-sucedido |
Valores de alert.status
O campo reflete o estado interno do alerta no DirectLuz no momento da resposta.
| Valor | Descrição |
|---|---|
Pending | Alerta registrado e aguardando triagem manual por um operador. Estado inicial quando a política da cidade é triagem manual |
Linked | Alerta vinculado a uma Ordem de Serviço — a OS foi criada automaticamente (modo automático) ou manualmente por um operador |
Resolved | OS vinculada ao alerta foi concluída. O problema foi atendido |
Cancelled | Alerta descartado por um operador antes de ser vinculado a uma OS |
Anexos e fotografias
Cada item da lista attachments deve conter:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | string | Sim | Atualmente apenas photo |
url | string (HTTPS) | Sim, se contentBase64 não enviado | URL pública ou assinada com expiração mínima de 24 horas |
contentBase64 | string | Sim, se url não enviado | Conteúdo binário codificado em base64. Limite de 5 MB por anexo |
filename | string | Não | Nome original do arquivo |
contentType | string | Sim | image/jpeg, image/png ou image/webp |
A DirectLuz baixa o conteúdo do anexo e mantém cópia em storage próprio. Anexos cuja URL retorne falha após três tentativas são descartados e registrados em log para reconsulta manual.
Atenção com
contentBase64: envie apenas o conteúdo binário codificado em base64 puro — sem o prefixodata:image/jpeg;base64,. Exemplo correto:"/9j/4AAQSkZJRgAB...".
O resultado de cada anexo é retornado no array alert.attachments da resposta. Use o campo error para diagnosticar falhas sem precisar de acesso ao servidor.
Erros
Ver lista completa em Response Codes.