Exemplos em Python
Setup
pip install requests python-dotenvMYSE_API_BASE=https://api.mysebr.com.br/nfemyse-v3/restMYSE_API_TOKEN=seu_token_aquiCliente HTTP centralizado
import osimport timefrom dataclasses import dataclassfrom typing import Any, Optionalimport requestsfrom dotenv import load_dotenv
load_dotenv()
BASE = os.environ["MYSE_API_BASE"]TOKEN = os.environ["MYSE_API_TOKEN"]
@dataclassclass ApiError(Exception): http_status: int message: str payload: Any request_id: Optional[str] = None
def __str__(self): return f"[{self.http_status}] {self.message} (request_id={self.request_id})"
def m_api(method: str, path: str, body: dict | None = None, timeout: int = 30) -> dict: headers = { "Authorization": f"Token {TOKEN}", "Content-Type": "application/json", } res = requests.request( method, f"{BASE}{path}", json=body, headers=headers, timeout=timeout )
request_id = res.headers.get("X-Request-Id")
# Erros HTTP duros if not res.ok: try: payload = res.json() msg = payload.get("error", {}).get("message", f"HTTP {res.status_code}") except Exception: payload = {} msg = f"HTTP {res.status_code}" raise ApiError(res.status_code, msg, payload, request_id)
json = res.json()
# Erro de domínio if json.get("status") == "999": raise ApiError( 200, json.get("data", {}).get("erro") or json.get("descricao", "erro"), json, (json.get("meta") or {}).get("request_id") or request_id, )
return jsonCenário: emitir NFCe com polling
import time
def emitir_nfce(pedido: dict) -> dict: # 1. Emissão body = { "serie": "65", "cfop": "5102", "numero-origem": f"PED-{pedido['id']}", "tp_pagamento": pedido["forma_pagamento"], "cliente": pedido["cpf_cnpj_cliente"], "itens": [ { "produto": i["sku"], "quantidade": str(i["qtd"]), "valor-unitario": str(i["preco"]), } for i in pedido["itens"] ], }
emissao = m_api("POST", "/nfce/emissao", body) if emissao["status"] not in ("001", "050"): raise RuntimeError(f"Emissão falhou: {emissao['descricao']}")
recibo = emissao["data"]["recibo"] print(f"[emissao] recibo={recibo} request_id={emissao['meta']['request_id']}")
# 2. Polling TERMINAIS = {"004", "010", "900", "999"} wait = 5.0 inicio = time.time()
while time.time() - inicio < 5 * 60: time.sleep(wait) consulta = m_api("GET", f"/nfce/cupom/{recibo}") print(f"[polling] status={consulta['status']}")
if consulta["status"] in TERMINAIS: if consulta["status"] == "004": return consulta["data"] raise RuntimeError(f"Status terminal não-sucesso: {consulta['status']}")
wait = min(wait * 1.5, 30)
raise TimeoutError(f"Timeout aguardando recibo {recibo}")Retry em erros transitórios
def com_retry(fn, max_tentativas=3): for i in range(1, max_tentativas + 1): try: return fn() except ApiError as e: if e.payload and e.payload.get("status") == "999": raise # Validação: não retentar if i == max_tentativas: raise time.sleep(2 ** (i - 1)) # 1s, 2s, 4s except (requests.Timeout, requests.ConnectionError): if i == max_tentativas: raise time.sleep(2 ** (i - 1))
# Usonfce = com_retry(lambda: emitir_nfce(pedido))Helper de polling reutilizável
TERMINAIS = {"004", "010", "900", "999"}
def aguardar_recibo(path_base: str, recibo: str, timeout: int = 300) -> dict: """Polling com backoff exponencial até status terminal.""" wait = 5.0 inicio = time.time()
while time.time() - inicio < timeout: time.sleep(wait) resp = m_api("GET", f"{path_base}/{recibo}") if resp["status"] in TERMINAIS: return resp wait = min(wait * 1.5, 30)
raise TimeoutError(f"Timeout aguardando recibo {recibo}")Emitir NF-e (com transporte completo)
def emitir_nfe(pedido: dict) -> dict: body = { "serie": "1", "cfop": "5102", "numero-origem": f"PED-{pedido['id']}", "tipo": "S", "finalidade": "N", "valor-despesas": "0.00", "cliente": pedido["cpf_cnpj_cliente"], "transporte": { "modalidade": "0", "transportadora": pedido["transportadora"], "placa-veiculo": pedido["placa"], "valor-frete": pedido.get("frete", "0.00"), "valor-seguro": "0.00", "peso-bruto": pedido["peso_bruto"], "peso-liquido": pedido["peso_liquido"], "quantidade-volume": "1", "especie-volume": "Caixa", "marca-volume": "Embalagem", }, "itens": [ { "produto": i["sku"], "quantidade": str(i["qtd"]), "valor-unitario": str(i["preco"]), } for i in pedido["itens"] ], "faturas": [ {"valor": str(f["valor"]), "data": f["data"]} for f in pedido.get("faturas", []) ], }
emissao = m_api("POST", "/nfe/emissao", body) if emissao["status"] not in ("001", "050"): raise RuntimeError(f"Emissão falhou: {emissao['descricao']}")
resultado = aguardar_recibo("/nfe/nota", emissao["data"]["recibo"]) if resultado["status"] != "004": raise RuntimeError(f"Status terminal não-sucesso: {resultado['status']}") return resultado["data"] # inclui sefaz.chave, url-danfe, url-xmlCancelar NF-e
def cancelar_nfe(chave: str, motivo: str) -> dict: assert 15 <= len(motivo) <= 255, "motivo deve ter entre 15 e 255 caracteres" cancel = m_api("POST", "/nfe/cancelamento", {"chave": chave, "motivo": motivo}) if cancel["status"] != "001": raise RuntimeError(cancel["descricao"]) return aguardar_recibo("/nfe/cancelamento", cancel["data"]["recibo"])Lançar carta de correção (CC-e)
def carta_correcao_nfe(chave: str, texto_correcao: str) -> dict: """O campo 'motivo' na CC-e é o TEXTO da correção (xCorrecao).""" assert 15 <= len(texto_correcao) <= 255, "texto deve ter 15-255 chars" cce = m_api("POST", "/nfe/correcao", {"chave": chave, "motivo": texto_correcao}) if cce["status"] != "001": raise RuntimeError(cce["descricao"]) return aguardar_recibo("/nfe/correcao", cce["data"]["recibo"])Cancelar NFC-e
def cancelar_nfce(chave: str, motivo: str) -> dict: assert 15 <= len(motivo) <= 255, "motivo deve ter 15-255 chars" cancel = m_api("POST", "/nfce/cancelamento", {"chave": chave, "motivo": motivo}) if cancel["status"] != "001": raise RuntimeError(cancel["descricao"]) return aguardar_recibo("/nfce/cancelamento", cancel["data"]["recibo"])Emitir NFS-e (Nota Fiscal de Serviço)
def emitir_nfse(servico: dict) -> dict: body = { "numero-origem": f"OS-{servico['id']}", "observacao": servico["observacao"], "nome-contato": servico.get("nome_contato"), "telefone-contato": servico.get("telefone_contato"), "data-fim": servico["data_fim"], "cliente": servico["cpf_cnpj_cliente"], "itens": [ { "produto": i["codigo_servico"], "quantidade": str(i["qtd"]), "valor-unitario": str(i["preco"]), } for i in servico["itens"] ], }
emissao = m_api("POST", "/nfse/emissao", body) if emissao["status"] not in ("001", "050"): raise RuntimeError(f"Emissão NFSe falhou: {emissao['descricao']}")
resultado = aguardar_recibo("/nfse/nota", emissao["data"]["recibo"]) if resultado["status"] != "004": raise RuntimeError(f"Status terminal não-sucesso: {resultado['status']}") return resultado["data"]Cancelar NFS-e
def cancelar_nfse(chave: str, motivo: str) -> dict: assert 15 <= len(motivo) <= 255, "motivo deve ter 15-255 chars" return m_api("POST", "/nfse/cancelamento", {"chave": chave, "motivo": motivo})Reconhecer nota de fornecedor (MD-e)
CODIGOS_RECONHECIMENTO = {"210200", "210210", "210220", "210240"}# 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
def reconhecer_nota_fornecedor(chave: str, codigo: str) -> dict: if codigo not in CODIGOS_RECONHECIMENTO: raise ValueError(f"Código inválido: {codigo}")
resp = m_api("POST", "/nffornecedor/reconhecimento", { "chave": chave, "codigo-reconhecimento": codigo, }) if resp["status"] != "001": raise RuntimeError(resp["descricao"]) return aguardar_recibo("/nffornecedor", resp["data"]["recibo"])Listar com paginação
def listar_todos_clientes() -> list: todos = [] page = 1 page_size = 100
while True: resp = m_api("GET", f"/clientes?page={page}&page_size={page_size}") if resp["status"] != "005" or not isinstance(resp.get("data"), list): break todos.extend(resp["data"]) if len(resp["data"]) < page_size: break page += 1 time.sleep(0.2)
return todosTratamento de erros
try: nfce = emitir_nfce(pedido) print("NFCe emitida:", nfce.get("sefaz", {}).get("chave"))except ApiError as e: print(f"API Error: {e}") # Log para suporte com request_id logger.error( "API call failed", extra={ "request_id": e.request_id, "http_status": e.http_status, "payload": e.payload, }, )except TimeoutError as e: print(f"Timeout: {e}")Variação: usando httpx (async)
import httpx
async def m_api_async(method, path, body=None): async with httpx.AsyncClient(timeout=30) as client: res = await client.request( method, f"{BASE}{path}", json=body, headers={"Authorization": f"Token {TOKEN}"}, ) # ... mesma lógica de tratamentoHealth check (sem autenticação)
import requests
def health() -> dict: res = requests.get(f"{BASE}/health", timeout=5) 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.
def alterar_nota(tipo: str, id_nota: str, dados: dict) -> dict: """tipo: 'nfe' | 'nfce' | 'nfse'""" return m_api("PUT", f"/{tipo}/{id_nota}", dados)
# Uso:alterar_nota("nfe", "12345", { "serie": "1", "cfop": "5102", "cliente": "12345678000199", "itens": [{"produto": "SKU-001", "quantidade": "3", "valor-unitario": "89.90"}],})Consulta detalhada por recibo
nfe_completa = m_api("GET", f"/nfe/nota/{recibo}/completa")nfce_completa = m_api("GET", f"/nfce/cupom/{recibo}/completa")Listagens
# NF-es paginadasnfes = m_api("GET", "/nfe?page=1&page_size=20")
# NFC-esnfces = m_api("GET", "/nfce?page=1&page_size=50")
# NFS-es com filtro de datanfses = m_api("GET", "/nfse?dataInicial=2026-05-01&page=1&page_size=20")
# Notas de fornecedorfornecedor = m_api("GET", "/nffornecedor?page=1&page_size=20")
# Forma legada (path-based)nfes_legado = m_api("GET", "/nfe/paginacao/1/20")Clientes (CRUD completo)
def cadastrar_pf(dados: dict) -> dict: return m_api("POST", "/clientes", { "tipo": "F", "nome": dados["nome"], "cpf": dados["cpf"], "rg": dados.get("rg"), "consumidor-final": "1", "contato": { "email": dados.get("email"), "telefone": dados.get("telefone"), }, "endereco": dados["endereco"], })
def cadastrar_pj(dados: dict) -> dict: return m_api("POST", "/clientes", { "tipo": "J", "razao-social": dados["razao_social"], "nome-fantasia": dados.get("nome_fantasia"), "cnpj": dados["cnpj"], "inscricao-municipal": dados.get("inscricao_municipal"), "tipo-inscricao-estadual": dados.get("contribuinte", "9"), "inscricao-estadual": dados.get("inscricao_estadual", "ISENTO"), "consumidor-final": dados.get("consumidor_final", "0"), "contato": { "email": dados.get("email"), "telefone": dados.get("telefone"), }, "endereco": dados["endereco"], })
# Consultar por CPF/CNPJcliente = m_api("GET", "/clientes/12345678000199")
# Buscar por nomeencontrados = m_api("GET", "/clientes/busca/exemplo")top = m_api("GET", "/clientes/busca/top_clientes")
# Atualizarm_api("PUT", "/clientes/12345678000199", { "tipo": "J", "razao-social": "Empresa Exemplo (Novo Nome) Ltda",})
# Excluirm_api("DELETE", "/clientes/12345678000199")Produtos (CRUD completo)
def cadastrar_produto(produto: dict) -> dict: return m_api("POST", "/produtos", { "referencia": produto["sku"], "nome": produto["nome"], "valor": str(produto["valor"]), "medida": produto.get("medida", "UN"), "categoria": produto.get("categoria"), "ncm": produto["ncm"], "origem": produto.get("origem", "0"), "cfop-preferencial": produto.get("cfop", "5102"), "tipo-produto": "P", "configuracoes-fiscais": produto.get("config_fiscal", []), })
def cadastrar_servico(servico: dict) -> dict: return m_api("POST", "/produtos", { "referencia": servico["sku"], "nome": servico["nome"], "valor": str(servico["valor"]), "medida": "H", "tipo-produto": "S", "codigo-servico": servico["codigo_servico"], })
# Consultar por referênciaproduto = m_api("GET", "/produtos/SKU-001")
# Buscar por nome (1º resultado)por_nome = m_api("GET", "/produtos/nome/Camiseta")
# Listar paginado com busca (use _todos para não filtrar)todos = m_api("GET", "/produtos/paginacao/1/20/_todos")filtrados = m_api("GET", "/produtos/paginacao/1/20/Camiseta")
# Atualizarm_api("PUT", "/produtos/SKU-001", { "referencia": "SKU-001", "nome": "Camiseta Polo M (Novo)", "valor": "99.90",})
# Excluirm_api("DELETE", "/produtos/SKU-001")