Create your secret websiteID.
Sign in to your Neurvance account, open the Cube dashboard, and subscribe with Stripe. Each subscription allows a maximum of 100 API calls per hour per account, shared across all sessions and endpoints in a rolling 60-minute period. Once payment is verified, choose Create API key and copy your secret websiteID. Canceling renewal keeps API access until the paid period ends. If a renewal payment fails, update your payment method in Manage billing to restore access after payment is verified.
- Choose Create API key in the dashboard.
- Copy the complete key when it appears. It is shown only once.
- Save this one secret as SHOP_WEBSITE_ID on your server. It is also your API key.
Your websiteID is your secret API key. These names refer to one credential. It is shown only when created or rotated. The backend keeps its account identifiers internal; you do not need a separate public ID.
How do I generate a replacement credential?
With an active subscription, choose Rotate API key. Copy the new secret websiteID and update SHOP_WEBSITE_ID on your website server. The old credential immediately stops working. Saved observations and sessions are preserved; queued retries keep their original sequence, timestamp, and data while using the replacement credential.
Keep the key on your server.
The browser sends observations to your own server. The secret websiteID/API key stays on that server, and websiteID must be masked in any request viewer. Your server adds the secret key and forwards them to Cube over HTTPS. Never put the key in frontend code, browser storage, URLs, or logs. After session finalization, Cube automatically uploads each labeled observation. The only customer-callable business operations are perbuy, optimizechance, recommendproducts, and spendmoretime. There is no generic function-dispatch endpoint and no direct customer uploadData endpoint.
SHOP_API_URL=https://YOUR_CUBE_APP.herokuapp.com
SHOP_WEBSITE_ID=YOUR_SECRET_WEBSITEID_API_KEYUse the API origin and secret websiteID supplied with your Cube account. There is no separate API key value to configure. Add these values in your hosting provider’s private environment settings. With Python, install requests and use this helper:
import os
import requests
# Run on YOUR WEBSITE SERVER. Never send this key to a browser.
def cube_request(path, payload):
response = requests.post(
os.environ["SHOP_API_URL"].rstrip("/") + path,
headers={"Authorization": "Bearer " + os.environ["SHOP_WEBSITE_ID"]},
json={**payload, "websiteID": os.environ["SHOP_WEBSITE_ID"]},
timeout=(3, 25),
allow_redirects=False,
)
response.raise_for_status()
return response.json()Call this helper from your own protected server routes. Bind every visitor session to the visitor who created it, check browser request origins and CSRF tokens, and keep any retry queue only in RAM. Do not expose an unrestricted relay that accepts another visitor’s session ID.
Send a sample every 10 seconds.
At website entry, call POST /api/v1/sessions/start from your server with websiteID, session_id, and visitor_id. Keep only opaque identifiers across page navigation, resume with the same identifiers and resume: true, and continue from the returned sample_seq. Its started_at and deadline are Unix seconds. Wait ten seconds before the first sample, then send one every ten seconds.
Create a unique session_id for each visit and a pseudonymous visitor_id. Keep them stable across page navigation. Start sample_seq at 1 and increase it for each new sample.
Send JSON to POST /api/v1/savedata using Authorization: Bearer YOUR_SECRET_WEBSITEID_API_KEY and Content-Type: application/json.
{
"websiteID": "YOUR_SECRET_WEBSITEID_API_KEY",
"session_id": "visit-unique-123",
"visitor_id": "visitor-456",
"sample_seq": 1,
"captured_at": "2026-09-11T14:30:00Z",
"data": {
"avgSpeed": 100,
"mouseClicksTotal": 10,
"colorsSeen": ["green", "blue"],
"productsSeen": ["cup", "vase"],
"productsviews": 4,
"productsSold": 0,
"productsClicked": 2,
"mouseSide": "left",
"mouseUpsideDown": "up",
"time": 10,
"temperature": 20,
"rain": false,
"day": "Friday",
"lastBought": null,
"lastBoughtItem": ""
}
}Replace the example observations with real values from your site and use the sample’s actual timestamp. In Python, pass this payload to cube_request("/api/v1/savedata", payload).
A 201 response with status: buffered means the sample is held only in backend RAM. It is not saved to Supabase yet. Retry with the same sequence, timestamp, and data if delivery is uncertain; identical retries return 200. Changed retries return 409. Keep retries only in RAM until acknowledged. Never write observations to browser storage, files, Redis, or another database. A backend restart loses unfinished observations.
Field types and validation
- Send every field shown in
data. Purchase labels belong in the completion request. - Counts are nonnegative integers up to 1,000,000,000. Speed is finite and nonnegative; temperature is finite, with an absolute limit of 1,000,000,000.
colorsSeenandproductsSeenhold the distinct colors and product IDs encountered so far. Supply both lists, with up to 64 nonempty strings each and 128 characters per string. No control characters. LegacyColorstext is optional when these lists are present.lastBoughtItemallows up to 256 characters without control characters.- Directions are
left/rightandup/down. Time is elapsed seconds on the website, from 0 to 21600; day is a full English weekday. rainis a boolean;lastBoughtis a validYYYY-MM-DDdate ornull.- Visitor/session IDs use 1–128 letters, digits, underscores, dots, colons or hyphens. Sequence numbers range from 1 to 1,000,000.
captured_atneeds a timezone and cannot be more than five minutes in the future. Requests are limited to 64 KiB.
A visibility-change handler can attempt a final flush to your server, but browser closure and background timers are not reliable completion signals. A sample that never reaches a server cannot be saved by Cube.
Example: 120 seconds → 12 buffered observations → buy 2 items → label every input boughtNumber 2 → Cube internally uploads each of the 12 inputs. Only the merged result is saved in cubeWebsiteData.
04 / FINAL PURCHASE OUTCOMELabel the whole session.
Your server’s order system must verify payment and associate orders with the Cube session. Add up the quantity of products in that session’s paid orders and deduplicate by order ID. Never trust a browser’s claim that an order was paid.
After verified purchase and delivery of all samples, send POST /api/v1/sessions/complete with the same authorization header:
{
"websiteID": "YOUR_SECRET_WEBSITEID_API_KEY",
"session_id": "visit-unique-123",
"last_sample_seq": 12,
"boughtNumber": 2
}boughtNumber is product quantity, not money spent. This example labels all 12 samples didBuy=1 and boughtNumber=2. A confirmed quantity of zero labels every sample zero.
Deliver all samples from 1 through last_sample_seq before completion. Missing samples return 409 samples_incomplete. Identical completion retries succeed; a different definitive outcome returns a conflict.
What if the visitor leaves without buying?
On page departure, your server calls POST /api/v1/sessions/exit with websiteID and session_id. Send the browser-to-server request with keepalive and include the connection_id returned by session start, so a delayed exit from the previous page cannot close the resumed session. Cube waits five seconds for same-site navigation or reload to resume through /api/v1/sessions/start, then labels every buffered sample boughtNumber: 0. Switching tabs alone is not an exit.
Missed exit signals are handled at six hours after session start, even if samples kept arriving: label zero and upload each sample individually. Before an external checkout redirect, your server calls /api/v1/sessions/checkout to suppress exit finalization while payment is processing. The six-hour deadline still applies.
The outcome is frozen when finalization starts. There is no later correction window. A 202 completion response means uploads are running; poll /api/v1/sessions/status with the same two identifiers until state: completed. Retries do not reapply successful merges. A restart can lose remaining uploads and report interrupted.
Ask about the current visit.
Customers may call only these four business endpoints. All use the same Bearer key and body:
POST /api/v1/perbuy— buying estimate.POST /api/v1/optimizechance— direction and shop recommendations.POST /api/v1/recommendproducts— product recommendation.POST /api/v1/spendmoretime— time-spending recommendation.
Each request uses this body:
{
"websiteID": "YOUR_SECRET_WEBSITEID_API_KEY",
"session_id": "visit-unique-123"
}Example buying estimate:
{
"session_id": "visit-unique-123",
"sample_seq": 12,
"probability_percent": 75.0,
"estimated_product_quantity": 2.5
}Example optimization response:
{
"session_id": "visit-unique-123",
"sample_seq": 12,
"direction": "down",
"recommendations": [
"moreProducts()",
"favoritLeft()",
"changeColors([\"green\", \"blue\"])",
"tryProducts([\"cup\", \"vase\"])"
]
}Cube reads the current session from RAM and builds userData server-side; callers cannot submit arbitrary user data. Your request needs only the secret websiteID and session_id. The returned sequence tells you which observation was evaluated. These calls do not save their request or result, and Cube does not provide a local fallback calculation. Unknown operation names and typo aliases such as optimizechacne and uplaodaata are rejected.
You need at least 500 usable completed history rows before predictions work; the dashboard shows this gate as 70% readiness and reaches 100% at 1,000 rows. Receipt/outcome metadata does not count, and merging may produce fewer rows than uploads; optimization also needs at least two samples in the current session. 422 insufficient_data means more data is needed. An empty recommendation list can be a successful result.
Results are estimates from the current matching method. Quantity is the mean among selected comparisons; recommendations are shop actions: moreProducts(), lessProducts(), favoritLeft(), favoritRight(), changeColors([...]), and tryProducts([...]). Parse and apply only supported actions; never evaluate strings as JavaScript. ChatGPT reports are not included.
Download your merged history.
Choose Download CSV in the Cube dashboard. The download streams directly from cubeWebsiteData; Cube creates no stored CSV or other observation copy. It contains merged history, so twelve individual uploads can produce fewer than twelve rows.
Active observations exist only in RAM until their outcome is known. Old migrated history is marked legacy, with unknown old purchase quantities left blank. Secrets never appear in exports.
Handle retries deliberately.
| Response | What to do |
|---|---|
| 401 | Check the secret key. It may be expired, revoked, or replaced. |
| 403 | Check the paid subscription and website ownership. An expired or unpaid subscription returns inactive_subscription; open Manage billing to renew or update your payment method. |
| 409 | Inspect the conflict: changed retry, missing samples, closed session, or conflicting final outcome. |
| 413 | Reduce the body below 64 KiB. |
| 422 | Collect the current sample and enough completed history. |
| 429 | Wait for Retry-After before retrying the same request. |
| 500 / 503 | Retry temporary failures with backoff. Contact support for repeated calculation failures or a capacity error. |
Subscription limit: maximum 100 API calls per hour per account across all /api/v1/ endpoints and sessions, measured over a rolling 60-minute period. Session starts, samples, completions, predictions, and retries admitted by the hourly limit all count, even if later validation fails. Calls rejected by the hourly limit do not extend the wait. Key rotation does not reset usage. Dashboard and billing actions do not use this allowance. Budget sampling and prediction calls across your visitors; a ten-second sample cadence can exhaust the allowance before an hour ends. On HTTP 429, wait for Retry-After.
Additional short-term limits: 600 collection/completion requests per minute per account, 12 samples per minute per session, and 60 calculation requests per minute per account. Retries count toward these limits.
Calculations use up to 20,000 completed history rows. A session can buffer at most 2,160 samples during its six-hour lifetime. Capacity errors are explicit; do not discard unacknowledged samples or treat a failed request as a saved observation.