---
title: "Emitir NF-e de medicamentos, rastreabilidade e retenções federais"
description: "Como preencher os grupos de medicamento (med/K01), rastreabilidade de lote (rastro/I80) e retenção de tributos federais (retTrib/W23) na emissão de NF-e."
source_url: https://nfe.io/docs/documentacao/nota-fiscal-produto-eletronica/integracao-api/emitir-nota-fiscal-de-medicamentos-e-rastreabilidade
last_updated: 2026-09-18
---

:::caution Disponibilidade
Estes três grupos estão em liberação. Confirme com o suporte se já estão ativos na sua conta antes
de alterar sua integração — enviá-los antes da liberação faz os campos serem ignorados, e a nota
sai sem o grupo.
:::

# Medicamentos, rastreabilidade e retenções federais

Esta página cobre três grupos do leiaute da NF-e que costumam aparecer juntos na distribuição
farmacêutica e na venda a órgãos públicos:

| Grupo | Tag | Onde entra no payload |
|---|---|---|
| Medicamentos | `med` (K01) | `items[].medicineDetail` |
| Rastreabilidade de lote | `rastro` (I80) | `items[].trackingDetails[]` |
| Retenção de tributos federais | `retTrib` (W23) | `totals.withheldTaxes` |

## Medicamento e rastreabilidade são dois grupos, não um

Até 2018, lote, validade e fabricação ficavam dentro do grupo de medicamentos. A **NT 2018.005**
separou os dois:

* O grupo **`med`** ficou apenas com **registro ANVISA**, **motivo da isenção** e **preço máximo ao
  consumidor**.
* Lote, quantidade, fabricação e validade passaram para o grupo **`rastro`**, que é o mesmo usado
  por agrotóxicos, produtos veterinários, bebidas e embalagens.

Vale conferir esse ponto na sua modelagem: é comum a integração ser construída tratando tudo como
um grupo só, o que resulta em XML recusado.

