API & MCP

Cotação

POST/api/v1/quote

Gera um orçamento a partir do consumo ou do valor da conta de luz. Campos do corpo (JSON, até 4 KB):

CampoTipoDescrição
consumoKwhMesnumberObrigatório, ou valorContaBrl. Consumo médio mensal em kWh, maior que zero. Fora de uma faixa plausível: 400 consumo-fora-de-faixa. Com os dois campos, vale o consumo.
valorContaBrlnumberO valor total de uma conta de luz, em reais, para quando o cliente não sabe o kWh. O consumo sai estimado pela tarifa e a resposta traz consumoEstimado: true. Veja Pelo valor da conta de luz.
ufstringUF de 2 letras. Define as horas de sol (a média do estado, quando a cidade não casa), a tarifa padrão e o estado do cliente para o deslocamento e o raio de atendimento. Padrão SP. UF desconhecida vale a média nacional (hspFonte: "fallback").
cidadestringRecomendado. Quando o nome casa com uma das 100 cidades do Atlas Brasileiro de Energia Solar, as horas de sol são as do município (hspFonte: "cidade-atlas"). Também decide o raio de atendimento e o deslocamento da conta. Mande já na simulação: sem ela, o número pode mudar na hora de gravar.
fasesstringmono, bi ou tri (aceitamos também monofasico, bifasico e trifasico, com ou sem acento, em qualquer caixa). Padrão mono. Outro valor: 422 fase-invalida.
kwpnumberPotência desejada. Sem ela, dimensionamos pelo consumo menos a taxa mínima da distribuidora (30, 50 ou 100 kWh, pela ligação). É um alvo: o sistema entregue é o do kit ou da composição mais próxima, e cobrePct diz quanto ele cobre.
tarifanumberTarifa em R$/kWh. Sem ela, vale a tarifa residencial B1 da UF homologada pela ANEEL (SP: 0,99), a mesma das páginas por cidade do site. O valor informado fica entre 0,01 e 10.
telhadostringceramico, metalico, fibrocimento, laje, solo ou carport. Escolhe a estrutura das cotações montadas por combinação automática ou pelo catálogo padrão. Muda o preço só no modo Custo → venda (nos outros, o preço sai da tabela por kWp e a estrutura vai como item do documento). Sem ele, o telhado padrão da conta. assumptions.telhado diz quando a estrutura entrou na conta.
nome, email, telefonestringDados do lead, usados ao gravar. E-mail fora do formato e telefone com menos de 8 dígitos são descartados.
previewbooleantrue só simula: não grava, não gera PDF. Padrão false. Aceita também "true" ou "1", e o parâmetro ?preview=true.
consentbooleanObrigatório ao gravar (preview ausente ou false): true confirma que o titular consentiu com o registro dos dados (LGPD). Registramos como prova a data, a versão do texto da política e metadados da requisição (IP, user-agent e origem, quando presentes). Sem ele, 400 consentimento-obrigatorio. Em preview é ignorado.
opcaostringsó no motor novo Qual opção vira a proposta: economica, recomendada ou bateria. Valor fora da lista: 400 opcao-invalida. Nas outras contas o campo é ignorado. Veja Contas com o motor novo.

Resposta, modo simulação (preview: true)

curl -X POST https://app.ionluz.com.br/api/v1/quote \
  -H "Authorization: Bearer ilz_sua_chave" \
  -H "Content-Type: application/json" \
  -d '{ "consumoKwhMes": 650, "uf": "SP", "fases": "bi", "preview": true }'
{
  "ok": true,
  "orcamento": {
    "potenciaKwp": 5.72,
    "valorFinal": 14296.8,
    "paybackAnos": 2.057067267830044,
    "economiaMes1": 569,
    "economia25Anos": 373181,
    "sePaga": true,
    "cobrePct": 111
  },
  "assumptions": {
    "uf": "SP",
    "fase": "bi",
    "hspHoraDia": 4.9,
    "hspFonte": "uf-media",
    "tarifaBrlKwh": 0.99,
    "tarifaAssumida": true,
    "kwpAlvo": 5.16,
    "kwpAlvoAssumido": true,
    "kitCatalogoPadrao": true
  },
  "preview": true,
  "aviso": "Estimativa sujeita a visita técnica.",
  "requestId": "req_9f2c1a7b3d4e5f6a90"
}

