Authentication
Every request to the API carries an API key as a Bearer token.
Keys
A store admin creates keys in the Octane AI dashboard under Developer > API keys (see the Keys tab). A key starts with oct_live_; it is shown once, when it is created, and never again. The dashboard keeps only the readable head and the last four characters, so copy it right away. If you lose it, revoke it and create a new one.
Send it in the Authorization header:
Authorization: Bearer oct_live_...The samples on this site read the key from the OCTANE_API_KEY environment variable; the Python ones use httpx (pip install httpx), the TypeScript ones fetch, the Go ones net/http:
curl https://api.octaneai.com/v1/store \ -H "Authorization: Bearer $OCTANE_API_KEY"import osimport httpx
r = httpx.get( "https://api.octaneai.com/v1/store", headers={"Authorization": f"Bearer {os.environ['OCTANE_API_KEY']}"}, timeout=30)r.raise_for_status()print(r.json()["scopes"]) # ["analytics:read", "quizzes:read"]const res = await fetch("https://api.octaneai.com/v1/store", { headers: { Authorization: `Bearer ${process.env.OCTANE_API_KEY}` },});if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);console.log((await res.json()).scopes); // ["analytics:read", "quizzes:read"]package main
import ( "encoding/json" "fmt" "log" "net/http" "os")
func main() { req, _ := http.NewRequest("GET", "https://api.octaneai.com/v1/store", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("OCTANE_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() if res.StatusCode != 200 { log.Fatalf("status %d", res.StatusCode) } var store struct { Scopes []string `json:"scopes"` } json.NewDecoder(res.Body).Decode(&store) fmt.Println(store.Scopes) // [analytics:read quizzes:read]}A key is always minted under the admin who is signed in; quiz versions and publishes made with it are recorded under that admin’s account.
Scopes
A key carries the permissions ticked when it was created (a key minted without an explicit choice is read-only: quizzes:read and analytics:read). The dashboard names them in plain words; the API calls them scopes:
| Dashboard permission | Scope | What it allows |
|---|---|---|
| Read quizzes | quizzes:read | List quizzes and their saved versions |
| Edit quizzes | quizzes:write | Archive, unarchive and soft-delete a quiz; save the draft as a version |
| Publish quizzes | quizzes:publish | Publish a saved version |
| Read analytics | analytics:read | Every analytics route; the quiz_analytics export |
| Read people | profiles:read | The permission that exposes personal data. Emails, phone numbers and names on people (GET /v1/profiles, POST /v1/profiles/search, GET /v1/profiles/{profile_id}), on the profiles and responses exports, and on webhook endpoints created with include_pii: true. Give it only to integrations that need to know who a person is. |
| Manage webhooks | webhooks:manage | Create, edit, delete and redeliver webhook endpoints; the event feed |
A route that needs a scope the key lacks answers 403 insufficient_scope and names the missing scope in the required field of the error body:
{ "type": "https://api.octaneai.com/errors/insufficient_scope", "title": "insufficient_scope", "detail": "The API key does not carry the scope this route needs", "status": 403, "request_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "required": ["profiles:read"]}Each operation in the API Reference states the scope it needs. GET /v1/store and GET /v1/exports work with any key.
Expiry
A key may carry an expiry chosen when it is created. An expired key answers 401 invalid_api_key with WWW-Authenticate: Bearer, the same as a missing, malformed or revoked key.
Revoking and rotating
Revoking a key in the dashboard takes effect within two minutes. To rotate, create the new key first, switch your integration to it, then revoke the old one. Disconnecting the store from Octane AI revokes every key and deletes every webhook endpoint.
