# API Reference ## Client-Side JavaScript API The Turnstile JavaScript API is available at `window.turnstile` after loading the script. ### `turnstile.render(container, options)` Renders a Turnstile widget into a container element. **Parameters:** - `container` (string | HTMLElement): CSS selector or DOM element - `options` (TurnstileOptions): Configuration object (see [configuration.md](configuration.md)) **Returns:** `string` - Widget ID for use with other API methods **Example:** ```javascript const widgetId = window.turnstile.render('#my-container', { sitekey: 'YOUR_SITE_KEY', callback: (token) => console.log('Success:', token), 'error-callback': (code) => console.error('Error:', code) }); ``` ### `turnstile.reset(widgetId)` Resets a widget (clears token, resets challenge state). Useful when form validation fails. **Parameters:** - `widgetId` (string): Widget ID from `render()`, or container element **Returns:** `void` **Example:** ```javascript // Reset on form error if (!validateForm()) { window.turnstile.reset(widgetId); } ``` ### `turnstile.remove(widgetId)` Removes a widget from the DOM completely. **Parameters:** - `widgetId` (string): Widget ID from `render()` **Returns:** `void` **Example:** ```javascript // Cleanup on navigation window.turnstile.remove(widgetId); ``` ### `turnstile.getResponse(widgetId)` Gets the current token from a widget (if challenge completed). **Parameters:** - `widgetId` (string): Widget ID from `render()`, or container element **Returns:** `string | undefined` - Token string, or undefined if not ready **Example:** ```javascript const token = window.turnstile.getResponse(widgetId); if (token) { submitForm(token); } ``` ### `turnstile.isExpired(widgetId)` Checks if a widget's token has expired (>5 minutes old). **Parameters:** - `widgetId` (string): Widget ID from `render()` **Returns:** `boolean` - True if expired **Example:** ```javascript if (window.turnstile.isExpired(widgetId)) { window.turnstile.reset(widgetId); } ``` ## Callback Signatures ```typescript type TurnstileCallback = (token: string) => void; type ErrorCallback = (errorCode: string) => void; type TimeoutCallback = () => void; type ExpiredCallback = () => void; type BeforeInteractiveCallback = () => void; type AfterInteractiveCallback = () => void; type UnsupportedCallback = () => void; ``` ## Siteverify API (Server-Side) **Endpoint:** `https://challenges.cloudflare.com/turnstile/v0/siteverify` ### Request **Method:** POST **Content-Type:** `application/json` or `application/x-www-form-urlencoded` ```typescript interface SiteverifyRequest { secret: string; // Your secret key (never expose client-side) response: string; // Token from cf-turnstile-response remoteip?: string; // User's IP (optional but recommended) idempotency_key?: string; // Unique key for idempotent validation } ``` **Example:** ```javascript // Cloudflare Workers const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ secret: env.TURNSTILE_SECRET, response: token, remoteip: request.headers.get('CF-Connecting-IP') }) }); const data = await result.json(); ``` ### Response ```typescript interface SiteverifyResponse { success: boolean; // Validation result challenge_ts?: string; // ISO timestamp of challenge hostname?: string; // Hostname where widget was solved 'error-codes'?: string[]; // Error codes if success=false action?: string; // Action name from widget config cdata?: string; // Custom data from widget config } ``` **Example Success:** ```json { "success": true, "challenge_ts": "2024-01-15T10:30:00Z", "hostname": "example.com", "action": "login", "cdata": "user123" } ``` **Example Failure:** ```json { "success": false, "error-codes": ["timeout-or-duplicate"] } ``` ## Error Codes | Code | Cause | Solution | |------|-------|----------| | `missing-input-secret` | Secret key not provided | Include `secret` in request | | `invalid-input-secret` | Secret key is wrong | Check secret key in dashboard | | `missing-input-response` | Token not provided | Include `response` token | | `invalid-input-response` | Token is invalid/malformed | Verify token from widget | | `timeout-or-duplicate` | Token expired (>5min) or reused | Generate new token, validate once | | `internal-error` | Cloudflare server error | Retry with exponential backoff | | `bad-request` | Malformed request | Check JSON/form encoding | ## TypeScript Types ```typescript interface TurnstileOptions { sitekey: string; action?: string; cData?: string; callback?: (token: string) => void; 'error-callback'?: (errorCode: string) => void; 'expired-callback'?: () => void; 'timeout-callback'?: () => void; 'before-interactive-callback'?: () => void; 'after-interactive-callback'?: () => void; 'unsupported-callback'?: () => void; theme?: 'light' | 'dark' | 'auto'; size?: 'normal' | 'compact' | 'flexible'; tabindex?: number; 'response-field'?: boolean; 'response-field-name'?: string; retry?: 'auto' | 'never'; 'retry-interval'?: number; language?: string; execution?: 'render' | 'execute'; appearance?: 'always' | 'execute' | 'interaction-only'; 'refresh-expired'?: 'auto' | 'manual' | 'never'; } interface Turnstile { render(container: string | HTMLElement, options: TurnstileOptions): string; reset(widgetId: string): void; remove(widgetId: string): void; getResponse(widgetId: string): string | undefined; isExpired(widgetId: string): boolean; execute(container?: string | HTMLElement, options?: TurnstileOptions): void; } declare global { interface Window { turnstile: Turnstile; onloadTurnstileCallback?: () => void; } } ``` ## Script Loading ```html ```