Conta que ainda cota pela referência de mercado (R$ 3.200 por obra + R$ 1.940 por kWp) e sem kit próprio na faixa. Com a sua tabela e os seus kits, os números mudam.

O bloco assumptions diz o que o motor assumiu quando você omitiu algo. tarifaAssumida: true: valeu a tarifa padrão da UF. hspFonte: cidade-atlas (horas de sol do município, com hspCidade), uf-media (média do estado) ou fallback (UF desconhecida, média nacional). kitCatalogoPadrao: true: a faixa não tinha kit seu e o equipamento veio do catálogo padrão ionluz. precoReferencia: true: a conta não tinha preço utilizável e o valor saiu da referência de mercado do catálogo. kitMultiplo: quantas unidades do mesmo kit foram somadas para chegar perto do alvo. Mostre ao cliente o que foi estimado.

orcamento.cobrePct vem em toda cotação: quanto do consumo o sistema cobre, em % da potência-alvo, que mira o consumo menos a taxa mínima da distribuidora (30, 50 ou 100 kWh, pela ligação). 100 é o alvo cheio; abaixo disso o sistema gera menos que o consumo.

Pelo valor da conta de luz (valorContaBrl)

Quem vai instalar em casa sabe quanto paga de luz e quase nunca sabe o consumo em kWh. Mande valorContaBrl, o valor total de uma conta, em reais, no lugar de consumoKwhMes. O consumo é estimado assim:

consumo estimado (kWh por mês) = valorContaBrl ÷ tarifa, arredondado ao kWh
  • A tarifa é a do campo tarifa ou, sem ele, a residencial B1 da UF (SP: 0,99), a mesma que entra no payback. Assim a economia do orçamento conversa com o valor que o cliente informou. Exemplo: R$ 500 em SP dão 500 ÷ 0,99 = 505 kWh por mês.
  • A taxa mínima da distribuidora (30, 50 ou 100 kWh, pela ligação) não sai do valor: sem energia solar, a conta cobra o maior entre o consumo e a taxa mínima, e o dimensionamento já desconta a taxa mínima. Conta que não passa dela responde 422 valor-conta-na-taxa-minima, com taxaMinimaKwh, taxaMinimaBrl, tarifaBrlKwh e fase: não há consumo para a energia solar compensar.
  • Valor que não é um número maior que zero: 400 valor-conta. Consumo estimado acima do plausível: 400 valor-conta-fora-de-faixa.
  • Com consumoKwhMes e valorContaBrl juntos, vale o consumo. Consumo zero ou vazio junto do valor conta como não informado.

A resposta traz, no topo, consumoEstimado: true e o consumoKwhMes usado; o resto é o de sempre. Diga ao cliente que o consumo foi estimado pelo valor da conta e peça a conta de luz para o número exato: iluminação pública, bandeira tarifária e a diferença entre a distribuidora dele e a de referência do estado ficam de fora da estimativa. Ao gravar, a proposta guarda o valor e a tarifa usados, e o webhook lead.criado traz consumoEstimado e valorContaBrl.

// pedido
{ "valorContaBrl": 500, "uf": "SP", "fases": "bi", "cidade": "Sorocaba", "preview": true }

// resposta: os campos de sempre, mais estes dois
{ "consumoEstimado": true, "consumoKwhMes": 505 }

Deslocamento e área de atendimento

Em Perfil e marca, aba Condições, bloco "Área de atendimento e deslocamento", a conta define a cidade-sede, um raio de atendimento em km e um valor por km.

  • Só cobra quem preencheu um valor por km maior que zero; em branco ou zero, nenhuma cotação ganha acréscimo.
  • A distância é a rodoviária estimada entre as sedes municipais (IBGE, a linha reta vezes 1,35). Mesma cidade não cobra. Cidade não reconhecida: no mesmo estado, sem acréscimo; em outro estado, a distância vai até o centro do estado.
  • Sem raio, o deslocamento vale para todo cliente fora da cidade-sede. Com raio, dentro dele o preço é o da tabela, sem deslocamento. Acima dele, conforme a escolha da conta: 422 fora-da-area (o padrão; o pedido vira contato) ou a cotação com o deslocamento da viagem inteira.
  • Quando há cobrança, ela entra no valorFinal antes do cálculo do payback e vira uma linha visível na proposta.

Conta com sede em Sorocaba cobrando R$ 2 por km, sem raio, e cliente de Campinas:

