Developers
A small REST API and outbound webhooks to read your games, pull enriched leads, and stream lead events into your own tools. Available on the Scale plan.
Authentication
Create an API key in your dashboard and send it as a bearer token on every request. Keys are shown once at creation — store them securely.
curl https://secret.pics/api/v1/games \ -H "Authorization: Bearer sp_your_key_here"
A missing or invalid key returns 401. A valid key on a plan without API access returns 403 upgrade_required.
Endpoints
All responses are JSON and scoped to the games you own. Base URL: https://secret.pics/api/v1
GET /games
List your games with headline metrics.
{
"data": [
{
"slug": "summer-quiz",
"title": "Summer quiz",
"status": "live",
"contentRating": "sfw",
"inviteOnly": false,
"url": "https://secret.pics/g/summer-quiz",
"createdAt": "2026-08-01T10:00:00.000Z",
"stats": { "views": 1240, "players": 380, "completions": 210 }
}
]
}GET /games/:slug
A single game with its masks and full stats (including the per-mask funnel).
GET /games/:slug/leads
Every captured lead with the answers they gave to each quiz mask — the JSON twin of the CSV export. Answers are keyed by mask reveal order.
{
"count": 2,
"data": [
{
"email": "alex@example.com",
"verified": true,
"capturedAt": "2026-08-02T09:12:00.000Z",
"verifiedAt": "2026-08-02T09:15:00.000Z",
"answers": { "1": "Marketing", "2": "50-200" }
}
]
}Webhooks
Register an HTTPS endpoint in your dashboard and we’ll POST a signed JSON payload when a lead event fires. Two event types today:
- lead.captured — a player submitted their email at a gate.
- lead.verified — the player confirmed via the double opt-in link.
{
"id": "evt_9f3c...",
"type": "lead.verified",
"created": 1756809600,
"data": {
"game": { "slug": "summer-quiz", "title": "Summer quiz" },
"email": "alex@example.com",
"verified": true
}
}Verifying signatures
Each delivery carries an X-SecretPics-Signature header of the form t=<unix>,v1=<hex>. Recompute it as an HMAC-SHA256 of `${t}.${rawBody}` using your endpoint’s signing secret and compare:
const [t, v1] = sigHeader.split(',').map(p => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret)
.update(`${t}.${rawBody}`).digest('hex');
// constant-time compare expected === v1Deliveries time out after 8 seconds and are not retried in this version — respond quickly and reconcile with GET /games/:slug/leads if you miss one.