PBN.LTD API docs
View as Markdown

Turnstile bot check

Cloudflare Turnstile on your WordPress forms (login, registration, lost password, comments, Contact Form 7, WPForms): status, switch on or off, choose forms, new keys.

Turnstile status of a site

GET/api/v1/sites/{site_id}/turnstile

Scope turnstile:read

What the site's Turnstile tab shows: whether the bot check can be used here (WordPress sites whose DNS is on Cloudflare), whether it is on, the mode, which forms it protects, the extra hostnames it accepts, when it was last read back from the site and when the keys were last replaced. The secret key is never returned.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123/turnstile" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/turnstile", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/turnstile", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "available": true,
    "status": "active",
    "status_label": "On",
    "account": "ours",
    "sitekey": "0x4AAAAAAAxxxxxxxxxxxxxx",
    "mode": "managed",
    "forms": [
      "login",
      "register",
      "lostpassword",
      "comments",
      "cf7",
      "wpforms"
    ],
    "extra_hosts": [],
    "hosts": [
      "example.com"
    ],
    "last_error": "",
    "revoked": false,
    "verified_at": "2026-09-24 10:00 UTC",
    "rotated_at": "",
    "modes": [
      {
        "key": "managed",
        "label": "Managed (recommended)"
      },
      {
        "key": "non-interactive",
        "label": "Non-interactive"
      },
      {
        "key": "invisible",
        "label": "Invisible"
      }
    ],
    "all_forms": [
      {
        "key": "login",
        "label": "Login form"
      },
      {
        "key": "register",
        "label": "Registration form"
      }
    ],
    "paused": false
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized

Switch Turnstile on (or change its settings)

POST/api/v1/sites/{site_id}/turnstile

Scope turnstile:write

Switches the bot check on for this site, or saves new settings when it is already on. We create the Turnstile widget, install our small WordPress plugin with the keys and read it back from the site. If there is no room yet the status is "waiting" and it finishes by itself. Turnstile that our team switched off ("revoked") cannot be switched back on here - contact support.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
modebodystring (one of: managed, non-interactive, invisible)noHow visitors are checked. managed (recommended) shows a checkbox only when needed; non-interactive never asks; invisible shows nothing. Default: managed.
formsbodyarraynoWhich forms to protect: login, register, lostpassword, comments, cf7 (Contact Form 7), wpforms. Default: all of them.
extra_hostsbodyarraynoUp to 4 more hostnames the check must accept - domains of your OTHER sites with us (e.g. a subdomain site).

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/turnstile" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/turnstile", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/turnstile", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "available": true,
    "status": "active",
    "status_label": "On",
    "account": "ours",
    "sitekey": "0x4AAAAAAAxxxxxxxxxxxxxx",
    "mode": "managed",
    "forms": [
      "login",
      "register",
      "lostpassword",
      "comments",
      "cf7",
      "wpforms"
    ],
    "extra_hosts": [],
    "hosts": [
      "example.com"
    ],
    "last_error": "",
    "revoked": false,
    "verified_at": "2026-09-24 10:00 UTC",
    "rotated_at": "",
    "modes": [
      {
        "key": "managed",
        "label": "Managed (recommended)"
      },
      {
        "key": "non-interactive",
        "label": "Non-interactive"
      },
      {
        "key": "invisible",
        "label": "Invisible"
      }
    ],
    "all_forms": [
      {
        "key": "login",
        "label": "Login form"
      },
      {
        "key": "register",
        "label": "Registration form"
      }
    ],
    "paused": false
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, validation_failed

Replace the Turnstile secret key

POST/api/v1/sites/{site_id}/turnstile/rotate

Scope turnstile:write

Issues a new secret key for the site's widget and installs it on the site straight away. The new key is never shown - it only lives on your site. Use it if you think the old key leaked.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/turnstile/rotate" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/turnstile/rotate", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/turnstile/rotate", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "available": true,
    "status": "active",
    "status_label": "On",
    "account": "ours",
    "sitekey": "0x4AAAAAAAxxxxxxxxxxxxxx",
    "mode": "managed",
    "forms": [
      "login",
      "register",
      "lostpassword",
      "comments",
      "cf7",
      "wpforms"
    ],
    "extra_hosts": [],
    "hosts": [
      "example.com"
    ],
    "last_error": "",
    "revoked": false,
    "verified_at": "2026-09-24 10:00 UTC",
    "rotated_at": "",
    "modes": [
      {
        "key": "managed",
        "label": "Managed (recommended)"
      },
      {
        "key": "non-interactive",
        "label": "Non-interactive"
      },
      {
        "key": "invisible",
        "label": "Invisible"
      }
    ],
    "all_forms": [
      {
        "key": "login",
        "label": "Login form"
      },
      {
        "key": "register",
        "label": "Registration form"
      }
    ],
    "paused": false
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized

Switch Turnstile off

POST/api/v1/sites/{site_id}/turnstile/disable

Scope turnstile:write · destructive

Takes the plugin off the site first (read back), then deletes the widget. Your forms are no longer protected by the bot check. Needs "confirm": true. You can switch it on again at any time.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
confirmbodybooleanyesMust be true: switching Turnstile off removes the bot check from the site's forms.

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/turnstile/disable" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/turnstile/disable", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/turnstile/disable", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "available": true,
    "status": "off",
    "status_label": "Off",
    "account": "ours",
    "sitekey": null,
    "mode": "managed",
    "forms": [
      "login",
      "register",
      "lostpassword",
      "comments",
      "cf7",
      "wpforms"
    ],
    "extra_hosts": [],
    "hosts": [
      "example.com"
    ],
    "last_error": "",
    "revoked": false,
    "verified_at": "2026-09-24 10:00 UTC",
    "rotated_at": "",
    "modes": [
      {
        "key": "managed",
        "label": "Managed (recommended)"
      },
      {
        "key": "non-interactive",
        "label": "Non-interactive"
      },
      {
        "key": "invisible",
        "label": "Invisible"
      }
    ],
    "all_forms": [
      {
        "key": "login",
        "label": "Login form"
      },
      {
        "key": "register",
        "label": "Registration form"
      }
    ],
    "paused": false
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, validation_failed