Pular para o conteúdo

Exemplos em Go

Setup

Requer Go 1.21+. Sem dependências externas (usa stdlib).

Terminal window
export MYSE_API_BASE="https://api.mysebr.com.br/nfemyse-v3/rest"
export MYSE_API_TOKEN="seu_token_aqui"

Cliente HTTP centralizado

package myse
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type ApiResponse struct {
Status string `json:"status"`
Descricao string `json:"descricao"`
Data json.RawMessage `json:"data,omitempty"`
Meta *Meta `json:"meta,omitempty"`
}
type Meta struct {
RequestID string `json:"request_id"`
Timestamp string `json:"timestamp"`
}
type ApiError struct {
HTTPStatus int
Message string
Payload json.RawMessage
RequestID string
}
func (e *ApiError) Error() string {
return fmt.Sprintf("[%d] %s (request_id=%s)", e.HTTPStatus, e.Message, e.RequestID)
}
type Client struct {
Base string
Token string
HTTP *http.Client
}
func New() *Client {
return &Client{
Base: os.Getenv("MYSE_API_BASE"),
Token: os.Getenv("MYSE_API_TOKEN"),
HTTP: &http.Client{Timeout: 30 * time.Second},
}
}
func (c *Client) Call(ctx context.Context, method, path string, body any) (*ApiResponse, error) {
var reader io.Reader
if body != nil {
b, err := json.Marshal(body)
if err != nil {
return nil, err
}
reader = bytes.NewReader(b)
}
req, err := http.NewRequestWithContext(ctx, method, c.Base+path, reader)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Token "+c.Token)
req.Header.Set("Content-Type", "application/json")
res, err := c.HTTP.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
bodyBytes, _ := io.ReadAll(res.Body)
requestID := res.Header.Get("X-Request-Id")
if res.StatusCode >= 400 {
return nil, &ApiError{
HTTPStatus: res.StatusCode,
Message: fmt.Sprintf("HTTP %d", res.StatusCode),
Payload: bodyBytes,
RequestID: requestID,
}
}
var apiResp ApiResponse
if err := json.Unmarshal(bodyBytes, &apiResp); err != nil {
return nil, err
}
if apiResp.Status == "999" {
// Extrai erro do data
var data map[string]any
json.Unmarshal(apiResp.Data, &data)
msg, _ := data["erro"].(string)
if msg == "" {
msg = apiResp.Descricao
}
rid := requestID
if apiResp.Meta != nil && apiResp.Meta.RequestID != "" {
rid = apiResp.Meta.RequestID
}
return nil, &ApiError{HTTPStatus: 200, Message: msg, Payload: bodyBytes, RequestID: rid}
}
return &apiResp, nil
}

Cenário: emitir NFCe com polling

package main
import (
"context"
"encoding/json"
"fmt"
"math"
"time"
)
var terminais = map[string]bool{"004": true, "010": true, "900": true, "999": true}
func EmitirNFCe(ctx context.Context, c *myse.Client, pedido Pedido) (json.RawMessage, error) {
body := map[string]any{
"serie": "65",
"cfop": "5102",
"numero-origem": fmt.Sprintf("PED-%d", pedido.ID),
"tp_pagamento": pedido.FormaPagamento,
"cliente": pedido.CpfCnpjCliente,
"itens": buildItens(pedido.Itens),
}
emissao, err := c.Call(ctx, "POST", "/nfce/emissao", body)
if err != nil {
return nil, err
}
if emissao.Status != "001" && emissao.Status != "050" {
return nil, fmt.Errorf("emissão falhou: %s", emissao.Descricao)
}
var emissaoData map[string]string
json.Unmarshal(emissao.Data, &emissaoData)
recibo := emissaoData["recibo"]
fmt.Printf("[emissao] recibo=%s request_id=%s\n", recibo, emissao.Meta.RequestID)
wait := 5 * time.Second
deadline := time.Now().Add(5 * time.Minute)
for time.Now().Before(deadline) {
time.Sleep(wait)
consulta, err := c.Call(ctx, "GET", "/nfce/cupom/"+recibo, nil)
if err != nil {
return nil, err
}
fmt.Printf("[polling] status=%s\n", consulta.Status)
if terminais[consulta.Status] {
if consulta.Status == "004" {
return consulta.Data, nil
}
return nil, fmt.Errorf("status terminal não-sucesso: %s", consulta.Status)
}
wait = time.Duration(math.Min(float64(wait)*1.5, float64(30*time.Second)))
}
return nil, fmt.Errorf("timeout aguardando recibo %s", recibo)
}
func buildItens(itens []Item) []map[string]string {
out := make([]map[string]string, len(itens))
for i, it := range itens {
out[i] = map[string]string{
"produto": it.SKU,
"quantidade": fmt.Sprintf("%d", it.Qtd),
"valor-unitario": fmt.Sprintf("%.2f", it.Preco),
}
}
return out
}

