API
Authentication
Get a token, send it with each request, revoke it, and create long-lived personal tokens.
Studio signs in with a cookie. Scripts, servers and other apps use a bearer token instead.
Get a token
POST /api/v1/auth/login
Send mode: "token" to get a token instead of a cookie.
curl -X POST 'http://127.0.0.1:8080/api/v1/auth/login' \
-H 'Content-Type: application/json' \
--data '{"email":"you@example.com","password":"YOUR_PASSWORD","mode":"token"}' const res = await fetch('http://127.0.0.1:8080/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'you@example.com', password: 'YOUR_PASSWORD', mode: 'token' })
});
const { data } = await res.json();
const token = data.access_token; package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]string{
"email": "you@example.com", "password": "YOUR_PASSWORD", "mode": "token",
})
res, err := http.Post("http://127.0.0.1:8080/api/v1/auth/login", "application/json", bytes.NewReader(body))
if err != nil {
panic(err)
}
defer res.Body.Close()
var out struct {
Data struct {
AccessToken string `json:"access_token"`
} `json:"data"`
}
if err := json.NewDecoder(res.Body).Decode(&out); err != nil {
panic(err)
}
fmt.Println(out.Data.AccessToken)
} import requests
res = requests.post(
"http://127.0.0.1:8080/api/v1/auth/login",
json={"email": "you@example.com", "password": "YOUR_PASSWORD", "mode": "token"},
)
token = res.json()["data"]["access_token"] Response:
{
"data": {
"access_token": "YOUR_ACCESS_TOKEN",
"token_type": "Bearer",
"expires_in": 28800,
"user": {
"id": "7685a20f71a2e3535cdee4566a8e1b9f",
"email": "you@example.com",
"display_name": ""
}
},
"meta": { "request_id": "TFUQWRNZQKSA27FNQMFQPBVR72", "response_time": 271.8 }
} The token lasts eight hours (expires_in is in seconds) and no cookie is set.
Send the token
GET /api/v1/auth/me
Add Authorization: Bearer <token> to every request.
curl 'http://127.0.0.1:8080/api/v1/auth/me' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' const res = await fetch('http://127.0.0.1:8080/api/v1/auth/me', {
headers: { Authorization: `Bearer ${token}` }
});
console.log(await res.json()); req, _ := http.NewRequest("GET", "http://127.0.0.1:8080/api/v1/auth/me", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, err := http.DefaultClient.Do(req) res = requests.get(
"http://127.0.0.1:8080/api/v1/auth/me",
headers={"Authorization": f"Bearer {token}"},
)
print(res.json()) Response:
{
"data": {
"id": "7685a20f71a2e3535cdee4566a8e1b9f",
"email": "you@example.com",
"display_name": ""
},
"meta": { "request_id": "2DOAELS4STJYJ5CVYUJXGC6RMI", "response_time": 2.3 }
} Sign out
POST /api/v1/auth/logout
Logging out with a token revokes that token right away. The response is 204 with no body.
curl -X POST 'http://127.0.0.1:8080/api/v1/auth/logout' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' await fetch('http://127.0.0.1:8080/api/v1/auth/logout', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` }
}); req, _ := http.NewRequest("POST", "http://127.0.0.1:8080/api/v1/auth/logout", nil)
req.Header.Set("Authorization", "Bearer "+token)
_, err := http.DefaultClient.Do(req) requests.post(
"http://127.0.0.1:8080/api/v1/auth/logout",
headers={"Authorization": f"Bearer {token}"},
) Personal API tokens
For a long-running server or a cron job, create a personal token in Account settings or with POST /api/v1/auth/tokens.
- Up to 20 tokens per user, each expiring within a year.
- The secret is shown once. Kanesh stores only a hash.
- A token acts as its owner, but cannot manage tokens or change the password.
To accept a raw token in a custom header such as X-Api-Key, set AUTH_TOKEN_HEADER. Authorization keeps working.
Rules for credentials
- Send exactly one credential. A cookie plus a token, or two headers, returns 401.
- Tokens in the query string are never accepted.
- A token alone does not allow calls from a browser on another origin. Set up CORS for that.
- Sign-in is refused from another origin with 403
access_denied. Sign in from your server, not from a browser on a different site.
Troubleshooting
- 401
authentication_required: check that you send one credential and that the token has not expired or been revoked. - 429 Too Many Requests: login has a stricter rate limit. Wait for the
Retry-Aftertime.