API reference

Sites

Management endpoints for sites: create, read, update allowed origins, rotate site keys and delete.

Base URL https://imnotahuman.ai/api/v1. Request bodies must be JSON with Content-Type: application/json (else 415) and at most 32,000 bytes. Bodies are validated strictly: unknown fields fail with VALIDATION_FAILED. Every response carries X-Request-Id (echoing yours if you send one) and Cache-Control: no-store except JWKS. Errors are { "error": { "code", "message", "details"? } }; see Errors.

Authentication

Management endpoints require Authorization: Bearer <access token> where the token is the Supabase Auth access token from an email-and-password dashboard session. It is validated on every request, so signing out revokes access immediately. Missing header: AUTHENTICATION_REQUIRED (401); invalid or expired: INVALID_SESSION (401). Resources you do not own return NOT_FOUND. Rate limits: 600/min per network, 300/min per user.

List and create sites

GET/api/v1/sitesBearer
POST/api/v1/sitesBearer
curl
curl -X POST https://imnotahuman.ai/api/v1/sites \
  -H 'authorization: Bearer <token>' -H 'content-type: application/json' \
  -d '{"name":"Example Shop","domain":"example.com","allowed_origins":["http://localhost:5173"]}'
FieldTypeDescription
namerequiredstring 1–80Display name.
domainrequiredhostnameLowercase hostname, e.g. example.com. Scheme and path are stripped; must contain a dot unless localhost.
allowed_originsstring[] ≤ 20Extra origins (scheme://host[:port]) allowed to create challenges, in addition to https://<domain> and subdomains.
201 Created
{
  "site": {
    "id": "site_c4n7xq2mfb3ktw6y",
    "name": "Example Shop",
    "domain": "example.com",
    "public_site_key": "site_live_exampleweb2ab3cd4ef5gh6j",
    "test_site_key": "site_test_exampleweb2ab3cd4ef5gh6j",
    "status": "active",
    "allowed_origins": ["http://localhost:5173"],
    "created_at": "2026-09-20T11:40:00.000Z",
    "updated_at": "2026-09-20T11:40:00.000Z"
  }
}

Limit 25 sites per account (CONFLICT beyond). Invalid domain or origin values return VALIDATION_FAILED with details.issues. GET returns { "sites": [...] }.

Read, update, delete

GET/api/v1/sites/:site_idBearer
PATCH/api/v1/sites/:site_idBearer
DELETE/api/v1/sites/:site_idBearer
curl
# { "site", "recent": [25 events] }
curl https://imnotahuman.ai/api/v1/sites/site_c4n7xq2mfb3ktw6y -H 'authorization: Bearer <token>'

curl -X PATCH https://imnotahuman.ai/api/v1/sites/site_c4n7xq2mfb3ktw6y \
  -H 'authorization: Bearer <token>' -H 'content-type: application/json' \
  -d '{"allowed_origins":["http://localhost:5173","https://staging.example.net"],"status":"active"}'

curl -X DELETE https://imnotahuman.ai/api/v1/sites/site_c4n7xq2mfb3ktw6y -H 'authorization: Bearer <token>'
# { "deleted": true }

PATCH accepts name, status (active | disabled) and allowed_origins (replaces the whole list). A disabled site rejects challenge creation with SITE_DISABLED.

Rotate site keys

POST/api/v1/sites/:site_id/rotate-keysBearer
curl
curl -X POST https://imnotahuman.ai/api/v1/sites/site_c4n7xq2mfb3ktw6y/rotate-keys -H 'authorization: Bearer <token>'
# { "site": { ..., "public_site_key": "site_live_<new>", "test_site_key": "site_test_<new>" } }

Both keys are replaced immediately; the old keys stop creating challenges. Update embeds first.