Retry com backoff

func ComRetry[T any](ctx context.Context, fn func() (T, error), max int) (T, error) {
var zero T
var lastErr error
for i := 1; i <= max; i++ {
result, err := fn()
if err == nil {
return result, nil
}
// Erros de validação: não retentar
if apiErr, ok := err.(*myse.ApiError); ok {
var p map[string]any
json.Unmarshal(apiErr.Payload, &p)
if p["status"] == "999" {
return zero, err
}
}
lastErr = err
if i < max {
time.Sleep(time.Duration(math.Pow(2, float64(i-1))) * time.Second)
}
}
return zero, lastErr
}

Helper de polling reutilizável

var terminais = map[string]bool{"004": true, "010": true, "900": true, "999": true}
func AguardarRecibo(ctx context.Context, c *myse.Client, pathBase, recibo string, timeout time.Duration) (*myse.ApiResponse, error) {
if timeout == 0 {
timeout = 5 * time.Minute
}
wait := 5 * time.Second
inicio := time.Now()
for time.Since(inicio) < timeout {
time.Sleep(wait)
resp, err := c.Call(ctx, "GET", pathBase+"/"+recibo, nil)
if err != nil {
return nil, err
}
if terminais[resp.Status] {
return resp, nil
}
wait = time.Duration(math.Min(float64(wait)*1.5, float64(30*time.Second)))
}
return nil, fmt.Errorf("timeout aguardando recibo %s", recibo)
}
func validarMotivo(motivo string) error {
if len(motivo) < 15 || len(motivo) > 255 {
return fmt.Errorf("motivo deve ter entre 15 e 255 caracteres")
}
return nil
}

Emitir NF-e (com transporte completo)

func EmitirNFe(ctx context.Context, c *myse.Client, pedido Pedido) (json.RawMessage, error) {
faturas := make([]map[string]string, 0, len(pedido.Faturas))
for _, f := range pedido.Faturas {
faturas = append(faturas, map[string]string{
"valor": fmt.Sprintf("%.2f", f.Valor),
"data": f.Data,
})
}
body := map[string]any{
"serie": "1",
"cfop": "5102",
"numero-origem": fmt.Sprintf("PED-%d", pedido.ID),
"tipo": "S",
"finalidade": "N",
"valor-despesas": "0.00",
"cliente": pedido.CpfCnpjCliente,
"transporte": map[string]string{
"modalidade": "0",
"transportadora": pedido.Transportadora,
"placa-veiculo": pedido.Placa,
"valor-frete": pedido.Frete,
"valor-seguro": "0.00",
"peso-bruto": pedido.PesoBruto,
"peso-liquido": pedido.PesoLiquido,
"quantidade-volume": "1",
"especie-volume": "Caixa",
"marca-volume": "Embalagem",
},
"itens": buildItens(pedido.Itens),
"faturas": faturas,
}
emissao, err := c.Call(ctx, "POST", "/nfe/emissao", body)
if err != nil {
return nil, err
}
if emissao.Status != "001" && emissao.Status != "050" {
return nil, fmt.Errorf("emissão falhou: %s", emissao.Descricao)
}
var d map[string]string
json.Unmarshal(emissao.Data, &d)
resultado, err := AguardarRecibo(ctx, c, "/nfe/nota", d["recibo"], 0)
if err != nil {
return nil, err
}
if resultado.Status != "004" {
return nil, fmt.Errorf("status terminal não-sucesso: %s", resultado.Status)
}
return resultado.Data, nil // inclui sefaz.chave, url-danfe, url-xml
}

