Exemplos em JavaScript / TypeScript
Setup
Requer Node.js 18+ (que tem fetch nativo).
# .env (use dotenv ou similar)MYSE_API_BASE=https://api.mysebr.com.br/nfemyse-v3/restMYSE_API_TOKEN=seu_token_aquiCliente HTTP centralizado
const BASE = process.env.MYSE_API_BASE;const TOKEN = process.env.MYSE_API_TOKEN;
export class ApiError extends Error { constructor(httpStatus, message, payload, requestId) { super(message); this.httpStatus = httpStatus; this.payload = payload; this.requestId = requestId; }}
export async function mApi(method, path, body, options = {}) { const url = `${BASE}${path}`; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 30_000);
try { const res = await fetch(url, { method, headers: { Authorization: `Token ${TOKEN}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, signal: controller.signal, });
const requestId = res.headers.get('x-request-id') ?? undefined;
if (!res.ok) { const errBody = await res.json().catch(() => ({})); throw new ApiError( res.status, errBody?.error?.message ?? `HTTP ${res.status}`, errBody, requestId ); }
const json = await res.json();
if (json.status === '999') { throw new ApiError( 200, json.data?.erro ?? json.descricao, json, json.meta?.request_id ?? requestId ); }
return json; } finally { clearTimeout(timeout); }}const BASE = process.env.MYSE_API_BASE!;const TOKEN = process.env.MYSE_API_TOKEN!;
export interface Meta { request_id: string; timestamp: string;}
export interface ApiResponse<T = unknown> { status: string; descricao: string; data?: T; meta?: Meta;}
export class ApiError extends Error { constructor( public readonly httpStatus: number, message: string, public readonly payload: unknown, public readonly requestId?: string ) { super(message); this.name = 'ApiError'; }}
export async function mApi<T = unknown>( method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: unknown, options: { timeoutMs?: number } = {}): Promise<ApiResponse<T>> { const controller = new AbortController(); const timeout = setTimeout( () => controller.abort(), options.timeoutMs ?? 30_000 );
try { const res = await fetch(`${BASE}${path}`, { method, headers: { Authorization: `Token ${TOKEN}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, signal: controller.signal, });
const requestId = res.headers.get('x-request-id') ?? undefined;
if (!res.ok) { const errBody = (await res.json().catch(() => ({}))) as { error?: { message?: string }; }; throw new ApiError( res.status, errBody?.error?.message ?? `HTTP ${res.status}`, errBody, requestId ); }
const json = (await res.json()) as ApiResponse<T>;
if (json.status === '999') { throw new ApiError( 200, (json.data as { erro?: string })?.erro ?? json.descricao, json, json.meta?.request_id ?? requestId ); }
return json; } finally { clearTimeout(timeout); }}Cenário: emitir NFCe completa
import { mApi, ApiError } from './myse-api.js';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function emitirNFCe(pedido) { // 1. Emitir const emissao = await mApi('POST', '/nfce/emissao', { serie: '65', cfop: '5102', 'numero-origem': `PED-${pedido.id}`, tp_pagamento: pedido.formaPagamento, cliente: pedido.cpfCnpjCliente, itens: pedido.itens.map((i) => ({ produto: i.sku, quantidade: String(i.qtd), 'valor-unitario': String(i.preco), })), });
if (emissao.status !== '001' && emissao.status !== '050') { throw new Error(`Emissão falhou: ${emissao.descricao}`); }
const recibo = emissao.data.recibo; console.log(`[emissao] recibo=${recibo} request_id=${emissao.meta.request_id}`);
// 2. Polling com backoff const terminais = new Set(['004', '010', '900', '999']); let wait = 2000; const inicio = Date.now();
while (Date.now() - inicio < 5 * 60_000) { await sleep(wait); const consulta = await mApi('GET', `/nfce/cupom/${recibo}`); console.log(`[polling] status=${consulta.status}`);
if (terminais.has(consulta.status)) { if (consulta.status === '004') { return consulta.data; // Inclui sefaz.chave, url-danfe, url-xml } throw new Error(`Status terminal não-sucesso: ${consulta.status}`); }
wait = Math.min(wait * 1.5, 30_000); }
throw new Error(`Timeout aguardando recibo ${recibo}`);}Retry com backoff em erros transitórios
async function comRetry(fn, max = 3) { for (let i = 1; i <= max; i++) { try { return await fn(); } catch (err) { // Não retentar erros de validação if (err instanceof ApiError && err.payload?.status === '999') throw err; if (i === max) throw err; await sleep(1000 * Math.pow(2, i - 1)); // 1s, 2s, 4s } }}
// Usoconst nfe = await comRetry(() => emitirNFCe(pedido));Helper de polling reutilizável
const TERMINAIS = new Set(['004', '010', '900', '999']);
async function aguardarRecibo(pathBase, recibo, timeoutMs = 5 * 60_000) { let wait = 2000; const inicio = Date.now();
while (Date.now() - inicio < timeoutMs) { await sleep(wait); const resp = await mApi('GET', `${pathBase}/${recibo}`); if (TERMINAIS.has(resp.status)) return resp; wait = Math.min(wait * 1.5, 30_000); } throw new Error(`Timeout aguardando recibo ${recibo}`);}Emitir NF-e (com transporte completo)
async function emitirNFe(pedido) { const emissao = await mApi('POST', '/nfe/emissao', { serie: '1', cfop: '5102', 'numero-origem': `PED-${pedido.id}`, tipo: 'S', finalidade: 'N', 'valor-despesas': '0.00', cliente: pedido.cpfCnpjCliente, transporte: { modalidade: '0', transportadora: pedido.transportadora, 'placa-veiculo': pedido.placa, 'valor-frete': pedido.frete ?? '0.00', 'valor-seguro': '0.00', 'peso-bruto': pedido.pesoBruto, 'peso-liquido': pedido.pesoLiquido, 'quantidade-volume': '1', 'especie-volume': 'Caixa', 'marca-volume': 'Embalagem', }, itens: pedido.itens.map((i) => ({ produto: i.sku, quantidade: String(i.qtd), 'valor-unitario': String(i.preco), })), faturas: pedido.faturas?.map((f) => ({ valor: String(f.valor), data: f.data })) ?? [], });
if (!['001', '050'].includes(emissao.status)) { throw new Error(`Emissão falhou: ${emissao.descricao}`); }
const resultado = await aguardarRecibo('/nfe/nota', emissao.data.recibo); if (resultado.status !== '004') { throw new Error(`Status terminal não-sucesso: ${resultado.status}`); } return resultado.data; // inclui sefaz.chave, url-danfe, url-xml}Cancelar NF-e
async function cancelarNFe(chave, motivo) { if (motivo.length < 15 || motivo.length > 255) { throw new Error('Motivo deve ter entre 15 e 255 caracteres'); } const cancel = await mApi('POST', '/nfe/cancelamento', { chave, motivo }); if (cancel.status !== '001') throw new Error(cancel.descricao); return aguardarRecibo('/nfe/cancelamento', cancel.data.recibo);}Lançar carta de correção (CC-e)
async function cartaCorrecaoNFe(chave, textoCorrecao) { if (textoCorrecao.length < 15 || textoCorrecao.length > 255) { throw new Error('Texto da correção deve ter entre 15 e 255 caracteres'); } // Nota: o campo "motivo" na CC-e é o TEXTO da correção (xCorrecao). const cce = await mApi('POST', '/nfe/correcao', { chave, motivo: textoCorrecao }); if (cce.status !== '001') throw new Error(cce.descricao); return aguardarRecibo('/nfe/correcao', cce.data.recibo);}Cancelar NFC-e
async function cancelarNFCe(chave, motivo) { if (motivo.length < 15 || motivo.length > 255) { throw new Error('Motivo deve ter entre 15 e 255 caracteres'); } const cancel = await mApi('POST', '/nfce/cancelamento', { chave, motivo }); if (cancel.status !== '001') throw new Error(cancel.descricao); return aguardarRecibo('/nfce/cancelamento', cancel.data.recibo);}Emitir NFS-e (Nota Fiscal de Serviço)
async function emitirNFSe(servico) { const emissao = await mApi('POST', '/nfse/emissao', { 'numero-origem': `OS-${servico.id}`, observacao: servico.observacao, 'nome-contato': servico.nomeContato, 'telefone-contato': servico.telefoneContato, 'data-fim': servico.dataFim, cliente: servico.cpfCnpjCliente, itens: servico.itens.map((i) => ({ produto: i.codigoServico, quantidade: String(i.qtd), 'valor-unitario': String(i.preco), })), });
if (!['001', '050'].includes(emissao.status)) { throw new Error(`Emissão NFSe falhou: ${emissao.descricao}`); }
const resultado = await aguardarRecibo('/nfse/nota', emissao.data.recibo); if (resultado.status !== '004') { throw new Error(`Status terminal não-sucesso: ${resultado.status}`); } return resultado.data;}Cancelar NFS-e
async function cancelarNFSe(chave, motivo) { if (motivo.length < 15 || motivo.length > 255) { throw new Error('Motivo deve ter entre 15 e 255 caracteres'); } return mApi('POST', '/nfse/cancelamento', { chave, motivo });}Reconhecer nota de fornecedor (MD-e)
/** * Códigos de reconhecimento: * '210200' — Confirmação da operação (terminal, irreversível) * '210210' — Ciência da operação (provisório, 15 dias) * '210220' — Desconhecimento da operação * '210240' — Operação não realizada */async function reconhecerNotaFornecedor(chave, codigoReconhecimento) { const VALIDOS = new Set(['210200', '210210', '210220', '210240']); if (!VALIDOS.has(codigoReconhecimento)) { throw new Error(`Código inválido: ${codigoReconhecimento}`); }
const resp = await mApi('POST', '/nffornecedor/reconhecimento', { chave, 'codigo-reconhecimento': codigoReconhecimento, }); if (resp.status !== '001') throw new Error(resp.descricao); return aguardarRecibo('/nffornecedor', resp.data.recibo);}Sincronizar todos os clientes
async function listarTodosClientes() { const todos = []; let page = 1; const pageSize = 100;
while (true) { const resp = await mApi('GET', `/clientes?page=${page}&page_size=${pageSize}`); if (resp.status !== '005' || !Array.isArray(resp.data)) break; todos.push(...resp.data); if (resp.data.length < pageSize) break; page++; await sleep(200); }
return todos;}Tratamento de erros completo
try { const nfe = await emitirNFCe(pedido); console.log('NFCe emitida:', nfe.sefaz?.chave);} catch (err) { if (err instanceof ApiError) { console.error( `API Error HTTP=${err.httpStatus} requestId=${err.requestId}`, err.message, err.payload ); // Logar para suporte: requestId é o que importa } else { console.error('Erro inesperado:', err); }}Health check (sem autenticação)
async function health() { const res = await fetch(`${BASE}/health`); return res.json(); // { status: 'ok', database: 'ok', timestamp: '...' }}Alterar nota antes da emissão (PUT)
Disponível em NF-e (/nfe/{id}), NFC-e (/nfce/{id}) e NFS-e (/nfse/{id}).
Só funciona enquanto a nota ainda não foi submetida para emissão.
async function alterarNota(tipo, id, dadosAtualizados) { // tipo: 'nfe' | 'nfce' | 'nfse' return mApi('PUT', `/${tipo}/${id}`, dadosAtualizados);}
// Uso:await alterarNota('nfe', '12345', { serie: '1', cfop: '5102', cliente: '12345678000199', itens: [{ produto: 'SKU-001', quantidade: '3', 'valor-unitario': '89.90' }],});Consulta detalhada por recibo
// NF-e completa (inclui itens, transporte, cliente, sefaz)const nfeCompleta = await mApi('GET', `/nfe/nota/${recibo}/completa`);
// NFC-e completaconst nfceCompleta = await mApi('GET', `/nfce/cupom/${recibo}/completa`);Listagens
// NF-es paginadasconst nfes = await mApi('GET', '/nfe?page=1&page_size=20');
// NFC-esconst nfces = await mApi('GET', '/nfce?page=1&page_size=50');
// NFS-es com filtro de dataconst nfses = await mApi( 'GET', '/nfse?dataInicial=2026-05-01&page=1&page_size=20');
// Notas de fornecedorconst fornecedor = await mApi('GET', '/nffornecedor?page=1&page_size=20');
// Forma legada (path-based)const nfesLegado = await mApi('GET', '/nfe/paginacao/1/20');Clientes (CRUD completo)
// Cadastrar pessoa físicaasync function cadastrarPF(dados) { return mApi('POST', '/clientes', { tipo: 'F', nome: dados.nome, cpf: dados.cpf, rg: dados.rg, 'consumidor-final': '1', contato: { email: dados.email, telefone: dados.telefone }, endereco: dados.endereco, });}
// Cadastrar pessoa jurídicaasync function cadastrarPJ(dados) { return mApi('POST', '/clientes', { tipo: 'J', 'razao-social': dados.razaoSocial, 'nome-fantasia': dados.nomeFantasia, cnpj: dados.cnpj, 'inscricao-municipal': dados.inscricaoMunicipal, 'tipo-inscricao-estadual': dados.contribuinte ?? '9', 'inscricao-estadual': dados.inscricaoEstadual ?? 'ISENTO', 'consumidor-final': dados.consumidorFinal ?? '0', contato: { email: dados.email, telefone: dados.telefone }, endereco: dados.endereco, });}
// Consultar por CPF/CNPJconst cliente = await mApi('GET', '/clientes/12345678000199');
// Buscar por nome (parcial)const encontrados = await mApi('GET', '/clientes/busca/exemplo');
// Top 20 clientes mais relevantesconst top = await mApi('GET', '/clientes/busca/top_clientes');
// Atualizarawait mApi('PUT', '/clientes/12345678000199', { tipo: 'J', 'razao-social': 'Empresa Exemplo (Novo Nome) Ltda',});
// Excluirawait mApi('DELETE', '/clientes/12345678000199');Produtos (CRUD completo)
// Cadastrar produto com configuração fiscalasync function cadastrarProduto(produto) { return mApi('POST', '/produtos', { referencia: produto.sku, nome: produto.nome, valor: String(produto.valor), medida: produto.medida ?? 'UN', categoria: produto.categoria, ncm: produto.ncm, origem: produto.origem ?? '0', 'cfop-preferencial': produto.cfop ?? '5102', 'tipo-produto': 'P', 'configuracoes-fiscais': produto.configFiscal ?? [], });}
// Cadastrar serviçoasync function cadastrarServico(servico) { return mApi('POST', '/produtos', { referencia: servico.sku, nome: servico.nome, valor: String(servico.valor), medida: 'H', 'tipo-produto': 'S', 'codigo-servico': servico.codigoServico, });}
// Consultar por referênciaconst produto = await mApi('GET', '/produtos/SKU-001');
// Buscar por nome (1º resultado)const porNome = await mApi('GET', '/produtos/nome/Camiseta');
// Listar paginado com busca (use _todos para não filtrar)const todos = await mApi('GET', '/produtos/paginacao/1/20/_todos');const filtrados = await mApi('GET', '/produtos/paginacao/1/20/Camiseta');
// Atualizarawait mApi('PUT', '/produtos/SKU-001', { referencia: 'SKU-001', nome: 'Camiseta Polo M (Novo)', valor: '99.90',});
// Excluirawait mApi('DELETE', '/produtos/SKU-001');