Documentation › First steps
Get started
From the test key to the first authorized NFS-e, step by step, with curl, C# and Delphi.
Your first invoice
Everything starts in the test environment: the invoice really goes to the Sistema Nacional test environment, with no fiscal value, no charge, and it does not appear in the dashboard. Later, for production, only the key changes.
Which path is yours?
- A. The company is already set up in the dashboard (A1 certificate and tax data): follow steps 1, 3, 4, 5, 6 and 7. It takes minutes.
- B. First setup: start at step 2. The time depends on having the A1 certificate at hand and knowing the company's tax regime and service code.
1. Test key. In the dashboard, under Integrações › Chaves de API
(Integrations › API keys), create a key with environment Teste (Test). It
starts with sdr_test_. In the examples below it is in the SUDREL_API_KEY
variable.
2. Company (path B only). Register the company and upload the A1
certificate in the dashboard, or through the API with a sdr_live_ key
(POST /v1/companies and POST /v1/companies/{id}/certificate). The test key
reads companies and issues on their behalf, but does not create or change a
company. The tax data (tax regime, default service code) is in the dashboard,
under Emissão › Empresa (Issuance › Company). Get each company's
empresa_id from GET /v1/companies.
3. Readiness. Goal: know whether the company can issue now, before you try.
curl https://api.sudrel.com.br/api/v1/companies/$EMPRESA/prontidao \
-H "Authorization: Bearer $SUDREL_API_KEY"
The response has situacao (ok, aviso or bloqueio) and one item per
requirement, each with mensagem and correcao. Next step: with ok or
aviso, continue. With bloqueio, fix the indicated item: issuance would be
refused with 412. Details in Readiness.
4. Validate before you issue. POST /v1/nfse/validacoes takes the same
JSON as issuance and returns the errors and the ajustes, without creating
anything.
curl https://api.sudrel.com.br/api/v1/nfse/validacoes \
-H "Authorization: Bearer $SUDREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"empresa_id":"'$EMPRESA'","referencia":"PED-1001","tomador":{"documento":"11222333000181"},"valor":"1500.00","servico":{"descricao":"Consultoria em sistemas"}}'
The response is always 200, with valida (true or false), erros (each
with campo, mensagem and correcao) and ajustes (what Sudrel filled in
or omitted because of the municipality). Next step: with valida: true, issue.
5. Issue. Always send referencia and Idempotency-Key: they make it
safe to repeat the call. See Idempotency.
curl https://api.sudrel.com.br/api/v1/nfse \
-H "Authorization: Bearer $SUDREL_API_KEY" \
-H "Idempotency-Key: 7f1c0d9a-4a51-4e0e-9b1f-2b6f0d3c5a10" \
-H "Prefer: wait=10" \
-H "Content-Type: application/json" \
-d '{"empresa_id":"'$EMPRESA'","referencia":"PED-1001","tomador":{"documento":"11222333000181"},"valor":"1500.00","servico":{"descricao":"Consultoria em sistemas"}}'
What can come back:
| Status | What happened | Next step |
|---|---|---|
| 201 | Authorized. numero and chave_acesso are filled in. |
Done. Download the PDF from GET /v1/nfse/{id}/pdf. |
| 202 | Still transmitindo. Sudrel keeps handling it. |
Wait for the webhook or query (step 6). Do not issue again. |
422 validacao |
The JSON has an error. Nothing was created. | Fix the items in erros and send again. |
422 nfse_rejeitada |
The Sistema Nacional refused it. The invoice exists as rejeitada. |
Read rejeicao, fix it and issue a new invoice, with a different referencia. |
412 empresa_nao_pronta |
The company cannot issue. Nothing was created. | Fix the items in bloqueio (step 3). |
Without an SDK, the same call in plain HTTP:
C# (.NET 6 or newer):
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Threading.Tasks;
public static class PrimeiraNota
{
// A Idempotency-Key é sua: gere uma por nota, guarde junto do pedido e
// reaproveite a MESMA se precisar repetir a chamada. Assim nunca sai nota dobrada.
public static async Task<string> EmitirAsync(string empresaId, string idempotencyKey)
{
using var http = new HttpClient { BaseAddress = new Uri("https://api.sudrel.com.br/api/") };
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SUDREL_API_KEY"));
var pedido = new HttpRequestMessage(HttpMethod.Post, "v1/nfse")
{
Content = JsonContent.Create(new
{
empresa_id = empresaId,
referencia = "PED-1001",
tomador = new { documento = "11222333000181" },
valor = "1500.00",
servico = new { descricao = "Consultoria em sistemas" },
}),
};
pedido.Headers.Add("Idempotency-Key", idempotencyKey);
pedido.Headers.Add("Prefer", "wait=10");
var resposta = await http.SendAsync(pedido);
var corpo = await resposta.Content.ReadAsStringAsync();
// 201: autorizada. 202: ainda transmitindo (acompanhe por webhook ou GET /v1/nfse/PED-1001).
// 422: veja "codigo" e "erros" (ou a nota rejeitada em "nfse"). Todo erro traz "requisicao_id".
return $"{(int)resposta.StatusCode} {corpo}";
}
}
Delphi (10.3 or newer, System.Net.HttpClient):
uses
System.SysUtils, System.Classes, System.Net.HttpClient, System.Net.URLClient;
// A Idempotency-Key é sua: gere uma por nota, guarde junto do pedido e
// reaproveite a MESMA se precisar repetir a chamada. Assim nunca sai nota dobrada.
function EmitirNota(const EmpresaId, IdempotencyKey: string): string;
var
Cliente: THTTPClient;
Corpo: TStringStream;
Resposta: IHTTPResponse;
begin
Cliente := THTTPClient.Create;
Corpo := TStringStream.Create(
'{"empresa_id":"' + EmpresaId + '","referencia":"PED-1001",' +
'"tomador":{"documento":"11222333000181"},"valor":"1500.00",' +
'"servico":{"descricao":"Consultoria em sistemas"}}', TEncoding.UTF8);
try
Cliente.ContentType := 'application/json';
Cliente.CustomHeaders['Authorization'] := 'Bearer ' + GetEnvironmentVariable('SUDREL_API_KEY');
Cliente.CustomHeaders['Idempotency-Key'] := IdempotencyKey;
Cliente.CustomHeaders['Prefer'] := 'wait=10';
Resposta := Cliente.Post('https://api.sudrel.com.br/api/v1/nfse', Corpo);
// 201: autorizada. 202: ainda transmitindo (acompanhe por webhook ou GET /v1/nfse/PED-1001).
// 422: veja "codigo" e "erros" (ou a nota rejeitada em "nfse"). Todo erro traz "requisicao_id".
Result := IntToStr(Resposta.StatusCode) + ' ' + Resposta.ContentAsString(TEncoding.UTF8);
finally
Corpo.Free;
Cliente.Free;
end;
end;
There are also TypeScript and PHP SDKs: see SDKs.
6. Track it. Goal: know the result of an invoice that returned 202. Query it by your reference:
curl https://api.sudrel.com.br/api/v1/nfse/PED-1001 \
-H "Authorization: Bearer $SUDREL_API_KEY"
Next step: status tells where the invoice is (see
NFS-e statuses). Do not query too often (every 30
seconds, for example); in production, prefer the webhook.
7. Webhook. Goal: get notified without querying. Register an endpoint of yours with the test key (it receives only test events):
curl https://api.sudrel.com.br/api/v1/webhooks \
-H "Authorization: Bearer $SUDREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://erp.exemplo.com.br/webhooks/sudrel","eventos":["NFSE_AUTORIZADA","NFSE_REJEITADA","NFSE_FALHA_TRANSMISSAO"]}'
Store the segredo from the response: it appears only here. Test it with
POST /v1/webhooks/{id}/teste. How to check the signature of each delivery
is in Webhooks.
8. Trigger the failures. With the X-Sudrel-Simular header (section
Failure simulator) you see a timeout, Sefin down,
an accrual period lock and falha_transmissao without touching the Sefin.
Got an error? Every response has the X-Request-Id header, and the
issuance, batch, webhook and event errors repeat the value in
requisicao_id. Search for it in the dashboard, under
Integrações › Log da API (Integrations › API log): it shows what your
system sent and what Sudrel answered, for 30 days. See also
Troubleshooting.