Authentication
JWT, API key prefixes, polling tokens, service keys, and Google OAuth2.
Choose the credential that matches who is calling and what they're calling. TurfAI uses a different credential for each kind of caller.
| 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: <ak_…> | 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. To get one, log in with your email and password:
curl -X POST https://apisandbox.turfai.in/api/auth/local \
-H "Content-Type: application/json" \
-d '{ "identifier": "[email protected]", "password": "yourpassword" }'{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": { "id": 1, "username": "user", "email": "[email protected]" }
}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. TurfAI returns
the secret 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:
ak_/ 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).