{
  "ok": true,
  "orcamento": {
    "potenciaKwp": 5.72,
    "valorFinal": 14502.8,
    "paybackAnos": 1.9319543963387549,
    "economiaMes1": 617,
    "economia25Anos": 396502,
    "sePaga": true,
    "cobrePct": 97,
    "deslocamento": {
      "km": 103,
      "valorBrl": 206,
      "destino": "Campinas",
      "descricao": "Deslocamento técnico até Campinas: 103 km, R$ 206 incluídos no valor"
    }
  },
  "assumptions": {
    "uf": "SP",
    "fase": "bi",
    "hspHoraDia": 5.2,
    "hspFonte": "cidade-atlas",
    "hspCidade": "Campinas",
    "tarifaBrlKwh": 0.99,
    "tarifaAssumida": true,
    "kwpAlvo": 5.91,
    "kwpAlvoAssumido": true,
    "kitCatalogoPadrao": true
  },
  "deslocamento": { "km": 103, "valorBrl": 206, "destino": "Campinas" },
  "...": "..."
}

O deslocamento de fora do orcamento é o mesmo, sem a descricao.

A mesma conta com um raio de 50 km e "pedir contato" acima dele:

{
  "ok": false,
  "error": "fora-da-area",
  "message": "O cliente está acima do raio de atendimento do perfil, e a conta pede contato em vez de preço fora do raio. A resposta traz distanciaKm, raioKm e destino.",
  "docs": "https://app.ionluz.com.br/docs/api",
  "requestId": "req_9f2c1a7b3d4e5f6a90",
  "kwp": 5.91,
  "fase": "bi",
  "distanciaKm": 103,
  "raioKm": 50,
  "destino": "Campinas"
}

Com "cotar e cobrar o deslocamento" no lugar de "pedir contato", o mesmo pedido volta cotado com os 103 km inteiros: R$ 206 na linha de deslocamento.

Resposta, modo gravação (preview: false, exige consent: true)

curl -X POST https://app.ionluz.com.br/api/v1/quote \
  -H "Authorization: Bearer ilz_sua_chave" \
  -H "Idempotency-Key: pedido-8421" \
  -H "Content-Type: application/json" \
  -d '{ "consumoKwhMes": 620, "uf": "SP", "cidade": "Sorocaba", "fases": "bi", "nome": "Cliente Exemplo", "email": "cliente@exemplo.com.br", "telefone": "(15) 99999-0000", "consent": true }'
{
  "ok": true,
  "orcamento": {
    "potenciaKwp": 5.72,
    "valorFinal": 14296.8,
    "paybackAnos": 2.1406530441031313,
    "economiaMes1": 545,
    "economia25Anos": 375409,
    "sePaga": true,
    "cobrePct": 120
  },
  "assumptions": {
    "uf": "SP",
    "fase": "bi",
    "hspHoraDia": 5.05,
    "hspFonte": "cidade-atlas",
    "hspCidade": "Sorocaba",
    "tarifaBrlKwh": 0.99,
    "tarifaAssumida": true,
    "kwpAlvo": 4.75,
    "kwpAlvoAssumido": true,
    "kitCatalogoPadrao": true
  },
  "proposalId": "clx8h2k9a0001abcd",
  "pdfUrl": "https://app.ionluz.com.br/api/pub/clx8h2k9a0001abcd/pdf?t=...",
  "propostaUrl": "https://app.ionluz.com.br/p/clx8h2k9a0001abcd?t=...",
  "aviso": "Estimativa sujeita a visita técnica.",
  "requestId": "req_9f2c1a7b3d4e5f6a90"
}

Ao gravar, você recebe o proposalId, o pdfUrl (PDF pronto, com a sua marca) e o propostaUrl (a página interativa que o cliente abre no celular, com antes e depois da conta, gráfico de payback e, se você ligou, financiamento). Os dois links já vêm assinados e podem ir ao cliente. A proposta aparece em Propostas com a origem "API", vale 15 dias (ou o que você definiu no perfil) e dispara o webhook lead.criado. Reenvio com a mesma Idempotency-Key devolve esta mesma resposta com "idempotent": true.

Contas com o motor novo

