Skip to content

Wizard

Wizard

Wizard is a mixture model for EEG classification. Describe a task in a sentence and send labeled trials in any 10-10 or 10-20 layout, of any length. Wizard selects compatible methods automatically, tests them on your data, and hosts the one that does best as your task model, with its held-out accuracy and a prediction route.

How Wizard works

  1. You describe the task and send labeled trials: one epoch per trial, the channel names, the sampling rate, the units, one label per trial, and a sentence saying what the task is and what each label means. The format is on Dataset format.
  2. Wizard selects compatible methods automatically. Your task, channels and available evidence determine which methods can be evaluated.
  3. Your description guides the choice. Wizard reads your description and the dataset’s shape, never the signal, to decide which methods to test.
  4. Your data decides. Each method tested is fitted on part of your trials and scored on the rest. Validation guides selection, with reference evidence retained when few labels make scores uncertain.
  5. The winner is refitted on all your trials, cross-validated and hosted under an id with its own prediction route, pinned to the exact version it was tested with.

Pretrained encoders are not retrained on your data. Each turns a trial into features, and a linear classifier on those frozen features (a frozen probe) is what your labels fit. Classical methods compute their features from the trials and are fitted per task.

Inputs

  • Trials shaped (trials, channels, samples), any length: an ERP epoch of 1 s, a motor-imagery trial of 4 s, a 30 s sleep epoch. Cut them around your own event markers; Wizard does not see triggers.
  • Channels: any 10-10 or 10-20 layout (3, 19, 64 channels…), one name per row. The task model remembers the names; predictions send the same names in any order. What each kind of method does with a layout is on Signals & montages.
  • Sampling rate: any; resampled for each method. Units: uV, mV or V.
  • Labels: at least two classes and at least 5 trials per class; 20 or more per class are recommended.
  • Description: at most 100 characters. Wizard reads it; it never sees the signal.
  • Sessions (optional): one session name per trial. With two or more, validation holds a whole session out.

One task model

Wizard chooses the method. The create fee ($1.00) is paid once and includes the fit. Hosting is $1.00 a month per model. Predictions are billed per window ($0.003).

Task selection

Wizard uses your task description and recording configuration to evaluate eligible methods. The response reports the task category and any practical limitations. Trial duration can affect accuracy; follow the returned warnings.

How your data decides

  1. Each method tested turns every trial into features once (long trials are cut into the method’s windows and averaged).
  2. A frozen probe per method is fitted on a stratified 75% of your labeled trials and scored on the other 25% by balanced accuracy (the mean of per-class recall, so a majority class cannot inflate it). With two or more sessions, a whole session is held out instead.
  3. The method with the best validation balanced accuracy wins.
  4. The winner’s probe is refitted on all your trials and cross-validated (training_report).

What comes back

A task model document carries its id (clf_ followed by 32 hex digits), status, category, classes, trials_per_class, channels, training_report(validation accuracy and chance), create_usd and hosting_usd_per_month.

Timestamps (created, expires_at, paid_through, next_charge_at) are Unix epoch seconds. Predictions return, per trial, a label and the probabilities of every class.

The validation scores are measured on your own labels. The winner’s score is optimistic, because it was selected after comparing methods on the same split: for a figure to report, predict on trials the model has not seen.

Supported tasks and limits

  • Supported categories. The benchmark evidence covers event-related potentials (erp), motor imagery and movement (motor) and sleep staging (sleep). Any other task is unknown and still fitted: the test on your labels decides.
  • Refused: emotion or affect recognition and other emotional or neurological profiling of a person, and clinical or diagnostic uses (detecting a disorder, grading a condition, screening), with 422 TASK_UNSUPPORTED at the dry run and at create. A task is refused only on positive evidence in the description or labels, never by default. The licences of the pretrained models Wizard uses forbid profiling without the person’s informed consent, which an API cannot check, and high-stakes decisions about people; a task model is not a medical device. See Acceptable use.
  • Performance. Dydema makes no accuracy claim for your task. The only measurement that applies is the validation score in your training_report, taken on your own held-out trials.
  • Few trials. Below 5 per class the create is refused (422 TOO_FEW_TRIALS). With few trials the validation split is small and its score is noisy.
  • Channels. Predictions must send the channel names the model was fitted on, in any order. The legacy names T3, T4, T5 and T6 are accepted and read as T7, T8, P7 and P8.
  • Research use only. Not validated for diagnosis or for any decision about a person.

