Skip to content

Cloudflare Turnstile

Cloudflare Turnstile is a CAPTCHA alternative that provides user verification.

Schema Integration

Turnstile integrates with the existing GraphQL schema through the CaptchaType enum:

enum CaptchaType {
TURNSTILE # Cloudflare Turnstile
HCAPTCHA # hCaptcha
V2_VISIBLE # reCAPTCHA v2 with visible checkbox
V2_INVISIBLE # reCAPTCHA v2 without visible elements
}
type CaptchaConfiguration {
type: CaptchaType!
siteKey: String!
}

Implementation Examples

Successful Login with Turnstile

When a user completes Turnstile verification, the token can bypass rate limiting:

mutation Login($input: LoginInput!) {
login(input: $input) {
success
token
customer {
id
email
fullName
}
error
fieldErrors {
fieldName
validators
requiredButNotProvided
}
}
}

Request Headers:

X-Captcha-Type: TURNSTILE
X-Captcha-Response: 0.ABC123DEF456GHI789JKL012MNO345PQR678STU901VWX234YZ567
Content-Type: application/json

Rate Limited Response with Turnstile Configuration

When rate limiting is triggered, Turnstile configuration is provided:

{
"data": {
"login": null
},
"errors": [
{
"message": "Rate limit exceeded",
"path": ["login"],
"extensions": {
"classification": "CAPTCHA_REQUIRED",
"rateLimited": true,
"rateLimitingBucket": "AUTHENTICATION"
}
}
],
"extensions": {
"rateLimitersFiring": [
{
"captchaBypassAvailable": [
{
"type": "TURNSTILE",
"siteKey": "0x4AAAAAAAA1234567890ABCDEF"
}
],
"rateLimitingBucket": "AUTHENTICATION"
}
]
}
}

Error Handling

Common Turnstile error responses:

Error CodeDescription
missing-input-responseNo token provided
invalid-input-responseInvalid or expired token
timeout-or-duplicateToken reuse or timeout
bad-requestMalformed request
sitekey-secret-mismatchConfiguration error

API Usage

Both web and mobile APIs support Turnstile using the same header pattern:

// Web API
fetch('https://horizon-api.www.myprotein.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Captcha-Type': 'TURNSTILE',
'X-Captcha-Response': turnstileToken
},
body: JSON.stringify({ query, variables })
});
// Mobile API
fetch('https://api.thehut.net/myprotein/en/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Captcha-Type': 'TURNSTILE',
'X-Captcha-Response': turnstileToken,
'Authorization': 'Opaque ' + authToken
},
body: JSON.stringify({ query, variables })
});