Documentação da API REST para integração de NFS-e e CT-e
A API REST do MeusPortais permite que sistemas externos consultem as notas fiscais de serviços (NFS-e) sincronizadas, com as mesmas regras de acesso da interface web.
https://baudenotas.com.br/api
YYYY-MM-DDMM/YYYYfloat (ex: 1500.00)null| Cabeçalho | Valor | Descrição |
|---|---|---|
Accept | application/json | Formato esperado |
Content-Type | application/json | Para requisições com body |
Authorization | Bearer {token} | Rotas autenticadas |
A API utiliza autenticação por token Bearer (Laravel Sanctum). O fluxo é:
POST /api/auth/login com e-mail e senhatoken na respostaAuthorization: Bearer {token}POST /api/auth/logout para invalidar o tokenAs permissões da API seguem exatamente as mesmas regras da interface web:
| Perfil | Acesso |
|---|---|
admin |
Vê todas as NFS-e de todas as filiais da empresa |
user |
Vê apenas NFS-e das filiais explicitamente liberadas pelo administrador |
GET /api/auth/me para verificar o perfil e as filiais do usuário autenticado.
| Código HTTP | Significado |
|---|---|
200 | Sucesso |
401 | Não autenticado — credenciais inválidas ou token ausente/expirado |
403 | Sem permissão para o recurso solicitado |
404 | Recurso não encontrado |
422 | Erro de validação — verifique o campo errors na resposta |
500 | Erro interno do servidor |
Formato padrão de erro:
{
"message": "Descrição do erro"
}
Para erros de validação (422):
{
"message": "The given data was invalid.",
"errors": {
"email": ["O campo e-mail é obrigatório."]
}
}
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
email | string | Sim | E-mail do usuário |
password | string | Sim | Senha do usuário |
POST /api/auth/login
Content-Type: application/json
{
"email": "usuario@empresa.com",
"password": "minha-senha"
}
{
"token": "1|abc123...",
"token_type": "Bearer",
"user": {
"id": 1,
"name": "João Silva",
"email": "usuario@empresa.com",
"role": "user"
}
}
POST /api/auth/logout Authorization: Bearer 1|abc123...
{
"message": "Logout realizado com sucesso."
}
GET /api/auth/me Authorization: Bearer 1|abc123...
{
"id": 2,
"name": "Maria Souza",
"email": "maria@empresa.com",
"role": "user",
"filiais": [
{
"id": 1,
"descricao": "Matriz SP",
"cnpj": "12345678000195"
}
]
}
admin, o campo filiais retorna array vazio — eles têm acesso total a todas as filiais.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | integer | 1 | Página atual |
per_page | integer | 25 | Registros por página (máx. 100) |
order_by | string | data_emissao | Campo para ordenação: data_emissao, numero_nfse, competencia, valor_servicos, razao_social_prestador |
order_dir | string | desc | asc ou desc |
numero | string | — | Filtra por número da NFS-e (parcial) |
cnpj_prestador | string | — | Filtra por CNPJ do prestador (somente dígitos) |
cnpj_filial | string | — | Filtra por CNPJ da filial tomadora (somente dígitos) |
prestador | string | — | Filtra por razão social do prestador (parcial) |
competencia | string | — | Filtra por competência no formato MM/YYYY |
emissao_de | string | — | Data de emissão inicial (YYYY-MM-DD) |
emissao_ate | string | — | Data de emissão final (YYYY-MM-DD) |
chave_acesso | string | — | Filtra por chave de acesso (parcial) |
status | string | — | normal, cancelada ou vazio (todos) |
GET /api/nfse?page=1&per_page=10 &emissao_de=2025-01-01 &emissao_ate=2025-12-31 &status=normal Authorization: Bearer 1|abc123...
{
"data": [
{
"id": 42,
"nsu": "000000100042",
"chave_acesso": "3525...",
"data_emissao": "2025-03-15",
"numero_nfse": "000000042",
"serie": "001",
"cnpj_filial": "12345678000195",
"nome_filial": "Matriz SP",
"cnpj_prestador": "98765432000188",
"razao_social_prestador": "Fornecedor Ltda",
"cnpj_tomador": "12345678000195",
"razao_social_tomador": "Minha Empresa SA",
"competencia": "03/2025",
"valor_servicos": 1500.00,
"valor_iss": 75.00,
"aliquota": 5.0000,
"iss_retido": false,
"discriminacao": "Serviços de TI...",
"codigo_municipio": "3550308",
"item_lista_servico": "01.01",
"status": "normal",
"data_hora_geracao": "2025-03-15T10:30:00"
}
],
"meta": {
"total": 1,
"per_page": 10,
"current_page": 1,
"last_page": 1
}
}
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
id | integer | — | ID interno da NFS-e (campo id do resultado da listagem) |
include_pdf | boolean | false | Se 1, inclui o DANFSe em PDF codificado em Base64 no campo pdf_base64 |
include_xml | boolean | false | Se 1, inclui o XML original codificado em Base64 no campo xml_base64 |
GET /api/nfse/42?include_pdf=1&include_xml=1 Authorization: Bearer 1|abc123...
{
"id": 42,
"nsu": "000000100042",
"chave_acesso": "3525...",
"data_emissao": "2025-03-15",
"numero_nfse": "000000042",
"serie": "001",
"cnpj_filial": "12345678000195",
"nome_filial": "Matriz SP",
"cnpj_prestador": "98765432000188",
"razao_social_prestador": "Fornecedor Ltda",
"cnpj_tomador": "12345678000195",
"razao_social_tomador": "Minha Empresa SA",
"competencia": "03/2025",
"valor_servicos": 1500.00,
"valor_iss": 75.00,
"aliquota": 5.0000,
"iss_retido": false,
"discriminacao": "Serviços de TI...",
"codigo_municipio": "3550308",
"item_lista_servico": "01.01",
"status": "normal",
"lancada": false,
"lancada_em": null,
"lancada_por": null,
"data_hora_geracao": "2025-03-15T10:30:00",
"xml_base64": "PD94bWwgdmVyc2lvbj...",
"pdf_base64": "JVBERi0xLjQKMSAw..."
}
404 se a NFS-e não existir ou se o usuário não tiver permissão para a filial correspondente.
include_pdf e include_xml quando não precisar destes dados para evitar resposta grande. Os campos retornam null quando não solicitados.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
lancada | boolean | Sim | true para marcar como lançada / false para remover a marcação |
POST /api/nfse/42/lancada
Authorization: Bearer 1|abc123...
Content-Type: application/json
{"lancada": true}
{
"id": 42,
"lancada": true,
"lancada_em": "2025-03-15T14:22:00+00:00",
"lancada_por": "João Silva <j@emp.com>",
"message": "NFS-e marcada como lançada."
}
Parâmetros de query:
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | integer | 1 | Página atual |
per_page | integer | 25 | Registros por página (máx. 100) |
order_by | string | data_emissao | Campo para ordenação: data_emissao, numero_nfe, valor_nf, razao_emit |
order_dir | string | desc | asc ou desc |
chave_acesso | string | — | Filtra por chave de acesso (parcial, 44 dígitos) |
numero | string | — | Filtra por número da NF-e (parcial) |
cnpj_emitente | string | — | Filtra por CNPJ do emitente (somente números, parcial) |
cnpj_filial | string | — | Filtra por CNPJ da filial consultada (14 dígitos) |
emitente | string | — | Filtra por razão social do emitente (parcial) |
tipo_nf | integer | — | 0 = Entrada, 1 = Saída |
situacao | string | — | autorizado, cancelado ou denegado |
emissao_de | date | — | Data de emissão inicial (YYYY-MM-DD) |
emissao_ate | date | — | Data de emissão final (YYYY-MM-DD) |
lancada | boolean | — | true / false — filtra por status de lançamento |
Exemplo de requisição:
GET /api/nfe?page=1&per_page=10
Authorization: Bearer {token}
Exemplo de resposta:
{
"data": [
{
"id": 1,
"nsu": 500,
"chave_acesso": "35250191154872000160550010000000071000000075",
"data_emissao": "2025-03-10 08:30:00",
"numero_nfe": "7",
"serie": "1",
"tipo_nf": 1,
"tipo_nf_descricao": "Saída",
"cnpj_filial": "91154872000160",
"nome_filial": "Haenssgen",
"cnpj_emitente": "91154872000160",
"cpf_emitente": null,
"razao_emitente": "Haenssgen Comércio Ltda",
"ie_emitente": "1234567890",
"cnpj_destinatario": "12345678000195",
"cpf_destinatario": null,
"razao_destinatario": null,
"valor_nf": 1500.00,
"situacao": "autorizado",
"numero_protocolo": "135250000012345",
"tipo_documento": "Resumo NF-e",
"data_hora_geracao": "2025-03-10 08:31:00",
"lancada": false,
"lancada_em": null,
"lancada_por": null
}
],
"meta": {
"total": 1,
"per_page": 10,
"current_page": 1,
"last_page": 1
}
}
Parâmetro de rota:
| Parâmetro | Tipo | Descrição |
|---|---|---|
id | integer | ID interno da NF-e (campo id da listagem) |
Exemplo de resposta:
GET /api/nfe/1
Authorization: Bearer {token}
{
"id": 1,
"nsu": 500,
"chave_acesso": "35250191154872000160550010000000071000000075",
"data_emissao": "2025-03-10 08:30:00",
"numero_nfe": "7",
"serie": "1",
"tipo_nf": 1,
"tipo_nf_descricao": "Saída",
"cnpj_filial": "91154872000160",
"nome_filial": "Haenssgen",
"cnpj_emitente": "91154872000160",
"cpf_emitente": null,
"razao_emitente": "Haenssgen Comércio Ltda",
"ie_emitente": "1234567890",
"cnpj_destinatario": "12345678000195",
"cpf_destinatario": null,
"razao_destinatario": null,
"valor_nf": 1500.00,
"situacao": "autorizado",
"numero_protocolo": "135250000012345",
"tipo_documento": "Resumo NF-e",
"data_hora_geracao": "2025-03-10 08:31:00",
"lancada": false,
"lancada_em": null,
"lancada_por": null,
"eventos": [
{
"nsu": 501,
"tipo_evento": "Cancelamento",
"data_hora_geracao": "2025-03-11 10:00:00"
}
]
}
404 se a NF-e não existir ou o usuário não tiver permissão para a filial.
Body (JSON):
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
lancada | boolean | Sim | true para marcar, false para desmarcar |
Exemplo:
POST /api/nfe/1/lancada
Authorization: Bearer {token}
Content-Type: application/json
{"lancada": true}
// Resposta 200
{
"id": 1,
"lancada": true,
"lancada_em": "2025-03-15T14:22:00+00:00",
"lancada_por": "João Silva <j@emp.com>",
"message": "NF-e marcada como lançada."
}
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
page | integer | 1 | Página atual |
per_page | integer | 25 | Registros por página (máx. 100) |
order_by | string | data_emissao | Campo para ordenação: data_emissao, numero_cte, modal, valor_total_prestacao, razao_emit |
order_dir | string | desc | asc ou desc |
numero | string | — | Filtra por número do CT-e (parcial) |
chave_acesso | string | — | Filtra por chave de acesso (parcial) |
cnpj_emitente | string | — | Filtra por CNPJ da transportadora (somente dígitos) |
cnpj_filial | string | — | Filtra por CNPJ da filial tomadora (somente dígitos) |
emitente | string | — | Filtra por razão social da transportadora (parcial) |
modal | string | — | Código do modal: 01 Rodoviário, 02 Aéreo, 03 Aquaviário, 04 Ferroviário, 05 Dutoviário, 06 Multimodal |
emissao_de | string | — | Data de emissão inicial (YYYY-MM-DD) |
emissao_ate | string | — | Data de emissão final (YYYY-MM-DD) |
status | string | — | normal, cancelada ou vazio (todos) |
lancada | boolean | — | true ou false para filtrar por status de lançamento |
GET /api/cte?page=1&per_page=10 &emissao_de=2025-01-01 &emissao_ate=2025-12-31 &modal=01 Authorization: Bearer 1|abc123...
{
"data": [
{
"id": 7,
"nsu": "000000100007",
"chave_acesso": "3525...",
"data_emissao": "2025-03-10T00:00:00",
"numero_cte": "000000007",
"serie": "1",
"cfop": "6352",
"nat_op": "PRESTACAO DE SERVICO",
"modal": "01",
"modal_descricao": "Rodoviário",
"cnpj_filial": "12345678000195",
"nome_filial": "Matriz SP",
"uf_ini": "SP",
"uf_fim": "RJ",
"cnpj_emitente": "11222333000181",
"razao_emitente": "Transportes ABC Ltda",
"cnpj_remetente": "12345678000195",
"razao_remetente": "Minha Empresa SA",
"cnpj_destinatario": "98765432000188",
"razao_destinatario": "Cliente Final Ltda",
"cnpj_tomador": "12345678000195",
"razao_tomador": "Minha Empresa SA",
"ind_tomador": 0,
"valor_total_prestacao": 850.00,
"valor_receber": 850.00,
"produto_predominante": "Eletrônicos",
"status": "normal",
"data_hora_geracao": "2025-03-10T08:45:00",
"lancada": false,
"lancada_em": null,
"lancada_por": null
}
],
"meta": {
"total": 1,
"per_page": 10,
"current_page": 1,
"last_page": 1
}
}
ind_tomador indica quem é o tomador do serviço: 0 = Remetente, 1 = Expedidor, 2 = Recebedor, 3 = Destinatário, 4 = Outros.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
id | integer | — | ID interno do CT-e (campo id do resultado da listagem) |
include_xml | boolean | false | Se 1, inclui o XML original codificado em Base64 no campo xml_base64 |
GET /api/cte/7?include_xml=1 Authorization: Bearer 1|abc123...
{
"id": 7,
"nsu": "000000100007",
"chave_acesso": "3525...",
"data_emissao": "2025-03-10T00:00:00",
"numero_cte": "000000007",
"serie": "1",
"cfop": "6352",
"nat_op": "PRESTACAO DE SERVICO",
"modal": "01",
"modal_descricao": "Rodoviário",
"cnpj_filial": "12345678000195",
"nome_filial": "Matriz SP",
"uf_ini": "SP",
"uf_fim": "RJ",
"cnpj_emitente": "11222333000181",
"razao_emitente": "Transportes ABC Ltda",
"cnpj_remetente": "12345678000195",
"cpf_remetente": null,
"razao_remetente": "Minha Empresa SA",
"cnpj_destinatario": "98765432000188",
"cpf_destinatario": null,
"razao_destinatario": "Cliente Final Ltda",
"cnpj_tomador": "12345678000195",
"cpf_tomador": null,
"razao_tomador": "Minha Empresa SA",
"ind_tomador": 0,
"valor_total_prestacao": 850.00,
"valor_receber": 850.00,
"produto_predominante": "Eletrônicos",
"status": "normal",
"tipo_documento": "CT-e",
"data_hora_geracao": "2025-03-10T08:45:00",
"lancada": false,
"lancada_em": null,
"lancada_por": null,
"xml_base64": "PD94bWwgdmVyc2lvbj..."
}
404 se o CT-e não existir ou se o usuário não tiver permissão para a filial correspondente.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
lancada | boolean | Sim | true para marcar como lançado / false para remover a marcação |
POST /api/cte/7/lancada
Authorization: Bearer 1|abc123...
Content-Type: application/json
{"lancada": true}
{
"id": 7,
"lancada": true,
"lancada_em": "2025-03-15T14:22:00+00:00",
"lancada_por": "João Silva <j@emp.com>",
"message": "CT-e marcado como lançado."
}
1. Fazer login e obter token:
curl -s -X POST https://baudenotas.com.br/api/auth/login \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email":"usuario@empresa.com","password":"senha123"}'
2. Listar NFS-e com filtro de período:
curl -s "https://baudenotas.com.br/api/nfse?emissao_de=2025-01-01&emissao_ate=2025-12-31&per_page=50" \ -H "Accept: application/json" \ -H "Authorization: Bearer SEU_TOKEN_AQUI"
3. Buscar NFS-e específica (com XML):
curl -s "https://baudenotas.com.br/api/nfse/42?include_xml=1" \ -H "Accept: application/json" \ -H "Authorization: Bearer SEU_TOKEN_AQUI"
4. Listar NF-e de entrada não lançadas:
curl -s "https://baudenotas.com.br/api/nfe?tipo_nf=0&lancada=false&per_page=50" \ -H "Accept: application/json" \ -H "Authorization: Bearer SEU_TOKEN_AQUI"
5. Listar CT-e rodoviário não lançado:
curl -s "https://baudenotas.com.br/api/cte?modal=01&lancada=false&per_page=50" \ -H "Accept: application/json" \ -H "Authorization: Bearer SEU_TOKEN_AQUI"
6. Marcar NF-e como lançada:
curl -s -X POST "https://baudenotas.com.br/api/nfe/1/lancada" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{"lancada": true}'
7. Marcar CT-e como lançado:
curl -s -X POST "https://baudenotas.com.br/api/cte/7/lancada" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{"lancada": true}'
8. Logout:
curl -s -X POST https://baudenotas.com.br/api/auth/logout \ -H "Accept: application/json" \ -H "Authorization: Bearer SEU_TOKEN_AQUI"
<?php
$base = 'https://baudenotas.com.br/api';
// 1. Login
$resp = file_get_contents($base . '/auth/login', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAccept: application/json\r\n",
'content' => json_encode([
'email' => 'usuario@empresa.com',
'password' => 'senha123',
]),
],
]));
$auth = json_decode($resp, true);
$token = $auth['token'];
// 2. Listar NFS-e
$opts = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "Accept: application/json\r\nAuthorization: Bearer $token\r\n",
],
]);
$resp = file_get_contents($base . '/nfse?per_page=50&status=normal', false, $opts);
$dados = json_decode($resp, true);
foreach ($dados['data'] as $nfse) {
echo $nfse['numero_nfse'] . ' — ' . $nfse['razao_social_prestador'] . "\n";
}
// 3. Listar NF-e de entrada e marcar como lançada
$resp = file_get_contents($base . '/nfe?per_page=50&tipo_nf=0&lancada=false', false, $opts);
$nfes = json_decode($resp, true);
foreach ($nfes['data'] as $nfe) {
echo $nfe['numero_nfe'] . ' — ' . $nfe['razao_emitente'] . ' R$ ' . $nfe['valor_nf'] . "\n";
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAccept: application/json\r\nAuthorization: Bearer $token\r\n",
'content' => json_encode(['lancada' => true]),
],
]);
file_get_contents($base . '/nfe/' . $nfe['id'] . '/lancada', false, $ctx);
}
// 4. Listar CT-e e marcar como lançado
$resp = file_get_contents($base . '/cte?per_page=50&lancada=false', false, $opts);
$ctes = json_decode($resp, true);
foreach ($ctes['data'] as $cte) {
echo $cte['numero_cte'] . ' — ' . $cte['razao_emitente'] . "\n";
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAccept: application/json\r\nAuthorization: Bearer $token\r\n",
'content' => json_encode(['lancada' => true]),
],
]);
file_get_contents($base . '/cte/' . $cte['id'] . '/lancada', false, $ctx);
}
// 4. Logout
file_get_contents($base . '/auth/logout', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Accept: application/json\r\nAuthorization: Bearer $token\r\n",
],
]));
import requests
BASE = 'https://baudenotas.com.br/api'
session = requests.Session()
session.headers.update({'Accept': 'application/json'})
# 1. Login
resp = session.post(f'{BASE}/auth/login', json={
'email': 'usuario@empresa.com',
'password': 'senha123',
})
resp.raise_for_status()
token = resp.json()['token']
session.headers['Authorization'] = f'Bearer {token}'
# 2. Listar NFS-e com paginação automática
page = 1
while True:
resp = session.get(f'{BASE}/nfse', params={
'page': page, 'per_page': 100, 'status': 'normal',
'emissao_de': '2025-01-01', 'emissao_ate': '2025-12-31',
})
dados = resp.json()
for nfse in dados['data']:
print(nfse['numero_nfse'], '-', nfse['razao_social_prestador'])
if page >= dados['meta']['last_page']:
break
page += 1
# 3. Buscar NFS-e específica (com XML)
nfse = session.get(f'{BASE}/nfse/42', params={'include_xml': 1}).json()
print('Valor:', nfse['valor_servicos'])
# 4. Listar NF-e de entrada não lançadas e processar
page = 1
while True:
resp = session.get(f'{BASE}/nfe', params={
'page': page, 'per_page': 100, 'lancada': 'false',
'tipo_nf': 0, 'emissao_de': '2025-01-01',
})
dados = resp.json()
for nfe in dados['data']:
print(nfe['numero_nfe'], '-', nfe['razao_emitente'], f"R$ {nfe['valor_nf']}")
session.post(f"{BASE}/nfe/{nfe['id']}/lancada", json={'lancada': True})
if page >= dados['meta']['last_page']:
break
page += 1
# 5. Listar CT-e não lançados e processar
page = 1
while True:
resp = session.get(f'{BASE}/cte', params={
'page': page, 'per_page': 100, 'lancada': 'false',
'emissao_de': '2025-01-01',
})
dados = resp.json()
for cte in dados['data']:
print(cte['numero_cte'], '-', cte['razao_emitente'], f"R$ {cte['valor_total_prestacao']}")
session.post(f"{BASE}/cte/{cte['id']}/lancada", json={'lancada': True})
if page >= dados['meta']['last_page']:
break
page += 1
# 5. Logout
session.post(f'{BASE}/auth/logout')
Utilize a classe FWRest disponível no Protheus (LIB ≥ 20180101) para chamar a API. As credenciais e o endereço do servidor são lidos de parâmetros MV via SUPERGETMV(), mantendo o código limpo e sem dados sensíveis expostos.
MPBASE, MPLOGIN e MPSENHA no configurador do Protheus (SIGACFG → Ambiente → Parâmetros) com a URL base da API e as credenciais de acesso.
Includes e definições:
#include 'protheus.ch'
#include 'restful.ch'
#define MP_BASE SUPERGETMV("MPBASE" ,.F.,"")
#define MP_LOGIN SUPERGETMV("MPLOGIN",.F.,"")
#define MP_SENHA SUPERGETMV("MPSENHA",.F.,"")
1. Login — obter token:
/*
* Autentica na MP e retorna o Bearer token.
* Retorna "" em caso de falha.
*/
Static Function MPLogin()
Local oRest := FWRest():New(MP_BASE)
Local cToken := ""
Local aHeader := {}
Local cBody := '{"email":"' + MP_LOGIN + '","password":"' + MP_SENHA + '"}'
Local oJson
aAdd(aHeader, 'Content-Type: application/json')
aAdd(aHeader, 'User-Agent: TotvsProtheus ' + GetBuild())
oRest:SetPath('/auth/login')
oRest:SetPostParams(cBody)
If oRest:POST(aHeader)
oJson := JSonObject():New()
oJson:FromJson(oRest:cResult)
cToken := oJson["token"]
FreeObj(oJson)
Else
Conout('Erro no login: ' + oRest:cResult, 'API NFS-e')
EndIf
FreeObj(oRest)
Return cToken
2. Listar NFS-e com filtros:
/*
* Lista NFS-e. Retorna array de registros ou NIL em erro.
* Parâmetros:
* dDe / dAte - Datas de emissão (tipo Date) ou CtoD('') para sem filtro
* cStatus - "normal", "cancelada" ou "" para todos
* nPage - Página inicial (começa em 1)
* lTodas - .T. para percorrer todas as páginas automaticamente
* cChave - Chave de acesso (parcial ou completa)
* cCnpjPrest - CNPJ do prestador (somente números)
* cNumeroNfse - Número da NFS-e
*
* Retorno — cada elemento do array:
* [1] ID [2] Número [3] Data emissão
* [4] CNPJ Filial [5] Nome Filial [6] CNPJ Prestador
* [7] Prestador [8] Vl. Serviços [9] Competência
* [10] Status [11] Lançada? [12] Chave Acesso
* [13] Cód. Serviço [14] Discriminação
*/
User Function MPListarNfse(dDe, dAte, cStatus, nPage, lTodas, cChave, cCnpjPrest, cNumeroNfse)
Local oRest := FWRest():New(MP_BASE)
Local cToken := MPLogin()
Local aNotas := {}
Local aHeader := {}
Local oJson, oData, oMeta, i, oItem
Local cDtS, cParams
Local nCurPage := If(nPage >= 1, nPage, 1)
Local nLastPage := nCurPage
If Empty(cToken)
Return NIL
EndIf
If ValType(lTodas) != 'L'
lTodas := .F.
EndIf
aAdd(aHeader, 'Content-Type: application/json')
aAdd(aHeader, 'Authorization: Bearer ' + cToken)
aAdd(aHeader, 'User-Agent: TotvsProtheus ' + GetBuild())
Do While nCurPage <= nLastPage
cParams := 'page=' + cValToChar(nCurPage) + '&per_page=100'
If !Empty(dDe)
cDtS := DToS(dDe)
cParams += '&emissao_de=' + SubStr(cDtS,1,4) + '-' + SubStr(cDtS,5,2) + '-' + SubStr(cDtS,7,2)
EndIf
If !Empty(dAte)
cDtS := DToS(dAte)
cParams += '&emissao_ate=' + SubStr(cDtS,1,4) + '-' + SubStr(cDtS,5,2) + '-' + SubStr(cDtS,7,2)
EndIf
If !Empty(cStatus)
cParams += '&status=' + cStatus
EndIf
If !Empty(cChave)
cParams += '&chave_acesso=' + cChave
EndIf
If !Empty(cCnpjPrest)
cParams += '&cnpj_prestador=' + cCnpjPrest
EndIf
If !Empty(cNumeroNfse)
cParams += '&numero_nfse=' + cNumeroNfse
EndIf
oRest:SetPath('/nfse')
oRest:SetGetParams(cParams)
If oRest:GET(aHeader)
oJson := JSonObject():New()
oJson:FromJson(oRest:cResult)
oData := oJson["data"]
For i := 1 To Len(oData)
oItem := oData[i]
aAdd(aNotas, {;
oItem["id"], ; // [1] ID
oItem["numero_nfse"], ; // [2] Número
oItem["data_emissao"], ; // [3] Data emissão
oItem["cnpj_filial"], ; // [4] CNPJ Filial
oItem["nome_filial"], ; // [5] Nome Filial
oItem["cnpj_prestador"], ; // [6] CNPJ Prestador
oItem["razao_social_prestador"], ; // [7] Prestador
oItem["valor_servicos"], ; // [8] Valor Serviços
oItem["competencia"], ; // [9] Competência
oItem["status"], ; // [10] Status
oItem["lancada"], ; // [11] Lançada?
oItem["chave_acesso"], ; // [12] Chave Acesso
oItem["item_lista_servico"], ; // [13] Cód. Serviço
oItem["discriminacao"] ; // [14] Discriminação
})
Next i
If lTodas
oMeta := oJson["meta"]
nLastPage := oMeta["last_page"]
EndIf
FreeObj(oJson)
Else
Conout('Erro ao listar NFS-e: ' + oRest:cResult, 'API NFS-e')
aNotas := NIL
Exit
EndIf
nCurPage++
EndDo
FreeObj(oRest)
Return aNotas
3. Marcar / desmarcar NFS-e como lançada:
/*
* Marca ou desmarca uma NFS-e como lançada no sistema.
* Localiza a nota pela chave de acesso OU por CNPJ do prestador + número.
*
* Parâmetros:
* cChave - Chave de acesso da nota (preferencial)
* lLancada - .T. para marcar como lançada, .F. para desmarcar
* cCnpjPrest - CNPJ do prestador (somente números) — alternativa à chave
* cNumeroNfse - Número da NFS-e — alternativa à chave
*
* Retorna .T. em caso de sucesso.
*/
User Function MPDemarcaLancada(cChave, lLancada, cCnpjPrest, cNumeroNfse)
Local cToken := MPLogin()
Local aNotas := {}
Local cId := ''
Local oRest
Local aHeader := {}
Local cBody
Local lOk := .F.
If Empty(cToken)
Return lOk
EndIf
Default lLancada := .T.
Default cChave := ''
Default cCnpjPrest := ''
Default cNumeroNfse := ''
If Empty(cChave) .And. (Empty(cCnpjPrest) .Or. Empty(cNumeroNfse))
Conout('Informe a chave de acesso ou o CNPJ do prestador + numero da NFS-e.', 'API NFS-e')
Return lOk
EndIf
// Localiza o ID da nota via listagem
aNotas := U_MPListarNfse(CtoD(''), CtoD(''), '', 1, .F., cChave, cCnpjPrest, cNumeroNfse)
If aNotas == NIL .Or. Len(aNotas) == 0
Conout('NFS-e nao encontrada para os dados informados.', 'API NFS-e')
Return lOk
EndIf
cId := cValToChar(aNotas[1][1])
oRest := FWRest():New(MP_BASE)
aAdd(aHeader, 'Content-Type: application/json')
aAdd(aHeader, 'Authorization: Bearer ' + MPLogin())
aAdd(aHeader, 'User-Agent: TotvsProtheus ' + GetBuild())
cBody := '{"lancada":' + If(lLancada, 'true', 'false') + '}'
oRest:SetPath('/nfse/' + cId + '/lancada')
oRest:SetPostParams(cBody)
If oRest:POST(aHeader)
lOk := .T.
Else
Conout('Erro ao atualizar NFS-e: ' + oRest:cResult, 'API NFS-e')
EndIf
FreeObj(oRest)
Return lOk
4. Exemplo de uso completo:
/*
* Exemplo: buscar todas as notas normais não lançadas
* e marcar cada uma após processar no Protheus.
* lTodas=.T. percorre todas as páginas automaticamente.
*/
User Function ImportaNFSe()
Local aNotas := U_MPListarNfse(CtoD(''), CtoD(''), 'normal', 1, .T., '', '', '')
Local i, aItem
If aNotas == NIL
Conout('Falha ao obter notas.', 'API NFS-e')
Return
EndIf
For i := 1 To Len(aNotas)
aItem := aNotas[i]
// [11] lancada — pula notas já lançadas
If aItem[11]
Loop
EndIf
// *** Insira aqui a lógica de lançamento no Protheus ***
// Ex: GravarNotaFiscal(aItem)
// Marca como lançada pela chave de acesso [12]
If !Empty(aItem[12])
U_MPDemarcaLancada(aItem[12], .T., '', '')
EndIf
Next i
MsgInfo('Importacao concluida! ' + cValToChar(Len(aNotas)) + ' nota(s) processada(s).', 'API NFS-e')
Return
lTodas := .T. em U_MPListarNfse para percorrer automaticamente todas as páginas de resultados — ideal para integrações em batch. Para buscas pontuais passe .F. e controle a paginação manualmente via nPage.
Funções equivalentes às de NFS-e, adaptadas para os campos do CT-e. Utilize os mesmos parâmetros MV (MPBASE, MPLOGIN, MPSENHA) e a função MPLogin() definida na seção anterior.
1. Listar CT-e com filtros:
/*
* Lista CT-e. Retorna array de registros ou NIL em erro.
* Parâmetros:
* dDe / dAte - Datas de emissão (tipo Date) ou CtoD('') para sem filtro
* cStatus - "normal", "cancelada" ou "" para todos
* nPage - Página inicial (começa em 1)
* lTodas - .T. para percorrer todas as páginas automaticamente
* cChave - Chave de acesso (parcial ou completa)
* cCnpjEmit - CNPJ da transportadora (somente números)
* cModal - Código do modal: 01=Rod, 02=Aéreo, 03=Aqua, 04=Ferr, 05=Duto, 06=Multi
* lNaoLancados - .T. para trazer apenas CT-e não lançados
*
* Retorno — cada elemento do array:
* [1] ID [2] Número CT-e [3] Data emissão
* [4] CNPJ Filial [5] Nome Filial [6] CNPJ Emitente
* [7] Razão Emitente [8] Razão Remetente [9] Razão Destinatário
* [10] CFOP [11] Modal [12] Modal (descrição)
* [13] UF Ini [14] UF Fim [15] Valor Total Prestação
* [16] Valor a Receber [17] Produto Predom. [18] Status
* [19] Lançado? [20] Chave Acesso [21] CNPJ Tomador
* [22] Razão Tomador [23] Ind. Tomador
*/
User Function MPListarCte(dDe, dAte, cStatus, nPage, lTodas, cChave, cCnpjEmit, cModal, lNaoLancados)
Local oRest := FWRest():New(MP_BASE)
Local cToken := MPLogin()
Local aCtes := {}
Local aHeader := {}
Local oJson, oData, oMeta, i, oItem
Local cDtS, cParams
Local nCurPage := If(nPage >= 1, nPage, 1)
Local nLastPage := nCurPage
If Empty(cToken)
Return NIL
EndIf
If ValType(lTodas) != 'L' ; lTodas := .F. ; EndIf
If ValType(lNaoLancados) != 'L'; lNaoLancados := .F. ; EndIf
aAdd(aHeader, 'Content-Type: application/json')
aAdd(aHeader, 'Authorization: Bearer ' + cToken)
aAdd(aHeader, 'User-Agent: TotvsProtheus ' + GetBuild())
Do While nCurPage <= nLastPage
cParams := 'page=' + cValToChar(nCurPage) + '&per_page=100'
If !Empty(dDe)
cDtS := DToS(dDe)
cParams += '&emissao_de=' + SubStr(cDtS,1,4) + '-' + SubStr(cDtS,5,2) + '-' + SubStr(cDtS,7,2)
EndIf
If !Empty(dAte)
cDtS := DToS(dAte)
cParams += '&emissao_ate=' + SubStr(cDtS,1,4) + '-' + SubStr(cDtS,5,2) + '-' + SubStr(cDtS,7,2)
EndIf
If !Empty(cStatus)
cParams += '&status=' + cStatus
EndIf
If !Empty(cChave)
cParams += '&chave_acesso=' + cChave
EndIf
If !Empty(cCnpjEmit)
cParams += '&cnpj_emitente=' + cCnpjEmit
EndIf
If !Empty(cModal)
cParams += '&modal=' + cModal
EndIf
If lNaoLancados
cParams += '&lancada=false'
EndIf
oRest:SetPath('/cte')
oRest:SetGetParams(cParams)
If oRest:GET(aHeader)
oJson := JSonObject():New()
oJson:FromJson(oRest:cResult)
oData := oJson["data"]
For i := 1 To Len(oData)
oItem := oData[i]
aAdd(aCtes, {;
oItem["id"], ; // [1] ID
oItem["numero_cte"], ; // [2] Número CT-e
oItem["data_emissao"], ; // [3] Data emissão
oItem["cnpj_filial"], ; // [4] CNPJ Filial
oItem["nome_filial"], ; // [5] Nome Filial
oItem["cnpj_emitente"], ; // [6] CNPJ Emitente
oItem["razao_emitente"], ; // [7] Razão Emitente
oItem["razao_remetente"], ; // [8] Razão Remetente
oItem["razao_destinatario"], ; // [9] Razão Destinatário
oItem["cfop"], ; // [10] CFOP
oItem["modal"], ; // [11] Modal (código)
oItem["modal_descricao"], ; // [12] Modal (descrição)
oItem["uf_ini"], ; // [13] UF Início
oItem["uf_fim"], ; // [14] UF Fim
oItem["valor_total_prestacao"], ; // [15] Valor Total Prestação
oItem["valor_receber"], ; // [16] Valor a Receber
oItem["produto_predominante"], ; // [17] Produto Predom.
oItem["status"], ; // [18] Status
oItem["lancada"], ; // [19] Lançado?
oItem["chave_acesso"], ; // [20] Chave Acesso
oItem["cnpj_tomador"], ; // [21] CNPJ Tomador
oItem["razao_tomador"], ; // [22] Razão Tomador
oItem["ind_tomador"] ; // [23] Ind. Tomador
})
Next i
If lTodas
oMeta := oJson["meta"]
nLastPage := oMeta["last_page"]
EndIf
FreeObj(oJson)
Else
Conout('Erro ao listar CT-e: ' + oRest:cResult, 'API CT-e')
aCtes := NIL
Exit
EndIf
nCurPage++
EndDo
FreeObj(oRest)
Return aCtes
2. Marcar / desmarcar CT-e como lançado:
/*
* Marca ou desmarca um CT-e como lançado.
* Localiza pelo ID direto ou pela chave de acesso.
*
* Parâmetros:
* nId - ID interno do CT-e (use 0 para localizar pela chave)
* lLancada - .T. para marcar, .F. para desmarcar
* cChave - Chave de acesso (usada quando nId = 0)
*
* Retorna .T. em caso de sucesso.
*/
User Function MPDemarcaCte(nId, lLancada, cChave)
Local cToken := MPLogin()
Local cId := ''
Local oRest
Local aHeader := {}
Local cBody
Local lOk := .F.
Local aCtes
If Empty(cToken)
Return lOk
EndIf
Default lLancada := .T.
Default cChave := ''
If nId > 0
cId := cValToChar(nId)
ElseIf !Empty(cChave)
// Localiza o ID via listagem pela chave de acesso
aCtes := U_MPListarCte(CtoD(''), CtoD(''), '', 1, .F., cChave, '', '', .F.)
If aCtes == NIL .Or. Len(aCtes) == 0
Conout('CT-e nao encontrado para a chave: ' + cChave, 'API CT-e')
Return lOk
EndIf
cId := cValToChar(aCtes[1][1])
Else
Conout('Informe o ID ou a chave de acesso do CT-e.', 'API CT-e')
Return lOk
EndIf
oRest := FWRest():New(MP_BASE)
aAdd(aHeader, 'Content-Type: application/json')
aAdd(aHeader, 'Authorization: Bearer ' + cToken)
aAdd(aHeader, 'User-Agent: TotvsProtheus ' + GetBuild())
cBody := '{"lancada":' + If(lLancada, 'true', 'false') + '}'
oRest:SetPath('/cte/' + cId + '/lancada')
oRest:SetPostParams(cBody)
If oRest:POST(aHeader)
lOk := .T.
Else
Conout('Erro ao atualizar CT-e: ' + oRest:cResult, 'API CT-e')
EndIf
FreeObj(oRest)
Return lOk
3. Exemplo de uso — importar CT-e não lançados:
/*
* Busca todos os CT-e não lançados do mês corrente
* e marca cada um após processar no Protheus.
* lTodas=.T. percorre todas as páginas automaticamente.
*/
User Function ImportaCTe()
Local dIni := CtoD('01/' + SubStr(DToS(Date()),5,2) + '/' + SubStr(DToS(Date()),1,4))
Local aCtes := U_MPListarCte(dIni, Date(), 'normal', 1, .T., '', '', '', .T.)
Local i, aItem
If aCtes == NIL
Conout('Falha ao obter CT-e.', 'API CT-e')
Return
EndIf
For i := 1 To Len(aCtes)
aItem := aCtes[i]
// [19] lancada — pula CT-e já lançados (redundante pois filtramos lNaoLancados=.T.)
If aItem[19]
Loop
EndIf
// *** Insira aqui a lógica de lançamento no Protheus ***
// Ex: GravarCTe(aItem)
// Marca como lançado pelo ID [1]
U_MPDemarcaCte(aItem[1], .T., '')
Next i
MsgInfo('Importacao concluida! ' + cValToChar(Len(aCtes)) + ' CT-e processado(s).', 'API CT-e')
Return
lNaoLancados := .T. em U_MPListarCte para receber apenas os CT-e ainda não processados — ideal para rotinas de importação incremental. Combine com lTodas := .T. para percorrer todas as páginas automaticamente.
Mesma estrutura do módulo CT-e — utilize FWRest e parâmetros MV para as credenciais.
1. Função auxiliar — listar NF-e:
/*
* U_MPListarNfe — Lista NF-e via API MeusPortais
* Parâmetros:
* dDe — Data inicial (Date)
* dAte — Data final (Date)
* cSituacao — "" | "autorizado" | "cancelado" | "denegado"
* nTipoNf — -1=todos | 0=Entrada | 1=Saida
* lTodas — .T. percorre todas as paginas
* cCnpjEmit — CNPJ emitente (somente digitos, opcional)
* cCnpjFilial — CNPJ filial (somente digitos, opcional)
* lNaoLancadas — .T. retorna somente nao lancadas
* Retorno: array de arrays ou NIL em caso de erro
*/
User Function MPListarNfe(dDe, dAte, cSituacao, nTipoNf, lTodas, cCnpjEmit, cCnpjFilial, lNaoLancadas)
Local cBase := SuperGetMV('MV_MPURL', .F., 'https://app.meusportais.com.br')
Local cToken := SuperGetMV('MV_MPTOKEN', .F., '')
Local oRest := FWRest():New(cBase)
Local aResult := {}
Local nPage := 1
Local lCont := .T.
Default cSituacao := ''
Default nTipoNf := -1
Default lTodas := .F.
Default cCnpjEmit := ''
Default cCnpjFilial := ''
Default lNaoLancadas := .F.
oRest:setHead('Authorization', 'Bearer ' + cToken)
oRest:setHead('Accept', 'application/json')
Do While lCont
Local cQuery := '/api/nfe?per_page=100&page=' + cValToChar(nPage)
If !Empty(dDe)
cQuery += '&emissao_de=' + SubStr(DToS(dDe), 1,4) + '-' + SubStr(DToS(dDe), 5,2) + '-' + SubStr(DToS(dDe), 7,2)
EndIf
If !Empty(dAte)
cQuery += '&emissao_ate=' + SubStr(DToS(dAte),1,4) + '-' + SubStr(DToS(dAte),5,2) + '-' + SubStr(DToS(dAte),7,2)
EndIf
If !Empty(cSituacao)
cQuery += '&situacao=' + cSituacao
EndIf
If nTipoNf >= 0
cQuery += '&tipo_nf=' + cValToChar(nTipoNf)
EndIf
If !Empty(cCnpjEmit)
cQuery += '&cnpj_emitente=' + cCnpjEmit
EndIf
If !Empty(cCnpjFilial)
cQuery += '&cnpj_filial=' + cCnpjFilial
EndIf
If lNaoLancadas
cQuery += '&lancada=false'
EndIf
If !oRest:Get(cQuery)
FreeObj(oRest)
Return NIL
EndIf
Local jResp := JsonObject():New()
jResp:fromJson(oRest:getResult())
Local aData := jResp:GetJsonArray('data')
Local jMeta := jResp:GetJsonObject('meta')
Local nLast := jMeta:GetJsonNumber('last_page')
Local i
For i := 1 To Len(aData)
Local jItem := aData[i]
AAdd(aResult, {;
jItem:GetJsonNumber('id'), ; // [1] ID interno
jItem:GetJsonText('nsu'), ; // [2] NSU
jItem:GetJsonText('chave_acesso'), ; // [3] Chave 44 digitos
jItem:GetJsonText('data_emissao'), ; // [4] Data emissao
jItem:GetJsonText('numero_nfe'), ; // [5] Numero NF-e
jItem:GetJsonText('serie'), ; // [6] Serie
jItem:GetJsonNumber('tipo_nf'), ; // [7] 0=Entrada 1=Saida
jItem:GetJsonText('tipo_nf_descricao'), ; // [8] "Entrada" | "Saida"
jItem:GetJsonText('cnpj_filial'), ; // [9] CNPJ filial
jItem:GetJsonText('nome_filial'), ; // [10] Descricao filial
jItem:GetJsonText('cnpj_emitente'), ; // [11] CNPJ emitente
jItem:GetJsonText('razao_emitente'), ; // [12] Razao social emitente
jItem:GetJsonText('ie_emitente'), ; // [13] IE emitente
jItem:GetJsonText('cnpj_destinatario'), ; // [14] CNPJ destinatario
jItem:GetJsonText('razao_destinatario'), ; // [15] Razao social destinatario
jItem:GetJsonNumber('valor_nf'), ; // [16] Valor total NF-e
jItem:GetJsonText('situacao'), ; // [17] "autorizado" | "cancelado" | "denegado"
jItem:GetJsonText('numero_protocolo'), ; // [18] Numero protocolo
jItem:GetJsonLogical('lancada') ; // [19] .T./.F.
})
Next i
If !lTodas .Or. nPage >= nLast
lCont := .F.
Else
nPage++
EndIf
EndDo
FreeObj(oRest)
Return aResult
2. Função auxiliar — marcar NF-e como lançada:
User Function MPMarcarNfeLancada(nId, lLancada)
Local cBase := SuperGetMV('MV_MPURL', .F., 'https://app.meusportais.com.br')
Local cToken := SuperGetMV('MV_MPTOKEN', .F., '')
Local oRest := FWRest():New(cBase)
Local lOk := .F.
Default lLancada := .T.
oRest:setHead('Authorization', 'Bearer ' + cToken)
oRest:setHead('Accept', 'application/json')
oRest:setHead('Content-Type', 'application/json')
Local jBody := JsonObject():New()
jBody['lancada'] := lLancada
oRest:setBody(jBody:toJson())
If oRest:Post('/api/nfe/' + cValToChar(nId) + '/lancada')
Local jResp := JsonObject():New()
jResp:fromJson(oRest:getResult())
lOk := jResp:GetJsonLogical('lancada') == lLancada
EndIf
FreeObj(oRest)
Return lOk
3. Exemplo de uso — importar NF-e de entrada não lançadas:
/*
* Busca todas as NF-e de entrada nao lancadas do mes corrente
* e marca cada uma apos processar no Protheus.
*/
User Function ImportaNFe()
Local dIni := CtoD('01/' + SubStr(DToS(Date()),5,2) + '/' + SubStr(DToS(Date()),1,4))
Local aNfes := U_MPListarNfe(dIni, Date(), 'autorizado', 0, .T., '', '', .T.)
Local i, aItem
If aNfes == NIL
Conout('Falha ao obter NF-e.')
Return
EndIf
For i := 1 To Len(aNfes)
aItem := aNfes[i]
// *** Insira aqui a logica de lancamento no Protheus ***
// aItem[3] = Chave de acesso (44 digitos)
// aItem[5] = Numero NF-e
// aItem[11] = CNPJ emitente
// aItem[16] = Valor NF-e
// Marca como lancada apos processar
U_MPMarcarNfeLancada(aItem[1], .T.)
Conout('NF-e ' + aItem[5] + ' processada — Emitente: ' + aItem[12])
Next i
MsgInfo('Importacao concluida: ' + cValToChar(Len(aNfes)) + ' NF-e(s).', 'NF-e')
Return
nTipoNf=0 para filtrar apenas entradas (NF-e recebidas), nTipoNf=1 para saídas. Combine com lNaoLancadas=.T. para importação incremental.