Pular para o conteúdo

Visão geral

A API pública do Faturei Hoje deixa outro sistema ler e escrever os dados da sua empresa sem passar pelo painel. São chamadas HTTP com JSON, autenticadas por uma chave que você cria, rotaciona e revoga no painel.

Cada chave pertence a uma empresa e age como um membro dela. A chave nunca faz mais do que esse membro faria pela tela.

  • ERP, CRM ou sistema próprio que precisa dos mesmos dados que a empresa vê no painel.
  • Site ou formulário que manda contato para dentro da empresa.
  • Ferramenta de automação que dispara chamadas HTTP, como n8n, Make e Zapier.

A chamada sai do seu servidor. A chave não deve aparecer no navegador do seu cliente nem dentro de um aplicativo instalado no celular dele.

https://api.fatureihoje.com/public/v1

Nesse endereço só existe /public/v1 e o arquivo /.well-known/security.txt. Qualquer outro caminho responde 404. Toda resposta traz o cabeçalho Request-Id e Cache-Control: no-store.

A v1 está em construção. Hoje a API publica treze áreas:

ÁreaRotas
ContaGET /public/v1/me: a empresa da chave, as permissões concedidas, o membro representado e o limite de chamadas
AgendaGET, POST, PATCH e DELETE em /public/v1/appointments, mais POST /public/v1/appointments/{id}/status
ClientesGET, POST, PATCH e DELETE em /public/v1/clients e em /public/v1/clients/{id}/addresses
LeadsPOST /public/v1/leads e GET /public/v1/leads/{id}: captação de formulário, com a tarefa de contato
OrçamentosGET, POST, PATCH e DELETE em /public/v1/quotes, mais POST em /{id}/send, /{id}/approve, /{id}/reject, /{id}/cancel e /{id}/convert_to_sale
Ordens de serviçoGET, POST, PATCH e DELETE em /public/v1/service_orders, mais POST em /{id}/status, /{id}/start, /{id}/complete e /{id}/cancel, POST /public/v1/service_orders/from_quote para converter orçamento aprovado em OS, e GET /public/v1/service_order_types com os tipos de OS da empresa
TarefasGET, POST, PATCH e DELETE em /public/v1/tasks, mais POST /public/v1/tasks/{id}/status
EquipeGET /public/v1/team: quem trabalha na empresa, numa lista só, para atribuir a tarefa de contato
Produtos e estoqueGET, POST, PATCH e DELETE em /public/v1/products, em /public/v1/products/{id}/variations e em /public/v1/product_categories, mais o saldo em GET /public/v1/products/{id}/stock e o extrato em GET /public/v1/stock_movements (estoque só leitura)
VendasGET, POST e PATCH em /public/v1/sales, mais POST e GET em /{id}/versions, POST em /{id}/cancel, /{id}/payments e /{id}/payments/{payment_id}/cancel, e PATCH /{id}/installments (sem exclusão: venda se cancela)
ServiçosGET, POST, PATCH e DELETE em /public/v1/services e em /public/v1/service_categories
FinanceiroGET, POST, PATCH e DELETE em /public/v1/financial_transactions, /public/v1/financial_accounts e /public/v1/financial_categories, mais POST /public/v1/financial_transactions/{id}/mark_paid, /transfer, /recurring e /installments
WebhooksGET, POST, PATCH e DELETE em /public/v1/webhook_endpoints, mais POST em /{id}/rotate_secret e /{id}/ping, o log de entregas em GET /{id}/deliveries e o reenvio em POST /{id}/deliveries/{delivery_id}/retry, só para dono e administradores; e os eventos dos últimos 30 dias em GET /public/v1/events e /public/v1/events/{id}, recortados pelos módulos que a chave lê

O envio de webhooks está descrito em Webhooks: o formato da entrega, a assinatura, as novas tentativas e o catálogo de eventos. Enquanto uma área não aparece na Referência, ela não responde: a Referência é gerada do código da API, então ela nunca lista rota que não existe.

