Preview & install your badge
Try the styles below to see exactly what your visitors will see. When you’re ready, your personalized code is waiting in your dashboard - no coding skills needed.
Preview your style
Pick a style, size, and color below to see your badge live. This is just a preview - no sign-in needed.
Copy your code from the dashboard
Log in and PCrisk gives you a ready-made snippet with your chosen style baked in - it works on every domain in your account.
Paste, save & publish
Add it to your site. Once your domain passes its daily scan, the badge appears automatically.
No cookies, no tracking, ~7 KB. The badge loads after your page (so it never slows it down), stores nothing about your visitors, and sets no cookies.
The badge only appears when your latest scan is clean. If you just signed up, allow a little time for your first scan to finish. A blank space simply means “not confirmed clean yet” - not that the code is broken.
Platform guides
Two ways to add the badge: install our plugin, or paste the snippet from your dashboard. Pick your platform below - either way it takes a few minutes.
WordPress
Easiest - install the free plugin, no code:
- In your WordPress dashboard go to Plugins → Add New.
- Search for PCrisk Trust Badge.
- Click Install Now, then Activate.
- Open Trust Badge in the admin menu, connect your site, then pick a style and where the badge appears. The live preview shows exactly what visitors will see.
Running WooCommerce? The plugin detects it automatically and adds store placements - shop page, product pages, cart and checkout - on top of your site-wide spot. Nothing extra to install.
Can’t install plugins? Paste the snippet from your dashboard into a footer code area instead - for example the free “WPCode” plugin, or Appearance → Theme File Editor (back up first).
ShopifyComing soon
Our Shopify app is on the way. It’s in development now - one-click install, no theme code, with placements for product, cart and checkout pages. Until it lands, you can add the badge with the snippet below.
In the meantime:
- In Shopify admin go to Online Store → Themes.
- On your current theme click ⋯ -> Edit code.
- Open layout/theme.liquid and paste the snippet from your dashboard just before the closing </body> tag.
- Save. The badge appears on every page of your store.
Wix
- Go to Settings → Custom Code in your site dashboard.
- Click + Add Custom Code, paste the snippet, and set it to load on All pages, placed in the Body - end.
- Apply & publish.
Squarespace
- Go to Settings → Advanced → Code Injection.
- Paste the snippet into the Footer box.
- Save. (Code Injection requires a Business plan or higher.)
Webflow, Framer & custom HTML
Webflow: Project Settings → Custom Code → paste in the Footer Code box → publish.
Framer: Site Settings → General → Custom Code → End of <body> tag.
Hand-coded sites: paste the snippet just before </body> in your template or layout file.
Customize your badge
Your choices live as data-* attributes on the snippet’s placement <div> - the dashboard designer sets them all when it generates your code. To restyle later, pick a new look there and paste the updated snippet over the old one. Here’s what each attribute controls.
| Attribute | Values | What it does |
|---|---|---|
data-style | shield, ribbon, minimal, bubble | The badge’s overall shape and layout. |
data-size | small, medium, large | Overall scale of the badge. |
data-theme | light, dark, auto | Match your site’s background. auto follows your site’s own light or dark mode. |
data-position | bottom-right, bottom-left, top-right, top-left | Pins the badge to that screen corner - works with any style. Omit it and the badge renders inline into its <div>. |
Want it in an exact spot, like mid-checkout? The <div> in your snippet IS the badge’s spot - drop it wherever the badge should appear (it even reserves the badge’s footprint so your layout never shifts). Want badges in several places? Paste more divs; the script tag only needs to appear once. Floating badges - any style with data-position - ignore where their div sits: they pin to the corner you pick.
Badge not showing up?
Work through these in order - the first two solve the large majority of cases.
Give it a moment - and check your latest scan is clean
The badge only displays when your most recent daily scan is clean. This is intentional - it keeps the badge’s meaning intact, so it’s never shown on a compromised site.
If you just installed it:
- Check your PCrisk dashboard - does it say “Badge live” next to your domain?
- If a scan is still pending, wait for it to finish, then refresh your page.
- If your site was flagged, resolve the issue; the badge returns automatically after the next clean scan.
Your site has a Content Security Policy (advanced)
The good news: 9 out of 10 sites can skip this. A Content Security Policy (CSP) is an advanced security setting you have to deliberately turn on. If you’ve never heard of it, you almost certainly don’t have one - and this isn’t your problem.
Think of a CSP as a guest list for your website - it only lets in services you’ve approved. If you have one, just add PCrisk to the list with these entries:
script-src https://scanner.pcrisk.com
connect-src https://scanner.pcrisk.com
style-src 'unsafe-inline'
The style-src line is only needed if your policy restricts styles (e.g. it sets style-src or only default-src): the badge applies its design through an inline <style> element, so without it the badge loads but stays invisible.
Quick paths:
• WordPress security plugin (Wordfence, Really Simple Security, etc.): open its settings and add these entries to the Content Security Policy section.
• Cloudflare: your site → Security → Settings → your CSP rule → add these entries.
• Wix / Squarespace / standard Shopify: nothing to do - these don’t use custom policies, so the badge just works.
Not sure, or it’s still not appearing? Copy this message and send it to whoever manages your website (your developer or hosting support) - they’ll know exactly what to do:
script-src https://scanner.pcrisk.com and connect-src https://scanner.pcrisk.com. If our policy blocks inline styles, also allow 'unsafe-inline' in style-src. Thanks!An ad blocker or privacy extension is hiding it
Some aggressive ad blockers or privacy extensions (or browsers like Brave with strict shields) can block third-party scripts. To check, open your site in a private/incognito window with extensions disabled, or temporarily pause your blocker.
This affects only the small share of visitors running such tools - most people will see the badge normally. The badge itself sets no cookies and tracks nothing, which keeps this rare.
Check the badge is on a domain you added
The badge checks the exact domain it’s loaded on. If you added example.com but the snippet is running on a different subdomain (like shop.example.com), a staging URL, or localhost, it won’t find a match.
Make sure the page showing the badge is on a domain you added in your dashboard. Need it on several subdomains? Add each one to your account.
Inline badge: did you add the container?
Inline badges (any style without data-position) render into the placement <div class="pcrisk-badge"> from your snippet - make sure that div made it onto the page along with the script tag (some page builders strip divs but keep scripts). No div and no data-* attributes on the script tag means the engine has nowhere to render.
How your visitors verify it
A badge anyone can fake builds no trust. Ours is independently verifiable - that’s what makes it worth displaying.
Click opens a live report
Every badge is clickable. Visitors land on your public report at scanner.pcrisk.com - hosted by us, not by you - showing the latest scan and trust score.
Independent by design
Because the report lives on a domain visitors don’t control, the result is trustworthy - the same way a browser padlock is independent of the site behind it.
Tip: the report link is shareable on its own. Drop it in invoices, partner pages, or support emails whenever you need to prove your site’s current safety status.
What happens if a threat is detected
You’ll never display a “verified” seal on a compromised site.
We alert you fast
The moment a scan flags an issue, you get a detailed email - severity, what was found, where, and how to fix it.
The badge auto-hides
Your badge is removed automatically until the problem is resolved - and returns the moment a follow-up scan confirms your site is clean again.
Slack & webhook alerts
Get the same alerts we email - threat found, all clear, scan couldn’t complete - pushed to Slack or to your own endpoint, at the same moments. Configure both in your dashboard under Settings → Alert integrations.
Slack
In Slack, create an Incoming Webhook for the channel you want (Slack: Apps → Incoming Webhooks → Add to a channel), paste the hooks.slack.com URL into your dashboard, and press Send Slack test. That’s the whole setup - no code needed.
Webhooks
We POST JSON to your HTTPS endpoint. Four event types:
• alert.opened - a scan flagged the site (score below 70); the badge is paused.
• alert.resolved - a later scan came back clean; the badge is restored.
• scan.failed - the daily scan couldn’t complete (site unreachable); sent once per outage.
• test - the dashboard’s Send webhook test button.
{
"event": "alert.opened",
"domain": "example.com",
"trust_score": 55,
"threat_type": "suspicious",
"severity": "medium",
"occurred_at": "2026-07-07T08:28:36.874Z"
}
Each request carries three headers: Content-Type: application/json, X-PCrisk-Event (the event type), and X-PCrisk-Signature - sha256= followed by the hex SHA-256 HMAC of the raw request body, keyed with the signing secret shown in your dashboard. Verify it before trusting a delivery.
Delivery semantics: we wait up to 5 seconds for a response and count any 2xx as accepted; a failed delivery is retried once, ~15 seconds later, then dropped. Treat webhooks as notifications, not as a ledger - your dashboard is the state of record.
Verify against the raw body. The signature covers the exact bytes we send. If your framework parses JSON before you can read the raw body (e.g. a global express.json()), re-serializing the parsed object will not reproduce the same bytes - capture the raw body, as each example below does.
const crypto = require('crypto');
const express = require('express');
const app = express();
// express.raw keeps the body as the exact bytes we signed
app.post('/hooks/pcrisk', express.raw({ type: 'application/json' }), (req, res) => {
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.PCRISK_WEBHOOK_SECRET)
.update(req.body) // Buffer
.digest('hex');
const given = req.get('X-PCrisk-Signature') || '';
const ok = given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body);
console.log(event.event, event.domain, event.trust_score);
res.status(200).end(); // any 2xx marks the delivery as accepted
});
app.listen(3000);
<?php
$secret = getenv('PCRISK_WEBHOOK_SECRET');
$raw = file_get_contents('php://input'); // raw body, before any parsing
$given = $_SERVER['HTTP_X_PCRISK_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $given)) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true);
// $event['event'], $event['domain'], $event['trust_score'], ...
http_response_code(200); // any 2xx marks the delivery as accepted
import hashlib, hmac, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["PCRISK_WEBHOOK_SECRET"].encode()
@app.post("/hooks/pcrisk")
def pcrisk_hook():
raw = request.get_data() # raw body, before parsing
expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
given = request.headers.get("X-PCrisk-Signature", "")
if not hmac.compare_digest(expected, given):
abort(401)
event = request.get_json()
# event["event"], event["domain"], event["trust_score"], ...
return "", 200 # any 2xx marks the delivery as accepted
# run with: flask --app hook run --port 3000
Rotating your endpoint? Save the new URL in the dashboard and send a test - the signing secret stays the same for your account. Alerts are per-incident (one alert.opened per below-70 episode, not one per scan), so your channel won’t be flooded by a site that stays flagged.
REST API
Pull your domains, scores, scan history and alerts into your own dashboards, reports and monitoring. Read access on Pro; the Business plan adds domain management. The API only ever exposes your own monitored domains.
Authentication
Create a key in your dashboard under Settings → API keys (it’s shown once - we store only a hash) and send it as a Bearer token. Keys inherit their permissions from your current plan, and revoking one in the dashboard disables it immediately.
curl https://scanner.pcrisk.com/api/v1/domains -H "Authorization: Bearer pcrisk_live_YOUR_KEY"
{
"success": true,
"data": [
{
"domain": "example.com",
"status": "verified",
"trust_score": 92,
"threat_type": "clean",
"last_scanned": "2026-07-07T06:12:04.000Z",
"open_alerts": 0,
"badge_visible": true,
"badge_live": true
}
]
}
Endpoints
GET /api/v1 - discovery: your plan and tier.
GET /api/v1/domains - all your domains with current score, threat type and badge state.
GET /api/v1/domains/{domain} - one domain.
GET /api/v1/domains/{domain}/history - scan-score trail (history depth follows your plan).
GET /api/v1/alerts - latest threat alerts across all domains, open and resolved.
POST /api/v1/domains (Business) - add a domain; monitoring starts immediately. Body: {"domain": "example.com"}.
DELETE /api/v1/domains/{domain} (Business) - remove a domain; scans stop, the badge stops rendering.
Try it
Paste an API key from your dashboard (Settings → API keys) and call any read endpoint right here. The key goes only into this one request, to this site - nothing is stored, and the request counts against your rate limit like any other.
Conventions: every response is JSON with success and either data or error.code + error.message (401 bad key, 403 plan/tier, 404 not yours, 429 rate limited). Rate limit is 60 requests/minute per key - watch the X-RateLimit-Remaining and Retry-After headers.
Push + pull: pair the API with webhook alerts - webhooks tell you the moment something changes, the API gives you the full current state whenever you ask. There is deliberately no endpoint for scanning arbitrary third-party domains.
Still need a hand?
We’re happy to look at your specific setup. Send us your website address and what you’re seeing.