Exemplos em C# (.NET)
Setup
Requer .NET 6+.
<PackageReference Include="DotNetEnv" Version="3.0.0" />MYSE_API_BASE=https://api.mysebr.com.br/nfemyse-v3/restMYSE_API_TOKEN=seu_token_aquiCliente HTTP centralizado
using System.Net.Http.Json;using System.Text.Json;using System.Text.Json.Nodes;
namespace Myse;
public class ApiError : Exception{ public int HttpStatus { get; } public JsonNode? Payload { get; } public string? RequestId { get; }
public ApiError(int httpStatus, string message, JsonNode? payload, string? requestId) : base(message) { HttpStatus = httpStatus; Payload = payload; RequestId = requestId; }}
public class MyseApi{ private readonly HttpClient _http;
public MyseApi(string baseUrl, string token) { _http = new HttpClient { BaseAddress = new Uri(baseUrl), Timeout = TimeSpan.FromSeconds(30), }; _http.DefaultRequestHeaders.Add("Authorization", $"Token {token}"); }
public async Task<JsonNode> CallAsync( HttpMethod method, string path, object? body = null, CancellationToken ct = default) { var req = new HttpRequestMessage(method, path); if (body != null) { req.Content = JsonContent.Create(body); }
var res = await _http.SendAsync(req, ct); var requestId = res.Headers.TryGetValues("X-Request-Id", out var v) ? v.FirstOrDefault() : null;
var bodyStr = await res.Content.ReadAsStringAsync(ct); var json = JsonNode.Parse(bodyStr);
if (!res.IsSuccessStatusCode) { var msg = json?["error"]?["message"]?.ToString() ?? $"HTTP {(int)res.StatusCode}"; throw new ApiError((int)res.StatusCode, msg, json, requestId); }
if (json?["status"]?.ToString() == "999") { var msg = json["data"]?["erro"]?.ToString() ?? json["descricao"]?.ToString() ?? "erro"; var rid = json["meta"]?["request_id"]?.ToString() ?? requestId; throw new ApiError(200, msg, json, rid); }
return json ?? throw new InvalidOperationException("Resposta vazia"); }}Cenário: emitir NFCe com polling
public class EmissorNFCe{ private readonly MyseApi _api; private static readonly HashSet<string> Terminais = new() { "004", "010", "900", "999" };
public EmissorNFCe(MyseApi api) => _api = api;
public async Task<JsonNode> EmitirAsync(Pedido p, CancellationToken ct = default) { var body = new { serie = "65", cfop = "5102", numero_origem = $"PED-{p.Id}", tp_pagamento = p.FormaPagamento, cliente = p.CpfCnpjCliente, itens = p.Itens.Select(i => new { produto = i.Sku, quantidade = i.Qtd.ToString(), valor_unitario = i.Preco.ToString("F2"), }), };
var emissao = await _api.CallAsync(HttpMethod.Post, "/nfce/emissao", body, ct); var status = emissao["status"]?.ToString();
if (status != "001" && status != "050") { throw new InvalidOperationException( $"Emissão falhou: {emissao["descricao"]}" ); }
var recibo = emissao["data"]!["recibo"]!.ToString(); Console.WriteLine($"[emissao] recibo={recibo} request_id={emissao["meta"]?["request_id"]}");
// Polling var wait = TimeSpan.FromSeconds(5); var inicio = DateTime.UtcNow;
while (DateTime.UtcNow - inicio < TimeSpan.FromMinutes(5)) { await Task.Delay(wait, ct); var consulta = await _api.CallAsync(HttpMethod.Get, $"/nfce/cupom/{recibo}", null, ct); var st = consulta["status"]?.ToString(); Console.WriteLine($"[polling] status={st}");
if (Terminais.Contains(st!)) { if (st == "004") return consulta["data"]!; throw new InvalidOperationException($"Status terminal não-sucesso: {st}"); }
wait = TimeSpan.FromMilliseconds(Math.Min(wait.TotalMilliseconds * 1.5, 30_000)); }
throw new TimeoutException($"Timeout aguardando recibo {recibo}"); }}Retry com backoff
public static async Task<T> ComRetryAsync<T>(Func<Task<T>> fn, int maxTentativas = 3){ Exception? last = null; for (int i = 1; i <= maxTentativas; i++) { try { return await fn(); } catch (ApiError e) when (e.Payload?["status"]?.ToString() == "999") { throw; // validação, não retentar } catch (Exception e) { last = e; if (i == maxTentativas) throw; await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, i - 1))); } } throw last!;}
// Uso:var nfce = await ComRetryAsync(() => emissor.EmitirAsync(pedido));Helper de polling reutilizável
public static class FiscalHelpers{ private static readonly HashSet<string> Terminais = new() { "004", "010", "900", "999" };
public static async Task<JsonNode> AguardarReciboAsync( MyseApi api, string pathBase, string recibo, TimeSpan? timeout = null, CancellationToken ct = default) { var max = timeout ?? TimeSpan.FromMinutes(5); var wait = TimeSpan.FromSeconds(5); var inicio = DateTime.UtcNow;
while (DateTime.UtcNow - inicio < max) { await Task.Delay(wait, ct); var resp = await api.CallAsync(HttpMethod.Get, $"{pathBase}/{recibo}", null, ct); if (Terminais.Contains(resp["status"]?.ToString() ?? "")) return resp; wait = TimeSpan.FromMilliseconds(Math.Min(wait.TotalMilliseconds * 1.5, 30_000)); } throw new TimeoutException($"Timeout aguardando recibo {recibo}"); }
public static void ValidarMotivo(string motivo) { if (motivo.Length < 15 || motivo.Length > 255) throw new ArgumentException("motivo deve ter entre 15 e 255 caracteres"); }}Emitir NF-e (com transporte completo)
public async Task<JsonNode> EmitirNFeAsync(Pedido p, CancellationToken ct = default){ var body = new { serie = "1", cfop = "5102", numero_origem = $"PED-{p.Id}", tipo = "S", finalidade = "N", valor_despesas = "0.00", cliente = p.CpfCnpjCliente, transporte = new { modalidade = "0", transportadora = p.Transportadora, placa_veiculo = p.Placa, valor_frete = p.Frete ?? "0.00", valor_seguro = "0.00", peso_bruto = p.PesoBruto, peso_liquido = p.PesoLiquido, quantidade_volume = "1", especie_volume = "Caixa", marca_volume = "Embalagem", }, itens = p.Itens.Select(i => new { produto = i.Sku, quantidade = i.Qtd.ToString(), valor_unitario = i.Preco.ToString("F2"), }), faturas = p.Faturas?.Select(f => new { valor = f.Valor.ToString("F2"), data = f.Data, }) ?? Enumerable.Empty<object>(), };
var emissao = await _api.CallAsync(HttpMethod.Post, "/nfe/emissao", body, ct); var status = emissao["status"]?.ToString(); if (status != "001" && status != "050") throw new InvalidOperationException($"Emissão falhou: {emissao["descricao"]}");
var recibo = emissao["data"]!["recibo"]!.ToString(); var resultado = await FiscalHelpers.AguardarReciboAsync(_api, "/nfe/nota", recibo, ct: ct); if (resultado["status"]?.ToString() != "004") throw new InvalidOperationException($"Status terminal não-sucesso: {resultado["status"]}"); return resultado["data"]!;}Cancelar NF-e
public async Task<JsonNode> CancelarNFeAsync(string chave, string motivo, CancellationToken ct = default){ FiscalHelpers.ValidarMotivo(motivo); var cancel = await _api.CallAsync(HttpMethod.Post, "/nfe/cancelamento", new { chave, motivo }, ct); if (cancel["status"]?.ToString() != "001") throw new InvalidOperationException(cancel["descricao"]?.ToString()); return await FiscalHelpers.AguardarReciboAsync(_api, "/nfe/cancelamento", cancel["data"]!["recibo"]!.ToString(), ct: ct);}Lançar carta de correção (CC-e)
/// <summary>/// O campo "motivo" na CC-e é o TEXTO da correção (xCorrecao)./// </summary>public async Task<JsonNode> CartaCorrecaoAsync(string chave, string textoCorrecao, CancellationToken ct = default){ FiscalHelpers.ValidarMotivo(textoCorrecao); var cce = await _api.CallAsync(HttpMethod.Post, "/nfe/correcao", new { chave, motivo = textoCorrecao }, ct); if (cce["status"]?.ToString() != "001") throw new InvalidOperationException(cce["descricao"]?.ToString()); return await FiscalHelpers.AguardarReciboAsync(_api, "/nfe/correcao", cce["data"]!["recibo"]!.ToString(), ct: ct);}Cancelar NFC-e
public async Task<JsonNode> CancelarNFCeAsync(string chave, string motivo, CancellationToken ct = default){ FiscalHelpers.ValidarMotivo(motivo); var cancel = await _api.CallAsync(HttpMethod.Post, "/nfce/cancelamento", new { chave, motivo }, ct); if (cancel["status"]?.ToString() != "001") throw new InvalidOperationException(cancel["descricao"]?.ToString()); return await FiscalHelpers.AguardarReciboAsync(_api, "/nfce/cancelamento", cancel["data"]!["recibo"]!.ToString(), ct: ct);}Emitir NFS-e (Nota Fiscal de Serviço)
public async Task<JsonNode> EmitirNFSeAsync(Servico s, CancellationToken ct = default){ var body = new { numero_origem = $"OS-{s.Id}", observacao = s.Observacao, nome_contato = s.NomeContato, telefone_contato = s.TelefoneContato, data_fim = s.DataFim, cliente = s.CpfCnpjCliente, itens = s.Itens.Select(i => new { produto = i.CodigoServico, quantidade = i.Qtd.ToString(), valor_unitario = i.Preco.ToString("F2"), }), };
var emissao = await _api.CallAsync(HttpMethod.Post, "/nfse/emissao", body, ct); var status = emissao["status"]?.ToString(); if (status != "001" && status != "050") throw new InvalidOperationException($"Emissão NFSe falhou: {emissao["descricao"]}");
var recibo = emissao["data"]!["recibo"]!.ToString(); var resultado = await FiscalHelpers.AguardarReciboAsync(_api, "/nfse/nota", recibo, ct: ct); if (resultado["status"]?.ToString() != "004") throw new InvalidOperationException($"Status terminal não-sucesso: {resultado["status"]}"); return resultado["data"]!;}Cancelar NFS-e
public async Task<JsonNode> CancelarNFSeAsync(string chave, string motivo, CancellationToken ct = default){ FiscalHelpers.ValidarMotivo(motivo); return await _api.CallAsync(HttpMethod.Post, "/nfse/cancelamento", new { chave, motivo }, ct);}Reconhecer nota de fornecedor (MD-e)
/// <summary>/// 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/// </summary>public async Task<JsonNode> ReconhecerNotaFornecedorAsync( string chave, string codigo, CancellationToken ct = default){ var validos = new HashSet<string> { "210200", "210210", "210220", "210240" }; if (!validos.Contains(codigo)) throw new ArgumentException($"Código inválido: {codigo}");
var resp = await _api.CallAsync(HttpMethod.Post, "/nffornecedor/reconhecimento", new { chave, codigo_reconhecimento = codigo }, ct); if (resp["status"]?.ToString() != "001") throw new InvalidOperationException(resp["descricao"]?.ToString());
return await FiscalHelpers.AguardarReciboAsync(_api, "/nffornecedor", resp["data"]!["recibo"]!.ToString(), ct: ct);}Listar com paginação
public async Task<List<JsonNode>> ListarTodosClientesAsync(CancellationToken ct = default){ var todos = new List<JsonNode>(); int page = 1; int pageSize = 100;
while (true) { var resp = await _api.CallAsync(HttpMethod.Get, $"/clientes?page={page}&page_size={pageSize}", null, ct);
if (resp["status"]?.ToString() != "005") break; if (resp["data"] is not JsonArray arr || arr.Count == 0) break;
todos.AddRange(arr.Select(x => x!));
if (arr.Count < pageSize) break; page++; await Task.Delay(200, ct); }
return todos;}Tratamento de erros + logging estruturado
try{ var nfce = await emissor.EmitirAsync(pedido); _logger.LogInformation("NFCe emitida: {Chave}", nfce["sefaz"]?["chave"]);}catch (ApiError e){ _logger.LogError( "API Error httpStatus={Status} requestId={RequestId} msg={Message}", e.HttpStatus, e.RequestId, e.Message ); throw;}Health check (sem autenticação)
using var http = new HttpClient();var res = await http.GetAsync($"{baseUrl}/health");var health = await res.Content.ReadFromJsonAsync<JsonNode>();// { 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.
public async Task<JsonNode> AlterarNotaAsync(string tipo, string id, object dados, CancellationToken ct = default){ // tipo: "nfe" | "nfce" | "nfse" return await _api.CallAsync(HttpMethod.Put, $"/{tipo}/{id}", dados, ct);}
// Uso:await AlterarNotaAsync("nfe", "12345", new { serie = "1", cfop = "5102", cliente = "12345678000199", itens = new[] { new { produto = "SKU-001", quantidade = "3", valor_unitario = "89.90" } }});Consulta detalhada por recibo
var nfeCompleta = await _api.CallAsync(HttpMethod.Get, $"/nfe/nota/{recibo}/completa", null);var nfceCompleta = await _api.CallAsync(HttpMethod.Get, $"/nfce/cupom/{recibo}/completa", null);Listagens
var nfes = await _api.CallAsync(HttpMethod.Get, "/nfe?page=1&page_size=20", null);var nfces = await _api.CallAsync(HttpMethod.Get, "/nfce?page=1&page_size=50", null);var nfses = await _api.CallAsync(HttpMethod.Get, "/nfse?dataInicial=2026-05-01&page=1&page_size=20", null);var fornecedor = await _api.CallAsync(HttpMethod.Get, "/nffornecedor?page=1&page_size=20", null);
// Forma legada (path-based)var nfesLegado = await _api.CallAsync(HttpMethod.Get, "/nfe/paginacao/1/20", null);Clientes (CRUD completo)
public async Task<JsonNode> CadastrarPFAsync(PessoaFisica pf, CancellationToken ct = default){ return await _api.CallAsync(HttpMethod.Post, "/clientes", new { tipo = "F", nome = pf.Nome, cpf = pf.Cpf, rg = pf.Rg, consumidor_final = "1", contato = new { email = pf.Email, telefone = pf.Telefone }, endereco = pf.Endereco, }, ct);}
public async Task<JsonNode> CadastrarPJAsync(PessoaJuridica pj, CancellationToken ct = default){ return await _api.CallAsync(HttpMethod.Post, "/clientes", new { tipo = "J", razao_social = pj.RazaoSocial, nome_fantasia = pj.NomeFantasia, cnpj = pj.Cnpj, inscricao_municipal = pj.InscricaoMunicipal, tipo_inscricao_estadual = pj.Contribuinte ?? "9", inscricao_estadual = pj.InscricaoEstadual ?? "ISENTO", consumidor_final = "0", contato = new { email = pj.Email, telefone = pj.Telefone }, endereco = pj.Endereco, }, ct);}
// Consultar por CPF/CNPJvar cliente = await _api.CallAsync(HttpMethod.Get, "/clientes/12345678000199", null);
// Buscar por nomevar encontrados = await _api.CallAsync(HttpMethod.Get, "/clientes/busca/exemplo", null);var top = await _api.CallAsync(HttpMethod.Get, "/clientes/busca/top_clientes", null);
// Atualizarawait _api.CallAsync(HttpMethod.Put, "/clientes/12345678000199", new { tipo = "J", razao_social = "Empresa Exemplo (Novo Nome) Ltda"});
// Excluirawait _api.CallAsync(HttpMethod.Delete, "/clientes/12345678000199", null);Produtos (CRUD completo)
public async Task<JsonNode> CadastrarProdutoAsync(Produto p, CancellationToken ct = default){ return await _api.CallAsync(HttpMethod.Post, "/produtos", new { referencia = p.Sku, nome = p.Nome, valor = p.Valor.ToString("F2"), medida = p.Medida ?? "UN", categoria = p.Categoria, ncm = p.Ncm, origem = p.Origem ?? "0", cfop_preferencial = p.Cfop ?? "5102", tipo_produto = "P", configuracoes_fiscais = p.ConfiguracoesFiscais ?? new object[] { }, }, ct);}
public async Task<JsonNode> CadastrarServicoAsync(Servico s, CancellationToken ct = default){ return await _api.CallAsync(HttpMethod.Post, "/produtos", new { referencia = s.Sku, nome = s.Nome, valor = s.Valor.ToString("F2"), medida = "H", tipo_produto = "S", codigo_servico = s.CodigoServico, }, ct);}
// Consultarvar produto = await _api.CallAsync(HttpMethod.Get, "/produtos/SKU-001", null);var porNome = await _api.CallAsync(HttpMethod.Get, "/produtos/nome/Camiseta", null);
// Listar paginado com busca (use _todos para não filtrar)var todos = await _api.CallAsync(HttpMethod.Get, "/produtos/paginacao/1/20/_todos", null);var filtrados = await _api.CallAsync(HttpMethod.Get, "/produtos/paginacao/1/20/Camiseta", null);
// Atualizarawait _api.CallAsync(HttpMethod.Put, "/produtos/SKU-001", new { referencia = "SKU-001", nome = "Camiseta Polo M (Novo)", valor = "99.90"});
// Excluirawait _api.CallAsync(HttpMethod.Delete, "/produtos/SKU-001", null);