O ionluz está ligando um motor novo de cotação conta a conta. Nessas contas a resposta mantém todos os campos acima e ganha outros; nenhum campo muda de nome. Para saber se a conta que você integra já responde assim, olhe a resposta: ela traz sistema e preco.publicado. Programe para as duas situações e a integração não quebra quando a conta mudar.

O preço ao cliente segue a publicação da conta. A conta escolhe em Sua página, cartão "Como a página mostra o preço", e a escolha vale para a página, a API e o MCP:

  • Automático (recomendado): o valor exato quando a tabela é da conta; uma faixa de 5% para baixo e para cima, com as pontas arredondadas para fora em R$ 100, enquanto a conta cota pela referência de mercado;
  • O valor exato: o valor;
  • Uma faixa de valor: "entre R$ X e R$ Y", de 1% a 25%, com o valor final confirmado na visita técnica;
  • Só o pedido, sem preço: nenhum preço; a empresa apresenta o valor em contato;
  • e, à parte, Eu aprovo antes: cada pedido espera o OK da empresa, e nenhum preço sai até lá.

Por isso: mostre ao cliente preco.publicado.rotulo (curto) ou preco.publicado.texto (a frase). Em faixa, orcamento.valorFinal é o centro da faixa: não mostre como valor exato. Com "eu aprovo antes" pendente ou "só o pedido", valorFinal e paybackAnos vêm null, o deslocamento vem sem o valor em reais, e o pdfUrl responde 409 até a empresa liberar o valor; o propostaUrl abre e diz ao cliente que o valor está em confirmação ou com a empresa.

// Mostra o preço como a conta publica. Serve para as duas situações:
// conta sem o motor novo (não há preco.publicado) e conta com ele.
function precoParaMostrar(resposta) {
  const p = resposta.preco && resposta.preco.publicado;
  if (p) return p.rotulo; // "R$ 14.297", "entre R$ 13.500 e R$ 15.100", "com a empresa", "em confirmação"
  return "R$ " + Math.round(resposta.orcamento.valorFinal).toLocaleString("pt-BR");
}

// Só os avisos escritos para o cliente final. Os de para: "integrador" são recados para você.
function avisosDoCliente(resposta) {
  return (resposta.avisos || []).filter(a => a.para === "cliente").map(a => a.texto);
}

O mesmo pedido de 600 kWh do começo, numa conta com o motor novo que ainda cota pela referência (por isso a faixa automática, entre R$ 13.500 e R$ 15.100):

{
  "ok": true,
  "orcamento": {
    "potenciaKwp": 5.72,
    "valorFinal": 14296.8,
    "paybackAnos": 2.208259927284166,
    "economiaMes1": 527,
    "economia25Anos": 363646,
    "sePaga": true,
    "cobrePct": 121
  },
  "assumptions": {
    "uf": "SP",
    "fase": "bi",
    "hspHoraDia": 4.9,
    "hspFonte": "uf-media",
    "tarifaBrlKwh": 0.99,
    "tarifaAssumida": true,
    "kwpAlvo": 4.73,
    "kwpAlvoAssumido": true,
    "kitCatalogoPadrao": true
  },
  "equipamento": [
    {
      "kind": "microinverter",
      "label": "Hoymiles HMS-2250",
      "qty": 2,
      "specId": "cmru6jcft0003ta7ilpghaxyf",
      "powerW": 2250,
      "origem": "equipamento"
    },
    {
      "kind": "module",
      "label": "Canadian Solar 715 Wp",
      "qty": 8,
      "specId": "cmru6jcg8000hta7i49lozqyj",
      "powerW": 715,
      "origem": "equipamento"
    },
    {
      "kind": "structure",
      "label": "Estrutura de fixação para telhado cerâmico",
      "qty": 8,
      "origem": "regra-da-casa"
    },
    {
      "kind": "monitoring",
      "label": "Hoymiles DTU (monitoramento)",
      "qty": 1,
      "origem": "regra-da-casa"
    }
  ],
  "sistema": {
    "kwp": 5.72,
    "kwpAlvo": 4.73,
    "cobrePct": 121,
    "modulos": 8,
    "saidaKw": 4.5,
    "geracaoMesKwh": 665,
    "fase": "bi"
  },
  "validade": { "dias": 15, "ate": "2026-10-10T14:05:00.000Z" },
  "avisos": [
    {
      "codigo": "preco-referencia",
      "texto": "Referência de mercado (pesquisa Greener, set/2026): a conta ainda não tem uma tabela de preço própria.",
      "para": "integrador"
    }
  ],
  "preco": {
    "publicado": {
      "modo": "faixa",
      "valor": 14296.8,
      "min": 13500,
      "max": 15100,
      "pct": 5,
      "rotulo": "entre R$ 13.500 e R$ 15.100",
      "texto": "Entre R$ 13.500 e R$ 15.100. O valor final é confirmado na visita técnica.",
      "nota": "O valor final é confirmado na visita técnica."
    }
  },
  "opcoes": [
    {
      "chave": "economica",
      "nome": "Econômica",
      "recomendada": false,
      "kwp": 4.29,
      "modulos": 6,
      "cobrePct": 91,
      "preco": { "modo": "faixa", "rotulo": "entre R$ 10.900 e R$ 12.100", "...": "..." },
      "...": "..."
    },
    {
      "chave": "recomendada",
      "nome": "Recomendada",
      "recomendada": true,
      "kwp": 5.72,
      "modulos": 8,
      "cobrePct": 121,
      "preco": { "modo": "faixa", "rotulo": "entre R$ 13.500 e R$ 15.100", "...": "..." },
      "...": "..."
    },
    {
      "chave": "bateria",
      "nome": "Com bateria",
      "recomendada": false,
      "kwp": 5.72,
      "modulos": 8,
      "cobrePct": 121,
      "bateriaKwh": 10,
      "nota": "Sistema híbrido, confirmado em projeto.",
      "preco": { "modo": "faixa", "rotulo": "entre R$ 46.800 e R$ 51.800", "...": "..." },
      "...": "..."
    }
  ],
  "opcao": "recomendada",
  "preview": true,
  "aviso": "Estimativa sujeita a visita técnica.",
  "requestId": "req_9f2c1a7b3d4e5f6a90"
}