Cadastrar um lead não sobrescreve cadastro que já existe: se o telefone ou o e-mail já estiver na base, a API reaproveita o cadastro, responde deduplicated: true e não muda nem os dados nem a situação dele.

A lista de equipe traz só o nome, o tipo e se a pessoa está ativa. Contato, custo por hora, comissão e código de acesso ao portal não saem na API.

A tarefa segue as mesmas regras do painel: ela nasce como todo, muda de situação por POST /public/v1/tasks/{id}/status (bloquear exige o motivo), e notify_via_whatsapp avisa o responsável pelo WhatsApp na criação. Excluir uma tarefa apaga de vez, e leva junto o histórico, os comentários, os anexos, as dependências e, quando a tarefa é a primeira de uma série que se repete, as demais ocorrências dela.

Tarefa interna do módulo de Facilities não aparece nesta área e não pode ser alterada por ela, nem na lista nem na consulta por identificador: para a API ela responde o mesmo 404 de tarefa que não existe.

O compromisso da agenda segue as mesmas regras do painel: ele conta na cota mensal do plano, aceita um período aproximado (morning, afternoon, evening, all_day) que move o horário para a janela daquele período no fuso da empresa, e o endereço escolhido precisa ser um endereço do cliente do compromisso. Quem conectou o Google Agenda vê o compromisso aparecer, mudar e sair de lá junto: criar espelha o evento, editar atualiza, cancelar e excluir removem. O lembrete de WhatsApp que a empresa configura nas automações vale para o compromisso marcado pela API exatamente como para o marcado pela tela, e usa quem marcou como um dos destinatários.

Aqui a API é diferente do painel, de propósito: o PATCH de compromisso não aceita status. No painel a edição grava a situação sem checar a transição, o que permite ressuscitar um compromisso cancelado ou pular direto para concluído; a API não copia isso. Mandar status no PATCH responde 422 nomeando o campo, e a mudança de situação acontece em POST /public/v1/appointments/{id}/status, que valida a transição: de agendado e de confirmado dá para ir a qualquer outra, concluído só volta para confirmado, e cancelado e não compareceu são pontos finais. Transição fora disso responde 409 conflict. Essa divergência é deliberada, e não é a única desta fase: a tarefa interna do módulo de Facilities responde 404 aqui, e assignee_type é obrigatório junto com assignee_id na tarefa, onde o painel adivinha o tipo da pessoa. Onde a API diverge do painel de propósito, a Referência diz no campo ou na operação.

Excluir um compromisso apaga de vez, e só o dono e os administradores da empresa podem. A ordem de serviço que apontava para ele continua existindo, sem o vínculo. Identificador e link do evento no Google Agenda, e o marcador de lembrete já enviado, nunca saem na resposta.

A ordem de serviço segue as mesmas regras do painel: conta nas cotas do plano, ganha o número da empresa, tem o total sempre calculado (subtotal menos desconto mais acréscimos), exige que cliente, técnicos, tipo e endereço sejam da empresa, e cobra pela API os campos que a empresa marcou como obrigatórios na configuração de OS. Quando a empresa exige tipo, os valores aceitos em type_id estão em GET /public/v1/service_order_types, que também diz, em required_on_create, se o tipo é obrigatório. Quem manda é a situação (status); a etapa do quadro do painel é apresentação e não sai na v1. Como no painel, não existe transição proibida: qualquer situação vai para qualquer outra por POST /public/v1/service_orders/{id}/status, e start, complete e cancel fazem o que os botões do painel fazem. O técnico responsável é technician_id, e a equipe de apoio é support_technicians; technician_id na lista traz a OS em que o técnico é o responsável ou está na equipe.

