Pular para o conteúdo

Exemplos em C# (.NET)

Setup

Requer .NET 6+.

.csproj
<PackageReference Include="DotNetEnv" Version="3.0.0" />
.env
MYSE_API_BASE=https://api.mysebr.com.br/nfemyse-v3/rest
MYSE_API_TOKEN=seu_token_aqui

Cliente 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/CNPJ
var cliente = await _api.CallAsync(HttpMethod.Get, "/clientes/12345678000199", null);
// Buscar por nome
var encontrados = await _api.CallAsync(HttpMethod.Get, "/clientes/busca/exemplo", null);
var top = await _api.CallAsync(HttpMethod.Get, "/clientes/busca/top_clientes", null);
// Atualizar
await _api.CallAsync(HttpMethod.Put, "/clientes/12345678000199", new {
tipo = "J",
razao_social = "Empresa Exemplo (Novo Nome) Ltda"
});
// Excluir
await _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);
}
// Consultar
var 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);
// Atualizar
await _api.CallAsync(HttpMethod.Put, "/produtos/SKU-001", new {
referencia = "SKU-001",
nome = "Camiseta Polo M (Novo)",
valor = "99.90"
});
// Excluir
await _api.CallAsync(HttpMethod.Delete, "/produtos/SKU-001", null);