Cancelar NF-e

func CancelarNFe(ctx context.Context, c *myse.Client, chave, motivo string) (*myse.ApiResponse, error) {
if err := validarMotivo(motivo); err != nil {
return nil, err
}
cancel, err := c.Call(ctx, "POST", "/nfe/cancelamento", map[string]string{
"chave": chave,
"motivo": motivo,
})
if err != nil {
return nil, err
}
if cancel.Status != "001" {
return nil, fmt.Errorf(cancel.Descricao)
}
var d map[string]string
json.Unmarshal(cancel.Data, &d)
return AguardarRecibo(ctx, c, "/nfe/cancelamento", d["recibo"], 0)
}

Lançar carta de correção (CC-e)

// O campo "motivo" na CC-e é o TEXTO da correção (xCorrecao).
func CartaCorrecaoNFe(ctx context.Context, c *myse.Client, chave, textoCorrecao string) (*myse.ApiResponse, error) {
if err := validarMotivo(textoCorrecao); err != nil {
return nil, err
}
cce, err := c.Call(ctx, "POST", "/nfe/correcao", map[string]string{
"chave": chave,
"motivo": textoCorrecao,
})
if err != nil {
return nil, err
}
if cce.Status != "001" {
return nil, fmt.Errorf(cce.Descricao)
}
var d map[string]string
json.Unmarshal(cce.Data, &d)
return AguardarRecibo(ctx, c, "/nfe/correcao", d["recibo"], 0)
}

Cancelar NFC-e

func CancelarNFCe(ctx context.Context, c *myse.Client, chave, motivo string) (*myse.ApiResponse, error) {
if err := validarMotivo(motivo); err != nil {
return nil, err
}
cancel, err := c.Call(ctx, "POST", "/nfce/cancelamento", map[string]string{
"chave": chave,
"motivo": motivo,
})
if err != nil {
return nil, err
}
if cancel.Status != "001" {
return nil, fmt.Errorf(cancel.Descricao)
}
var d map[string]string
json.Unmarshal(cancel.Data, &d)
return AguardarRecibo(ctx, c, "/nfce/cancelamento", d["recibo"], 0)
}

Emitir NFS-e (Nota Fiscal de Serviço)

func EmitirNFSe(ctx context.Context, c *myse.Client, servico Servico) (json.RawMessage, error) {
itens := make([]map[string]string, 0, len(servico.Itens))
for _, i := range servico.Itens {
itens = append(itens, map[string]string{
"produto": i.CodigoServico,
"quantidade": fmt.Sprintf("%d", i.Qtd),
"valor-unitario": fmt.Sprintf("%.2f", i.Preco),
})
}
body := map[string]any{
"numero-origem": fmt.Sprintf("OS-%d", servico.ID),
"observacao": servico.Observacao,
"nome-contato": servico.NomeContato,
"telefone-contato": servico.TelefoneContato,
"data-fim": servico.DataFim,
"cliente": servico.CpfCnpjCliente,
"itens": itens,
}
emissao, err := c.Call(ctx, "POST", "/nfse/emissao", body)
if err != nil {
return nil, err
}
if emissao.Status != "001" && emissao.Status != "050" {
return nil, fmt.Errorf("emissão NFSe falhou: %s", emissao.Descricao)
}
var d map[string]string
json.Unmarshal(emissao.Data, &d)
resultado, err := AguardarRecibo(ctx, c, "/nfse/nota", d["recibo"], 0)
if err != nil {
return nil, err
}
if resultado.Status != "004" {
return nil, fmt.Errorf("status terminal não-sucesso: %s", resultado.Status)
}
return resultado.Data, nil
}

Cancelar NFS-e