Os outros modos de preco.publicado, no mesmo pedido:

// conta com o motor novo que escolheu publicar o valor exato
{
  "preco": {
    "publicado": {
      "modo": "exato",
      "valor": 14296.8,
      "rotulo": "R$ 14.297",
      "texto": "R$ 14.297"
    }
  },
  "...": "..."
}

// "só o pedido": valorFinal e paybackAnos vêm null
{
  "orcamento": {
    "potenciaKwp": 5.72,
    "valorFinal": null,
    "paybackAnos": null,
    "economiaMes1": 527,
    "economia25Anos": 363646,
    "sePaga": true,
    "cobrePct": 121
  },
  "preco": {
    "publicado": {
      "modo": "contato",
      "rotulo": "com a empresa",
      "texto": "A Empresa Exemplo Energia Solar apresenta o valor em contato com você."
    }
  },
  "...": "..."
}

// "eu aprovo antes" pendente, com deslocamento cobrado: valorFinal, paybackAnos e o valor do deslocamento vêm null
{
  "orcamento": {
    "potenciaKwp": 5.72,
    "valorFinal": null,
    "paybackAnos": null,
    "economiaMes1": 617,
    "economia25Anos": 396502,
    "sePaga": true,
    "cobrePct": 97,
    "deslocamento": {
      "km": 103,
      "valorBrl": null,
      "destino": "Campinas",
      "descricao": "Deslocamento técnico até Campinas: 103 km, incluído no valor"
    }
  },
  "deslocamento": { "km": 103, "valorBrl": null, "destino": "Campinas" },
  "preco": {
    "publicado": {
      "modo": "aprovacao",
      "rotulo": "em confirmação",
      "texto": "A Empresa Exemplo Energia Solar confirma o valor e te chama."
    }
  },
  "...": "..."
}

Campos novos. sistema: kWp entregue, kWp-alvo, cobrePct, módulos, saída CA em kW, geração média mensal do 1º ano e a ligação. equipamento: marca, modelo e quantidade de cada item, com origem (equipamento, extras-da-conta ou regra-da-casa), sem fornecedor e sem custo. validade: os dias do perfil (padrão 15) e a data. avisos: códigos estáveis com texto pronto; para diz a quem o texto se dirige. Mostre ao cliente só os de para: "cliente" (cobre-pouco e gera-mais); os demais (kit-multiplicado, preco-referencia, tabela-sem-preco, custo-desconhecido, preferencia-indisponivel e extras-sem-custo) são para você.

Opções e a escolha do cliente