Os três efeitos colaterais da OS são os do painel, pelo mesmo código: send_to_technician na criação avisa o técnico e a equipe pelo WhatsApp, e só quando a OS tem técnico responsável (technician_id preenchido): sem técnico, ninguém é avisado; create_appointment na criação (com scheduled_start) cria o compromisso na agenda e o espelha no Google Agenda; e POST /public/v1/service_orders/{id}/complete com link_financial_transaction lança a receita no financeiro. Abrir compromisso e lançar no financeiro escrevem em outro módulo, então, quando o compromisso é de fato criado ou o lançamento é pedido, a chave precisa também de appointments:create e de finance:create, respectivamente; sem elas a resposta é 403 permission_missing e nada é gravado.

OS concluída ou cancelada não pode ser editada, como no painel: o PATCH responde 409 conflict. Pelo mesmo motivo, start, complete e cancel respondem 409 numa OS concluída ou cancelada, e repetir a conclusão não cria outra receita; para reabrir, use POST /public/v1/service_orders/{id}/status. Uma OS antiga ligada a cliente de outra empresa não lança receita ao concluir (422). Excluir uma OS arquiva, que é o que o painel faz: a OS não é apagada do banco, mas passa a responder 404 em toda rota e sai de toda lista. Só o dono e os administradores podem. Numa OS antiga ligada a cliente de outra empresa, nenhum dado daquele cliente sai: nome, e-mail, telefone e endereço vêm null. O link público do documento, o rascunho que o WhatsApp monta, a localização do técnico, o custo de mão de obra e o telefone do técnico nunca saem na resposta. Duas diferenças do painel, as duas a favor de quem integra: a OS arquivada responde 404 também na consulta por identificador, e um PATCH sem priority mantém a prioridade que a OS tinha.

O orçamento segue as mesmas regras do painel: conta nas cotas do plano, ganha o número da empresa, tem o subtotal, o total e a mensalidade sempre calculados a partir dos itens (item opcional só soma quando incluído, e a mensalidade, que são os itens da seção recurring_monthly, nunca soma com o total), deriva o tipo dos itens e usa os termos e as condições de pagamento padrão da empresa quando o campo não vem. Cliente e vendedor precisam ser da empresa. A lista é o funil do painel: modelo de orçamento, versão antiga e orçamento absorvido numa junção não aparecem nela; os dois últimos continuam consultáveis por identificador, e o modelo responde 404. Montar blocos, juntar orçamentos, versionar e o espelho de custo continuam só no painel.

As ações do orçamento fazem o que o painel faz, pelo mesmo código, e como no painel não existe transição proibida. send marca o orçamento como enviado e ativa o portal do cliente vinculado; ele não manda mensagem nem e-mail e não gera link público, porque no painel a mensagem e o código de acesso ao portal são passos à parte. Enviar pela API só marca o orçamento como enviado: o código de acesso do cliente ao portal é gerado e entregue pelo painel, e sem ele o cliente não abre o portal. approve é o seletor de situação do painel: registra a aprovação no histórico com o nome de quem aprovou, aprova todos os blocos, dispara as automações de orçamento aprovado e, quando a empresa ligou o contrato automático e o orçamento tem mensalidade, cria o contrato, uma vez só. reject exige o motivo. convert_to_sale cria a venda com itens, plano de pagamento, lançamentos no financeiro e baixa de estoque, como a janela de conversão do painel, e POST /public/v1/service_orders/from_quote cria a OS a partir de um orçamento aprovado, como a janela “Converter em OS”, com o mesmo aviso ao técnico e o mesmo compromisso da criação de OS; a chave precisa também de quotes:read, e orçamento antigo ligado a cliente de outra empresa é recusado com 422. Converter em venda escreve em outros módulos: a chave precisa de sales:create, e também de finance:create quando a requisição pede o lançamento no financeiro (track_in_finance: true ou uma conta); a baixa de estoque não pede products:update, porque baixar estoque é parte de vender. Sem track_in_finance, a venda vai para o financeiro pela configuração da empresa, e isso não cobra finance:create (veja Permissões).

