# TISS Solicitação de Procedimento — Postman
Como configurar uma chamada SOAP TISS 4.01.00 contra o ambiente de
homologação Unimed RJ. Todo este doc foi escrito a partir de testes reais
contra o endpoint — não é especulação.
> **Só quer importar e usar?** Veja
> [IMPORTAR_COLLECTION.md](IMPORTAR_COLLECTION.md) — passo-a-passo de 5
> minutos. O documento abaixo entra na anatomia técnica (estrutura do
> envelope, algoritmo de hash, códigos de erro).
## Conteúdo da pasta
```
tiss-solicitacao-procedimento/
├── README.md ← você está aqui (referência técnica)
├── IMPORTAR_COLLECTION.md ← guia de importação no Postman
├── postman/
│ ├── collection.json ← request + pre-request + tests
│ └── environment.json ← variáveis de homologação
├── exemplos/
│ ├── request.xml ← envelope SOAP que retornou HTTP 200
│ └── response.xml ← resposta real do servidor
├── wsdl.xml ← WSDL do endpoint (referência offline)
└── test_hash.js ← reproduz o algoritmo de hash em Node
```
## 1. Endpoint e autenticação
| Item | Valor |
|---|---|
| URL | `https://htz.unimedrj.coop.br/htzfoundation-HTZFoundationEJB/TISSSolicitacaoProcedimentoV_4_01_00WS` |
| Método | `POST` |
| Auth | Basic Auth |
| Usuário | código do prestador na operadora (12 dígitos) |
| Senha | senha do prestador |
> O WSDL está em `<URL>?wsdl`. **Não use `Savon.client(wsdl: ...)`** — o WSDL
> publica o endpoint interno (`http://ferj-htz-01:80/...`); aponte direto
> para a URL pública.
## 2. Headers obrigatórios
| Header | Valor |
|---|---|
| `Content-Type` | `text/xml; charset=utf-8` |
| `SOAPAction` | `""` (duas aspas, vazio) |
| `Accept` | `text/xml` |
## 3. Estrutura do envelope
A estrutura desse endpoint **não segue o padrão TISS 3.x** (com
`prestadorParaOperadora` e `epilogo`). É a estrutura achatada do TISS 4.01:
```
solicitacaoProcedimentoWS
├── cabecalho
├── solicitacaoProcedimento
│ └── solicitacaoSP-SADT (ou solicitacaoInternacao / Prorrogacao / Odontologia)
└── hash ← direto, sem epilogo
```
**Namespace correto**: `http://www.ans.gov.br/padroes/tiss/schemas`
(NÃO use `http://ans.gov.br` — o servidor rejeita com `does not contain
operation meta data`).
## 4. Body — XML completo
Cole no Postman em **Body → raw → XML**. Variáveis `{{...}}` vêm do
Environment (próximo passo).
```xml
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ans="http://www.ans.gov.br/padroes/tiss/schemas">
<soapenv:Header/>
<soapenv:Body>
<ans:solicitacaoProcedimentoWS>
<ans:cabecalho>
<ans:identificacaoTransacao>
<ans:tipoTransacao>SOLICITACAO_PROCEDIMENTOS</ans:tipoTransacao>
<ans:sequencialTransacao>{{sequencial}}</ans:sequencialTransacao>
<ans:dataRegistroTransacao>{{data_hoje}}</ans:dataRegistroTransacao>
<ans:horaRegistroTransacao>{{hora_agora}}</ans:horaRegistroTransacao>
</ans:identificacaoTransacao>
<ans:origem>
<ans:identificacaoPrestador>
<ans:codigoPrestadorNaOperadora>{{codigo_prestador}}</ans:codigoPrestadorNaOperadora>
</ans:identificacaoPrestador>
</ans:origem>
<ans:destino>
<ans:registroANS>{{registro_ans}}</ans:registroANS>
</ans:destino>
<ans:Padrao>4.01.00</ans:Padrao>
</ans:cabecalho>
<ans:solicitacaoProcedimento>
<ans:solicitacaoSP-SADT>
<ans:cabecalhoSolicitacao>
<ans:registroANS>{{registro_ans}}</ans:registroANS>
<ans:numeroGuiaPrestador>{{numero_guia}}</ans:numeroGuiaPrestador>
</ans:cabecalhoSolicitacao>
<ans:tipoEtapaAutorizacao>2</ans:tipoEtapaAutorizacao>
<ans:dadosBeneficiario>
<ans:numeroCarteira>{{carteira}}</ans:numeroCarteira>
<ans:atendimentoRN>N</ans:atendimentoRN>
</ans:dadosBeneficiario>
<ans:dadosSolicitante>
<ans:contratadoSolicitante>
<ans:codigoPrestadorNaOperadora>{{codigo_prestador}}</ans:codigoPrestadorNaOperadora>
</ans:contratadoSolicitante>
<ans:nomeContratadoSolicitante>{{nome_prestador}}</ans:nomeContratadoSolicitante>
<ans:profissionalSolicitante>
<ans:nomeProfissional>{{nome_profissional}}</ans:nomeProfissional>
<ans:conselhoProfissional>06</ans:conselhoProfissional>
<ans:numeroConselhoProfissional>{{crm}}</ans:numeroConselhoProfissional>
<ans:UF>33</ans:UF>
<ans:CBOS>{{cbos}}</ans:CBOS>
</ans:profissionalSolicitante>
</ans:dadosSolicitante>
<ans:caraterAtendimento>1</ans:caraterAtendimento>
<ans:dataSolicitacao>{{data_hoje}}</ans:dataSolicitacao>
<ans:procedimentosSolicitados>
<ans:procedimento>
<ans:codigoTabela>22</ans:codigoTabela>
<ans:codigoProcedimento>{{cod_procedimento}}</ans:codigoProcedimento>
<ans:descricaoProcedimento>{{desc_procedimento}}</ans:descricaoProcedimento>
</ans:procedimento>
<ans:quantidadeSolicitada>1</ans:quantidadeSolicitada>
</ans:procedimentosSolicitados>
<ans:dadosExecutante>
<ans:codigonaOperadora>{{codigo_prestador}}</ans:codigonaOperadora>
<ans:CNES>{{cnes}}</ans:CNES>
</ans:dadosExecutante>
</ans:solicitacaoSP-SADT>
</ans:solicitacaoProcedimento>
<ans:hash>{{hash}}</ans:hash>
</ans:solicitacaoProcedimentoWS>
</soapenv:Body>
</soapenv:Envelope>
```
### Observações estruturais (descobertas em teste)
- `tipoTransacao` é `SOLICITACAO_PROCEDIMENTOS` (com **S** no final).
- Em `dadosSolicitante`, a ordem é: `contratadoSolicitante`,
`nomeContratadoSolicitante` (obrigatório, **minLength=1**, não pode ser
vazio), `profissionalSolicitante`.
- Em `procedimentosSolicitados`, `quantidadeSolicitada` é **irmão** de
`procedimento`, não filho.
- `UF` aceita código numérico (`33` = RJ).
- `CBOS` é a classificação do médico (ex: `225335` clínico geral).
- `tipoEtapaAutorizacao` = `2` (solicitação).
## 5. Environment (variáveis)
Cria um Environment no Postman com:
| Variable | Exemplo (homologação) |
|---|---|
| `endpoint` | `https://htz.unimedrj.coop.br/htzfoundation-HTZFoundationEJB/TISSSolicitacaoProcedimentoV_4_01_00WS` |
| `usuario` | `097200011100` |
| `senha` | (current value, não initial) |
| `codigo_prestador` | `097200011100` |
| `registro_ans` | `312363` |
| `nome_prestador` | `UNIMED` |
| `nome_profissional` | `teste` |
| `crm` | `52458444` |
| `cbos` | `225335` |
| `cnes` | `7402074` |
| `cod_procedimento` | `40304361` |
| `desc_procedimento` | `Hemograma com contagem de plaquetas ou frações` |
| `carteira` | `09720210626000547` |
| `sequencial` | `989898` |
| `numero_guia` | (gerado a cada envio — ver script) |
> **Senha em "Current value", não "Initial"** — Initial sincroniza pra
> workspace compartilhado.
## 6. Authorization
Aba **Authorization** → Type **Basic Auth**:
- Username: `{{usuario}}`
- Password: `{{senha}}`
## 7. Pre-request Script (calcula hash + timestamps + guia)
Esta é a parte que faz tudo funcionar. **Sem este script o servidor
retorna `O hash calculado difere do informado`.**
```javascript
// 1. Timestamps no formato TISS
const now = new Date();
const pad = n => String(n).padStart(2, '0');
pm.environment.set('data_hoje',
`${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}`);
pm.environment.set('hora_agora',
`${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`);
// 2. Número de guia único (timestamp últimos 9 dígitos)
pm.environment.set('numero_guia',
String(Date.now()).slice(-9));
// 3. Calcula hash MD5 do conteúdo
// Algoritmo TISS: text nodes do solicitacaoProcedimentoWS
// (excluindo o próprio hash), encoding ISO-8859-1, MD5 hex.
let body = pm.request.body.raw;
body = pm.variables.replaceIn(body);
// Pega só o conteúdo do solicitacaoProcedimentoWS, sem a tag <ans:hash>
const m = body.match(/<ans:solicitacaoProcedimentoWS>([\s\S]*)<ans:hash>/);
if (!m) throw new Error('Não achou solicitacaoProcedimentoWS no body');
const miolo = m[1];
// Remove todas as tags, sobram só os text nodes concatenados
const concat = miolo
.replace(/<[^>]+>/g, '') // tira tags
.replace(/\s*\n\s*/g, '') // tira quebras de linha + indentação
.trim();
// MD5 em Latin-1 (ISO-8859-1) — CryptoJS.enc.Latin1 mapeia char codes 0-255
const wordArray = CryptoJS.enc.Latin1.parse(concat);
const hash = CryptoJS.MD5(wordArray).toString();
pm.environment.set('hash', hash);
```
### Por que ISO-8859-1?
Descoberto por engenharia reversa: enviamos um hash deliberadamente
errado, o servidor responde com o hash que ele calculou
(`Conteúdo esperado: <md5>`). Reproduzir esse hash exigiu encoding
**Latin-1**, não UTF-8. Caracteres como `ç`, `ã`, `é` mudam de bytes
entre UTF-8 e Latin-1 — usar UTF-8 falha sempre que a descrição do
procedimento tem acento.
## 8. Tests (validação da resposta)
```javascript
pm.test('HTTP 200', () => pm.response.to.have.status(200));
const body = pm.response.text();
pm.test('Não é SOAP Fault', () =>
pm.expect(body).to.not.include('<env:Fault'));
pm.test('Tem cabecalho de resposta', () =>
pm.expect(body).to.include('RESPOSTA_SOLICITACAO'));
// Extrai número da guia da operadora se autorizada
const m = body.match(/<numeroGuiaOperadora>(\d+)<\/numeroGuiaOperadora>/);
if (m) console.log('Guia operadora:', m[1]);
// Extrai motivos de negativa, se houver
const negs = [...body.matchAll(/<descricaoGlosa>([^<]+)<\/descricaoGlosa>/g)];
if (negs.length) console.log('Negativas:', negs.map(n => n[1]));
```
## 9. Cenários e respostas (testados)
### 9.1 Sucesso técnico, negativa de negócio
`HTTP 200` com `RESPOSTA_SOLICITACAO`. A resposta vem com
`autorizacaoServico` e dentro de cada `servicoAutorizado` pode haver:
- `quantidadeAutorizada > 0` → autorizado
- `motivosNegativa` com `codigoGlosa` → negado
Códigos de glosa observados em homologação:
| Código | Descrição |
|---|---|
| `1212` | Atendimento/referência fora da vigência do contrato do prestador |
| `1402` | Procedimento não autorizado |
| `5007` (com `0012`) | Beneficiário repassado pré-pagamento |
| `5007` (com `0650`) | Guia já utilizada por outro documento |
### 9.2 SOAP Faults observados (e o que significam)
| Fault | Causa real |
|---|---|
| `does not contain operation meta data for: {http://ans.gov.br}solicitacaoProcedimentoWS` | Namespace errado — use `http://www.ans.gov.br/padroes/tiss/schemas` |
| `O conteúdo do elemento 'ans:epilogo' não está completo. Um dos seguintes elementos é esperado: {ans:hash}` | Está usando estrutura TISS 3.x. Esse endpoint não tem `epilogo` — `hash` é direto |
| `O elemento 'ans:profissionalSolicitante' lido não é esperado. É esperado o elemento 'ans:nomeContratadoSolicitante'` | Falta `<ans:nomeContratadoSolicitante>` (obrigatório, minLength=1) |
| `O conteúdo do elemento 'ans:procedimentosSolicitados' não está completo. Um dos seguintes elementos é esperado: {ans:quantidadeSolicitada}` | `quantidadeSolicitada` está aninhada dentro de `procedimento`. Tem que ser irmã |
| `Value '' with length = '0' is not facet-valid with respect to minLength '1'` | Algum campo string obrigatório está vazio |
| `O hash calculado difere do informado na mensagem. Conteúdo lido: <X>. Conteúdo esperado: <Y>` | Hash incorreto. **Use a resposta** — ela traz o hash que o servidor calculou |
## 10. Receita rápida pra debugar
1. Mensagem de SOAP fault sempre dá pista clara — leia a `faultstring`.
2. Se reclamar de hash, a resposta do servidor traz o hash esperado, então
dá pra reverter o algoritmo (foi exatamente como descobrimos
ISO-8859-1).
3. Se reclamar de schema, o erro vem com o nome exato do elemento e o
esperado.
4. Se HTTP 200 + `RESPOSTA_SOLICITACAO` + `motivosNegativa`, a integração
técnica está OK — problema é de negócio (contrato, cobertura, etc.).
## 11. Resumo do fluxo descoberto
1. **Auth**: Basic Auth com user = código prestador, sem mTLS.
2. **Schema validation**: rigoroso, XSD do TISS 4.01.00.
3. **Hash**: MD5 dos text nodes em Latin-1.
4. **Resposta**: TISS `autorizacaoProcedimentoWS` com `RESPOSTA_SOLICITACAO`.
5. **Cloudflare na frente**: TLS é terminado lá, sem problemas com
certificados.