Epoch each trial from its event marker:

TaskEpoch per trial
ERP (event-related potentials)1 s from the stimulus (0 to 1 s)
Motor imagery or movement0 to 4 s from the cue (1 to 4 s also works: it leaves out the response to the cue itself)
Sleep staging30 s scoring epochs

Zero-shot: no labeled trials

In the dashboard, generic classifiers and device commands can use your own output labels, such as “fan”, “lights on” and “lights off”. Wizard looks for an available EEG task with that number of classes and shows an Explanation with the action for each label before Create. The saved model keeps these instructions and the reference evidence. The labels refer to those actions, not arbitrary thoughts. There is no special two- or three-class limit; reference coverage determines which tasks can be proposed. The API accepts up to 64 labels per request.

With the API, opt in with choose_task: true and dry_run: true. Review the returned explanation, then send the same request with dry_run: falseand its returned reference_classes mapping. A changed mapping requires a new estimate. Specific tasks still need matching reference data; a proposal does not measure accuracy on your recordings. Missing reference coverage does not prove that a distinction is impossible. Weak reference results are disclosed before creation.

A three-command proposal may map your labels to left-hand, right-hand and feet imagery. Follow the proposed actions for each trial; your application translates the returned label into a device command. Every trial returns a class, including during rest: there is no automatic idle detector. ERP can address multiple choices through timed stimuli and aggregation of target responses, but a binary target/non-target reference alone does not supply that command-selection protocol. This direct trial classifier does not provide a stimulus interface.

With no labeled trials yet, send "mode": "zero_shot", a description, the class names and your recording’s shape. Wizard maps each class to a reference class, then fits your task model on reference trials: labeled recordings of other people from public datasets whose licences allow commercial use. You send no signal to create it, and it predicts through the same route as any task model.

Zero-shot is offered only where it measurably works on recordings it has never seen. For each task Wizard held out one dataset at a time, fitted on the others, and scored the held-out one. A task is offered when its mean balanced accuracy is at least 5 points above chance, the lower end of its 95% interval is above chance, and at least 3 datasets were scored. Any other task answers 422 NO_REFERENCE_DATA: send labeled trials.
TaskAccuracy on unseen datasetsDatasets scored
eyes closed (resting) vs eyes open (resting)5 s trials63% (95% lower bound 51%; chance 50%)4
eyes closed (resting) vs eyes open (resting)5 s trials61% (95% lower bound 51%; chance 50%)4

Measured and not offered, because the gate above was not met: imagined left-hand movement vs imagined right-hand movement (52% over 9 datasets, chance 50%); imagined feet movement vs imagined left-hand movement vs imagined right-hand movement (33% over 1 dataset, chance 33%); correct response vs error (incorrect response) (51% over 2 datasets, chance 50%); non-target stimulus vs attended target stimulus (P300/oddball) (54% over 9 datasets, chance 50%). Wizard with labeled trials still works for these tasks.

  • Send mixed batches. A model shown above as re-centring each request expects every prediction request to mix classes: at least twice as many trials as classes (422 TOO_FEW_TRIALS below that). "align": "none" predicts single trials, with a warning that accuracy may differ from the figure above.
  • Expect lower accuracy than with your own labels. The model has never seen your headset, your lab or your participants. When you have labeled trials, create a task model with them.
  • Cut trials like the reference ones (the trial length is under each task above); other lengths answer with a warning.
  • Refused as for every task model (422 TASK_UNSUPPORTED): emotion or affect recognition, clinical or diagnostic uses, identifying, re-identifying or looking up a specific person (biometrics), and predicting a person’s age, sex or gender. Class names that are people (sub-03) or demographic groups are refused too.
  • Data rights. Reference trials come only from datasets licensed for commercial use (CC0, CC-BY, ODC-By; no share-alike, no-derivatives, non-commercial or restricted-access data). Each zero-shot model is used subject to its applicable licence. No reference recordings or source datasets are distributed by this API.
  • Cost. The create fee and monthly hosting, as for any task model; nothing is embedded at create. Predictions are billed per window.
