# Verify bot protection

```yaml
capability_id: anti_bot.verify
display_name: Verify bot protection
ui_capability_key: verify_bot_protection
scope: anti_bot.verify
endpoints:
  - provider: turnstile
    method: POST
    path: /turnstile/get
  - provider: recaptcha
    method: POST
    path: /recaptcha/get
operations:
  turnstile: turnstile.credentials.get
  recaptcha: recaptcha.credentials.get
billable: true
environment: not_applicable
```

## Purpose and authorization

This capability supplies an authenticated Gateman application with the
configured credentials needed to integrate bot protection with its selected
provider. The application performs browser-widget integration and native
provider verification itself.

Gateman does **not** receive browser challenge tokens, call a provider's
verification API, or return a completed bot-verification result.

Each request MUST use authenticated [Contract v1](https://docs.getgateman.com/contract-v1/)
transport and the `anti_bot.verify` scope. The scope authorizes the two
credential-bundle routes below. It does not require `secrets:read` or the
provider's general **Allow app retrieval** setting.

Cloudflare Turnstile and Google reCAPTCHA currently have independent
credential routes under this capability. Provider selection is made by the
endpoint. This capability does not provision provider widgets, sites, domains,
or provider accounts.

## Implementation contract

### Cloudflare Turnstile: `POST /turnstile/get`

```http
POST /turnstile/get
Content-Type: application/json
X-GM-App: app_example
X-GM-Ts: 1791748800
X-GM-Sig: <Contract-v1-signature>
```

Intended body:

```json
{}
```

An empty HTTP body is also accepted by the current implementation. No request
parameters are supported. Browser tokens, remote IP addresses, hostnames,
actions, scores, and other provider-native values MUST NOT be sent to this
route. A non-empty JSON object is rejected.

Successful response:

```json
{
  "ok": true,
  "site_key": "configured-site-key",
  "secret_key": "configured-secret-key"
}
```

### Google reCAPTCHA: `POST /recaptcha/get`

```http
POST /recaptcha/get
Content-Type: application/json
X-GM-App: app_example
X-GM-Ts: 1791748800
X-GM-Sig: <Contract-v1-signature>
```

Intended body:

```json
{}
```

An empty HTTP body is also accepted. No request parameters are supported. A
non-empty JSON object is rejected. Provider selection occurs because the
application chose `/recaptcha/get`, not through a request field.

Successful response:

```json
{
  "ok": true,
  "site_key": "configured-site-key",
  "secret_key": "configured-secret-key"
}
```

Malformed JSON returns HTTP `400` with `{"ok":false,"error":"Invalid JSON"}`.
Any non-empty request object returns HTTP `400` with
`{"ok":false,"error":"This endpoint accepts no parameters"}`.

### Credentials

Turnstile uses these application-scoped configured names:

- `TURNSTILE_SITE_KEY`
- `TURNSTILE_SECRET_KEY`

reCAPTCHA uses:

- `RECAPTCHA_SITE_KEY`
- `RECAPTCHA_SECRET_KEY`

Both providers MAY be configured for one application. The site key is used by
the provider widget or browser integration. The secret key is for trusted
server-side use only. Applications MUST NOT expose a secret key to browser
code, public responses, client-side bundles, logs, or prompts.

Gateman returns the configured bundle to the authenticated backend application;
it does not return a provider verification result.

### Environment behavior

These routes are environment-independent:

- no `environment` field is required or accepted;
- production, staging, and development do not select different bundles;
- configured credentials are shared across those Gateman environment labels;
- Gateman does not simulate verification or capture provider requests;
- there is no environment-specific credential fallback.

Staging and development do not automatically select provider test credentials.
If an application requires provider test credentials, it must configure and
manage that provider arrangement itself.

### Application verification responsibilities

The integrating application MUST:

1. Integrate the selected provider's widget or challenge in the browser.
2. Receive the browser-generated challenge token.
3. Submit that token from its trusted backend directly to the provider's
   verification API.
4. Interpret the provider's native response.
5. Apply the resulting decision to the protected application action.

For Turnstile, the native server-side interface is [Cloudflare Siteverify](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/).
For classic reCAPTCHA integrations, the native interface is [Google Siteverify](https://developers.google.com/recaptcha/docs/verify).

The application MUST NOT treat successful Gateman credential retrieval as
evidence that a visitor passed a challenge. Gateman does not implement a
provider-neutral token-verification request or response format, reCAPTCHA
Enterprise verification, verification sessions, visitor tracking, or a
universal `human_verified` result.

### Provider-native requirements

Turnstile requires server-side Siteverify validation. Cloudflare documents
that Turnstile tokens are single-use and time-limited; the application must
process the provider's `success`, error, and contextual response fields as
appropriate for its integration.

Classic reCAPTCHA response tokens are single-use and time-limited. reCAPTCHA
v3 returns a score and action. Applications using score-based verification
MUST establish and apply their own appropriate score and action policy. The
application is also responsible for any provider-required hostname or other
context checks.

Gateman does not enforce hostname, action, score, `cdata`, expiry, or replay
rules.

## Operation and replay obligations

The operation identifiers are:

| Provider | Operation | Accounting |
| --- | --- | --- |
| Turnstile | `turnstile.credentials.get` | One operation per admitted retrieval |
| reCAPTCHA | `recaptcha.credentials.get` | One operation per admitted retrieval |

Each admitted credential retrieval consumes one Gateman operation, whether the
bundle is returned successfully or the accepted request fails because the
credentials are missing or unavailable. Authentication, scope, malformed
request, concurrency, and allowance rejections before operation admission do
not consume an operation. Direct provider verification does not consume a
Gateman operation.

The optional `X-GM-Idempotency-Key` is part of Contract v1 signing, but these
routes do not implement a Gateman idempotency replay ledger. Repeating a
retrieval performs another retrieval and consumes another operation. Do not
borrow Email or SMS idempotency semantics for this capability.

Applications SHOULD securely reuse retrieved credentials and obtain updated
credentials when their configuration changes. Credential retrieval SHOULD NOT
occur for every visitor interaction or verification attempt. The routes use
Gateman's shared application concurrency controls. No universal cache TTL or
cache implementation is prescribed.

Verification tokens and verification results are not credential bundles and
MUST NOT be treated as reusable credentials.

## Errors and responses

The following are the relevant current Gateman-generated outcomes:

| Condition | HTTP | Public behavior |
| --- | ---: | --- |
| Invalid JSON | `400` | `Invalid JSON` |
| Non-empty request parameters | `400` | `This endpoint accepts no parameters` |
| Missing configured credentials | `503` | Provider-specific missing-credentials error |
| Credential retrieval failure | `500` | Provider-specific credentials-unavailable error |
| Authentication or scope failure | `401` | Request rejected by Contract v1 authentication |
| Application concurrency exceeded | `429` | `app_concurrency_limit_exceeded` with `Retry-After` |
| Application concurrency guard unavailable | `503` | `app_concurrency_guard_unavailable` |
| Operation allowance exhausted | `403` | `monthly_operation_limit_reached` |
| Accounting unavailable | `503` | `Operation accounting unavailable` |

Turnstile and reCAPTCHA provider verification failures are not Gateman
responses. They are returned by the provider to the integrating application,
which must decide whether to reject, retry with a fresh token, or apply another
provider- and action-specific policy.

## Implementation outcome

A correct integration should:

1. Authenticate to Gateman with `anti_bot.verify`.
2. Retrieve the selected provider's configured credential bundle.
3. Keep the secret key on its trusted backend.
4. Use the site key in the provider widget or browser integration.
5. Submit browser challenge tokens directly to the provider from the backend.
6. Interpret native provider results in the context of the protected action.
7. Avoid unnecessary repeated Gateman credential retrievals.

## Implementation instructions for agents

- Select `/turnstile/get` or `/recaptcha/get` according to the customer's
  chosen provider; do not invent a generic verification endpoint.
- Use authenticated Contract v1 transport with the `anti_bot.verify` scope
  and the supported empty request body.
- Handle the route's HTTP status and Gateman error body before using returned
  credentials.
- Keep `secret_key` backend-only and use `site_key` only for the provider's
  supported browser integration.
- Perform native token verification directly with the selected provider.
- Apply provider-required hostname, action, score, expiry, and other checks in
  the application where that provider and integration require them.
- Do not treat credential retrieval as proof that a visitor passed a challenge.
- Reuse credentials securely where appropriate and do not make a Gateman
  credential request for every visitor or token.

## Reusable `AGENTS.md` block

```markdown
<!-- gateman:begin capability=anti_bot.verify -->
- Use authenticated Contract v1 with the `anti_bot.verify` scope.
- Retrieve credentials from the selected provider route: `POST /turnstile/get`
  or `POST /recaptcha/get`, using the supported empty request body.
- Gateman supplies `site_key` and `secret_key`; it does not receive browser
  challenge tokens or perform provider verification.
- Keep `secret_key` on the trusted backend and never expose it to browser code,
  public responses, or client-side bundles.
- Use `site_key` in the selected provider's supported widget or challenge.
- Submit browser tokens directly from the backend to the selected provider's
  native verification API and apply that provider's required checks.
- Do not treat Gateman credential retrieval as proof that a visitor passed.
- Reuse retrieved credentials securely; do not request them for every visitor.
- Full capability reference:
  https://docs.getgateman.com/capabilities/anti_bot.verify/
<!-- gateman:end capability=anti_bot.verify -->
```

## Related references

- [Contract v1](https://docs.getgateman.com/contract-v1/)
- [Authentication](https://docs.getgateman.com/authentication/)
- [Environments](https://docs.getgateman.com/environments/)
- [Operation accounting](https://docs.getgateman.com/operation-accounting/)
- [Rate limits and concurrency](https://docs.getgateman.com/rate-limits-and-concurrency/)
- [Idempotency](https://docs.getgateman.com/idempotency/)
- [Errors and responses](https://docs.getgateman.com/errors-and-responses/)
- [Data retention](https://docs.getgateman.com/data-retention/)
- [Official PHP client](https://docs.getgateman.com/canonical-client-php/)
- [Official Node.js client](https://docs.getgateman.com/canonical-client-node/)
- [Official Python client](https://docs.getgateman.com/canonical-client-python/)
- [Official Go client](https://docs.getgateman.com/canonical-client-go/)
- [Cloudflare Turnstile server-side validation](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/)
- [Google reCAPTCHA verification](https://developers.google.com/recaptcha/docs/verify)
