Proteside Docs

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.html

O 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.

ErroCausa
409 conflictO domínio já é protegido por outra loja, de qualquer organização
403 plan_limitO plano atingiu o limite de lojas (a resposta traz limit e plan)
400 invalid_requestDomí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"
}
  • target aceita domain, src (URL do script) ou hash; type aceita block ou allow.
  • 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.pdf

Opçã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 302 com um link assinado válido por 1 hora; o -L do curl segue o link. Em outras linguagens, siga o redirecionamento sem reenviar o header Authorization.
  • O período máximo é de 366 dias, e period_end não pode estar no futuro.
  • format=json devolve os dados do relatório; format=csv&csv_table=alerts (ou scripts, headers) devolve uma tabela em CSV.
  • locale aceita pt-BR ou en.
  • Para receber o relatório automaticamente toda semana ou todo mês, agende com POST /reports/pci/schedule (escopo reports:generate) e assine o evento report.ready num 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"

Nesta página