stackin
SDKs

SDKs

  • Uma classe, três métodos

    Um client Invoice com issue(), consult() e cancel() — nada mais pra instanciar.

  • Modelos tipados

    Cada item é um Product tipado, com campos fiscais do Brasil validados no cliente antes da requisição sair.

  • Mesmo contrato em toda linguagem

    Python, Go e PHP seguem o mesmo contrato de API — métodos, erros e recursos se comportam igual, então trocar de linguagem não significa reaprender a stackin.

Começando agora?

Crie uma conta gratuita, gere uma SDK key e emita seu primeiro documento em homologation — é grátis e ilimitado, sem cartão.

Copy
pip install stackin-python-sdk

Autenticação

O SDK autentica com uma SDK key — passe uma vez ao criar o client, a biblioteca cuida do resto. Você nunca monta URL nem define header manualmente.

Conseguindo uma SDK key

  1. 1
    Cadastre-se e faça login em .
  2. 2
    Crie uma empresa — obrigatório antes da key.
  3. 3
    Vá em Settings → API key no dashboard, e deixe o contexto em sdk. Nenhum certificado necessário nesta etapa — a key pode ser criada e usada na hora; certificado só é exigido depois, na emissão de fato.
  4. 4
    Nomeie a key e escolha o ambiente (homologation ou production, padrão homologation) — fixo pelo resto da vida da key, não muda depois.
  5. 5
    Copie a key na hora — ela aparece , na criação, e não pode ser recuperada depois. Perdeu, revoga e cria outra.
Copy
from stackin import Invoice

client = Invoice(api_key="$STACKIN_API_KEY")

Uma empresa pode ter até 3 keys no total (qualquer mix de contexto sdk/api e ambiente homologation/production), então dá pra manter keys separadas por superfície ou ambiente.

Token ausente ou inválido retorna 401 — veja abaixo.

Ambientes

Toda SDK key é criada para exatamente um ambiente — ou or — escolhido ao gerar no dashboard, fixo pelo resto da vida da key. Não existe campo de ambiente nas chamadas do SDK: a key usada decide pra onde vai o documento.

HomologationAmbiente de teste

Grátis e ilimitado — é o ambiente de teste real do SEFAZ/ADN, não é mock — então integre e teste tudo antes de trocar pra production.

ProductionAmbiente real

Para documentos reais. Garanta que tudo funciona em homologation antes de trocar.

Uma empresa pode ter keys dos dois ambientes ao mesmo tempo (até 3 no total).

Erros

Os SDKs traduzem cada falha em um tipo distinto: APIError pro que a API respondeu (com status e detail), ConnectionFailedError pra falha de rede antes de qualquer resposta, e o erro de validação da própria linguagem pro que é pego no cliente, antes da requisição sair.

Copy
from stackin import APIError, ConnectionFailedError

try:
    result = client.issue(...)
except ValueError as error:
    # Caught before the request left — empty items, missing ncm/cfop,
    # incomplete recipient_address on an NFE.
    print(f"Invalid request: {error}")
except APIError as error:
    # 4xx/5xx from the API. 502 carries the authorizer's own message.
    print(f"[{error.status_code}] {error.detail}")
except ConnectionFailedError as error:
    print(f"Could not reach the API: {error}")
HTTPErroQuando
400Configuração inválidaA configuração do emissor (certificado, endereço, campos fiscais) está ausente ou inválida.
401Não autorizadoO Bearer token está ausente, expirado, inválido ou não foi criado para o host que você está chamando (api.stackin.io vs sdk.stackin.io).
402Limite atingidoA cota do plano da empresa para notas de production foi atingida e o plano não tem preço de excedente (trial).
409Conflito de operaçãoA operação pedida não se aplica a esse document_type (ex.: operação exclusiva de nfse numa nfe).
422Erro de validaçãoO corpo da requisição falhou na validação — campo obrigatório faltando ou malformado.
501Não implementadoAssinatura para esse tipo de documento/país ainda não implementada no servidor.
502Autorizador rejeitouRejeição do autorizadorO autorizador (SEFAZ/UF ou ADN) rejeitou a requisição. detail.message traz o erro do próprio autorizador, e detail.invoice_id (só na emissão) permite consultar a tentativa rejeitada.

Rejeições do autorizador

Quando a SEFAZ ou ADN rejeita uma operação, a resposta inclui informações adicionais.

O detail do APIError carrega esses campos quando o autorizador rejeita — use detail.invoice_id pra consultar a tentativa depois.

Copy
{
  "detail": {
    "message": "IE do emitente inválida",
    "invoice_id": "inv_8f3c9b2e6a7d"
  }
}
detail.message
Mensagem retornada pelo autorizador.
detail.invoice_id
Identifica a tentativa de emissão.
Endpoints

Notas fiscais

Emite um novo documento. items[].product.br (ncm, cfop) é obrigatório para , ignorado em , ignored for — serviço não tem código de classificação fiscal, então nfse só lê description e amount de cada item.

A mesma chamada funciona para nfse — basta trocar document_type.

Copy
from stackin import Address, DocumentType, Invoice
from stackin.br import Product

client = Invoice(api_key="$STACKIN_API_KEY")

result = client.issue(
    document_type=DocumentType.NFE,
    client_name="Buyer Company Ltd",
    tax_id="11222333000181",
    items=[
        Product(
            description="Rosa Holambra Vermelha",
            amount=112.44,
            ncm="06031100",
            cfop="5102",
        )
    ],
    recipient_address=Address(
        street="Rua das Flores",
        number="1200",
        neighborhood="Centro",
        city="Joinville",
        state="SC",
        zip_code="89201100",
        city_code="4209102",
    ),
)

Consulta o status atual de um documento direto no autorizador pela access key. Requer como query param.

Copy
result = client.consult(
    document_type=DocumentType.NFE,
    access_key="42260831112223330001815500000012341123456789",
)

Cancela um documento previamente autorizado. precisa ter pelo menos 15 caracteres para nfe (o campo xJust do autorizador exige). Cancelar não devolve cota do plano — a capacidade do autorizador já foi gasta na emissão.

Copy
result = client.cancel(
    document_type=DocumentType.NFE,
    access_key="42260831112223330001815500000012341123456789",
    reason="Duplicate order, cancelled by the buyer within 24h",
)

Reenvia uma emissão que falhou, pelo `id` local do documento (não pela chave de acesso — uma nota rejeitada não tem uma). Útil pra retentar depois de corrigir um problema de configuração ou uma indisponibilidade do autorizador. Consome crédito como uma emissão nova, já que é uma nova transmissão.

Copy
result = client.reissue(
    invoice_id="9f2c1e3a-4b5d-6e7f-8a9b-0c1d2e3f4a5b",
)