Documentation Index Fetch the complete documentation index at: https://docs.stric.io/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks são a forma recomendada de saber quando algo muda na sua conta:
Pix recebido (pix.in.*), ciclo da transferência Pix enviada (pix.payout.*), cobranças
(pix.charge.*). A Stric envia um HTTP POST para cada URL registrada.
1. Registrar um endpoint
curl -X POST " $STRIC_BASE_URL /v1/pix-accounts/ $ACCOUNT_ID /webhooks" \
-H "Authorization: Bearer $STRIC_API_KEY " \
-H "Idempotency-Key: $( uuidgen )" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.minhaempresa.com/webhooks/stric",
"events": [
"pix.in.received",
"pix.payout.succeeded",
"pix.charge.paid",
"pix.charge.expired"
],
"authToken": "um-segredo-forte-gerado-por-voce"
}'
O authToken só aparece na resposta de criação. Em listagens
subsequentes, ele vem mascarado em authTokenPreview. Salve-o no
momento da criação.
2. Validar a assinatura
A Stric envia o token que você registrou no header Authorization da
requisição de webhook. Compare com o valor que você guardou:
Node.js (Express)
Python (FastAPI)
import express from "express" ;
import crypto from "node:crypto" ;
const app = express ();
function timingSafeEqualUtf8 ( a , b ) {
if ( typeof a !== "string" || typeof b !== "string" || a . length !== b . length ) {
return false ;
}
const ab = Buffer . from ( a , "utf8" );
const bb = Buffer . from ( b , "utf8" );
return ab . length === bb . length && crypto . timingSafeEqual ( ab , bb );
}
app . post (
"/webhooks/stric" ,
express . json (),
( req , res ) => {
const incoming = req . headers . authorization ;
const expected = `Bearer ${ process . env . STRIC_WEBHOOK_TOKEN } ` ;
if ( ! timingSafeEqualUtf8 ( incoming ?? "" , expected )) {
return res . status ( 401 ). end ();
}
// `req.body.event` === header `webhook-event-type`; o payload útil está em `req.body.data`
saveEvent ( req . body );
// Responda 2xx rápido — processamento real fica em fila
res . status ( 200 ). end ();
enqueue ( req . body );
}
);
Header Descrição AuthorizationQuando você definiu authToken ao registrar: Bearer <authToken> (valide igual ao exemplo acima). Content-Typeapplication/jsonwebhook-event-typeMesmo valor que o campo event no JSON do body (ex.: pix.in.received). Útil pra rotear antes de parsear o corpo.
3. Boas práticas no handler
Responda 2xx em menos de 5 segundos
Endpoints lentos podem ser considerados como falha. Persista o evento e
responda imediatamente — processe assincronamente em fila.
Idempotência no seu lado também
A Stric pode reentregar um evento. Deduplique pelo eventId (quando existir
no JSON) ou por um par estável dentro de data — ex.: endToEndId,
{ chargeId, endToEndId }, { payoutId, refundEndToEndId } em refunds com
múltiplos parciais.
Comparação de tokens à prova de timing attack
Use crypto.timingSafeEqual (Node) ou hmac.compare_digest (Python).
Nunca ==.
Guarde o JSON completo. Se sua lógica mudar, você pode reprocessar
eventos passados.
Tipos de evento (events)
Ao registrar o webhook, cada string deve ser exatamente um dos nomes abaixo.
Pix recebido (cash-in direto na conta)
Evento Significado pix.in.receivedPix creditado na conta (sem fluxo de cobrança pré-cadastrado).
Transferência Pix enviada
Os nomes dos eventos permanecem pix.payout.* na API; aqui chamamos o fluxo de transferência Pix (envio para terceiros).
Evento Significado pix.payout.succeededLiquidação concluída com sucesso. pix.payout.failedFalha definitiva (reason com código quando aplicável). pix.payout.cancelledPagamento cancelado (reason pode ser null). pix.payout.refundedEstorno relacionado à transferência (parcial ou total). Veja exemplos abaixo.
Cobrança (charge / QR Cobrança)
Evento Significado pix.charge.paidCobrança paga. pix.charge.expiredCobrança expirou sem pagamento. pix.charge.refundedEstorno relacionado à cobrança (parcial ou total). Veja exemplos abaixo.
Corpo da entrega (JSON)
Cada POST traz um objeto com:
event : string igual ao tipo assinado (e ao header webhook-event-type ).
data : objeto com os campos específicos daquele evento (Pix recebido, transferência, cobrança, etc.).
Os exemplos abaixo mostram o corpo completo enviado pela Stric.
event / webhook-event-type : pix.in.received{
"event" : "pix.in.received" ,
"data" : {
"payerName" : "Pedro Ferreira de Souza" ,
"endToEndId" : "E18236120202605041049s078600ecc6" ,
"movementId" : "cmor2uwya002inxqkoyow2167" ,
"receivedAt" : "2026-05-04T10:49:34.558Z" ,
"amountCents" : "120" ,
"payerBranch" : "1" ,
"paymentDate" : "2026-05-04 10:49:33.995783+00:00" ,
"payerAccount" : "49773517" ,
"pixAccountId" : "cmokguchz0004duqk7pj3f2e4" ,
"coreAccountId" : "cmokguchu0003duqkodsv49bx" ,
"payerDocument" : "17227372361" ,
"payerInstitution" : "18236120"
}
}
event / webhook-event-type : pix.payout.succeeded{
"event" : "pix.payout.succeeded" ,
"data" : {
"payoutId" : "cmoul96ld003w5vqkgrmqky77" ,
"endToEndId" : "E373198592026050621476T92Q4O7ZJF" ,
"movementId" : "cmoul96kx003t5vqkzkaqzskl" ,
"occurredAt" : "2026-05-06T21:48:08.809Z" ,
"amountCents" : "1"
}
}
event / webhook-event-type : pix.payout.failed{
"event" : "pix.payout.failed" ,
"data" : {
"payoutId" : "cmor35abc002vnxqkp1q9wzj4" ,
"movementId" : "cmor35abc0030nxqk5kf2x8d1" ,
"endToEndId" : "E18236120202605041102k08aabb1234" ,
"amountCents" : "5000" ,
"occurredAt" : "2026-05-04T11:02:14.227Z" ,
"reason" : "recipient_not_found"
}
}
event / webhook-event-type : pix.payout.cancelled{
"event" : "pix.payout.cancelled" ,
"data" : {
"payoutId" : "cmor36def0031nxqkqr2xahg9" ,
"movementId" : "cmor36def0035nxqk7a8tc4e2" ,
"endToEndId" : "E18236120202605041105m08c12cd567" ,
"amountCents" : "2500" ,
"occurredAt" : "2026-05-04T11:05:42.918Z" ,
"reason" : null
}
}
event / webhook-event-type : pix.payout.refundedDentro de data , amountCents é o valor original da transferência e refundAmountCents o valor deste estorno. Para refund parcial, compare refundAmountCents < amountCents. Estornos parciais geram vários webhooks separados — deduplique por par estável (ex.: refundEndToEndId); se existir eventId no envelope, use-o também (referência conceitual: transferencia-refunded-{payoutId}-{refundEndToEndId}). Refund parcial : {
"event" : "pix.payout.refunded" ,
"data" : {
"payoutId" : "cmor37ghi0040nxqk83df9k1m" ,
"movementId" : "cmor37ghi0044nxqkpw5lzny6" ,
"endToEndId" : "E31841474202605041107T08DEF12345" ,
"amountCents" : "250" ,
"occurredAt" : "2026-05-04T11:07:23.488Z" ,
"refundEndToEndId" : "D00416968202605041108UpxYi1K5gFV" ,
"refundAmountCents" : "100"
}
}
Refund total (valor do estorno = valor original): {
"event" : "pix.payout.refunded" ,
"data" : {
"payoutId" : "cmor38jkl0050nxqkk3ce8fr1" ,
"movementId" : "cmor38jkl0054nxqkx2td8j6n" ,
"endToEndId" : "E18236120202605041109K08FGH67890" ,
"amountCents" : "5000" ,
"occurredAt" : "2026-05-04T11:09:05.612Z" ,
"refundEndToEndId" : "D31841474202605041110ZqRm2L6hHWb" ,
"refundAmountCents" : "5000"
}
}
event / webhook-event-type : pix.charge.paid{
"event" : "pix.charge.paid" ,
"data" : {
"tag" : null ,
"txid" : "a7b2507c757f4881859d6f72efc4309d" ,
"chargeId" : "cmor30g1s002ungqkovmpy3b7" ,
"endToEndId" : "E18236120202605041054s07e2c3edef" ,
"movementId" : "cmor30wh50036nxqkuzw3fqpv" ,
"occurredAt" : "2026-05-04T10:54:13.879Z" ,
"amountCents" : "300" ,
"refundEndToEndId" : null
}
}
event / webhook-event-type : pix.charge.refundedMesma convenção que em pix.payout.refunded , nos campos dentro de data : amountCents = valor original da cobrança liquidada; refundAmountCents = valor deste refund. Exemplo para R100 , 00 ∗ ∗ p a g o s ( ∗ ∗ ‘ " 10000 " ‘ ∗ ∗ c e n t a v o s ) c o m r e f u n d d e ∗ ∗ R 100,00** pagos (**`"10000"`** centavos) com refund de **R 100 , 00 ∗ ∗ p a g os ( ∗ ∗ ‘"10000"‘ ∗ ∗ ce n t a v os ) co m re f u n dd e ∗ ∗ R 25,00 : {
"event" : "pix.charge.refunded" ,
"data" : {
"tag" : "pedido-9821" ,
"txid" : "8f3b22c1d4e74d6a9e1f55a62efa7b3c" ,
"chargeId" : "cmor39mno0060nxqkzz1kr7p3" ,
"movementId" : "cmor39mno0064nxqkqo2lc8h7" ,
"endToEndId" : "E18236120202605041112s08efgh4321" ,
"refundEndToEndId" : "D31841474202605041113XyAb3M8jJYc" ,
"amountCents" : "10000" ,
"occurredAt" : "2026-05-04T11:13:18.954Z" ,
"refundAmountCents" : "2500"
}
}
Refund total : {
"event" : "pix.charge.refunded" ,
"data" : {
"tag" : null ,
"txid" : "1c4a7e9f2b8d4e3a9c5f1b6d2e7a3c8f" ,
"chargeId" : "cmor3aqrs0070nxqkb1d2ef34" ,
"movementId" : "cmor3aqrs0074nxqkv4w2l5m8" ,
"endToEndId" : "E18236120202605041115z08ijkl9876" ,
"refundEndToEndId" : "D00416968202605041116KkLm4N9pPMd" ,
"amountCents" : "10000" ,
"occurredAt" : "2026-05-04T11:16:42.103Z" ,
"refundAmountCents" : "10000"
}
}
Múltiplos refunds parciais disparam um webhook cada; quando o valor acumulado cobre amountCents em data , o status da cobrança evolui para REFUNDED . tag em data repete o tag opcional enviado no POST /v1/charges .
event / webhook-event-type : pix.charge.expired{
"event" : "pix.charge.expired" ,
"data" : {
"tag" : null ,
"txid" : "7c2bb88ca5b842369562de14254c4f2c" ,
"chargeId" : "cmor2yuob002qnxqksoxtm6sl" ,
"occurredAt" : "2026-05-04T11:08:00.669Z" ,
"amountCents" : "1000"
}
}
Gerenciar endpoints
# Listar
curl " $STRIC_BASE_URL /v1/pix-accounts/ $ACCOUNT_ID /webhooks" \
-H "Authorization: Bearer $STRIC_API_KEY "
# Consultar um
curl " $STRIC_BASE_URL /v1/pix-accounts/ $ACCOUNT_ID /webhooks/ $WEBHOOK_ID " \
-H "Authorization: Bearer $STRIC_API_KEY "
# Remover
curl -X DELETE " $STRIC_BASE_URL /v1/pix-accounts/ $ACCOUNT_ID /webhooks/ $WEBHOOK_ID " \
-H "Authorization: Bearer $STRIC_API_KEY "
Testando localmente
Use ngrok ou Cloudflare Tunnel
pra expor seu servidor local pra Stric durante o desenvolvimento:
ngrok http < porta-do-seu-servido r >
# O ngrok imprime uma URL HTTPS pública na saída do terminal — use esse host.
Registre essa URL HTTPS pública nos webhooks apenas para testes; em produção
use sempre um endpoint estável sob seu próprio domínio.