Da API Key à primeira impressão em menos de 15 minutos.
A documentação pública do Print STA foi desenhada para ser suficiente sozinha: URL real, exemplos executáveis, respostas tipadas, estados de jobs, Webhooks HMAC, limites e diagnóstico do Agent Windows.
QUICKSTART EXECUTÁVEL
Sua primeira impressão.
computers:read printers:read jobs:create jobs:read- Instale o Agent. Use Downloads, conecte o computador no ambiente
teste aguarde o status Online. - Crie a API Key. No painel → Desenvolvedores, crie uma chave
pst_test_...com os quatro scopes acima. O segredo é credencial de servidor. - Valide a chave. Chame
GET /print-sta-api-account. - Descubra o destino. Liste computadores online e depois impressoras online.
- Crie um job. Envie um cupom de texto com um
Idempotency-Keyúnico. - Acompanhe. Consulte
GET /print-sta-api-print-jobs/{job_id}ou assine Webhooks.
export PRINT_STA_API_KEY="pst_test_SUA_CHAVE"
BASE="https://bryzmdvfbowtljycefbs.supabase.co/functions/v1"
AUTH="Authorization: Bearer $PRINT_STA_API_KEY"
# 1) validar a chave/conta
curl -sS "$BASE/print-sta-api-account" -H "$AUTH" | jq
# 2) computador online
COMPUTER_ID=$(curl -sS "$BASE/print-sta-api-computers?status=online" -H "$AUTH" \
| jq -r '.data.items[0].computer_id')
# 3) primeira impressora online desse computador
PRINTER_ID=$(curl -sS "$BASE/print-sta-api-printers?computer_id=$COMPUTER_ID&status=online" -H "$AUTH" \
| jq -r '.data.items[0].printer_id')
# 4) criar impressão de texto (idempotente)
JOB=$(curl -sS -X POST "$BASE/print-sta-api-print-jobs" \
-H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-$(date +%s)" \
-d "{\"printer_id\":\"$PRINTER_ID\",\"content\":{\"type\":\"text\",\"encoding\":\"utf8\",\"data\":\"PRINT STA\\nPrimeira impressão\\n\"},\"copies\":1}")
echo "$JOB" | jq
JOB_ID=$(echo "$JOB" | jq -r '.data.job_id')
# 5) consultar estado e histórico
curl -sS "$BASE/print-sta-api-print-jobs/$JOB_ID" -H "$AUTH" | jqconst BASE = "https://bryzmdvfbowtljycefbs.supabase.co/functions/v1";
const API_KEY = process.env.PRINT_STA_API_KEY;
const headers = { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json" };
async function api(path, init = {}) {
const r = await fetch(BASE + path, { ...init, headers: { ...headers, ...init.headers } });
const body = await r.json();
if (!r.ok) throw new Error(`${r.status} ${body.code}: ${body.message}`);
return body;
}
console.log(await api("/print-sta-api-account"));
const computers = await api("/print-sta-api-computers?status=online");
const computerId = computers.data.items[0].computer_id;
const printers = await api(`/print-sta-api-printers?computer_id=${computerId}&status=online`);
const printerId = printers.data.items[0].printer_id;
const job = await api("/print-sta-api-print-jobs", {
method: "POST",
headers: { "Idempotency-Key": `quickstart-${Date.now()}` },
body: JSON.stringify({
printer_id: printerId,
content: { type: "text", encoding: "utf8", data: "PRINT STA\nPrimeira impressão\n" },
copies: 1,
}),
});
console.log(job);
console.log(await api(`/print-sta-api-print-jobs/${job.data.job_id}`));<?php
$base = 'https://bryzmdvfbowtljycefbs.supabase.co/functions/v1';
$key = getenv('PRINT_STA_API_KEY');
function api($method, $url, $key, $body = null, $idem = null) {
$headers = ['Authorization: Bearer '.$key, 'Accept: application/json'];
if ($body !== null) $headers[] = 'Content-Type: application/json';
if ($idem) $headers[] = 'Idempotency-Key: '.$idem;
$ch = curl_init($url); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_CUSTOMREQUEST=>$method, CURLOPT_HTTPHEADER=>$headers]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$raw = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $json = json_decode($raw, true);
if ($status >= 400) throw new Exception($json['code'].': '.$json['message']); return $json;
}
$computers = api('GET', $base.'/print-sta-api-computers?status=online', $key);
$computer = $computers['data']['items'][0]['computer_id'];
$printers = api('GET', $base.'/print-sta-api-printers?computer_id='.$computer.'&status=online', $key);
$printer = $printers['data']['items'][0]['printer_id'];
$job = api('POST', $base.'/print-sta-api-print-jobs', $key, ['printer_id'=>$printer,'content'=>['type'=>'text','encoding'=>'utf8','data'=>"PRINT STA\nPrimeira impressão\n"],'copies'=>1], 'quickstart-'.time());
print_r($job); print_r(api('GET', $base.'/print-sta-api-print-jobs/'.$job['data']['job_id'], $key));import os, time, requests
BASE = "https://bryzmdvfbowtljycefbs.supabase.co/functions/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PRINT_STA_API_KEY']}"}
def get(path):
r = requests.get(BASE + path, headers=HEADERS); r.raise_for_status(); return r.json()
computer_id = get("/print-sta-api-computers?status=online")["data"]["items"][0]["computer_id"]
printer_id = get(f"/print-sta-api-printers?computer_id={computer_id}&status=online")["data"]["items"][0]["printer_id"]
r = requests.post(BASE + "/print-sta-api-print-jobs",
headers={**HEADERS, "Idempotency-Key": f"quickstart-{time.time_ns()}"},
json={"printer_id": printer_id, "content": {"type":"text","encoding":"utf8","data":"PRINT STA\nPrimeira impressão\n"}, "copies":1})
r.raise_for_status(); job = r.json(); print(job)
print(get("/print-sta-api-print-jobs/" + job["data"]["job_id"]))using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
const string Base = "https://bryzmdvfbowtljycefbs.supabase.co/functions/v1";
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("PRINT_STA_API_KEY"));
JsonElement Get(string p) => JsonSerializer.Deserialize<JsonElement>(http.GetStringAsync(Base + p).Result);
var computerId = Get("/print-sta-api-computers?status=online").GetProperty("data").GetProperty("items")[0].GetProperty("computer_id").GetString();
var printerId = Get($"/print-sta-api-printers?computer_id={computerId}&status=online").GetProperty("data").GetProperty("items")[0].GetProperty("printer_id").GetString();
using var req = new HttpRequestMessage(HttpMethod.Post, Base + "/print-sta-api-print-jobs");
req.Headers.Add("Idempotency-Key", $"quickstart-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}");
req.Content = JsonContent.Create(new { printer_id = printerId, content = new { type="text", encoding="utf8", data="PRINT STA\nPrimeira impressão\n" }, copies=1 });
var response = await http.SendAsync(req); response.EnsureSuccessStatusCode();
var job = await response.Content.ReadFromJsonAsync<JsonElement>(); Console.WriteLine(job);
var jobId = job.GetProperty("data").GetProperty("job_id").GetString(); Console.WriteLine(await http.GetStringAsync(Base + "/print-sta-api-print-jobs/" + jobId));ENDPOINT E AMBIENTES
Uma URL real, sem descobrir Project Ref.
https://bryzmdvfbowtljycefbs.supabase.co/functions/v1https://api.print-sta.com.br/v1DNS/ALIAS PENDENTEO contrato já está preparado para o domínio próprio. Enquanto DNS, alias do Netlify e HTTPS não forem validados, o Quickstart usa o endpoint ativo para continuar 100% executável. Após o cutover, https://api.print-sta.com.br/v1 vira o primeiro servidor do OpenAPI sem alterar o contrato v1.
Test
pst_test_...Desenvolvimento e homologação. Recursos são isolados do ambiente live.
Production / Live
pst_live_...Operação real. Uma chave test nunca acessa recursos live e vice-versa.
AUTENTICAÇÃO
API Keys e menor privilégio.
Todas as chamadas da API pública usam Authorization: Bearer <API_KEY>. O segredo deve ficar no backend do seu ERP/serviço, nunca no browser ou app distribuído.
Authorization: Bearer pst_test_SUA_CHAVE| Scope | Permite |
|---|---|
account:read | Consultar conta e ambiente. |
computers:read | Listar computadores. |
computers:write | Operações autorizadas de computador. |
printers:read | Listar impressoras e capacidades públicas. |
jobs:read | Listar/consultar jobs e histórico. |
jobs:create | Criar impressões. |
jobs:cancel | Cancelar quando o estado permitir. |
jobs:retry | Reprocessar falhas seguras. |
webhooks:read | Listar endpoints e entregas. |
webhooks:write | Criar, alterar, excluir, testar e reprocessar Webhooks. |
DESTINOS
Computadores e impressoras.
O Agent Windows sincroniza o inventário local. Seu backend não deve guardar nomes de impressora como identidade primária: armazene printer_id e use nomes/códigos apenas para exibição.
Computer
Status: pending, online, offline, disabled, revoked.
Campos tipados incluem ID, código PSTD, hostname, sistema operacional, versões Agent/Installer e heartbeat.
Printer
Status: online, offline, disabled, removed.
Campos tipados incluem driver, porta, computador, flags default/virtual/test e última detecção.
online e enabled=true. Se o Agent está online mas não há impressoras, consulte o troubleshooting.CONTEÚDO
PDF, RAW e TEXT sem ambiguidade.
Texto UTF-8
type=text + encoding=utf8. Ideal para cupom textual quando o provider da impressora está configurado.
Arquivo PDF
type=pdf + encoding=base64. Envie o arquivo inteiro; o binário precisa iniciar com %PDF-.
RAW / ESC-POS
type=raw + encoding=base64. Use apenas em impressora/provider compatível.
{
"printer_id": "SEU_PRINTER_UUID",
"content": { "type": "text", "encoding": "utf8", "data": "LOJA STA\nPedido #123\nTotal R$ 42,90\n" },
"copies": 1,
"external_id": "pedido-123",
"title": "Cupom pedido 123"
}PDF_BASE64=$(base64 -w 0 ./cupom.pdf)
curl -X POST "$BASE/print-sta-api-print-jobs" \
-H "Authorization: Bearer $PRINT_STA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pdf-pedido-123" \
-d "{\"printer_id\":\"$PRINTER_ID\",\"content\":{\"type\":\"pdf\",\"encoding\":\"base64\",\"data\":\"$PDF_BASE64\"},\"copies\":1,\"external_id\":\"pdf-123\"}"Windows PowerShell: [Convert]::ToBase64String([IO.File]::ReadAllBytes('cupom.pdf')).
const escpos = Buffer.concat([
Buffer.from([0x1b, 0x40]), // initialize
Buffer.from("PRINT STA\nTOTAL R$ 10,00\n\n", "utf8"),
Buffer.from([0x1d, 0x56, 0x00]), // cut
]);
const body = {
printer_id: printerId,
content: { type: "raw", encoding: "base64", data: escpos.toString("base64") },
copies: 1,
};RECURSO CENTRAL
O Job agora é completamente tipado.
O OpenAPI não responde mais com data: {}. A resposta de criação/consulta usa PrintJob, com campos reais para destino, conteúdo, retries, falha e spooler.
{
"code": "API_JOB_CREATED",
"message": "Trabalho criado.",
"data": {
"contract": "print_sta_job_v1",
"job_id": "52c026c9-9c44-43f0-b424-139c8e4e8794",
"printer_id": "d6ce75cf-0646-4cbc-a2a3-7caeb62af521",
"printer_code": "PST-F2FB0B",
"printer_name": "Cupom Fiscal",
"computer_name": "MESA-1",
"environment": "test",
"external_id": "pedido-123",
"content_type": "text",
"content_hash": "8d3a...64-hex",
"content_size_bytes": 42,
"copies": 1,
"status": "queued",
"attempt_count": 0,
"retry_count": 0,
"retry_state": "none",
"created_at": "2026-08-14T14:00:00Z",
"updated_at": "2026-08-14T14:00:00Z",
"expires_at": "2026-08-15T14:00:00Z",
"duration_ms": 0,
"is_final": false,
"can_cancel": true,
"can_retry": false
},
"request_id": "req_...",
"correlation_id": "cor_..."
}Campos sem valor podem ser omitidos pelo backend (`failure_code`, timestamps de spooler etc.). O schema OpenAPI marca o conjunto obrigatório e tipa os campos opcionais.
CICLO DE VIDA
Estados oficiais e transições.
Estado existente no contrato interno/compatibilidade. A API pública de criação já coloca o job em queued.
Falha conhecida. Pode permitir retry conforme failure_class e expiração.
Falha ambígua ou risco de duplicidade; exige decisão antes de reprocessar.
Cancelado antes da execução irreversível.
Prazo do job terminou antes da conclusão.
spooler_completed comprova que o spooler concluiu o trabalho; não comprova fisicamente que o papel saiu. O contrato atual não afirma physical_print_confirmed=true.DUPLICIDADE
Idempotência para imprimir com segurança.
Criação e retry exigem Idempotency-Key. Gere a chave a partir de uma identidade estável do seu negócio, por exemplo pedido:98421:cupom:1.
Mesmo key + mesmo payload
Replay seguro. O Print STA devolve o trabalho original; criação pode retornar HTTP 200 em vez de 201.
Mesmo key + payload diferente
HTTP 409 com PRINT_STA_JOB_IDEMPOTENCY_CONFLICT.
Retenção
Mínimo de 24h e pelo menos até a expiração do job. A chave pode ser retida por mais tempo; não a recicle.
Retry manual é bloqueado nos estados leased, accepted_by_agent, sent_to_spooler e spooler_completed para reduzir risco de impressão duplicada.
EVENTOS ASSÍNCRONOS
Webhooks HMAC-SHA256, definidos byte a byte.
O segredo whsec_test_... ou whsec_live_... é exibido somente na criação/rotação. Valide a assinatura usando o corpo bruto recebido, antes de parsear e serializar novamente o JSON.
| Header | Conteúdo |
|---|---|
X-Print-STA-Event | Tipo do evento. |
X-Print-STA-Event-Id | ID estável do evento. |
X-Print-STA-Delivery-Id | ID da entrega/tentativa lógica. |
X-Print-STA-Timestamp | Unix timestamp em segundos. |
X-Print-STA-Signature | v1=<hex-hmac-sha256> |
X-Print-STA-Version | Versão do produtor. |
X-Print-STA-API-Version | v1. |
X-Print-STA-Event-Schema-Version | v1. |
X-Print-STA-Secret-Version | Versão do segredo rotacionável. |
<timestamp>.<event_id>.<product_version>.<api_contract_version>.<event_schema_version>.<raw_body>Assinatura: lowercase_hex(HMAC-SHA256(secret, canonical_message)). Compare em tempo constante.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, h, rawBody) {
const received = h["x-print-sta-signature"].replace(/^v1=/, "");
const msg = [h["x-print-sta-timestamp"], h["x-print-sta-event-id"], h["x-print-sta-version"], h["x-print-sta-api-version"], h["x-print-sta-event-schema-version"], rawBody].join(".");
const expected = createHmac("sha256", secret).update(msg).digest("hex");
const a = Buffer.from(received, "hex"), b = Buffer.from(expected, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}event_id. Essa janela é uma recomendação do consumidor; o Print STA não controla o relógio do seu endpoint.Eventos oficiais
Entrega e retry
Qualquer HTTP 2xx conclui a entrega. Timeout de 10s, falha de rede ou não-2xx agenda retry em 30s → 2min → 10min → 1h → 6h. Máximo padrão: 6 tentativas; depois dead_letter. Redirecionamentos não são seguidos.
DIAGNÓSTICO DE API
Erros previsíveis, com IDs de correlação.
400Entrada inválida401API Key inválida402Plano/uso bloqueou403Scope ou ambiente404Recurso não encontrado409Estado/idempotência413Payload grande415Content-Type inválido429Rate limit503Serviço/política indisponível{
"contract": "print_sta_api_error_v1",
"code": "PRINT_STA_JOB_IDEMPOTENCY_CONFLICT",
"message": "Idempotency-Key já usada com conteúdo diferente.",
"status": 409,
"retryable": false,
"request_id": "req_...",
"correlation_id": "cor_checkout_20260814_001"
}| Código | Ação recomendada |
|---|---|
API_KEY_INVALID | Revise chave, ambiente, revogação/expiração. |
API_KEY_SCOPE_INSUFFICIENT | Adicione somente o scope necessário. |
PRINT_STA_PRINTER_NOT_FOUND | Atualize inventário e valide printer_id/ambiente. |
PRINT_STA_PRINTER_NOT_READY | Aguarde impressora online/habilitada. |
PRINT_STA_JOB_IDEMPOTENCY_CONFLICT | Não troque payload para a mesma chave. |
PRINT_STA_JOB_RETRY_DUPLICATION_RISK | Não reenvie: consulte o estado/histórico. |
RATE_LIMIT_EXCEEDED | Backoff com jitter; não faça retry em loop. |
PRINT_STA_PUBLIC_API_DISABLED | Ambiente/política não está liberado. |
Envie opcionalmente X-Correlation-Id no seu fluxo e sempre grave os quatro headers X-Print-STA-* de versão/request/correlation para suporte.
CAPACIDADE
Limites públicos concretos.
Limites de plano e políticas de conta podem ser mais restritivos. A API sempre aplica o menor limite efetivo.
VERSIONAMENTO
Contrato v1 é independente do produto.
Uma atualização do Agent/Backend não muda automaticamente o contrato público. Mudanças aditivas compatíveis permanecem em v1; uma quebra de contrato exige nova versão de API. O OpenAPI expõe x-print-sta-product-version, x-print-sta-docs-version e x-print-sta-schema-version separadamente.
FERRAMENTAS DE DESENVOLVIMENTO
Postman, Insomnia e SDKs.
Você pode começar sem escrever o primeiro cliente HTTP. A coleção Postman já contém Quickstart, TEXT/PDF/RAW, jobs e Webhooks, com variáveis que capturam account_id, computer_id, printer_id e job_id.
@print-sta/sdk, print-sta/sdk, print-sta e PrintSTA estão documentados como roadmap, não como pacotes já publicados. Hoje o OpenAPI 3.1 é a fonte para gerar clientes.DOMÍNIO CANÔNICO
api.print-sta.com.br/v1 sem acoplar o cliente ao Supabase.
O gateway já está programado no netlify.toml: chamadas em /v1/* são reescritas internamente para as Edge Functions autoritativas. Falta apenas a ativação operacional do subdomínio no DNS/Netlify e a validação HTTPS antes de promovê-lo a servidor padrão.
- Associar
api.print-sta.com.brao projeto Netlifyprint-sta. - Configurar o DNS do subdomínio conforme o provedor.
- Confirmar HTTPS válido.
- Executar smoke GET + POST idempotente + headers de correlação.
- Promover o domínio próprio a
servers[0]no OpenAPI e abase_urlpadrão das collections.
AGENT WINDOWS
Troubleshooting sem adivinhação.
Computador está Online, mas aparecem 0 impressoras
Confirme se as impressoras existem no Windows e se o inventário sincronizou. No app da bandeja use Sincronizar status agora; no painel clique Atualizar impressoras. Se continuar 0, verifique se a política de TEST permite printer_sync e se o Agent está na versão esperada.
Job fica em queued
O Agent precisa estar online e a fila operacional habilitada. Verifique computador, impressora e heartbeat. Não crie um segundo job com outra Idempotency-Key para “forçar”; isso pode duplicar impressão.
Job chega a manual_review
Leia failure_code, failure_class e o histórico. manual_review é intencional quando a falha é ambígua ou existe risco de duplicidade. Só faça retry após resolver a causa.
PDF retorna PRINT_STA_PDF_HANDLER_UNAVAILABLE
Esse erro pertence ao caminho antigo que dependia de leitor externo. Atualize o Agent para 2.0.25 ou superior, que usa PDFium interno para renderização direta no spooler.
401 depois que um computador foi revogado
Uma credencial revogada não deve continuar válida. Conecte novamente o mesmo Windows para receber uma nova credencial. Não apague o installation_id; o histórico revogado é preservado.
O app abre uma janela toda vez que o Windows inicia
Agent 2.0.25+ inicia o companion com --background: fica silencioso na bandeja. O atalho manual abre a tela de Status.
O atalho aparece com ícone branco/genérico
O instalador 2.0.25+ referencia explicitamente o ícone oficial. Após atualizar, reinicie o Explorer/Windows para limpar cache de ícones antigos se necessário.
spooler_completed significa que imprimiu no papel?
Não. Significa que o spooler do Windows concluiu o processamento. O contrato é deliberadamente conservador e não afirma confirmação física do papel.
GUIAS DEDICADOS
Uma URL própria para cada assunto.
OPENAPI 3.1
Referência de endpoints, gerada do contrato.
Cada endpoint abaixo mostra autenticação, scopes, parâmetros, request e respostas tipadas. O mesmo arquivo pode alimentar Swagger, Redoc e geradores de SDK.
JSON SCHEMA
Schemas reais das respostas.
Não há mais um único Envelope.data genérico nos endpoints principais. Expanda os modelos para ver propriedades, enums e obrigatoriedade.
PRONTO PARA O PRIMEIRO JOB?
Instale o Agent e execute o Quickstart.
Comece em test, valide o fluxo ponta a ponta e só então crie uma API Key live.