Orçamento aprovado ou cancelado não pode ser editado, como no painel: o PATCH responde 409 conflict. Orçamento absorvido numa junção continua legível, mas não aceita edição, ação, exclusão nem conversão (409): quem vale é o orçamento pai, e a tela do painel também o tranca. As duas conversões cobram as cotas do plano, como o cadastro direto de OS e de venda. Na edição, items substitui a lista, e o item que já existia mantém o custo congelado de quando foi orçado, como na tela. Excluir um orçamento apaga de vez, e só o dono e os administradores podem; orçamento que já virou venda ativa não pode ser excluído (409) até a venda ser cancelada. O mesmo documento (ou o mesmo bloco) não vira duas vendas ativas, e o orçamento não vira duas OS: a segunda conversão responde 409. O custo unitário dos itens, a formação de preço e o link público do documento nunca saem na resposta, e num orçamento antigo ligado a cliente de outra empresa nenhum dado daquele cliente sai.

O catálogo segue as mesmas regras do painel: produto e serviço contam nas cotas do plano, o código interno (sku) não se repete na empresa, a categoria precisa ser da própria empresa (outra responde 422 com param category_id), e a árvore de categorias de produto tem dois níveis. Toda escrita em produto, variação, categoria e serviço revalida a loja virtual, como quando se salva pela tela. Custo de reposição, custo médio, custo de material e o custo dos movimentos de estoque nunca saem na resposta, e também não são campo de entrada.

O estoque é só leitura. GET /public/v1/products/{id}/stock traz o saldo do produto e de cada variação, o estoque mínimo e a unidade, e GET /public/v1/stock_movements traz o extrato: cada entrada, saída e ajuste, com o saldo depois do movimento. O saldo muda pelo cadastro (saldo de abertura), pela venda, pela entrada de mercadoria e pelo stock do PATCH do produto ou da variação. Como no painel, esse stock é o saldo absoluto (“deixe em 40”): a diferença para o saldo atual vira um ajuste manual (manual_adjustment) no extrato, assinado pelo membro que a chave representa.

Excluir um produto apaga de vez, como no painel, e leva junto as variações, o extrato de estoque inteiro do produto e das variações, e os vínculos com fornecedor. A entrada de mercadoria continua existindo, com a descrição do item e sem o vínculo, e orçamentos, OS e vendas guardam o item como texto e não mudam. Excluir uma variação leva o extrato dela junto. Categoria com produto ou serviço não pode ser excluída (409). Excluir um serviço também apaga de vez.

Quem tem mais de uma loja na mesma assinatura pode compartilhar produtos entre elas pelo painel. Cada loja tem a sua cópia do produto, e a chave de uma loja só enxerga e só altera a cópia da loja dela: a cópia de outra loja responde 404 em toda rota. is_shared e is_share_master dizem se o produto está compartilhado e se esta cópia é a ficha mestre. Editar a mestre pela API leva os campos sincronizados às cópias das outras lojas, exatamente como no painel, e revalida a loja virtual de cada uma; editar uma cópia muda só a loja dela. O saldo nunca viaja entre lojas. Excluir a mestre não apaga as cópias: a mais antiga passa a ser a mestre.

O PATCH de produto, variação, categoria e serviço altera só os campos que você mandar: campo que não vem fica exatamente como está, inclusive preço, saldo, descrição e a situação do serviço. Algumas conferências que no painel acontecem no formulário valem aqui no servidor e respondem 422 nomeando o campo: o vídeo precisa ser um endereço do YouTube e só aparece com o endereço preenchido; o texto do botão personalizado e o “substituir os botões padrão” só valem com o endereço do botão; as imagens (image_url) precisam ser um endereço http ou https completo; e o lc116_code do serviço precisa ser um item da lista de serviços da LC 116 que o painel oferece, porque ele vai direto para a emissão da nota fiscal.

