> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wegly.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Propostas

> Crie e atualize propostas com itens, recorrência, campos personalizados, cronograma de entrega e anexos.

As propostas usam a base `/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`.

| Operação | Método e caminho | Permissão da chave |
| - | - | - |
| Listar | `GET /integrations/external/proposals` | `crm/proposal.read` |
| Consultar detalhes | `GET /integrations/external/proposals/{id}` | `crm/proposal.read` |
| Criar | `POST /integrations/external/proposals` | `crm/proposal.create` |
| Atualizar | `PUT /integrations/external/proposals/{id}` | `crm/proposal.update` |
| Excluir logicamente | `DELETE /integrations/external/proposals/{id}` | `crm/proposal.delete` |

Os anexos da proposta têm rotas próprias, descritas em [Anexos](#anexos). Consulte [Autenticação](/documentacao-da-api/integracoes/autenticacao) 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.

```json theme={null}
{
  "deal_code": "1032",
  "title": "Implantação e assinatura Wegly",
  "notes": "Implantação remota e acompanhamento inicial.",
  "valid_until": "2026-10-05T23:59:59.000Z",
  "items": [
    {
      "sku": "IMPLANTACAO",
      "quantity": 1,
      "unit_price": 1500,
      "position": 0
    },
    {
      "sku": "ASSINATURA",
      "quantity": 1,
      "unit_price": 299.9,
      "position": 1,
      "is_recurring_default": true,
      "billing_interval_default": 2,
      "billing_interval_count": 1,
      "contract_term_months": 12,
      "recurring_payment_method": 1
    }
  ],
  "delivery_schedule": {
    "starts_on": "2026-09-14",
    "deadlines": [
      {
        "stage": "Implantação",
        "qualifier": "UP_TO",
        "quantity": 5,
        "unit": "DAYS",
        "counting_mode": "BUSINESS",
        "notes": "Após recebimento dos acessos."
      }
    ]
  }
}
```

Os códigos de produto do exemplo precisam existir no catálogo da sua empresa. O POST retorna `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.

| `billing_interval_default` | Periodicidade |
| - | - |
| `0` | Diária |
| `1` | Semanal |
| `2` | Mensal |
| `3` | Bimestral |
| `4` | Trimestral |
| `5` | Semestral |
| `6` | Anual |

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ção `items` 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.

```json theme={null}
{
  "title": "Proposta revisada",
  "items": [
    {
      "id": "b3c2a9cf-603a-4167-99ba-215f84646080",
      "quantity": 2,
      "unit_price": 299.9,
      "position": 0
    },
    {
      "id": "8fa49a2d-59a6-4c8d-bf15-685829b13876",
      "delete": true
    }
  ]
}
```

Substitua os IDs pelos itens retornados na consulta da proposta. A operação não pode deixar a proposta sem itens. Informe `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:

| Local | Formato de entrada | Uso |
| - | - | - |
| `custom_fields` da proposta | `id` e `value`, `option_id`, `option_ids` ou `boolean_value` | Respostas às definições de campos personalizados da proposta |
| `items[].custom_fields` | `field_id` e `value` | Respostas aos campos do produto vinculado ao item; `value` aceita string, número, booleano ou `null`, conforme a definição |
| `items[].category_values` | `category_id` e `value` | Classificação comercial do item; `value` é string ou `null` |

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.

| Campo de etapa | Valores e limites |
| - | - |
| `stage` | Nome obrigatório, até 150 caracteres |
| `qualifier` | `EXACT` ou `UP_TO` |
| `quantity` | Inteiro positivo, limitado pela unidade |
| `unit` | `DAYS` (máximo 36.525), `WEEKS` (5.217), `MONTHS` (1.200) ou `YEARS` (100) |
| `counting_mode` | `CALENDAR` ou `BUSINESS`; meses e anos aceitam apenas `CALENDAR` |
| `notes` | Opcional, até 2.000 caracteres |

`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:

| Parâmetros | Filtro |
| - | - |
| `lead_id`, `deal_id`, `deal_code` | Lead ou negócio de origem. `deal_code` inexistente retorna uma lista vazia. |
| `created_by`, `status`, `approved`, `archived`, `code` | Autor, status, aprovação, arquivamento e código da proposta. |
| `is_primary` | `true` retorna apenas a proposta principal de cada lead ou negócio; `false`, as demais. |
| `deal_status` | Status do negócio vinculado: `1` aberto, `2` ganho ou `3` perdido. Propostas vinculadas a leads ficam fora do resultado. |
| `min_value`, `max_value` | Faixa do valor total da proposta, com até duas casas decimais. |
| `created_from`/`created_to`, `valid_from`/`valid_to`, `rejected_from`/`rejected_to` | Períodos de criação, validade e rejeição. |
| `search` | Título da proposta, título do lead ou do negócio e, quando o termo é numérico (com ou sem `#`), código da proposta. |

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:

```http theme={null}
GET /integrations/external/proposals?skip=0&take=10&include_delivery_schedule=true
```

Com esse parâmetro, cada registro possui `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](#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](#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 o `id` de uma proposta já criada.

| Operação | Método e caminho | Permissões da chave |
| - | - | - |
| Listar anexos | `GET /integrations/external/proposals/{id}/attachments` | `crm/proposal.read` |
| Enviar arquivos | `POST /integrations/external/proposals/{id}/attachments/files` | `crm/proposal.update` e `crm/file.create` |
| Criar documento anexado | `POST /integrations/external/proposals/{id}/attachments/documents` | `crm/proposal.update` e `crm/document.create` |
| Anexar itens da biblioteca | `POST /integrations/external/proposals/{id}/attachments/library` | `crm/proposal.update` e `crm/document.read`; com `file_ids`, também `crm/file.create` |
| Ler documento anexado | `GET /integrations/external/proposals/{id}/attachments/{attachmentId}/document` | `crm/proposal.read` e `crm/document.read` |
| Atualizar documento anexado | `PUT /integrations/external/proposals/{id}/attachments/{attachmentId}/document` | `crm/proposal.read` e `crm/document.update` |
| Gerar link de download | `GET /integrations/external/proposals/{id}/attachments/{attachmentId}/download-url` | `crm/proposal.read` e `crm/file.read` |
| Remover anexo | `DELETE /integrations/external/proposals/{id}/attachments/{attachmentId}` | `crm/proposal.update` |

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

```bash theme={null}
curl --request POST \
  --url 'https://api.wegly.com.br/integrations/external/proposals/3f0a8c2d-6b1e-4c7a-9f2d-1e5b7a9c3d40/attachments/files' \
  --header "Authorization: Bearer $WEGLY_API_KEY" \
  --form 'files=@/caminho/escopo-tecnico.pdf;type=application/pdf' \
  --form 'files=@/caminho/planilha-de-custos.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
```

Use `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](/documentacao-da-api/integracoes/recursos-complementares#arquivos).

### Documentos da biblioteca

`POST /integrations/external/proposals/{id}/attachments/documents` cria um documento na biblioteca já anexado à proposta:

```json theme={null}
{
  "title": "Escopo técnico",
  "content_markdown": "# Escopo\n\n- Implantação remota\n- Treinamento da equipe",
  "visibility": "PRIVATE"
}
```

Todos os campos são opcionais. `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.

```json theme={null}
{
  "document_ids": ["5d0c1f0e-7b1a-4d53-9c0e-2f6a1b7c9e10"],
  "file_ids": ["a7e2b3c4-1d2e-4f5a-8b9c-0d1e2f3a4b5c"]
}
```

Envie ao menos um item e no máximo 20 por requisição, somando as duas listas. Documentos já anexados são ignorados e devolvidos na resposta. Um documento que não esteja visível para toda a empresa (`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 retorna `total` 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.

```json theme={null}
{
  "total": 2,
  "records": [
    {
      "id": "0b9f6c1e-52a4-4f0e-9d3b-7a1c2e4f6a80",
      "proposal_id": "3f0a8c2d-6b1e-4c7a-9f2d-1e5b7a9c3d40",
      "draft_token": null,
      "kind": "FILE",
      "status": "READY",
      "position": 1,
      "created_at": "2026-10-05T14:20:11.000Z",
      "updated_at": "2026-10-05T14:20:11.000Z",
      "created_by_user": {
        "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
        "name": "Ana Souza",
        "avatar": null
      },
      "file": {
        "name": "escopo-tecnico.pdf",
        "extension": "pdf",
        "mime_type": "application/pdf",
        "size_bytes": 482133,
        "can_preview": true,
        "can_play_inline": false
      },
      "document": null
    },
    {
      "id": "6e2a4c8b-91d3-4b7f-a5c0-3d9e1f2a7b64",
      "proposal_id": "3f0a8c2d-6b1e-4c7a-9f2d-1e5b7a9c3d40",
      "draft_token": null,
      "kind": "DOCUMENT",
      "status": "READY",
      "position": 2,
      "created_at": "2026-10-05T14:22:40.000Z",
      "updated_at": "2026-10-05T14:22:40.000Z",
      "created_by_user": {
        "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
        "name": "Ana Souza",
        "avatar": null
      },
      "file": null,
      "document": {
        "id": "5d0c1f0e-7b1a-4d53-9c0e-2f6a1b7c9e10",
        "title": "Escopo técnico",
        "summary": "Escopo Implantação remota Treinamento da equipe",
        "workflow_status": "DRAFT",
        "has_unpublished_changes": true,
        "published_version": null,
        "updated_at": "2026-10-05T14:22:40.000Z",
        "last_edited_by_user": {
          "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
          "name": "Ana Souza",
          "avatar": null
        },
        "is_available": true,
        "access": {
          "can_open": true,
          "can_edit": true
        }
      }
    }
  ]
}
```

`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ão `1` (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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.