Welcome
The Octane AI developer platform lets you build on your store’s quizzes from your own code: list and publish quizzes, pull analytics, read the people who took a quiz and what they answered, export files, and react the moment a shopper starts or finishes a quiz.
The four surfaces
| Surface | What it is for | Where it runs |
|---|---|---|
| REST API | Quizzes, analytics, people, exports and webhook management over HTTPS at https://api.octaneai.com/v1, with an API key. | Your server |
| Webhooks | Octane AI POSTs a signed event to your endpoint when a quiz is started, finished or abandoned, when a person is created or updated, when a quiz is published and when an export file is ready. | Your server |
| Storefront JS API | Your code on the store’s page hears what the shopper does in the quiz (pages, answers, products, the finish) and can drive it (prefill, navigate, open, close). Part of the quiz embed already on the page; nothing to install. | The shopper’s browser |
| Developer dashboard | Where a store admin creates API keys and webhooks, reads the request log and downloads exports. | The Octane AI dashboard, under Developer |
Who can use it
Stores on the Plus and Enterprise plans. A key of a store on another plan answers 403 plan_required; a plan change takes effect within five minutes. Exports from the dashboard are available to every store.
Your first call in 60 seconds
Every path on this site is relative to https://api.octaneai.com. The API is described by an OpenAPI 3.1 file at /openapi/merchant-api.json: it is the same file the API Reference is built from, it refreshes when the API changes, and you can feed it to a client generator.
1. Create a key
In the Octane AI dashboard, open Developer > API keys and click New API key. Give it a name, tick the permissions you need, and copy the key: it is shown once. Put it in the OCTANE_API_KEY environment variable of the machine that will call the API.
2. Read your store
Call GET /v1/store. It works with any key and answers with the store’s plan, the limits in force, the scopes your key carries and the store’s monthly quiz credits (credits.limit is a number or "unlimited"; used counts the current billing cycle):
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()store = r.json()print(store["plan_class"], store["scopes"], store["limits"]["capacity"])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()}`);const store = await res.json();console.log(store.plan_class, store.scopes, store.limits.capacity);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 { PlanClass string `json:"plan_class"` Scopes []string `json:"scopes"` Limits struct { Capacity int `json:"capacity"` } `json:"limits"` } json.NewDecoder(res.Body).Decode(&store) fmt.Println(store.PlanClass, store.Scopes, store.Limits.Capacity)}{ "plan_class": "plus", "limits": { "capacity": 72, "leak_per_second": 12, "bot_multiplier": 3, "analytics_soft_ttl_seconds": 60, "webhook_endpoints": 5, "webhook_sends_per_second": 50 }, "scopes": ["analytics:read", "quizzes:read"], "credits": { "limit": 50000, "used": 12450, "overages_enabled": true, "threshold": 60000 }}limits are explained on Rate limits (analytics_soft_ttl_seconds is how long an analytics answer is served before a refresh; see Analytics).
3. List your quizzes
curl "https://api.octaneai.com/v1/quizzes?status=published" \ -H "Authorization: Bearer $OCTANE_API_KEY"import osimport httpx
r = httpx.get( "https://api.octaneai.com/v1/quizzes", params={"status": "published"}, headers={"Authorization": f"Bearer {os.environ['OCTANE_API_KEY']}"}, timeout=30)r.raise_for_status()for quiz in r.json()["data"]: print(quiz["id"], quiz["name"]) # quiz_2ccb8aebb27c463fa783cafb3bf858e0 Skin routine finderconst res = await fetch("https://api.octaneai.com/v1/quizzes?status=published", { headers: { Authorization: `Bearer ${process.env.OCTANE_API_KEY}` },});if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);const quizzes = await res.json();for (const quiz of quizzes.data) console.log(quiz.id, quiz.name); // quiz_2ccb8aebb27c463fa783cafb3bf858e0 Skin routine finderpackage main
import ( "encoding/json" "fmt" "log" "net/http" "os")
func main() { req, _ := http.NewRequest("GET", "https://api.octaneai.com/v1/quizzes?status=published", 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() var page struct { Data []struct { ID string `json:"id"` Name string `json:"name"` } `json:"data"` } json.NewDecoder(res.Body).Decode(&page) for _, q := range page.Data { fmt.Println(q.ID, q.Name) // quiz_2ccb8aebb27c463fa783cafb3bf858e0 Skin routine finder }}Every quiz comes back as a summary with a quiz_ id; that id is what the analytics, the people and the webhook payloads name. See Quizzes and versions.
4. Keep going
Read Authentication and Rate limits, then pick a guide or a Cookbook recipe.
Trying it out
There is no sandbox: every key belongs to a real store. To experiment without touching a live quiz:
- Use a development store, or a draft quiz on your store: a draft is never served to shoppers, and
GET /v1/quizzes?status=draftlists it. - Take the quiz yourself on the storefront with
localStorage['octaneai:debug'] = '1'set in the browser console; every storefront event is logged as it fires, and your own session shows up on the People page and inGET /v1/profiles?q=<your email>a few seconds later. - For webhooks,
POST /v1/webhooks/{endpoint_id}/testsends a signedpingto check your receiver and its signature check; once a real event has been delivered,POST .../deliveries/{delivery_id}/redeliverreplays it as often as you like, with the samewebhook-id. - Every request you make is listed under Developer > API logs with its
Request-Id.
Ids
Every object has a prefixed id: quiz_ (quiz), ver_ (saved version), sess_ (a quiz session), prof_ (a person), exp_ (an export), whe_ (a webhook endpoint), whd_ (a delivery), evt_ (an event), vis_ (a visitor). A resource id means the same thing on the REST API, in a webhook payload and in a storefront event (event ids are the exception: the browser mints its own evt_ ids). Product and variant ids are Shopify GIDs (gid://shopify/Product/..., gid://shopify/ProductVariant/...).
Good to know
- Every response carries a
Request-Idheader. Quote it when you write to support; it finds the request in your store’s API logs. - Errors are RFC 9457 problem bodies with a stable machine code in
title; see Errors. - The API only grows within
/v1; see Versioning. - Field names are
snake_caseon the REST API, in webhook payloads and in storefront event objects (the storefront’s methods and options are JavaScript-style:getState,quizId); dates and times are ISO 8601 in UTC unless atzparameter says otherwise.