A venda segue as mesmas regras do painel, pelo mesmo código: conta na cota do plano, ganha o número da empresa, monta o plano de pagamento, grava os recebimentos, lança no financeiro e baixa o estoque dos produtos do catálogo, como a tela. Subtotal e total são calculados a partir dos itens exatamente como o formulário calcula, com um modo de desconto só (discount_cents ou discount_percentage, de 0 a 100). O item do catálogo precisa ser um produto da empresa, com a variação quando o produto tem variações, e o nome e o preço promocional vêm do cadastro; o item avulso leva o nome que você mandar. Venda fiada (unpaid) precisa de cliente. A venda, a versão e o recebimento feitos pela API ficam marcados com created_channel igual a public_api, inclusive a venda que nasce de convert_to_sale; o que é feito pelo painel continua panel.

O id da venda é um só, para sempre. No painel, toda mudança comercial (itens, valores, cliente, vendedor ou plano do saldo) cria uma versão nova da venda; pela API essa mudança é POST /public/v1/sales/{id}/versions, com o motivo, e a venda continua respondendo pelo mesmo id, com version maior. Na versão nova, o item do catálogo que já estava na venda mantém o nome e a promoção gravados, como na tela; só o item novo vem do cadastro. GET /public/v1/sales/{id}/versions traz o histórico. A lista mostra cada venda uma vez, na versão que vale hoje, e o sale_id do extrato de estoque e do orçamento convertido é esse mesmo id. O PATCH só muda observações e produção; campo comercial no PATCH responde 422 nomeando o campo, porque mudança comercial tem rota própria. Não existe exclusão: venda se cancela, e cancelar estorna os recebimentos, devolve o estoque baixado e libera o orçamento ou a OS de origem para converter de novo.

As travas da venda são as do painel, ação por ação, incluindo o que no painel só a tela impede:

Situação da vendaResponde 409Continua funcionando
Canceladaeditar, versão nova, cancelar de novo, receber, estornar, reagendarler
Com nota fiscal ativa (da venda, da origem ou de um recebimento), invoice_locked: trueversão nova e cancelar; estornar o recebimento que tem a nota (has_active_invoice: true)editar observações e produção, receber, reagendar, estornar os outros recebimentos
Com a comissão do vendedor já pagaversão novacancelar, receber, reagendar, editar observações e produção
De contrato (receivable_managed_by: contract)receber, reagendar, estornarversão nova, cancelar, editar observações e produção

Também como na tela: receber só em parcela em aberto (installment_id de parcela quitada responde 422), reagendar só com parcela em aberto (409), estornar só recebimento que não foi estornado (409), versão nova só quando algo comercial muda (422) e os campos de produção só com a produção ligada (422).

Conta e lançamentos são dados do financeiro. account_id (da venda e de cada recebimento) e finance_transactions só vêm na resposta quando a chave tem finance:read e o membro que ela representa tem acesso ao financeiro (dono e administrador têm). Sem isso, esses campos não vêm, exatamente como no painel. Parcelas (installments) e recebimentos (payments) vêm sempre, e o membro sem o financeiro recebe, estorna e reagenda como faz na tela; ele só não escolhe a conta nem decide se a venda vai para o financeiro (403). Comissão do vendedor, se a venda baixou estoque, a distribuição interna dos recebimentos pelas parcelas, os ids de usuário e os ids das versões internas nunca saem. Numa venda antiga ligada a cliente, vendedor, conta ou produto de outra empresa, esse vínculo sai null.

O financeiro segue as mesmas regras do painel, pelo mesmo código: o lançamento confere conta, categoria e cliente, a despesa numa conta de cartão vai para a fatura do ciclo (on_statement), e o vencido é recalculado quando o lançamento é gravado. Como no formulário da tela, a categoria precisa ser do mesmo tipo do lançamento, “mostrar no portal do cliente” só vale em despesa com cliente, a data de pagamento só existe no lançamento pago (sem ela, a do lançamento), e sem account_id o lançamento vai para a conta padrão da empresa, que a tela já deixa marcada; account_id: null é o “sem conta”. Recorrência e parcelamento criam a série inteira de uma vez, com os mesmos limites da tela, e a transferência cria a saída e a entrada ligadas uma à outra. O lançamento feito pela API fica com created_channel igual a public_api.

