Authentication & API Keys
Everything about API keys — publishable (pk_) vs secret (sk_), where to find them, which to use where, header formats, security rules, and key rotation.
VerifyStack uses two types of API keys — publishable keys (pk_) and secret keys (sk_). Understanding the difference is critical for security. Using the wrong key in the wrong place can either break your integration or expose sensitive credentials.
Two Key Types Explained
Every VerifyStack account comes with both a publishable key and a secret key. They look similar but grant very different levels of access:
| Property | Publishable Key (pk_) | Secret Key (sk_) |
|---|---|---|
| Prefix | pk_live_xxxxxxxxx | sk_live_xxxxxxxxx |
| Safe to expose publicly? | ✅ Yes — designed for client-side use | ❌ Never — server-side only |
| Where to use | Browser SDK, iOS SDK, Android SDK, any client-side code | Your backend server, cURL, server-side API calls only |
| Can call /decide | ✅ Yes | ✅ Yes |
| Can call /analyze | ✅ Yes | ✅ Yes |
| Can call /feedback | ❌ No (returns 403) | ✅ Yes |
| Can call /cases | ❌ No (returns 403) | ✅ Yes |
| Can call /webhooks | ❌ No (returns 403) | ✅ Yes |
| Can call /policies | ❌ No (returns 403) | ✅ Yes |
| Permissions | decisions:write only | All permissions (decisions, evidence, policies, webhooks, settings, audit logs) |
Where to Find Your API Keys
- Log in to your VerifyStack Dashboard at verifystack.io/dashboard
- Navigate to Settings → API Keys
- You will see both your Publishable Key (pk_live_xxxxxxxxx) and Secret Key (sk_live_xxxxxxxxx)
- Click the copy icon next to each key to copy it
- Store your secret key in a secure password manager or environment variable — it is only shown once
Which Key to Use Where
Client-Side Code → Always pk_ (Publishable Key)
Any code that runs on the user's device must use a publishable key. This includes all SDKs:
const vs = new VerifyStack({
apiKey: 'pk_live_xxxxxxxxx', // ← Publishable key
endpoint: 'https://verifystack.io'
});let vs = VerifyStack(
apiKey: "pk_live_xxxxxxxxx", // ← Publishable key
endpoint: "https://verifystack.io"
)val vs = VerifyStack.Builder(context)
.apiKey("pk_live_xxxxxxxxx") // ← Publishable key
.endpoint("https://verifystack.io")
.build()Server-Side Code → Always sk_ (Secret Key)
API calls from your backend server (feedback, cases, webhooks, policies) require a secret key:
curl -X POST https://verifystack.io/api/v1/feedback \
-H "X-API-Key: sk_live_xxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"decisionId": "dec_abc123", "actualOutcome": "fraud"}'// Store your secret key in an environment variable — NEVER hardcode it
const response = await fetch('https://verifystack.io/api/v1/feedback', {
method: 'POST',
headers: {
'X-API-Key': process.env.VERIFYSTACK_SECRET_KEY, // sk_live_xxxxxxxxx
'Content-Type': 'application/json'
},
body: JSON.stringify({
decisionId: 'dec_abc123',
actualOutcome: 'fraud'
})
});Security Rules
| ❌ Never Do This | ✅ Do This Instead |
|---|---|
| Hardcode sk_ in your iOS/Android app source code | Use pk_ in mobile SDKs. Call /feedback from your backend. |
| Put sk_ in JavaScript that runs in the browser | Use pk_ in the Browser SDK. Call /feedback from your server. |
| Commit sk_ to a public GitHub repository | Use environment variables (VERIFYSTACK_SECRET_KEY) and .env files. |
| Share sk_ in Slack, email, or documentation | Use a password manager. Rotate the key if accidentally exposed. |
| Use sk_ in a mobile app 'for testing' | Use pk_ even for testing. Create a test environment key pair. |
| Log sk_ to console or analytics | Log only the last 4 characters for debugging: sk_live_…xxxx |
Common Mistakes & Fixes
| Mistake | What Happens | Fix |
|---|---|---|
| Using sk_ in the Browser SDK | Your secret key is visible in browser DevTools → Network tab. Anyone can steal it and call /feedback, /policies, etc. | Replace with your pk_ key immediately. Rotate the exposed sk_ key in Dashboard → Settings. |
| Using sk_ in iOS or Android SDK | Your secret key is embedded in the app binary. It can be extracted via reverse engineering. | Replace with your pk_ key. Rotate the compromised sk_ key. |
| Using pk_ on server-side /feedback call | Returns 403 Forbidden — publishable keys cannot access /feedback, /cases, /webhooks, or /policies. | Use your sk_ key for server-side API calls. |
| Using pk_ on server-side /policies call | Returns 403 Forbidden. | Use your sk_ key. Only sk_ keys have policies:read and policies:write permissions. |
| Mixing up test and live keys | Requests succeed but hit the wrong environment. Test decisions don't appear in production dashboard. | Check the key prefix: pk_live_ or pk_test_. Use the matching pair for each environment. |
Passing Your API Key in Requests
For direct API calls (not using an SDK), include your key in one of these headers:
curl -X POST https://verifystack.io/api/v1/decide \
-H "X-API-Key: sk_live_xxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"userId": "user_123", "action": "login"}'curl -X POST https://verifystack.io/api/v1/decide \
-H "Authorization: Bearer sk_live_xxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"userId": "user_123", "action": "login"}'Typical Architecture
Here's how pk_ and sk_ keys fit into a typical integration:
┌─────────────────────────────────────────────────────────┐
│ CLIENT (User's Device) │
│ │
│ Browser SDK → pk_live_xxxxxxxxx → /api/v1/decide │
│ iOS SDK → pk_live_xxxxxxxxx → /api/v1/decide │
│ Android SDK → pk_live_xxxxxxxxx → /api/v1/decide │
│ │
│ ✅ pk_ is safe here — it can only call /decide │
│ and /analyze. Even if extracted, an attacker │
│ cannot access feedback, cases, or policies. │
└────────────────────────────┬────────────────────────────┘
│ decision response
▼
┌─────────────────────────────────────────────────────────┐
│ YOUR BACKEND SERVER │
│ │
│ POST /feedback → sk_live_xxxxxxxxx → confirmed outcomes │
│ GET /cases → sk_live_xxxxxxxxx → fraud case mgmt │
│ POST /policies → sk_live_xxxxxxxxx → rule configuration │
│ POST /webhooks → sk_live_xxxxxxxxx → event subscriptions │
│ │
│ 🔒 sk_ stays on your server — never sent to client │
└─────────────────────────────────────────────────────────┘Environment Variables Setup
Always use environment variables for your secret key. Never hardcode it.
# VerifyStack API Keys
VERIFYSTACK_PUBLISHABLE_KEY=pk_live_xxxxxxxxx
VERIFYSTACK_SECRET_KEY=sk_live_xxxxxxxxx// Client-side (SDK initialization)
const publishableKey = process.env.NEXT_PUBLIC_VERIFYSTACK_KEY; // pk_live_xxxxxxxxx
// Server-side (feedback, cases, policies)
const secretKey = process.env.VERIFYSTACK_SECRET_KEY; // sk_live_xxxxxxxxxKey Rotation
Rotate keys immediately if a secret key is compromised. You can also rotate proactively on a schedule.
- Go to Dashboard → Settings → API Keys
- Click "Rotate" next to the key you want to replace
- Copy the new key and update your application
- The old key remains active for 24 hours to allow a graceful transition
- After 24 hours, the old key is permanently deactivated
Error Responses
| Status | Code | Meaning | What to Check |
|---|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key | Verify the key is correct and not expired. Check for extra whitespace. |
| 403 | FORBIDDEN | Key lacks permission for this endpoint | You're using a pk_ key on a secret-only endpoint (/feedback, /cases, /policies). Use your sk_ key instead. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests | You're exceeding your plan's rate limit. The Retry-After header tells you when to retry. |
Quick Reference Cheat Sheet
| I want to... | Use this key | Where |
|---|---|---|
| Initialize the Browser SDK | pk_live_xxxxxxxxx (publishable) | Frontend JavaScript |
| Initialize the iOS SDK | pk_live_xxxxxxxxx (publishable) | Swift app code |
| Initialize the Android SDK | pk_live_xxxxxxxxx (publishable) | Kotlin app code |
| Call /decide from cURL | Either pk_ or sk_ | Terminal / server |
| Call /analyze from cURL | Either pk_ or sk_ | Terminal / server |
| Submit feedback (/feedback) | sk_live_xxxxxxxxx (secret) | Your backend server only |
| Manage cases (/cases) | sk_live_xxxxxxxxx (secret) | Your backend server only |
| Configure policies (/policies) | sk_live_xxxxxxxxx (secret) | Your backend server only |
| Set up webhooks (/webhooks) | sk_live_xxxxxxxxx (secret) | Your backend server only |
| Check system status (/status) | sk_live_xxxxxxxxx (secret) | Your backend server only |