/integrations/external/proposals. A empresa é definida pela chave de integração e o usuário que criou a chave atua como responsável pela operação; esse usuário precisa continuar ativo na empresa.
A API só lista, consulta, altera e exclui propostas cujo lead ou negócio esteja visível para esse usuário no CRM. Administradores e usuários master enxergam todos os registros. Os demais enxergam os registros públicos, os de equipe cujo responsável seja o próprio usuário ou um colega de equipe, e os privados em que o usuário seja responsável, seguidor ou supervisor da equipe do responsável. Uma proposta fora desse alcance retorna 404, como se não existisse, e a criação em um lead ou negócio que o usuário não enxerga também retorna 404.
Os anexos da proposta têm rotas próprias, descritas em Anexos. Consulte Autenticação para configurar a chave.
Criar uma proposta
Informe exatamente um contexto:lead_id ou um negócio identificado por deal_id/deal_code. O contexto precisa estar aberto e permitir negociação por proposta. Leads ou negócios ganhos, perdidos ou arquivados bloqueiam a criação e a edição; a criação para um negócio também é bloqueada quando ele já possui produtos diretamente associados.
deal_code é o código público numérico do negócio, enviado como string. Prefira apenas uma forma de identificação: se enviar deal_id e deal_code juntos, o POST prioriza o ID e o PUT prioriza o código.
201 com os detalhes completos da proposta.
Itens e valores
items deve conter ao menos um item válido. Cada item enviado que não seja uma remoção exige quantity, inclusive na atualização por ID: mínimo 0.001, com até três casas decimais. unit_price aceita até duas casas decimais e não pode ser negativo; quando omitido, usa o valor já salvo no item ou o preço de venda do produto.
Use product_id ou sku para vincular um produto. O SKU resolve o código do catálogo quando product_id está ausente; SKU não encontrado retorna 404. Para um item livre, informe seus dados comerciais, como name, quantity e unit_price. Também é possível enviar new_product dentro do item para cadastrar o produto na mesma operação, com type (0 produto ou 1 serviço) e name obrigatórios. Essa criação exige que o usuário responsável pela chave tenha permissão para criar produtos. Não combine new_product com uma referência a produto existente.
discount_type usa 0 para percentual e 1 para valor fixo, acompanhado de discount_value. O desconto do item aceita até três casas decimais; o desconto geral da proposta aceita até duas. Use os totais calculados pela API no retorno.
A recorrência é configurada por is_recurring_default, billing_interval_default, billing_interval_count, contract_term_months, start_date, end_date e billing_cycles. Um item recorrente precisa de periodicidade válida após combinar o payload com os dados existentes e os padrões do produto.
Os contadores e limites de recorrência, quando informados, devem ser inteiros positivos.
end_date não pode anteceder start_date. recurring_payment_method aceita 1 (Pix), 2 (cartão de crédito) ou 3 (boleto); em itens não recorrentes, o retorno desse campo é null.
notes contém a observação do item. internal_notes contém a observação interna, limitada a 5.000 caracteres; ela aparece no retorno da API e no CRM, mas é omitida da exportação em PDF.
Atualização parcial e remoção de itens
O PUT preserva os campos omitidos. A coleçãoitems descreve alterações incrementais: um item com id atualiza o existente, sem id adiciona um novo e com id e delete: true remove. Itens não mencionados são preservados. Enviar items: [] não remove todos os itens.
position ao precisar manter uma ordem específica nos itens alterados.
Na implementação externa atual, deal_id: null é tratado como campo omitido e preserva o negócio existente. Portanto, não use esse valor para desvincular uma proposta de negócio.
Campos personalizados e categorias
Existem três estruturas distintas:
Ao enviar
custom_fields da proposta, envie o conjunto completo de respostas que deseja manter. O serviço substitui as respostas ativas e valida os campos obrigatórios. Omitir a propriedade preserva as respostas; [] limpa somente quando as regras de obrigatoriedade permitem.
No detalhe, custom_fields inclui todas as definições ativas, com seção, opções e answers, inclusive campos ainda sem resposta. Os campos e categorias de cada item retornam o snapshot da definição usado naquele item. Campos personalizados de produto exigem produto vinculado; itens livres não aceitam essas respostas.
Cronograma de entrega
delivery_schedule reúne uma data-base opcional (starts_on) e entre 1 e 100 etapas (deadlines). A ordem do array define a sequência das etapas.
starts_on usa uma data civil real no formato YYYY-MM-DD, entre 1980-01-01 e 2100-12-31. Sem data fixa, o cálculo usa a data atual no fuso da empresa e o retorno indica is_dynamic: true. O horizonte total das etapas não pode exceder 100 anos.
Dias úteis consideram segunda a sexta-feira, sem descontar feriados; uma semana útil corresponde a cinco dias úteis. O retorno informa effective_starts_on, timezone, estimated_delivery_date, final_qualifier, totais declarados e a previsão de cada etapa. Quando uma previsão termina em fim de semana, suggested_next_business_date informa a sugestão de ajuste. Se qualquer etapa usar UP_TO, o prazo acumulado seguinte também usa UP_TO.
No PUT, omitir delivery_schedule preserva o cronograma; enviar um objeto substitui todas as etapas; enviar null remove o cronograma. A referência preset_id não cria vínculo com configurações salvas de prazo e é descartada pela integração.
Listagem e detalhes retornados
A listagem retorna{ "total": 0, "records": [] } quando não há resultados. Usa skip e take, com padrões 0 e 10; take aceita de 1 a 100. A ordenação padrão é por criação decrescente; para alterá-la, envie order_by (created_at, updated_at, code, total ou title) e order_direction (asc ou desc).
Os filtros disponíveis são:
Nos períodos, uma data sem horário (
YYYY-MM-DD) cobre o dia inteiro no horário de Brasília; com horário, o instante informado é usado como limite. Intervalos invertidos, de data ou de valor, retornam 400.
Por padrão, os registros da listagem não têm delivery_schedule. Para incluir o cronograma calculado em cada registro, envie:
delivery_schedule, que pode ser null. A consulta por ID e os retornos de criação/atualização sempre incluem o cronograma ou null.
O resumo inclui items_count e payment_options_count. Já o detalhe inclui os arrays items, payment_options, custom_fields e o cronograma, sem as contagens do resumo. As condições de pagamento existentes incluem descontos, totais e parcelas com valor, vencimento, método de pagamento e informações de cartão. Nenhum dos dois inclui os anexos; consulte-os pela rota de anexos.
Em ambos, lead e deal identificam a origem da proposta: o que não se aplica vem null, e o outro traz id, title, status (1 aberto, 2 ganho, 3 perdido), archived_at e negotiation_mode. Use status e archived_at para saber se a proposta ainda pode ser alterada.
Ambos retornam notes_format (HTML ou MARKDOWN), projected_values com as chaves 6, 12, 18 e 24, indicadores de recorrência e metadados de projeção. Esses valores são calculados pelo serviço; para registros de cálculo legado, as projeções usam o valor atual como fallback.
O contrato de escrita externo não declara payment_options, notes_format, new_products ou create_new_version. Não dependa desses campos internos na integração. attachment_draft_token pertence ao editor do CRM e é descartado quando enviado; anexe pelas rotas de anexos depois de criar a proposta. Se uma proposta já possui condições de pagamento e uma alteração muda seu total, o serviço exige revisar essas condições; faça esse ajuste pelo fluxo do CRM.
Anexos
Uma proposta salva aceita dois tipos de anexo: arquivos enviados pela integração e documentos da biblioteca de documentos da Wegly. Os anexos não fazem parte do corpo de criação ou atualização da proposta; use as rotas abaixo com oid de uma proposta já criada.
Quando a rota indica mais de uma permissão, a chave precisa de todas. Para incluir ou remover anexos, o usuário criador da chave também precisa ter
crm/proposal.update no CRM, além de crm/file.create para arquivos e crm/document.create para documentos novos, e o lead ou o negócio da proposta precisa estar aberto e não arquivado. Cada proposta aceita até 50 anexos. Quando o CRM gera uma nova versão da proposta, ela nasce com os mesmos anexos da versão de origem.
Enviar arquivos
multipart/form-data com os campos files ou file, somando até 10 arquivos por requisição, cada um com até 20 MB. As extensões aceitas são .pdf, .docx, .xls, .xlsx, .txt, .md, .csv, .jpg, .jpeg, .png, .webp, .gif, .mp3 e .mp4. A API confere a extensão, o MIME declarado e o conteúdo real do arquivo: um arquivo apenas renomeado é recusado com 400, e um arquivo acima do limite de tamanho retorna 413. A resposta 201 é a lista dos anexos criados.
Essa lista de formatos é própria dos anexos de proposta e difere da aceita no upload de arquivos de lead ou negócio.
Documentos da biblioteca
POST /integrations/external/proposals/{id}/attachments/documents cria um documento na biblioteca já anexado à proposta:
title aceita até 180 caracteres; sem título, o documento nasce como “Documento sem título”. content_markdown é o conteúdo inicial da página principal, com até 200.000 caracteres. folder_id escolhe a pasta da biblioteca (raiz quando omitido) e visibility aceita PRIVATE (padrão), RESTRICTED ou COMPANY. O documento pertence ao usuário criador da chave e continua na biblioteca mesmo que o anexo seja removido.
POST /integrations/external/proposals/{id}/attachments/library anexa itens que já existem na biblioteca: document_ids vincula documentos e file_ids copia arquivos do Drive da biblioteca para a proposta.
COMPANY) só pode ser anexado se o criador da chave puder compartilhá-lo; caso contrário, a API retorna 403.
Para ler o conteúdo, use GET /integrations/external/proposals/{id}/attachments/{attachmentId}/document. O retorno traz os metadados do documento e as páginas na ordem de leitura, cada uma com level (1 para a página raiz) e content_markdown. Páginas gravadas em HTML retornam content_markdown: null e o texto em plain_text. Sem version_scope, a API usa a versão publicada quando ela existe e, caso contrário, o rascunho; envie DRAFT ou PUBLISHED para escolher. Pedir PUBLISHED de um documento sem publicação retorna 404. Quem enxerga a proposta consegue ler o documento por essa rota, mesmo sem acesso direto a ele na biblioteca.
PUT no mesmo caminho altera title e/ou substitui o conteúdo da página principal por content_markdown. A alteração é feita no documento da biblioteca, portanto aparece em todas as propostas que o anexam, e exige que o criador da chave possa editar o documento. Essa operação não depende de o lead ou o negócio estar aberto.
Baixar e remover
GET /integrations/external/proposals/{id}/attachments/{attachmentId}/download-url retorna url, expires_at, file_name e disposition. O link vale 15 minutos e força o download com o nome original; gere outro quando precisar. A rota atende apenas anexos do tipo arquivo.
DELETE /integrations/external/proposals/{id}/attachments/{attachmentId} remove o anexo e retorna { "id": "UUID do anexo", "message": "Anexo removido com sucesso." }. Um documento removido da proposta continua na biblioteca.
Formato do anexo
A listagem retornatotal e records, ordenados por position. As rotas de inclusão retornam o mesmo objeto de anexo: uma lista no envio de arquivos e nos itens da biblioteca, e um único objeto na criação de documento.
kind é FILE ou DOCUMENT, e apenas o objeto correspondente (file ou document) vem preenchido. document.is_available é false quando o documento foi para a lixeira da biblioteca, e document.access indica o que o criador da chave pode fazer com ele. draft_token é sempre null na API externa.
Informe o attachmentId sempre junto com o id da proposta à qual ele pertence: o anexo de outra proposta retorna 404. Chamar a leitura de documento para um anexo do tipo arquivo, ou o link de download para um anexo do tipo documento, retorna 400.
Status e erros de negócio
Os status são1 (negociação), 2 (aprovada), 3 (rejeitada), 4 (arquivada) e 5 (dispensada). Na criação, o padrão é negociação. approved_by recebe o ID de uma pessoa da empresa, e não o ID de um usuário. created_by e approved_by retornam objetos resumidos ou null.
A exclusão é lógica e retorna { "id": "UUID da proposta" }. Contextos encerrados também impedem excluir propostas.
Erros 400 indicam payload ou regra de negócio inválida; 403 pode indicar permissão ausente ou usuário responsável inativo; 404 indica uma referência não encontrada na empresa ou fora do alcance do usuário criador da chave; 409 pode indicar código de negócio ambíguo ou alteração concorrente da proposta. Em conflito de atualização, consulte a proposta novamente antes de reenviar as mudanças.