Na série, PATCH e DELETE aceitam ?scope=this, this_and_future ou all, como a janela de série do painel, e o DELETE aceita também keep_paid (o padrão é true, como a janela, que já vem marcada). O PATCH altera só os campos enviados: o que não vem, inclusive a situação, fica como está. Lançamento gerado por venda (source: sale) só muda categoria, observações e portal, e não é excluído; sale_id é o mesmo id de GET /public/v1/sales/{id}. Lançamento importado do banco (source: bank) também tem os campos que a tela trava e não é excluído. Mudar um campo protegido responde 409 conflict: é conflito com o estado do lançamento, não falta de permissão. mark_paid baixa o lançamento em aberto, e no lançamento já pago devolve ele como está, sem erro; numa parcela de venda ele registra o recebimento da venda, e a resposta é o lançamento recebido novo; a parcela fica cancelada, então repetir no mesmo id responde 409, sem cobrar de novo. Para repetir com segurança depois de um timeout, mande Idempotency-Key.

A exclusão nunca apaga mais do que o pedido. Os lançamentos de uma série ficam presos ao PRIMEIRO lançamento dela, então excluir esse primeiro com scope=this levaria a série inteira, e excluir com keep_paid quando esse primeiro não está pago levaria também os já pagos. Nesses dois casos a API responde 409 conflict com param: "scope" e não apaga nada: exclua com scope=all (e keep_paid=false, se for para apagar os pagos também) ou a partir de outro lançamento da série.

Toda leitura do financeiro (lançamentos, contas e categorias) agenda, como a tela, a sincronização das vendas antigas com o financeiro, então o que você lê é o mesmo que o painel mostra. Contas trazem o saldo atual calculado como a tela (current_balance_cents) e, no cartão, o limite usado e o disponível; limite, fechamento, vencimento e alerta só existem no cartão e no crédito de fornecedor. Conta com lançamento não é excluída (409), e só dono e administrador excluem conta. Categorias só o dono e os administradores criam, editam e excluem, e a cor é uma das 18 da paleta da tela (outra responde 422 com param color); a primeira leitura de uma empresa sem categoria cria as padrão, como o painel. Dados do Open Finance (identificadores externos, saldo sincronizado, conexão com o banco), da conciliação e do anexo nunca saem.

Excluir um cliente apaga o cadastro de vez, como no painel, e leva junto endereços, observações, vínculos de grupo, acesso ao portal, inventário e obras. Ordens de serviço, vendas, orçamentos, tarefas e lançamentos continuam existindo, sem o cliente.

A tabela de permissões desta seção já mostra o vocabulário completo de módulos e ações, porque ele faz parte do contrato e é o que o painel usa para criar chaves. Ter a permissão marcada na chave não significa que a rota daquele módulo já exista.

  • Não existe ambiente de teste. Toda chamada usa o dado real da sua empresa.
  • A chave não gerencia chaves. Criar, rotacionar e revogar acontece no painel, com a senha do usuário.
  • Custo e margem não saem na API.
  • A API não configura a empresa. Plano, membros e preferências continuam no painel.

A API está disponível a partir do plano Start. No plano gratuito a empresa não cria chave, e uma chamada feita com chave de uma empresa que perdeu o recurso responde 403 com o código plan_feature_unavailable.

O teto de chamadas por minuto é da empresa, somando todas as chaves. GET /public/v1/me devolve o teto vigente em limits.requests_per_minute.

Esta documentação existe em português, inglês e espanhol. O que muda é só o texto. Nome de campo, caminho de rota, valor de enum e código de erro são iguais nos três idiomas.

Na API, o cabeçalho Accept-Language escolhe o idioma da mensagem de erro (pt-BR é o padrão, en e es também são aceitos). O campo code do erro nunca muda de idioma: programe por ele, nunca pelo texto.