func CancelarNFSe(ctx context.Context, c *myse.Client, chave, motivo string) (*myse.ApiResponse, error) {
if err := validarMotivo(motivo); err != nil {
return nil, err
}
return c.Call(ctx, "POST", "/nfse/cancelamento", map[string]string{
"chave": chave,
"motivo": 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
func ReconhecerNotaFornecedor(ctx context.Context, c *myse.Client, chave, codigo string) (*myse.ApiResponse, error) {
validos := map[string]bool{"210200": true, "210210": true, "210220": true, "210240": true}
if !validos[codigo] {
return nil, fmt.Errorf("código inválido: %s", codigo)
}
resp, err := c.Call(ctx, "POST", "/nffornecedor/reconhecimento", map[string]string{
"chave": chave,
"codigo-reconhecimento": codigo,
})
if err != nil {
return nil, err
}
if resp.Status != "001" {
return nil, fmt.Errorf(resp.Descricao)
}
var d map[string]string
json.Unmarshal(resp.Data, &d)
return AguardarRecibo(ctx, c, "/nffornecedor", d["recibo"], 0)
}

Listar com paginação

func ListarTodosClientes(ctx context.Context, c *myse.Client) ([]json.RawMessage, error) {
var todos []json.RawMessage
page := 1
pageSize := 100
for {
path := fmt.Sprintf("/clientes?page=%d&page_size=%d", page, pageSize)
resp, err := c.Call(ctx, "GET", path, nil)
if err != nil {
return nil, err
}
if resp.Status != "005" {
break
}
var batch []json.RawMessage
json.Unmarshal(resp.Data, &batch)
if len(batch) == 0 {
break
}
todos = append(todos, batch...)
if len(batch) < pageSize {
break
}
page++
time.Sleep(200 * time.Millisecond)
}
return todos, nil
}

Tratamento de erros

func main() {
ctx := context.Background()
c := myse.New()
data, err := EmitirNFCe(ctx, c, pedido)
if err != nil {
if apiErr, ok := err.(*myse.ApiError); ok {
log.Printf("API error: status=%d request_id=%s msg=%s",
apiErr.HTTPStatus, apiErr.RequestID, apiErr.Message)
}
os.Exit(1)
}
log.Printf("NFCe emitida: %s", string(data))
}

Health check (sem autenticação)

res, _ := http.Get(myse.Base + "/health")
defer res.Body.Close()
var health map[string]string
json.NewDecoder(res.Body).Decode(&health)
// map[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.

// tipo: "nfe" | "nfce" | "nfse"
func AlterarNota(ctx context.Context, c *myse.Client, tipo, id string, dados any) (*myse.ApiResponse, error) {
return c.Call(ctx, "PUT", fmt.Sprintf("/%s/%s", tipo, id), dados)
}
// Uso:
AlterarNota(ctx, c, "nfe", "12345", map[string]any{
"serie": "1", "cfop": "5102", "cliente": "12345678000199",
"itens": []map[string]string{
{"produto": "SKU-001", "quantidade": "3", "valor-unitario": "89.90"},
},
})

Consulta detalhada por recibo

nfeCompleta, _ := c.Call(ctx, "GET", "/nfe/nota/"+recibo+"/completa", nil)
nfceCompleta, _ := c.Call(ctx, "GET", "/nfce/cupom/"+recibo+"/completa", nil)

Listagens

nfes, _ := c.Call(ctx, "GET", "/nfe?page=1&page_size=20", nil)
nfces, _ := c.Call(ctx, "GET", "/nfce?page=1&page_size=50", nil)
nfses, _ := c.Call(ctx, "GET", "/nfse?dataInicial=2026-05-01&page=1&page_size=20", nil)
fornecedor, _ := c.Call(ctx, "GET", "/nffornecedor?page=1&page_size=20", nil)
// Forma legada (path-based)
nfesLegado, _ := c.Call(ctx, "GET", "/nfe/paginacao/1/20", nil)

Clientes (CRUD completo)

type PessoaFisica struct {
Nome, Cpf, Rg, Email, Telefone string
Endereco map[string]string
}
func CadastrarPF(ctx context.Context, c *myse.Client, pf PessoaFisica) (*myse.ApiResponse, error) {
return c.Call(ctx, "POST", "/clientes", map[string]any{
"tipo": "F",
"nome": pf.Nome,
"cpf": pf.Cpf,
"rg": pf.Rg,
"consumidor-final": "1",
"contato": map[string]string{"email": pf.Email, "telefone": pf.Telefone},
"endereco": pf.Endereco,
})
}
type PessoaJuridica struct {
RazaoSocial, NomeFantasia, Cnpj, InscricaoMunicipal string
Contribuinte, InscricaoEstadual, Email, Telefone string
Endereco map[string]string
}
func CadastrarPJ(ctx context.Context, c *myse.Client, pj PessoaJuridica) (*myse.ApiResponse, error) {
contribuinte := pj.Contribuinte
if contribuinte == "" {
contribuinte = "9"
}
ie := pj.InscricaoEstadual
if ie == "" {
ie = "ISENTO"
}
return c.Call(ctx, "POST", "/clientes", map[string]any{
"tipo": "J",
"razao-social": pj.RazaoSocial,
"nome-fantasia": pj.NomeFantasia,
"cnpj": pj.Cnpj,
"inscricao-municipal": pj.InscricaoMunicipal,
"tipo-inscricao-estadual": contribuinte,
"inscricao-estadual": ie,
"consumidor-final": "0",
"contato": map[string]string{"email": pj.Email, "telefone": pj.Telefone},
"endereco": pj.Endereco,
})
}
// Consultar por CPF/CNPJ
cliente, _ := c.Call(ctx, "GET", "/clientes/12345678000199", nil)
// Buscar por nome
encontrados, _ := c.Call(ctx, "GET", "/clientes/busca/exemplo", nil)
top, _ := c.Call(ctx, "GET", "/clientes/busca/top_clientes", nil)
// Atualizar
c.Call(ctx, "PUT", "/clientes/12345678000199", map[string]string{
"tipo": "J",
"razao-social": "Empresa Exemplo (Novo Nome) Ltda",
})
// Excluir
c.Call(ctx, "DELETE", "/clientes/12345678000199", nil)

Produtos (CRUD completo)

type Produto struct {
Sku, Nome, Medida, Categoria, Ncm, Origem, Cfop string
Valor float64
ConfiguracoesFiscais []any
}
func CadastrarProduto(ctx context.Context, c *myse.Client, p Produto) (*myse.ApiResponse, error) {
medida := p.Medida
if medida == "" {
medida = "UN"
}
origem := p.Origem
if origem == "" {
origem = "0"
}
cfop := p.Cfop
if cfop == "" {
cfop = "5102"
}
return c.Call(ctx, "POST", "/produtos", map[string]any{
"referencia": p.Sku,
"nome": p.Nome,
"valor": fmt.Sprintf("%.2f", p.Valor),
"medida": medida,
"categoria": p.Categoria,
"ncm": p.Ncm,
"origem": origem,
"cfop-preferencial": cfop,
"tipo-produto": "P",
"configuracoes-fiscais": p.ConfiguracoesFiscais,
})
}
type Servico struct {
Sku, Nome, CodigoServico string
Valor float64
}
func CadastrarServico(ctx context.Context, c *myse.Client, s Servico) (*myse.ApiResponse, error) {
return c.Call(ctx, "POST", "/produtos", map[string]any{
"referencia": s.Sku,
"nome": s.Nome,
"valor": fmt.Sprintf("%.2f", s.Valor),
"medida": "H",
"tipo-produto": "S",
"codigo-servico": s.CodigoServico,
})
}
// Consultar
produto, _ := c.Call(ctx, "GET", "/produtos/SKU-001", nil)
porNome, _ := c.Call(ctx, "GET", "/produtos/nome/Camiseta", nil)
// Listar paginado com busca (use _todos para não filtrar)
todos, _ := c.Call(ctx, "GET", "/produtos/paginacao/1/20/_todos", nil)
filtrados, _ := c.Call(ctx, "GET", "/produtos/paginacao/1/20/Camiseta", nil)
// Atualizar
c.Call(ctx, "PUT", "/produtos/SKU-001", map[string]string{
"referencia": "SKU-001",
"nome": "Camiseta Polo M (Novo)",
"valor": "99.90",
})
// Excluir
c.Call(ctx, "DELETE", "/produtos/SKU-001", nil)