Authentication
JWT, API key prefixes, polling tokens, service keys, and Google OAuth2.
TurfAI uses different credentials for different callers. Pick the one that matches who is calling and what they're calling.
| Credential | Header | Who uses it | Scope |
|---|---|---|---|
| JWT | Authorization: Bearer <jwt> | A logged-in user / your backend | All authenticated endpoints, owner-scoped |
| Polling token | Authorization: Bearer <token> | External trigger callers | One execution's status, ~1 hour |
| Webhook secret | X-Webhook-Secret: <secret> | Whoever triggers a webhook | Triggering that one webhook |
| Agent key | X-Agent-Key: <ag_…> | Public agent chat clients | One agent's public chat |
| Chatbot key | X-Chatbot-Key: <cb_…> | Embedded widget / public chat | One chatbot's public chat |
| Service key | X-Service-Key: <key> | Internal services only | Internal endpoints (not for app developers) |
JWT (user authentication)
Most endpoints require a user JWT. Obtain one by logging in with email and password:
curl -X POST https://apisandbox.turfai.in/api/auth/local \
-H "Content-Type: application/json" \
-d '{ "identifier": "user@example.com", "password": "yourpassword" }'{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": { "id": 1, "username": "user", "email": "user@example.com" }
}Send it on every authenticated call:
curl https://apisandbox.turfai.in/api/agents \
-H "Authorization: Bearer $TURFAI_JWT"JWTs are owner-scoped: you only see and act on resources you own. Tokens are long-lived
(default 30 days, configurable) and are blacklisted immediately on logout
(POST /auth/logout).
Polling tokens
Triggering a webhook returns a polling_token — a short-lived JWT (~1 hour) that can only
read that one execution's status. Use it as a Bearer token. It exists so an external caller
can follow a run to completion without holding a full user JWT. Don't store it long-term.
curl https://apisandbox.turfai.in/api/workflow-executions/456 \
-H "Authorization: Bearer $POLLING_TOKEN"Webhook secrets
Each webhook has a 64-character hex secret_key (256 bits). The public trigger endpoint
authenticates with it via the X-Webhook-Secret header — no JWT required, so external
systems (forms, SaaS apps) can trigger workflows without TurfAI credentials. The secret is
returned only on create and on POST /webhooks/:id/regenerate-secret (which invalidates the
old one immediately). Validation is timing-safe.
API key prefixes
Public chat surfaces are protected by prefixed API keys rather than user JWTs, so you can embed them in clients without exposing a user account:
ag_/ agent key — sent asX-Agent-KeytoPOST /agents/:slug/public-chat.cb_/ chatbot key — sent asX-Chatbot-KeytoPOST /chatbots/:slug/chat(48 hex chars).
Both keys are owner-bound: a public chat request runs under the resource owner's context
(the owner's documents are searched, the owner's quota is charged). Rotate either key via
its POST /…/:id/regenerate-key endpoint; rotation invalidates the previous key. Regenerating
an agent key requires step-up verification (see below).
Treat agent and chatbot keys as public-client credentials, not secrets — anyone with the
embed snippet can see them. Lock them down with allowed_origins and per-key rate limits
(default 60 requests/minute), and rotate if abused.
Step-up authentication
Sensitive operations require a second factor on top of a valid JWT. Request an OTP, verify
it, then retry the protected call with X-Step-Up-Verified: true:
# 1. Send a 6-digit code to the user's email
curl -X POST https://apisandbox.turfai.in/api/auth/step-up \
-H "Authorization: Bearer $TURFAI_JWT"
# 2. Verify it (grants a step-up flag for ~5 minutes)
curl -X POST https://apisandbox.turfai.in/api/auth/step-up/verify \
-H "Authorization: Bearer $TURFAI_JWT" \
-H "Content-Type: application/json" \
-d '{ "code": "123456" }'Operations that require step-up include deleting a credential
(DELETE /credentials/:id) and regenerating an agent key
(POST /agents/:id/regenerate-key), sent with the X-Step-Up-Verified: true header.
Google OAuth2
TurfAI supports two distinct Google OAuth flows — don't confuse them:
- Sign in with Google — authenticate a user. The browser hits
GET /api/connect/google, Google redirects back toGET /api/connect/google/callback, and TurfAI returns a normal user JWT. Use this for login/signup; you then call the rest of the API with that JWT. - Delegated Google access — let a workflow act on a user's Drive/Gmail. Start with
GET /google/oauth/authorize(returns anauthUrlto redirect the user to), handle the callback, then checkGET /google/oauth/status. Once connected, tasks likegoogle_drive_fetchand Drive event sources can read the user's files. Revoke withPOST /google/oauth/revoke. Requested scopes are limited to what the feature needs.
Credentials for integrations
For non-Google integrations (e.g. a third-party REST API your workflow calls), store secrets
as credentials rather than hardcoding them. Fields are encrypted at rest (AES-256-GCM)
and never returned by the API — list/get return metadata and field_names only. Reference
them from rest_api tasks via {{variable}} templates. See the
custom REST integration guide.
Error responses
Auth failures use the platform's standard error shape:
{ "error": "Unauthorized", "message": "Authentication required or invalid token" }Common codes: 401 (missing/invalid credential), 403 (valid credential, no permission —
e.g. an inactive webhook or a resource you don't own), 429 (rate limit; check
retryAfter).