:::warning Informar `medicineDetail` obriga informar `trackingDetails`
É a regra **K01-20** do Manual de Orientação ao Contribuinte. Medicamento sem os campos de
rastreabilidade é recusado com a **rejeição 873** ("Operação com medicamentos e não informado os
campos de rastreabilidade"). Nossa API valida isso na entrada e devolve `400` com mensagem
explícita, antes de enviar o documento à SEFAZ.

O caminho inverso é livre: `trackingDetails` sozinho é válido para qualquer produto rastreável.
:::

## Grupo de medicamentos (`medicineDetail`)

| Campo | Tag | Obrigatório | Observação |
|---|---|---|---|
| `anvisaCode` | `cProdANVISA` | Sim | 11 ou 13 dígitos, ou o literal `ISENTO` |
| `exemptionReason` | `xMotivoIsencao` | Não | Até 255 caracteres. Para medicamento isento, informe o número da decisão (por exemplo, a RDC da ANVISA) |
| `maximumPrice` | `vPMC` | Sim no leiaute | Se não houver preço tabelado, informe `0` — omitido, é emitido como `0.00` |

## Grupo de rastreabilidade (`trackingDetails`)

É uma **lista**: aceita até **500 lotes por item**. Vários lotes numa mesma linha da nota é o caso
normal em distribuição hospitalar.

| Campo | Tag | Obrigatório | Observação |
|---|---|---|---|
| `batchNumber` | `nLote` | Sim | 1 a 20 caracteres |
| `batchQuantity` | `qLote` | Sim | Maior que zero, até 8 dígitos inteiros e 3 decimais |
| `manufactureOn` | `dFab` | Sim | Emitido no XML como `AAAA-MM-DD` |
| `expireOn` | `dVal` | Sim | Emitido como `AAAA-MM-DD`. Se a validade não especificar o dia, informe o último dia do mês |
| `aggregationCode` | `cAgreg` | Não | Até 20 caracteres |

### Exemplo — item de medicamento com dois lotes

```json
{
  "code": "MED-001",
  "description": "FOLINATO DE CALCIO 10MG/ML 30ML INJ",
  "ncm": "30045010",
  "cfop": 5102,
  "unit": "CX",
  "quantity": 5,
  "unitAmount": 120.00,
  "totalAmount": 600.00,
  "unitTax": "CX",
  "tax": {
    "icms": { "origin": "0", "cst": "00", "baseTax": 600.00, "rate": 18.00, "amount": 108.00 }
  },
  "medicineDetail": {
    "anvisaCode": "1004310310091",
    "maximumPrice": 0
  },
  "trackingDetails": [
    {
      "batchNumber": "188918",
      "batchQuantity": 2.000,
      "manufactureOn": "2026-06-17",
      "expireOn": "2028-06-01"
    },
    {
      "batchNumber": "190455",
      "batchQuantity": 3.000,
      "manufactureOn": "2026-07-02",
      "expireOn": "2028-07-01"
    }
  ]
}
```

:::warning Grupos de produto específico são mutuamente exclusivos — e o conflito é silencioso
O leiaute aceita no máximo **um** entre `medicineDetail`, `vehicleDetail` e `fuelDetail` por item.
A API **não recusa** o envio de mais de um: ela resolve o conflito sozinha, na ordem de precedência
**medicamento → veículo → combustível**, e os demais grupos simplesmente não saem no XML — sem erro
e sem aviso. Garanta na sua integração que só um deles é preenchido.

O grupo `trackingDetails` não faz parte dessa exclusividade — ele é irmão deles e pode acompanhar
qualquer um.
:::

## Retenção de tributos federais (`withheldTaxes`)

Aplica-se quando a fonte pagadora retém tributos federais — tipicamente a venda a órgão ou hospital
público (IN SRF 480/2004; Lei 10.833/2003, arts. 30 a 36; Lei 7.450/85, art. 52).

| Campo | Tag | Observação |
|---|---|---|
| `pisAmount` | `vRetPIS` | Valor retido de PIS |
| `cofinsAmount` | `vRetCOFINS` | Valor retido de COFINS |
| `csllAmount` | `vRetCSLL` | Valor retido de CSLL |
| `irrfBasis` | `vBCIRRF` | Base de cálculo do IRRF |
| `irrfAmount` | `vIRRF` | Valor retido do IRRF |
| `socialSecurityBasis` | `vBCRetPrev` | Base de cálculo da retenção da Previdência Social |
| `socialSecurityAmount` | `vRetPrev` | Valor da retenção da Previdência Social |

Todos os campos são opcionais e todos aceitam no máximo 13 dígitos inteiros.

:::info Os valores são informados por você, não calculados
A plataforma **não apura** retenção federal. O que você enviar é o que vai para o documento. Se
precisar do cálculo automático, fale com o suporte — hoje isso não é feito pela API.
:::

:::warning Valor zero é o mesmo que não informar
O leiaute tipa esses campos de um jeito que **não aceita zero** — `0` e `0.00` são recusados pelo
schema da SEFAZ. Por isso, campo com valor zero é **omitido** do XML, e o grupo inteiro desaparece
quando nenhum valor é informado. Não é preciso tratar isso na sua integração — pode enviar zero à
vontade, que a plataforma cuida de omitir.
:::

### Exemplo — totais com IRRF retido

```json
{
  "totals": {
    "icms": {
      "invoiceAmount": 600.00
    },
    "withheldTaxes": {
      "irrfBasis": 600.00,
      "irrfAmount": 9.00,
      "pisAmount": 3.90,
      "cofinsAmount": 18.00,
      "csllAmount": 6.00
    }
  }
}
```

:::note Retenção não altera o valor da nota
`retTrib` registra o que a fonte pagadora retém **do pagamento**. O valor do documento (`vNF`) e o
total com reforma tributária (`vNFTot`) permanecem inalterados — mesmo comportamento do ISS retido
(`vISSRet`).
:::

## Rejeições que esses grupos evitam

| Código | Descrição | Causa comum | Recusado antes da SEFAZ? |
|---|---|---|---|
| **215** | Falha no schema do XML | `anvisaCode` fora do padrão (11/13 dígitos ou `ISENTO`) | ✅ `400` na entrada |
| **215** | Falha no schema do XML | Valor de retenção acima de 13 dígitos inteiros, ou negativo | ✅ `400` na entrada |
| **215** | Falha no schema do XML | Lote sem `batchNumber`, `batchQuantity`, `manufactureOn` ou `expireOn`; `batchNumber` acima de 20 caracteres | ✅ `400` na entrada |
| **873** | Operação com medicamentos e não informado os campos de rastreabilidade | `medicineDetail` enviado sem `trackingDetails` | ✅ `400` na entrada |

Em todos esses casos a API recusa na entrada, com `400` e a mensagem apontando o campo — você não
descobre o problema só depois da ida à SEFAZ.

Valor de retenção **igual a zero não está nesta lista**: ele não gera rejeição nem `400`, porque a
plataforma omite a tag antes de montar o XML (ver o aviso acima).

## Veja também

* [Emitir uma nota fiscal de Produto](./emitir-uma-nota-fiscal-de-produto.md)
* [Emitir uma nota fiscal de Produto utilizando Motor de Cálculo de Tributos](./emitir-uma-nota-fiscal-de-produto-utilizando-motor-de-calculo-de-tributos..md)