Com as opções ligadas (o padrão; no mesmo cartão, "Mostrar três opções"), a resposta traz opcoes quando há pelo menos duas: a econômica (a mesma linha de equipamento, o menor sistema que cobre pelo menos 80% do consumo compensável, e só quando sai mais barata), a recomendada (a cotação principal) e a com bateria (a recomendada com a bateria sugerida pelo consumo da noite, sistema híbrido confirmado em projeto; só quando a tabela da conta tem preço de bateria, e a referência de mercado tem). Cada opção traz potência, economia, payback, equipamento e o preço como a conta publica.

Mande em opcao a que o cliente escolheu. Sem o campo, vale a recomendada. A resposta descreve a opção que vale (orcamento, sistema, equipamento e preco.publicado) e diz qual em opcao; em preview é igual, sem gravar. Opção que não veio nesta cotação vira a recomendada, e opcao mostra isso. Valor fora da lista responde 400 opcao-invalida, com parametro e aceito, mesmo quando a conta desligou as opções. A proposta gravada guarda as opções e a escolhida, e a página da proposta mostra a escolhida com o resumo das outras.

// pedido
{ "consumoKwhMes": 600, "uf": "SP", "fases": "bi", "nome": "Cliente Exemplo", "email": "cliente@exemplo.com.br", "telefone": "(15) 99999-0000", "opcao": "bateria", "consent": true }

// resposta: orcamento, sistema, equipamento e preco.publicado são os da opção escolhida
{
  "ok": true,
  "orcamento": { "potenciaKwp": 5.72, "valorFinal": 49296.8, "...": "..." },
  "...": "...",
  "equipamento": [
    "...",
    {
      "kind": "battery",
      "label": "Bateria de 10 kWh (sistema híbrido, confirmado em projeto)",
      "qty": 1,
      "powerW": null,
      "origem": "equipamento"
    }
  ],
  "preco": {
    "publicado": { "modo": "faixa", "rotulo": "entre R$ 46.800 e R$ 51.800", "...": "..." }
  },
  "opcoes": [
    { "chave": "economica", "...": "..." },
    { "chave": "recomendada", "...": "..." },
    { "chave": "bateria", "...": "..." }
  ],
  "opcao": "bateria",
  "proposalId": "clx8h2k9a0001abcd",
  "pdfUrl": "https://app.ionluz.com.br/api/pub/clx8h2k9a0001abcd/pdf?t=...",
  "propostaUrl": "https://app.ionluz.com.br/p/clx8h2k9a0001abcd?t=...",
  "aviso": "Estimativa sujeita a visita técnica.",
  "requestId": "req_9f2c1a7b3d4e5f6a90"
}

Exemplos de código

// Node.js 18+ (fetch nativo), no seu servidor. A chave nunca vai ao navegador.
async function cotar(consumoKwhMes, uf = "SP", cidade) {
  const res = await fetch("https://app.ionluz.com.br/api/v1/quote", {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + process.env.IONLUZ_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ consumoKwhMes, uf, cidade, preview: true }),
  });
  const data = await res.json();
  if (!data.ok) throw new Error(data.error + ": " + data.message);
  return data; // orcamento, assumptions e, nas contas com o motor novo, preco.publicado
}
import os, requests

def cotar(consumo_kwh_mes, uf="SP", cidade=None, gravar=False):
    corpo = {"consumoKwhMes": consumo_kwh_mes, "uf": uf, "preview": not gravar}
    if cidade:
        corpo["cidade"] = cidade
    if gravar:
        corpo["consent"] = True  # obrigatório ao gravar: o titular consentiu (LGPD)
    r = requests.post(
        "https://app.ionluz.com.br/api/v1/quote",
        headers={"Authorization": f"Bearer {os.environ['IONLUZ_API_KEY']}"},
        json=corpo,
        timeout=30,
    )
    data = r.json()
    if not data["ok"]:
        raise RuntimeError(f"{data['error']}: {data['message']}")
    publicado = (data.get("preco") or {}).get("publicado")
    if publicado:
        preco = publicado["rotulo"]  # o preço como a conta publica
    else:
        preco = "R$ " + f"{round(data['orcamento']['valorFinal']):,}".replace(",", ".")
    return data, preco
API v1 · ionluz, propostas solares honestas. Dúvidas? Cite o requestId ao escrever para suporte@ionluz.com.br.
Cotação, POST /api/v1/quote · API ionluz