ParameterTypeDescription
moderequiredstring"zero_shot".
descriptionrequiredstringAt most 100 characters: the task and what each class means.
classesoptionalstring[]2 to 64 class names, answered with these names. Without them Wizard proposes the classes of the task that fits the description.
channels, sampling_rate_hz, trial_secondsrequiredstring[], number, numberThe layout, rate and trial length you will predict with (no signal).
choose_task, reference_classesoptionalboolean, objectAsk Wizard to propose a task for your labels; send back the estimate's reference_classes unchanged when you create.
name, expires_at or expires_in_days, dry_runoptionalAs for a task model with labels.

The answer is the task model document with mode: "zero_shot", n_trials: 0, an evidence field with aggregate internal evaluation scores, and the supported class names. These scores do not establish performance on your recordings.

Some tasks need reference data prepared before the first estimate can be answered. The estimate starts that preparation itself and answers "ready": false with a preparation status instead of evidence; nothing is charged and there is nothing else to call. Repeat the same estimate after a few minutes until it is ready, then create. A create sent while preparation is still running answers 202 with no task model and no create fee or hosting charge; retry it with the same Idempotency-Key.

POST /v1/task-models (zero-shot dry run)
Python
import os
import requests

response = requests.post(
    'https://dydema--eegapi-gateway.modal.run/v1/task-models',
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    json={"mode":"zero_shot","description":"resting state: eyes open versus eyes closed","classes":["eyes open","eyes closed"],"channels":["Fp1","Fp2","F7","F3","Fz","F4","F8","T7","C3","Cz","C4","T8","P7","P3","Pz","P4","P8","O1","O2"],"sampling_rate_hz":250,"trial_seconds":5,"dry_run":True},
    timeout=120,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
answer = response.json()
if answer.get("ready") is False:
    # Reference data for this task is being prepared, which this estimate started. Run it again in a few minutes.
    raise SystemExit(f"preparing: {answer['preparation']['status']}")
print(answer["explanation"])  # what the participant does for each class
print(answer["reference_classes"])  # your classes -> the actions they were matched to
print(answer["evidence"])  # measured on recordings the model never saw
print(answer["classes"])
POST /v1/task-models (zero-shot)
Python
import os
import requests

response = requests.post(
    'https://dydema--eegapi-gateway.modal.run/v1/task-models',
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    json={"mode":"zero_shot","description":"resting state: eyes open versus eyes closed","classes":["eyes open","eyes closed"],"channels":["Fp1","Fp2","F7","F3","Fz","F4","F8","T7","C3","Cz","C4","T8","P7","P3","Pz","P4","P8","O1","O2"],"sampling_rate_hz":250,"trial_seconds":5,"expires_in_days":1},
    timeout=120,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
answer = response.json()
open("zero_shot_id.txt", "w").write(answer["id"])  # the delete example reads it
print(answer["id"], answer["classes"], answer["evidence"])
DELETE /v1/task-models/{id} (the zero-shot model)
Python
import os
import requests

ZERO_SHOT_ID = open("zero_shot_id.txt").read().strip()  # written by the zero-shot create example

response = requests.delete(
    f'https://dydema--eegapi-gateway.modal.run/v1/task-models/{ZERO_SHOT_ID}',
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    timeout=30,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
print(response.json())

API reference

Every example runs as written: the create example saves the new model’s id in task_model_id.txt, and the predict, expiry and delete examples read it. The Python and JavaScript examples build 40 synthetic trials (a 10 Hz rhythm over C4 or C3, plus noise); the cURL examples write 20 of them as a JSON body with awk. None of it is real EEG.

Routes
POST   /v1/task-models  {"dry_run": true}     estimate and create fee; nothing fitted or charged
POST   /v1/task-models                        test, fit, host                              -> active
       for a dataset larger than one request:
POST   /v1/task-models  {"defer_fit": true}   first batch                                  -> collecting
POST   /v1/task-models/{id}/trials            each further batch
POST   /v1/task-models/{id}/fit               test, fit, host                              -> active
POST   /v1/task-models/{id}/predictions       trials in, label and class probabilities out
GET    /v1/task-models, /v1/task-models/{id}  list, read
PATCH  /v1/task-models/{id}                   expires_at (or null), name
DELETE /v1/task-models/{id}                   stop hosting                                  -> deleted

Dry run

The create body with "dry_run": true answers 200 with category, create fee, monthly hosting and limitations. Nothing is fitted or charged.

200 OK (shape)
{
  "object": "task_model.dry_run",
  "category": "motor",
  "create_usd": "...",
  "hosting_usd_per_month": "...",
  "warnings": []
}
POST /v1/task-models (dry run)
Python
import os
import json
import base64
import numpy as np
import requests

API = 'https://dydema--eegapi-gateway.modal.run/v1'
CHANNELS = ["Fp1","Fp2","F7","F3","Fz","F4","F8","T7","C3","Cz","C4","T8","P7","P3","Pz","P4","P8","O1","O2"]

# Synthetic stand-in, NOT real EEG: 40 trials of 4 s, 19 channels at 128 Hz, in microvolts. "left" trials carry
# a 10 Hz rhythm over C4 and "right" trials over C3, on top of noise. Replace with your own trials and labels.
rng = np.random.default_rng(40)
t = np.arange(4 * 128) / 128
labels = ["left", "right"] * 20
x = 10 * rng.standard_normal((len(labels), len(CHANNELS), t.size))
for i, label in enumerate(labels):
    x[i, CHANNELS.index("C4" if label == "left" else "C3")] += 20 * np.sin(2 * np.pi * 10 * t)
x = np.ascontiguousarray(x, dtype="<f4")  # shape (trials, channels, samples), sent as base64 float32

response = requests.post(
    f"{API}/task-models",
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    json={
        "x": base64.b64encode(x.tobytes()).decode("ascii"),
        "shape": list(x.shape),
        "sampling_rate_hz": 128,
        "units": "uV",
        "channels": CHANNELS,
        "labels": labels,  # one per trial, in the order of x
        "description": "Motor imagery: after a cue, imagine squeezing the left or the right hand. Labels: left, right.",
        "name": "hand imagery (example)",
        "expires_in_days": 1,  # hosting stops after a day; leave it out to keep the model until you delete it
        "dry_run": True,  # only the estimated cost and warnings: nothing is fitted or charged
    },
    timeout=300,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
print(json.dumps(response.json(), indent=2))  # the estimated cost and limitations

Create

POST /v1/task-models answers 201 with the task model document. The body:

ParameterTypeDescription
x, shaperequirednumber[][][]Trials shaped (trials, channels, samples), as nested lists or base64 little-endian float32 with shape.
channelsrequiredstring[]One 10-10 or 10-20 name per channel row.
sampling_rate_hz, unitsrequirednumber, stringThe rate of x, and uV, mV or V.
labelsrequiredstring[]One per trial, in the order of x.
descriptionrequiredstringAt most 100 characters: what the task is and what each label means.
sessionsoptionalstring[]One session name per trial; validation keeps sessions apart.
nameoptionalstringShown in listings and on the dashboard.
expires_at or expires_in_daysoptionalstring, numberAn ISO 8601 date (00:00 UTC that day) or date-time (naive = UTC), or a number of days; at most 3,650 days away. Hosting stops then. Without either, the model is kept until deleted. Answered as epoch seconds.
dry_runoptionalbooleanEstimate only: the create fee, hosting and warnings; nothing fitted or charged.
defer_fitoptionalbooleanKeep this first batch’s features and fit later, after /trials.
stride_secondsoptionalnumberHow a trial at least two model windows long is cut into windows (Signals & montages).
POST /v1/task-models
Python
import os
import json
import base64
import numpy as np
import requests

API = 'https://dydema--eegapi-gateway.modal.run/v1'
CHANNELS = ["Fp1","Fp2","F7","F3","Fz","F4","F8","T7","C3","Cz","C4","T8","P7","P3","Pz","P4","P8","O1","O2"]

# Synthetic stand-in, NOT real EEG: 40 trials of 4 s, 19 channels at 128 Hz, in microvolts. "left" trials carry
# a 10 Hz rhythm over C4 and "right" trials over C3, on top of noise. Replace with your own trials and labels.
rng = np.random.default_rng(40)
t = np.arange(4 * 128) / 128
labels = ["left", "right"] * 20
x = 10 * rng.standard_normal((len(labels), len(CHANNELS), t.size))
for i, label in enumerate(labels):
    x[i, CHANNELS.index("C4" if label == "left" else "C3")] += 20 * np.sin(2 * np.pi * 10 * t)
x = np.ascontiguousarray(x, dtype="<f4")  # shape (trials, channels, samples), sent as base64 float32

response = requests.post(
    f"{API}/task-models",
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    json={
        "x": base64.b64encode(x.tobytes()).decode("ascii"),
        "shape": list(x.shape),
        "sampling_rate_hz": 128,
        "units": "uV",
        "channels": CHANNELS,
        "labels": labels,  # one per trial, in the order of x
        "description": "Motor imagery: after a cue, imagine squeezing the left or the right hand. Labels: left, right.",
        "name": "hand imagery (example)",
        "expires_in_days": 1,  # hosting stops after a day; leave it out to keep the model until you delete it
    },
    timeout=300,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
task = response.json()
print(task["id"], task["status"])  # active: fitted and hosted
print(json.dumps(task["training_report"], indent=2))  # validation accuracy and chance
with open("task_model_id.txt", "w") as f:  # the next examples read it
    f.write(task["id"])

Datasets larger than one request

A request carries at most 4,000,000 numbers (trials × channels × samples, as sent and again after resampling) and 4,096 trials. Beyond that, create with "defer_fit": true and the first batch (status collecting), send each further batch to POST /v1/task-models/{id}/trials (signal fields, labels, optional sessions), then call POST /v1/task-models/{id}/fit, which tests and fits on everything collected. The methods to test are fixed at creation, and each batch is turned into features for each of them when it arrives; those windows are included in the create fee. Only the features are kept, never the signal, and they are deleted at /fit, or after 7 days if /fit never comes. The helper on Dataset format does this.

Predictions

POST /v1/task-models/{id}/predictions takes the signal fields without labels and returns one item per trial in data, in the order sent, with label and the probabilities of every class, computed with the pinned model version. An unknown id answers 404 CLASSIFIER_NOT_FOUND; an expired or deleted model answers the same generic unavailable response. Send an Idempotency-Key header and reuse it on a retry so a retried request is answered once and billed once (Retrying).

POST /v1/task-models/{id}/predictions
Python
import os
import base64
import numpy as np
import requests

API = 'https://dydema--eegapi-gateway.modal.run/v1'
CHANNELS = ["Fp1","Fp2","F7","F3","Fz","F4","F8","T7","C3","Cz","C4","T8","P7","P3","Pz","P4","P8","O1","O2"]  # the channel names the model was fitted on (any order)
TASK_MODEL_ID = open("task_model_id.txt").read().strip()  # written by the create example

# Synthetic stand-in, NOT real EEG: 6 trials of 4 s, 19 channels at 128 Hz, in microvolts. "left" trials carry
# a 10 Hz rhythm over C4 and "right" trials over C3, on top of noise. Replace with your own trials and labels.
rng = np.random.default_rng(6)
t = np.arange(4 * 128) / 128
labels = ["left", "right"] * 3
x = 10 * rng.standard_normal((len(labels), len(CHANNELS), t.size))
for i, label in enumerate(labels):
    x[i, CHANNELS.index("C4" if label == "left" else "C3")] += 20 * np.sin(2 * np.pi * 10 * t)
x = np.ascontiguousarray(x, dtype="<f4")  # shape (trials, channels, samples), sent as base64 float32

response = requests.post(
    f"{API}/task-models/{TASK_MODEL_ID}/predictions",
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    json={"x": base64.b64encode(x.tobytes()).decode("ascii"), "shape": list(x.shape),
          "sampling_rate_hz": 128, "units": "uV", "channels": CHANNELS},
    timeout=120,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
for truth, prediction in zip(labels, response.json()["data"]):  # one per trial, in the order sent
    print(f"sent {truth}, predicted {prediction['label']}", prediction["probabilities"])

Expiry and deletion

PATCH /v1/task-models/{id} sets expires_at (an ISO date or date-time, or null to keep the model until deleted) and name. DELETE is idempotent: deleting a deleted model answers 200 again. The dashboard lists your task models and sets expiry or deletes them too.

PATCH /v1/task-models/{id}
Python
import os
from datetime import date, datetime, timedelta, timezone
import requests

TASK_MODEL_ID = open("task_model_id.txt").read().strip()  # written by the create example
expires = (date.today() + timedelta(days=30)).isoformat()  # or None: keep it until you delete it

response = requests.patch(
    f'https://dydema--eegapi-gateway.modal.run/v1/task-models/{TASK_MODEL_ID}',
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    json={"expires_at": expires, "name": "hand imagery (kept for a month)"},
    timeout=30,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
task = response.json()
expires_at = datetime.fromtimestamp(task["expires_at"], timezone.utc)  # timestamps are epoch seconds
print(task["id"], task["name"], "expires", expires_at.isoformat(), "paid through", task["paid_through"])
DELETE /v1/task-models/{id}
Python
import os
import requests

TASK_MODEL_ID = open("task_model_id.txt").read().strip()  # written by the create example

response = requests.delete(
    f'https://dydema--eegapi-gateway.modal.run/v1/task-models/{TASK_MODEL_ID}',
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    timeout=30,
)
if not response.ok:
    error = response.json()
    raise SystemExit(f"{response.status_code} {error['code']}: {error['message']} {error['alternatives']}")
print(response.json())  # deleted: no further hosting charge; the current month is not refunded
StatusMeaning
collectingCreated with defer_fit: batches are added with /trials and billed per window; the create fee is charged at the fit, nothing is hosted yet. Deleted with its collected features if /fit does not come within 7 days.
activeFitted and hosted: predictions work, and hosting is charged a month at a time, in advance.
expiredIts expires_at has passed: GET and predictions answer 404 CLASSIFIER_NOT_FOUND; nothing more is charged.
deletedDeleted: GET and predictions answer 404 CLASSIFIER_NOT_FOUND; nothing more is charged.

Billing

  • Create fee: $1.00, once per model (the document’s create_usd). It includes every window of the fit itself. Dry runs and failed fits are free (a dry run is refused with 402 INSUFFICIENT_CREDIT when the balance cannot cover the create fee and the first month of hosting). A model created with defer_fit pays its collected batches (the first and every /trials batch) per window, and the create fee at its successful /fit.
  • Hosting: $1.00 a month per model (the document’s hosting_usd_per_month), charged in whole 30-day months in advance: the first month when the model is fitted, then every 30 days, until expires_at or deletion. paid_through and next_charge_at say where a model stands. Nothing is charged after expiry. Deleting early stops later months and does not refund the current one.
  • Predictions: billed per window ($0.003); see pricing. The X-Usage-Usd header on each response gives the cost.
  • Test keys pay nothing, and their task models are deleted after 30 days.
  • If the balance is at or below the credit cutoff when a month falls due, that month is not charged and the model keeps working; after 30 days like that, overdue models are deleted (with an email warning 7 days before).
  • In the dashboard’s Logs these appear as task_models.create_fee, task_models.hosting and task_models.predict. All of it comes off the prepaid balance.
Treat task models, their predictions and the trials you send as personal data, like the recordings they come from. Remove names, dates of birth and other direct identifiers, including from labels, session names and the description. Requests are processed in the United States.