Sites API

REST API reference for creating, listing, and managing NeoCaptcha sites.

Updated 4/12/2026

Site Management API

The Site Management API lets you programmatically create and manage NeoCaptcha sites, rotate secrets, and control site lifecycle. All endpoints require authentication.


Getting an API key

API keys are created from the API Keys page in your dashboard (Dashboard → API Keys). Each key is scoped to a single account and is shown only once at creation time — store it in a secure secret manager immediately.

API keys start with nct_ and are never sent to the browser; they are for server-to-server use only.


Authentication

Every request to the Site Management API must include your API key as a Bearer token in the Authorization header:

Authorization: Bearer nct_your_api_key_here

Requests without a valid key receive a 401 Unauthorized response. Keys that have been revoked or have expired also return 401.

Note: API keys are for site management only. Captcha verification (POST /api/v1/verify) uses your site secret, not an API key.


Endpoints

Create a site

POST /api/v1/sites

Creates a new site with a generated siteKey / siteSecret pair and registers its allowed origins.

Important: The siteSecret is only returned once in this response. Store it immediately in a secure secret manager.

Request body

FieldTypeRequiredDescription
namestringYesHuman-readable name for the site
allowedOriginsstring[]YesNon-empty list of allowed origins (e.g. ["https://app.example.com"])
accountIdstringYesUUID of the account to associate this site with

Response — 201 Created

{
  "siteKey": "sk_live_abc123",
  "siteSecret": "ss_live_xyz789",
  "name": "My App",
  "allowedOrigins": ["https://app.example.com"],
  "isActive": true,
  "createdAt": "2026-04-12T00:00:00Z",
  "message": "Site created successfully. Save the siteSecret - it cannot be retrieved again."
}

Example

const res = await fetch('/api/v1/sites', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'My App',
    allowedOrigins: ['https://app.example.com'],
    accountId: 'your-account-uuid',
  }),
});
 
const { siteKey, siteSecret } = await res.json();
// Store siteSecret securely — it will not be shown again

List sites

GET /api/v1/sites?accountId={accountId}

Returns all sites belonging to the specified account, including their allowed origins.

Query parameters

ParameterTypeRequiredDescription
accountIdstringYesUUID of the account

Response — 200 OK

{
  "sites": [
    {
      "site_key": "sk_live_abc123",
      "name": "My App",
      "is_active": true,
      "created_at": "2026-04-12T00:00:00Z",
      "updated_at": "2026-04-12T00:00:00Z",
      "allowed_origins": ["https://app.example.com"]
    }
  ]
}

Example

const res = await fetch(`/api/v1/sites?accountId=your-account-uuid`);
const { sites } = await res.json();

Deactivate a site

DELETE /api/v1/sites/{siteKey}

Soft-deletes a site by setting is_active to false. The site record is retained; active verification for this site will stop.

Path parameters

ParameterTypeDescription
siteKeystringThe siteKey of the site to deactivate

Response — 200 OK

{ "success": true }

Example

const res = await fetch(`/api/v1/sites/sk_live_abc123`, {
  method: 'DELETE',
});

Rotate site secret

POST /api/v1/sites/{siteKey}/rotate-secret

Generates a new siteSecret for the given site and invalidates the previous one. The new secret is only shown once in this response.

Important: Update your backend environment variables immediately after rotating. The old secret stops working as soon as this request succeeds.

Path parameters

ParameterTypeDescription
siteKeystringThe siteKey of the site whose secret should be rotated

Response — 200 OK

{
  "siteKey": "sk_live_abc123",
  "newSiteSecret": "ss_live_newxyz",
  "name": "My App",
  "updatedAt": "2026-04-12T00:00:00Z",
  "message": "Site secret rotated successfully. Save the new siteSecret - it cannot be retrieved again. Update your backend immediately."
}

Example

const res = await fetch(`/api/v1/sites/sk_live_abc123/rotate-secret`, {
  method: 'POST',
});
 
const { newSiteSecret } = await res.json();
// Store newSiteSecret securely and update your backend environment

Error responses

All endpoints return a consistent error shape:

{ "error": "Description of what went wrong" }
StatusMeaning
400Missing or invalid request parameters
401Not authenticated
403Authenticated but not authorized for this resource
404Account or site not found
500Internal server error

Security checklist

  • siteSecret stored in a backend secret manager — never committed to source control or exposed to the browser.
  • Rotate secrets immediately if a leak is suspected.
  • Deactivate sites that are no longer in use.
  • Restrict which team members have access to the account in your account settings.