Receitas
Fluxos completos com curl para cadastrar lojas, revisar scripts, criar regras, tratar alertas, baixar relatórios PCI DSS e configurar webhooks.
Cada receita é um fluxo de ponta a ponta que você pode copiar e adaptar. Os exemplos usam curl e jq e estas
variáveis:
export B="https://app.proteside.com/api/v1"
export PROTESIDE_TOKEN="ps_live_Ab3dEf6hIj9kLm2nOp5qRs8t_..."Os IDs e dados das respostas são fictícios. As respostas aparecem resumidas: "..." indica campos omitidos.
Criar uma loja e obter o snippet do SDK
Escopos: stores:write e stores:read.
Crie a loja
Informe o domínio, os meios de pagamento e, se quiser, as páginas de pagamento e as chaves de recebimento. O esquema
(https://), o caminho e a porta do domínio são descartados.
curl -s -X POST "$B/stores" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"domain": "https://www.minhaloja.com.br/",
"name": "Minha Loja",
"payment_methods": ["pix", "card"],
"expected_iframe_origins": ["js.stripe.com"],
"recipients": [{ "method": "pix", "key": "12.345.678/0001-90", "label": "CNPJ principal" }],
"payment_pages": [{ "url_pattern": "/checkout/*", "payment_methods": ["pix", "card"] }]
}' | tee loja.json | jq '{id, domain, sdk_key, safe_slug}'{
"id": "0c1d2e3f-1111-4222-8333-444455556666",
"domain": "minhaloja.com.br",
"sdk_key": "pk_live_3f9c0a1b2c3d4e5f60718293a4b5c6d7",
"safe_slug": "minhaloja-ps3fa9c1"
}A resposta completa (201) traz também sdk_config, recipients (a chave Pix volta mascarada, como
**.345.678/****-**; a chave em claro não é guardada), pages e snippets com o código de instalação para HTML,
Next.js, Nuxt e WordPress. A loja também ganha um canal "E-mail (padrão)" para o dono da organização.
Salve o snippet
jq -r '.snippets.html' loja.json > proteside-snippet.htmlO snippet tem duas partes, entre <!-- Proteside Start --> e <!-- Proteside End -->: um script inline (o
bootstrapper) e o shield.js carregado do CDN com data-key igual à sdk_key. Cole o bloco inteiro como primeiro
item do <head> da página de checkout, seguindo o guia de instalação.
Confira a instalação
Depois de publicar o snippet e abrir o checkout, consulte o status:
STORE=$(jq -r .id loja.json)
curl -s "$B/stores/$STORE/status" -H "Authorization: Bearer $PROTESIDE_TOKEN"{
"id": "0c1d2e3f-1111-4222-8333-444455556666",
"domain": "minhaloja.com.br",
"status": "active",
"installed": true,
"last_event_at": "2026-10-06T12:20:12.000Z",
"entitled": true,
"paused": false,
"channel": "stable",
"mode": "monitor",
"developer_mode": false
}installed fica true quando o SDK enviou algum evento nas últimas 24 horas.
| Erro | Causa |
|---|---|
409 conflict | O domínio já é protegido por outra loja, de qualquer organização |
403 plan_limit | O plano atingiu o limite de lojas (a resposta traz limit e plan) |
400 invalid_request | Domínio inválido ou campo desconhecido |
Para trocar a chave do SDK depois, use POST /stores/{id}/rotate-key: a resposta traz a nova sdk_key e os
snippets, e a chave antiga continua valendo por 24 horas (previous_key_valid_until).
Revisar scripts pendentes e autorizar com justificativa
Escopos: scripts:read e scripts:review. O PCI DSS 4.0, requisito 6.4.3, pede que cada script da página de
pagamento seja autorizado com justificativa. A API registra a justificativa, a data e o autor (api:<prefixo>).
Liste o que aguarda revisão
curl -s "$B/scripts?store_id=$STORE&status=needs_review&limit=100" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
| jq '.data[] | {id, src, type, category, first_seen_at}'{
"id": "b2c3d4e5-6666-4777-8888-999900001111",
"src": "https://www.googletagmanager.com/gtm.js",
"type": "known_third",
"category": "tag_manager",
"first_seen_at": "2026-10-05T18:02:44.000Z"
}Autorize com justificativa
A justification é obrigatória (10 a 2000 caracteres). expires_days (1 a 3650) define quando a autorização vence e
o script volta para revisão.
SCRIPT=b2c3d4e5-6666-4777-8888-999900001111
curl -s -X POST "$B/scripts/$SCRIPT/authorize" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"justification": "Container GTM do marketing; não acessa campos de pagamento. Aprovado no ticket SEC-142.",
"expires_days": 90
}'{
"id": "b2c3d4e5-6666-4777-8888-999900001111",
"src": "https://www.googletagmanager.com/gtm.js",
"status": "authorized",
"authorization_method": "api",
"authorized_by": "api:ps_live_Ab3dEf6hIj9kLm2nOp5qRs8t",
"justification": "Container GTM do marketing; não acessa campos de pagamento. Aprovado no ticket SEC-142.",
"reviewed_at": "2026-10-06T12:00:00.000Z",
"expires_at": "2027-01-04T12:00:00.000Z",
"review_due_at": "2027-01-04T12:00:00.000Z",
"...": "...",
"block_rules_deactivated": false
}Ao autorizar, as regras de bloqueio ativas que casam com o script (por domínio, URL ou hash) são desativadas.
Diferenças em relação ao spec
Scripts autorizados pela API ficam com authorization_method: "api" (o spec diz manual e não lista api entre
os valores possíveis). O campo block_rules_deactivated é um booleano que indica se alguma regra foi desativada, e
não uma contagem. Se você gerou um cliente a partir do spec, aceite esses valores.
Para bloquear em vez de autorizar, use POST /scripts/{id}/block com uma justification opcional. O Proteside cria a
regra de bloqueio sozinho: por hash, para script inline, ou pelo domínio, para script externo. A regra vem em rule
na resposta.
Criar uma regra de bloqueio por domínio
Escopo: rules:write (e scripts:read para listar regras).
curl -s -X POST "$B/rules" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"store_id": "'"$STORE"'",
"type": "block",
"target": "domain",
"value": "cdn-skimmer.example",
"label": "Skimmer conhecido"
}'{
"id": "4d5e6f70-aaaa-4bbb-8ccc-dddd00001111",
"store_id": "0c1d2e3f-1111-4222-8333-444455556666",
"type": "block",
"target": "domain",
"value": "cdn-skimmer.example",
"label": "Skimmer conhecido",
"active": true,
"created_at": "2026-10-06T12:01:00.000Z"
}targetaceitadomain,src(URL do script) ouhash;typeaceitablockouallow.- Se já existir uma regra igual (mesma loja, tipo, alvo e valor), ela é reativada em vez de duplicada.
- As regras ativas chegam ao SDK na configuração da loja.
Para desativar sem excluir:
curl -s -X PATCH "$B/rules/4d5e6f70-aaaa-4bbb-8ccc-dddd00001111" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"active": false}'Para excluir: DELETE $B/rules/{id}, que responde {"id": "...", "deleted": true}.
Listar alertas abertos e resolver
Escopos: alerts:read e alerts:write.
curl -s "$B/alerts?store_id=$STORE&status=open&severity=critical&since=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
| jq '.data[] | {id, type, severity, page, occurrences, last_seen_at}'{
"id": "e1f2a3b4-8888-4999-8000-111122223333",
"type": "PIX_TAMPERED",
"severity": "critical",
"page": "/checkout/pagamento",
"occurrences": 3,
"last_seen_at": "2026-10-06T09:52:10.000Z"
}since filtra pela última ocorrência do alerta. Depois de investigar, resolva com uma nota:
curl -s -X PATCH "$B/alerts/e1f2a3b4-8888-4999-8000-111122223333" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "resolved", "note": "Falso positivo: chave Pix nova cadastrada pelo financeiro."}'{
"id": "e1f2a3b4-8888-4999-8000-111122223333",
"type": "PIX_TAMPERED",
"severity": "critical",
"status": "resolved",
"resolved_at": "2026-10-06T12:02:00.000Z",
"resolved_by_label": "api:ps_live_Ab3dEf6hIj9kLm2nOp5qRs8t",
"note": "Falso positivo: chave Pix nova cadastrada pelo financeiro.",
"...": "..."
}Para reabrir, envie {"status": "open"}. Se já houver outro alerta aberto do mesmo tipo e origem, a resposta é
409 conflict.
Resolver um alerta não dispara o webhook alert.resolved. Se outros sistemas precisam saber, avise-os a partir da
sua própria integração.
Baixar o relatório PCI DSS em PDF
Escopo: reports:read.
Sem datas, cada chamada gera um relatório novo
GET /reports/pci sem period_start e period_end usa "os últimos 7 dias até agora". Como "agora" muda a cada
chamada, o Proteside gera um relatório novo toda vez, o que é lento e enche o histórico. Para relatórios
recorrentes, liste os que já existem; para um período específico, envie datas fixas.
Opção 1: baixar um relatório que já existe. Liste os relatórios da loja e baixe o PDF pelo id:
curl -s "$B/reports/pci/snapshots?store_id=$STORE&kind=weekly&limit=1" \
-H "Authorization: Bearer $PROTESIDE_TOKEN"{
"data": [
{
"id": "5a6b7c8d-9999-4aaa-8bbb-cccc0000dddd",
"store_id": "0c1d2e3f-1111-4222-8333-444455556666",
"kind": "weekly",
"period_start": "2026-09-28T09:00:00.000Z",
"period_end": "2026-10-05T09:00:00.000Z",
"locale": "pt-BR",
"schema_version": 2,
"sha256": "9b74c9897bac770ffc029102a200c5de3e2b4c1f0a8d7e6c5b4a392817060504",
"generated_by": "cron:weekly-reports",
"has_pdf": true,
"csv_tables": ["scripts", "headers", "alerts"],
"created_at": "2026-10-05T09:02:11.000Z"
}
],
"next_cursor": "WyIyMDI2LTEwLTA1VDA5OjAyOjExLjAwMFoiLCI1YTZiN2M4ZC05OTk5LTRhYWEtOGJiYi1jY2NjMDAwMGRkZGQiXQ"
}curl -sL "$B/reports/pci/snapshots/5a6b7c8d-9999-4aaa-8bbb-cccc0000dddd?format=pdf" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-o pci-2026-09-28.pdfOpção 2: gerar o relatório de um período fechado. Envie datas fixas, de preferência dias inteiros. Repetir a mesma chamada reaproveita o relatório já gerado:
curl -sL "$B/reports/pci?store_id=$STORE&period_start=2026-09-01T00:00:00Z&period_end=2026-10-01T00:00:00Z&format=pdf&locale=pt-BR" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-o pci-2026-09.pdf- Para PDF, a API responde
302com um link assinado válido por 1 hora; o-Ldocurlsegue o link. Em outras linguagens, siga o redirecionamento sem reenviar o headerAuthorization. - O período máximo é de 366 dias, e
period_endnão pode estar no futuro. format=jsondevolve os dados do relatório;format=csv&csv_table=alerts(ouscripts,headers) devolve uma tabela em CSV.localeaceitapt-BRouen.- Para receber o relatório automaticamente toda semana ou todo mês, agende com
POST /reports/pci/schedule(escoporeports:generate) e assine o eventoreport.readynum webhook.
Criar um webhook e disparar um teste
Escopo: webhooks:manage.
Crie o webhook
curl -s -X POST "$B/webhooks" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://ops.minhaloja.com.br/hooks/proteside",
"events": ["alert.created", "integrity.changed", "sdk.silent", "report.ready"],
"min_severity": "medium"
}' | tee webhook.json | jq '{id, has_secret, secret}'{
"id": "a9b8c7d6-5555-4666-8777-888899990000",
"has_secret": true,
"secret": "whsec_9f8e7d6c5b4a39281706f5e4d3c2b1a0"
}Guarde o secret no cofre de segredos do serviço que recebe o webhook: ele não aparece de novo. Usamos
min_severity: "medium" para receber também sdk.silent.
Dispare o teste
WH=$(jq -r .id webhook.json)
curl -s -X POST "$B/webhooks/$WH/test" -H "Authorization: Bearer $PROTESIDE_TOKEN"{ "id": "a9b8c7d6-5555-4666-8777-888899990000", "delivered": true, "error": null }A resposta é sempre 200. Se delivered vier false, o motivo está em error (por exemplo http_401: ... quando o
seu endpoint recusou a assinatura, ou timeout).
Valide a assinatura no receptor
Implemente a verificação de X-Proteside-Signature como em
Verificar a assinatura e repita o teste até delivered vir true.
Parceiro: criar uma loja numa organização filha
Escopos: orgs:manage, stores:write, stores:read e webhooks:manage. Veja
Parceiros para o modelo completo.
Crie a organização filha
curl -s -X POST "$B/organizations" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name": "Minha Loja", "owner_email": "voce@minhaloja.com.br", "plan": "growth"}' \
| tee filha.json | jq '{id, billing_mode, owner_invite}'{ "id": "11112222-3333-4444-8555-666677778888", "billing_mode": "partner", "owner_invite": "sent" }Crie a loja na filha
Passe org_id na query string. Use uma Idempotency-Key nova: a chave vale para a organização do token, não para a
filha.
CHILD=$(jq -r .id filha.json)
curl -s -X POST "$B/stores?org_id=$CHILD" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"domain": "minhaloja.com.br", "payment_methods": ["pix", "card"]}' \
| tee loja-filha.json | jq '{id, org_id, sdk_key}'{
"id": "0c1d2e3f-1111-4222-8333-444455556666",
"org_id": "11112222-3333-4444-8555-666677778888",
"sdk_key": "pk_live_3f9c0a1b2c3d4e5f60718293a4b5c6d7"
}Entregue ao cliente o snippet em .snippets.html (ou instale você mesmo).
Crie o webhook da filha
Eventos da filha só vão para webhooks criados nela:
curl -s -X POST "$B/webhooks?org_id=$CHILD" \
-H "Authorization: Bearer $PROTESIDE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://ops.parceiro.example/hooks/proteside", "min_severity": "medium"}' \
| jq '{id, secret}'Acompanhe a instalação e o consumo
STORE=$(jq -r .id loja-filha.json)
curl -s "$B/stores/$STORE/status?org_id=$CHILD" -H "Authorization: Bearer $PROTESIDE_TOKEN"
curl -s "$B/organizations/$CHILD/usage" -H "Authorization: Bearer $PROTESIDE_TOKEN"