Files
2026-07-25 13:38:18 +05:30

177 lines
6.1 KiB
YAML

openapi: 3.1.0
info:
title: WRNexus Managed CAPTCHA API
version: 0.3.6
servers:
- url: https://captcha.example.com
paths:
/v1/challenges:
post:
summary: Create a public challenge
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [siteKey, action]
properties:
siteKey: { type: string }
action: { type: string }
type: { type: string, enum: [number, alpha, alphanumeric, calculation, image, honeypot, timing, not-robot] }
presentation: { type: string, enum: [visual, audio, invisible] }
difficulty: { type: string, enum: [easy, normal, hard] }
disturbance: { type: integer, minimum: 25, maximum: 75, default: 50 }
imageStyle:
type: string
default: random
enum: [random, classic, collision, snow, corrosion, spiderweb, cross-shadow, split, split2, cut, darts, distortion, stitch, striped, wave, grid-noise, scribble, pixel, broken-lines]
allowedStyles:
oneOf:
- { type: string, description: Comma-separated renderer names }
- type: array
items: { type: string }
excludedStyles:
oneOf:
- { type: string, description: Comma-separated renderer names }
- type: array
items: { type: string }
randomizeStyle: { type: boolean, default: false }
locale: { type: string }
responses:
'201':
description: Challenge created
content:
application/json:
schema: { $ref: '#/components/schemas/Challenge' }
'403': { description: Invalid site key or hostname }
'429': { description: Project quota exceeded }
/v1/solve:
post:
summary: Solve a public challenge and receive a temporary response token
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [challengeId, action]
properties:
challengeId: { type: string }
action: { type: string }
answer: { oneOf: [{ type: string }, { type: number }] }
selections: { type: array, items: { type: string } }
honeypot: { type: string }
timingToken: { type: string }
responses:
'200':
description: Challenge solved
content:
application/json:
schema: { $ref: '#/components/schemas/Verification' }
'400': { description: Challenge rejected }
/v1/verify:
post:
summary: Verify and consume a response token on the application server
security: [{ projectSecret: [] }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [responseToken, action]
properties:
responseToken: { type: string }
action: { type: string }
hostname: { type: string }
sessionId: { type: string }
responses:
'200':
description: Token accepted and consumed
content:
application/json:
schema: { $ref: '#/components/schemas/Verification' }
'400': { description: Token invalid, expired, mismatched, or already used }
'401': { description: Invalid project secret }
/v1/projects:
get:
summary: List projects
security: [{ adminToken: [] }]
responses:
'200': { description: Project list without secrets }
post:
summary: Create a project and return keys once
security: [{ adminToken: [] }]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name: { type: string }
allowedHostnames: { type: array, items: { type: string } }
monthlyQuota: { type: integer, minimum: 1 }
responses:
'201': { description: Project and initial keys }
/v1/projects/{id}/rotate-secret:
post:
summary: Rotate a project verification secret
security: [{ adminToken: [] }]
parameters:
- in: path
name: id
required: true
schema: { type: string }
responses:
'200': { description: New secret returned once }
'404': { description: Project not found }
components:
securitySchemes:
projectSecret:
type: http
scheme: bearer
adminToken:
type: http
scheme: bearer
schemas:
Challenge:
type: object
required: [id, provider, type, presentation, action, prompt, createdAt, expiresAt, responseField]
properties:
id: { type: string }
provider: { type: string }
type: { type: string }
presentation: { type: string }
action: { type: string }
prompt: { type: string }
image: { type: string }
audioUrl: { type: string }
items: { type: array, items: { type: object } }
createdAt: { type: integer }
expiresAt: { type: integer }
responseField: { type: string }
metadata:
type: object
properties:
difficulty: { type: string }
disturbance: { type: integer }
imageStyle: { type: string }
requestedImageStyle: { type: string }
imageStylePool:
type: array
items: { type: string }
Verification:
type: object
required: [success, provider, action]
properties:
success: { type: boolean }
provider: { type: string }
action: { type: string }
code: { type: string }
message: { type: string }
responseToken: { type: string }
expiresAt: { type: integer }