# upstratus Docs > How to use upstratus: record examples with your phone, train a model on our GPUs, and download plain C for one of six supported microcontroller boards. upstratus trains small machine-learning models for microcontrollers. You record labelled examples with a phone (motion or sound) or import a CSV of sensor readings, train a model on upstratus's GPUs, and download a zip of plain C source for one of six supported boards, which you then build and flash with that board's own tools. upstratus does not flash boards itself yet. agents/index.md says what an AI agent can and cannot do here. # Start here # upstratus documentation Record labelled examples with your phone, train a small model on our GPUs, and download a package of plain C for one of six supported microcontroller boards. You then build and flash that code with the board's own tools: upstratus does not flash boards for you yet. [Get started](https://upstratus.com/docs/get-started/first-model/index.md) [For AI Agents](https://upstratus.com/docs/agents/index.md) ## What you can do - **Collect data** Scan a QR code with your phone and record motion or sound in the phone's browser. There is no app to install. Sensor readings from a data logger can be imported as a CSV file instead. - **Train a model** Pick a recipe on the Design screen, or describe the task in a sentence and get one suggested. The report tells you which labels the model confuses. It does not quote an accuracy your data cannot support. - **Download C for your board** Package a trained version for a board and download a zip of C source: the model and the kernels it runs on. See [Supported boards](https://upstratus.com/docs/reference/boards/index.md) for the six boards and what each one needs. - **Read an error** Every API error carries a `code` and a link to [its entry](https://upstratus.com/docs/reference/error-codes/index.md), which says what it means and what to do next. ## What upstratus does not do yet These docs describe only what the product does. These things are not built: - **Flashing a board from the browser.** The Collect screen's **Connect board** button is disabled for this reason. You flash the downloaded code with your own tools. - **On-device signal processing in the package.** The downloaded C runs the model on a window of features that has already been computed. Your firmware has to compute those features from the sensor. - **Live labels from a board** and **trying a trained model on your phone**. - **A published API schema.** A small set of endpoints accepts an API key, but no OpenAPI description of them exists yet. See [For AI Agents](https://upstratus.com/docs/agents/index.md). ## How these docs stay accurate The pages are Markdown in the product's own repository and are built by a command. The [Supported boards](https://upstratus.com/docs/reference/boards/index.md) and [Error codes](https://upstratus.com/docs/reference/error-codes/index.md) pages are generated from the same registries the product reads, and a test fails if either page falls out of date. These docs have no screenshots yet: the screenshot pipeline captures them from automated QA runs, and that pipeline is not connected to upstratus yet. # For AI Agents This page is for an AI agent working out whether upstratus can do what its user asked. In short, an agent can read these docs. With the user's API key it can read some of the user's data. It cannot create, change, train or flash anything for the user. ## What you can read | Resource | What it is | | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | [`/llms.txt`](/llms.txt) and [`/docs/llms.txt`](/docs/llms.txt) | A curated index of these docs ([llmstxt.org](https://llmstxt.org)) | | [`/docs/llms-full.txt`](/docs/llms-full.txt) | Every page in one file | | `/docs/.md` | A Markdown copy of any page. For example, `/docs/get-started/first-model.md` | | [Error codes](https://upstratus.com/docs/reference/error-codes/index.md) | Every error `code` the API returns, with its meaning and the fix | | [Supported boards](https://upstratus.com/docs/reference/boards/index.md) | The six boards, what each can sense, and the setup each needs | ## What an API key can do An organisation's owner or admin can create an API key under **Account → API keys**; other members can see the keys but not create or revoke them. Send it as `Authorization: Bearer `. - **A key is read-only.** It works for `GET` and `HEAD` requests only. It is never accepted for a request that changes something. Every `POST`, `PATCH` and `DELETE` needs a signed-in browser session and a CSRF token. - **The key-accepting endpoints are not documented.** About a dozen read endpoints accept a key: projects, their recordings and summaries, training jobs and their logs, trained versions, and the organisation. No OpenAPI schema describes them yet, so their paths and response shapes are not a stable interface. Do not build an integration that depends on them. - A wrong or revoked key gets `401` with the code `invalid_api_key`. For the same reason these docs publish no capability matrix and no `openapi.json`. The estate's docs standard derives both from a live OpenAPI schema, and upstratus has none. Publishing the matrix anyway would mean listing every capability as "no public API", and that would be false. ## What an agent cannot do - **Sign up, sign in or pay** for the user. These are done in a browser by a person. During the alpha, signing up needs an access key, a confirmed email and a verified mobile number, none of which an agent can supply. - **Record data.** Recording uses the sensors of the phone in the person's hand. - **Start training or package a model.** Both are `POST` requests and need a session. - **Flash a board.** upstratus itself does not do this yet. The person builds and flashes the downloaded C with the board's own tools. ## Answering "can upstratus do X?" - Train a small classifier from motion or sound: **yes**, in the web app. An anomaly detector or a value predictor can be trained from motion or imported sensor readings. - Produce C source for one of the [six supported boards](https://upstratus.com/docs/reference/boards/index.md): **yes**. The package holds the model and its kernels, but not the feature-extraction step. - Run on a board that is not one of the six: **no**. - Flash the board, show live labels from it, or run the model on the phone: **not yet**. # Get started # Get started One tutorial takes you from a new account to C source for your board: - [Your first model in 15 minutes](https://upstratus.com/docs/get-started/first-model/index.md): record two gestures with your phone, train a classifier, and download the code for your board. You flash it with your own tools. If you teach a class, [Set up a classroom](https://upstratus.com/docs/get-started/classrooms/index.md) shows how to add students without storing any student's name, email or phone number, and how they join. Before you buy a board, read [Supported boards](https://upstratus.com/docs/reference/boards/index.md). It lists what each board can sense and the setup it needs on your computer before you can program it. # Set up a classroom A classroom account lets a teacher add a class without giving upstratus any student's personal data. Each student is an **alias** you choose, such as *Blue Fox*. A student joins with a one-time code and a password they choose. Their account holds no name, no email address and no phone number. What you need Classroom tools turned on for your account, and a CSV file of your students (a spreadsheet saved as CSV). The Education plan is the plan for classroom accounts; see the pricing page. ## 0. Turn on classroom tools Open **Account**, then **Set up classroom tools**. The Classroom page explains what the tools do; choose **Turn on classroom tools**. The page reloads as the Classroom screen with an empty roster. Any member of the account who is not a student seat can do this; it stores no student data by itself. To turn the tools off again, use **Turn off classroom tools** at the bottom of the Classroom screen. The button is available only while the roster is empty, because a roster row is a student seat; the page says so when it is not available. ## 1. Add students from a file Open **Account**, then **Classroom**. Choose **Roster file (CSV)** and pick your file, then **Upload and add students**. The file needs a header row and one student per row. It may have only two columns: - **alias**: a nickname you choose for the student. Required. - **group**: a table or class group. Optional. Any other column, such as a name or an email address, is refused by name and nothing is added, so no personal data is stored. One file adds up to 200 students. An alias cannot start with `=`, `+`, `-` or `@`, because a spreadsheet would read it as a formula when you copy the codes. Two aliases that differ only by capital letters count as the same alias. ## 2. Hand out the join codes After a good upload, the page shows a **join code** for each new student. **These codes are shown only once**: copy them or write them down before you leave the page. Only a scrambled form of each code is kept, so upstratus cannot show a code again. Use **Copy** on one row, or **Copy all codes**. ## 3. Replace or revoke one student's code Each student who has not joined has two buttons on their row: - **Replace code** gives that student a new code, shown once. The old code stops working at once. - **Revoke code** stops the code working without issuing a new one. The student stays on the roster with **No active code**, and **Issue a new code** gives them one. A student who has already joined has no code to replace. There is no confirmation step on **Revoke code** because **Issue a new code** puts a code back. ## 4. How a student joins Students go to `/education/join/` on the same site and enter: 1. **Join code**: the code from the sheet. Capital or small letters both work. 1. **Choose a password**: at least 8 characters, not a common word and not only numbers. There is no email on file, so there is no password reset. The student should write it down somewhere safe. They choose **Join class** and are signed in. If a code is wrong, the screen says so next to the code box and keeps what they typed, except the password. After 10 wrong codes from one network address, the screen refuses every code from that address for 15 minutes. A visitor who is already signed in sees a note instead of the form, so a teacher who opens this page is not signed out into a student account. ## What a student account can do A student can sign in and use projects in the classroom's organisation. They see the roster and plan screens read-only and cannot change the plan or the roster. There is no grading, no assignment feature and no connection to a school's own systems, so this guide describes none. Errors from the roster API use the codes in the [error reference](https://upstratus.com/docs/reference/error-codes/index.md). # Your first model in 15 minutes You will teach a board to tell two gestures apart: waving your hand, and holding still. You record the examples with your phone, train a model, and download C source built for your board. Then you build and flash it with that board's own tools. The product is designed so that the steps up to the download take about 15 minutes, most of it recording. That is a design target and has not been measured with real users. The time you need for building and flashing depends on your board and on what you already have installed. Before you start - A computer with a web browser, and a phone with a web browser and a camera. You install no app. - The phone must be able to reach upstratus. The QR code opens a web page on the phone. - For the last step: one of the [six supported boards](https://upstratus.com/docs/reference/boards/index.md), a USB cable, and the board's own build tools. upstratus does not flash boards for you yet. ## 1. Create an account upstratus is in **alpha**, so an account needs an **access key** (it looks like `ALPHA-XXXX-XXXX`). Ask for one from **Request an invitation** on the sign-up page; it is emailed to the address it was issued for. If an organisation has invited you, its invitation replaces the key (see [Invite people and manage your organisation](https://upstratus.com/docs/guides/organisations/index.md)). Open **Start free** (or go to `/signup/`) and enter the key, then choose **Continue**. The rest of the form appears. You then confirm three things: 1. **Your email.** Either choose **Continue with Google**, which has already confirmed the address, or enter an **Email** and a **Password** and then the six-digit code we email you. The rules for the password are shown under the field before you submit. The email must be the address the key was issued for. 1. **Your mobile number.** Every account proves a phone number with a text message. A number can belong to one account. 1. **The terms.** Tick the box to accept the Terms and Privacy Policy. You can name your organisation now or leave it. Choose **Create account**. If an account you had before this requirement signs in, it is held on a **Finish your account** page until it has a confirmed email and a verified mobile number. Nothing else works until both are done. You are signed in and taken to an example project, **Wave detector (example)**. Its recordings are made-up example data, so every step already has something to show. A card offers a short tour: choose **Show me around** to see the six steps, or **No thanks**. You can come back to the example at any time from **All projects**. ## 2. Create a project Open the project menu at the top and choose **New project** (or **All projects**, then **New project**). Type a **Project name** such as *wave detector*, choose **Motion** under *What will the model listen to?*, then choose **Create project**. Motion records your phone's motion sensors. The new project opens on its overview. The steps are listed on the left; choose **Data**, then **Record with a phone**. The project's **Collect** screen shows a QR code. ## 3. Record examples with your phone 1. Scan the QR code with your phone's camera and open the link. The phone shows **Connect your phone**. 1. Choose **Allow motion access** and accept the browser's permission prompt. Nothing leaves the phone until you press **Record**. 1. Type `wave` in **Label**. Choose **Record**, wave the phone for a second or two, then choose **Stop**. The recording uploads by itself. If the connection drops, it is kept on the phone and sent when the connection is back. 1. Record `wave` five times, then change the label to `still` and record five takes of holding the phone still. 1. On the computer, choose **New QR** and scan the new code. Record five more of each label. Why the second QR code: each scan is one *session*. upstratus keeps a whole session on one side of its accuracy check. With only one session per label it cannot tell you how accurate the model is, so it does not offer a classifier until every label has at least **5 recordings from 2 or more sessions**. ## 4. Check your recordings On the computer, choose **Label your samples →**. Every recording is listed with its label. Fix a wrong label or delete a bad take here. Then choose **Design your recipe →**. ## 5. Train The **Design** screen opens with the features and model that suit a motion project already selected. Leave them as they are and choose **Train**. Then choose **See its status →**. The status page shows **Pending** until a training worker picks the job up, then **Running** with a live log, then the report. The report gives the recall for each label and which labels the model confused. It compares the full-precision model with the 8-bit model that will run on the board. If you recorded too little data to measure accuracy, the report says so and shows no accuracy figure. ## 6. Package it for your board Go back to **Design** and choose **View past versions →**. On the version you just trained, choose your board in the board list. The list says if a board needs administrator access or a button press to program. Choose **Package for board**. When the package is ready, choose **Download SDK**. You get a zip of C source. It contains the model (`us_model.h`, its weights and graph), the kernels the model needs for your board, the label names (`us_class_labels.h`) and a `NOTICE` file. For the Arduino Nano 33 BLE and the ESP32-S3 it also contains an Arduino example sketch under `examples/arduino/`. ## 7. Build and flash it with your own tools Your firmware calls `us_model_predict()` with one window of input and gets back a label id. Look up the name in `US_CLASS_LABELS`. Add the zip's files to your project and build it with your board's usual toolchain. The toolchain for each board is listed in [Supported boards](https://upstratus.com/docs/reference/boards/index.md). What the package does not do yet - **It does not flash the board.** You flash it the usual way for your board, for example uploading from the Arduino IDE or copying a `.uf2` or `.bin` file onto the board's USB drive. - **It does not include the signal-processing step.** `us_model_predict()` takes a window of features that has already been computed and quantised to 8 bits. Your firmware has to compute those features from the sensor. The Arduino example runs the model once on placeholder input. It shows that the code builds and runs on your board. It does not classify real sensor data. Read the setup notes for your board before plugging it in. Some boards need a button held the first time, and one needs a one-time administrator step on Linux. ## If something goes wrong | What you see | What it means | What to do | | ------------------------------------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------ | | The phone says the QR code has expired or is no longer valid | A code lasts 15 minutes, and **Disconnect** ends it | Choose **New QR** on the computer and scan again | | The phone's permission prompt was refused | The browser blocked the sensor | Allow motion access in the browser's site settings, then reload the page | | **Design** says *Nothing is recorded in this project yet* | The project has no recordings | Choose **Back to Collect** and record some data | | **Design** says *Record a few more first* and lists reasons | At least one label is below the minimum for every model | Choose **Back to Collect** and record what the list names | | A model on **Design** is greyed out with a reason | That model's own data minimum is not met yet | Record what the reason names, or choose a model that is offered | | Training stays **Pending** | No training worker has picked the job up yet | Wait and choose **Refresh** | | **Train** is refused because your allowance is used up | Your plan's GPU minutes for this period are spent | See **Plan and billing** under **Account** | | Packaging fails | The package build failed | Choose **Retry**. Your board choice is kept | Every error the API returns has a code. [Error codes](https://upstratus.com/docs/reference/error-codes/index.md) lists them all with what to do. **Next:** [Supported boards](https://upstratus.com/docs/reference/boards/index.md) has the setup each board needs before you flash it. # Install the desktop app The Upstratus desktop app sets up a supported board over USB. It installs the board's tools, builds the firmware and flashes it, so you never run a toolchain command yourself. It signs in to your account through your browser. Download it from the **Desktop app** page (`/download/`), linked at the top of every page. The page lists the installers built for the site you are on, and puts the one for your computer first. | Platform | Installer | Board setup | | --------------------------------------- | ---------------------- | ------------------------------------ | | Ubuntu, Debian and derivatives (64-bit) | `.deb` | Yes | | Other 64-bit Linux | `.AppImage` | Yes, on distributions that use `apt` | | Windows 10 and 11 (64-bit) | `-setup.exe` (preview) | Not yet | | macOS | No installer yet | Not yet | Windows is a preview The Windows app installs, opens and signs in. Setting up a board uses Linux package tools today, so on Windows the app cannot install a board's toolchain or flash it yet. ## Check your download Each installer on the download page shows its SHA-256. Compare it with the file you downloaded before you install: On Linux: ``` sha256sum upstratus_*_amd64.deb ``` On Windows, in PowerShell: ``` Get-FileHash .\upstratus_*_x64-setup.exe -Algorithm SHA256 ``` If the two values differ, delete the file and download it again. ## Linux ### Ubuntu or Debian: the .deb ``` sudo apt install ./upstratus_*_amd64.deb ``` `apt` installs the libraries the app needs. Open **upstratus** from your applications menu. ### Any other distribution: the AppImage ``` chmod +x upstratus_*_amd64.AppImage ./upstratus_*_amd64.AppImage ``` An AppImage needs FUSE 2. On Ubuntu 24.04 install it with `sudo apt install libfuse2t64`; on older releases the package is `libfuse2`. ### Let the app reach your board Boards appear as serial ports, which on most distributions belong to the `dialout` group. Add yourself once, then sign out and back in: ``` sudo usermod -aG dialout "$USER" ``` When a board needs a tool that is not installed, the app installs it with `apt`. If your account cannot run `sudo` without a password, the app shows the exact command to run instead. Some boards need a one-time step before your computer can reach them, and some need a button held while you plug them in. The board picker shows this for each board; see [Supported boards](https://upstratus.com/docs/reference/boards/index.md). ## Windows 1. Run `upstratus_*_x64-setup.exe`. 1. The installer is not code-signed yet, so Windows SmartScreen says *Windows protected your PC*. Choose **More info**, check the file name, then choose **Run anyway**. 1. If Microsoft Edge WebView2 is not already installed, the installer downloads it. This needs an internet connection. 1. Open **upstratus** from the Start menu. ## macOS There is no macOS installer yet. A Mac build has to be made on a Mac and signed with an Apple certificate, and neither is in place. If you have access to the upstratus source code, you can build it yourself: 1. Install Apple's command line tools: `xcode-select --install`. 1. Install Rust from [rustup.rs](https://rustup.rs), then the Tauri command line tool: `cargo install tauri-cli --version "^2"`. 1. In the `int_edgeai_client` source folder, run `cargo tauri build --bundles app`. 1. Open `src-tauri/target/release/bundle/macos/upstratus.app`. Because the build is not signed, the first time you open it you must right-click the app and choose **Open**. A self-built Mac app signs in like any other copy, but it cannot set up a board yet. ## Next [Sign the app in to your account](https://upstratus.com/docs/guides/desktop-app/index.md). Choose **Sign in**, then approve the short code in your browser. # Reference # Reference Both pages are generated from the same data the product reads, so they cannot describe a board or an error the product does not have. - [Supported boards](https://upstratus.com/docs/reference/boards/index.md): the six boards, what each can sense, their specifications, and the setup each needs before you can program it. - [Error codes](https://upstratus.com/docs/reference/error-codes/index.md): every `code` an API error can carry, with what it means and what to do. The same table is served live at [`/docs/errors/`](/docs/errors/), which is where every error's `remedy_url` points. There is no API reference and no capability matrix yet. [For AI Agents](https://upstratus.com/docs/agents/index.md) explains why. # Supported boards upstratus generates code for these six boards and no others. The table says which kinds of data each board can run a model on; the sections below give its specifications and what you must do on your own computer before you can program it. Specifications are from datasheets The memory and core figures are datasheet values, not measurements from our bench. The setup notes are measured on a Linux computer unless a note says otherwise. ## What each board can sense | Board | Motion | Sound | Camera | Sensor values | | -------------------------------------------- | ------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------- | | [ESP32-S3-DevKitC-1 (N8R8)](#esp32s3) | Supported (module) | Supported | Supported (only tier-1 board that can) | Supported | | [Arduino Nano 33 BLE Sense Rev2](#nano33ble) | Supported (onboard) | Supported (onboard mic) | Not supported (256 KB RAM cannot hold a 96×96 MobileNetV2 arena) | Supported | | [Raspberry Pi Pico 2 W](#pico2w) | Supported (module) | Limited (module + tight RAM) | Not supported (no camera interface, no PSRAM) | Supported | | [Nordic nRF52840 DK](#nrf52840dk) | Supported (module) | Limited (module) | Not supported | Supported | | [ST Nucleo-H753ZI](#nucleoh743) | Supported (module) | Supported | Supported (parallel camera + 1 MB RAM) | Supported | | [ST Nucleo-F446RE](#nucleof446re) | Supported (module) | Limited (module + 128 KB is tighter than nano33ble/nrf52840dk's 256 KB) | Not supported (no camera interface, 128 KB too small for a vision arena) | Supported | ## The boards ### ESP32-S3-DevKitC-1 (N8R8) Board id `esp32s3` · tier 1 · the flagship: the most capable tier-1 board. | | | | ---------------------------------------- | ----------------------------------------------------------------- | | Core | Xtensa LX7 dual @240 MHz | | RAM | 512 KB + 8 MB PSRAM | | Flash | 8 MB QSPI | | Sensors on the board | none (needs a module) | | Connectivity | Wi-Fi + BLE | | USB | native USB-Serial-JTAG | | The board's usual toolchain | ESP-IDF 5.x (CMake) | | Kernels in the generated classifier code | portable C99 (optimised kernels for this board are not built yet) | **Before you choose it:** No admin needed. Plug in the native USB port, the one labelled USB rather than UART. - Build with USB CDC On Boot enabled (Arduino IDE: Tools > USB CDC On Boot > Enabled; arduino-cli: esp32:esp32:esp32s3:CDCOnBoot=cdc). Without it Serial is the hardware UART0, nothing reaches the USB port and the board looks dead. ### Arduino Nano 33 BLE Sense Rev2 Board id `nano33ble` · tier 1 · the classroom board: sensors onboard, zero wiring. | | | | ---------------------------------------- | ------------------------------------------------------------ | | Core | Cortex-M4F @64 MHz (nRF52840) | | RAM | 256 KB | | Flash | 1 MB internal | | Sensors on the board | BMI270 IMU + BMM150 mag + MP34DT06 PDM mic + baro/temp/light | | Connectivity | BLE only — no Wi-Fi | | USB | USB CDC (nRF52840 USBD) | | The board's usual toolchain | arduino-cli + ArduinoCore-mbed | | Kernels in the generated classifier code | CMSIS-NN (Arm) | **Before you choose it:** No admin needed. Uploading resets the board into its bootloader automatically. ### Raspberry Pi Pico 2 W Board id `pico2w` · tier 1 · the cheap board: best price, needs a sensor module. | | | | ---------------------------------------- | --------------------------------- | | Core | Cortex-M33 dual @150 MHz (RP2350) | | RAM | 520 KB | | Flash | 4 MB QSPI | | Sensors on the board | none (needs a module) | | Connectivity | Wi-Fi + BLE (CYW43439) | | USB | USB CDC (RP2350) | | The board's usual toolchain | Pico SDK 2.x (CMake) | | Kernels in the generated classifier code | CMSIS-NN (Arm) | **Before you choose it:** No admin needed, but the first flash needs the BOOTSEL button held while you plug the board in. Needs a step by hand: Hold the BOOTSEL button while plugging in USB, the first time only. - In BOOTSEL the board appears as a USB drive named RP2350 (USB ID 2e8a:000f). Copy the .uf2 file onto it. - Our firmware then runs as a USB serial device (2e8a:0009), so you can reflash without the button: opening that serial port at 1200 baud reboots the board back into BOOTSEL. - A board that has only ever run a program with no USB (such as the blink example) is invisible to the computer, so it needs the button again. ### Nordic nRF52840 DK Board id `nrf52840dk` · tier 2 · Zephyr needs a west workspace; browser flashing needs J-Link drivers. | | | | ---------------------------------------- | ------------------------------ | | Core | Cortex-M4F @64 MHz | | RAM | 256 KB | | Flash | 1 MB | | Sensors on the board | none onboard (Arduino headers) | | Connectivity | None | | USB | None | | The board's usual toolchain | nRF Connect SDK (Zephyr, west) | | Kernels in the generated classifier code | CMSIS-NN (Arm) | **Before you choose it:** Needs one-time admin setup on Linux (a udev rule for the onboard J-Link); see details for other systems. Needs a one-time administrator (root) step on at least one operating system. - Linux: install SEGGER's udev rule once with sudo. Without it flashing fails with 'No J-Link device found' because an ordinary user cannot open the J-Link. - If your kit shows a JLINK drive, copying a .hex file onto it flashes the board with no admin step. The unit tested here had that drive disabled, and re-enabling it needs the same J-Link access. - The chip's debug lock comes back after every power cycle, so flashing always starts with a mass erase. - Windows needs SEGGER's driver (admin) and macOS needs none, per SEGGER's documentation; only Linux was tested on this bench. ### ST Nucleo-H753ZI Board id `nucleoh743` · tier 2 · Not a hobbyist board; the value is headroom for an engineer evaluating larger models. | | | | ---------------------------------------- | ----------------------------------------------------- | | Core | Cortex-M7 @480 MHz | | RAM | 1 MB total (128 KB DTCM + 512 KB AXI + D2/D3 domains) | | Flash | 2 MB | | Sensors on the board | none onboard | | Connectivity | None | | USB | None | | The board's usual toolchain | CMake + arm-none-eabi-gcc, CubeMX-compatible | | Kernels in the generated classifier code | CMSIS-NN (Arm) | **Before you choose it:** No admin needed: copy the firmware file onto the board's USB drive (NOD_H753ZI). - Copying a .bin onto the NOD_H753ZI USB drive flashes the board with no driver, udev rule or root (verified 2026-10-01). - The command-line st-flash route is faster but needs the stlink udev rules on Linux, which the stlink-tools package installs. ### ST Nucleo-F446RE Board id `nucleof446re` · tier 2 · the compact Cortex-M4 board: a quarter of the H753's RAM -- useful for testing tier-2 minimums, not the flagship capacity story. | | | | ---------------------------------------- | -------------------------------------------- | | Core | Cortex-M4F @180 MHz | | RAM | 128 KB | | Flash | 512 KB | | Sensors on the board | none onboard | | Connectivity | None | | USB | None | | The board's usual toolchain | CMake + arm-none-eabi-gcc, CubeMX-compatible | | Kernels in the generated classifier code | CMSIS-NN (Arm) | **Before you choose it:** No admin needed: copy the firmware file onto the board's USB drive (NOD_F446RE). - Copying a .bin onto the NOD_F446RE USB drive flashes the board with no driver, udev rule or root (verified 2026-10-01). - The command-line st-flash route is faster but needs the stlink udev rules on Linux, which the stlink-tools package installs. ## Flashing upstratus does not flash boards for you yet: the browser flashing path is not built. You download a package of C source for the board you chose and build and flash it with that board's own tools (the toolchain row above). See [Get started](https://upstratus.com/docs/get-started/first-model/#7-build-and-flash-it-with-your-own-tools). Anomaly detectors, value predictors and the recurrent (GRU) classifier are packaged as portable C on every board; the kernels row applies to the other classifiers. # Error codes Every error response from the upstratus API has the same JSON shape: a `code` from this list, a `message` written for a person, an optional machine-readable `detail`, and a `remedy_url` that points at the code's entry on the live error reference, `/docs/errors/#`. This page is the same table, generated from the same catalogue. ``` {"code": "invalid_json", "message": "...", "detail": null, "remedy_url": "/docs/errors/#invalid_json"} ``` There are 174 codes. | Code | What it means | What to do | | ------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | | `access_denied` | The person denied the sign-in, or the account cannot sign in. | Start again with a new code if it was a mistake. | | `account_incomplete` | This account has not confirmed an email address and a mobile number yet. | Open `detail.next` in a browser and confirm the steps listed in `detail.missing`; API keys are not affected. | | `alias_starts_with_symbol` | A roster alias starts with = + - or @, which a spreadsheet reads as a formula. | Rename that student, for example by adding a letter first, and import again. | | `already_finalized` | The sample has already been completed or rejected. | Create a new sample instead of completing this one again. | | `already_invited` | That address already has a pending invitation. | Use Resend on the pending invitation instead of inviting again. | | `already_joined` | That student has already joined with their code, so their seat has no code to replace or revoke. | Nothing to do: the student can sign in with the password they chose. | | `already_member` | That person is already a member of this organisation. | Nothing to do; change their role from the member list if needed. | | `authorization_pending` | The person has not approved the sign-in in their browser yet. | Keep polling `/auth/device/token` at the `interval` you were given. | | `axis_value_out_of_range` | A row has a value that is not a finite number. | Remove infinities and NaN on the row named. | | `billing_unavailable` | Live billing is not enabled on this deployment. | Use test mode, or contact the operator. | | `blob_no_longer_matches_import` | The uploaded file changed after the import was validated. | Start the import again with the file you intend. | | `blob_not_found` | No upload exists at the sample's storage key. | PUT the payload to the presigned URL first, then call complete. | | `board_sizes_not_supported_for_model_block` | Board sizing is not available for this model. | Use a supported model. | | `budget_too_small` | The budget cannot cover even the cheapest trial. | Raise the budget. | | `cannot_recompute_sizes` | Board sizes could not be recomputed. | Retrain the recipe, then retry. | | `captcha_failed` | The security check on the sign-in form did not finish. | Wait for the check to complete in the browser, then sign in again. API clients should use an API key instead. | | `code_busy` | Another change to this student's join code was made at the same moment. | Reload the Classroom page and try again. | | `column_reused` | The label and group columns are the same. | Choose two different columns. | | `conformance_build_failed` | The model's reference build failed. | Retrain and retry; if it persists, report it with the message. | | `conformance_not_supported` | This model cannot be conformance-tested on a bench. | The bench compares class ids; use a classifier. | | `cross_project_diff` | The two versions belong to different projects. | Compare versions from the same project. | | `cursor_filter_mismatch` | The cursor was issued for different filters. | Repeat the same filters you used for the first page. | | `dataset_in_use` | A model was trained on this dataset, so it is kept as a record. | Archive it instead of deleting it, or duplicate it to change its cleaning. | | `dataset_name_taken` | Another dataset of this project already has that name. | Choose a different name. | | `deployment_failed` | This deployment is marked failed. | Use Deploy again to start a new deployment. | | `description_invalid` | The description is not text. | Send text. | | `description_required` | No description was given. | Describe the project in a sentence. | | `description_too_long` | The description is too long. | Shorten it to the stated limit. | | `device_code_expired` | The code on the approval page has expired. | Start signing in again in the app to get a new code. | | `device_code_unavailable` | A sign-in code could not be issued just now. | Try again in a moment. | | `device_code_used` | The code on the approval page was already approved or denied. | Start signing in again in the app if you still need to. | | `device_mismatch` | This session is already connected from another device. | Start a new session from the desktop to connect a different phone. | | `duplicate_alias` | Two students share an alias. | Make every alias unique within the class. | | `duplicate_axis_column` | A column is listed twice as an axis. | List each axis column once. | | `duplicate_too_large` | The project has more recordings than one duplicate can copy. | Duplicate without recordings, or export the recordings first. | | `email_not_confirmed` | This sign-up has not confirmed its email address. | Open `detail.next` and enter the 6-digit code we emailed you; Resend is on that page. | | `empty_group_key` | A row's group value is empty. | Fill the group column on the row named, or choose another column. | | `expired_token` | The sign-in request expired before it was approved (10 minutes). | Ask for a new code and show it again. | | `explorer_not_supported_for_block` | The data explorer works on photo features only. | Open the explorer on a vision project. | | `feature_computation_failed` | Features could not be computed from the samples. | Check the samples are complete and readable, then retry. | | `fit_not_supported_for_dsp_block` | Fit is not available for this feature block. | Choose a supported block. | | `fit_not_supported_for_model_block` | Fit is not available for this model. | Choose a supported model. | | `grid_locator_needs_greyscale` | Grid Locator works on greyscale photos. | Set Colour to greyscale. | | `idempotency_key_conflict` | This Idempotency-Key was used for a different request. | Use a fresh key for a different request, or resend the original. | | `inconsistent_target` | Rows of one recording carry different target values. | Give every row of a recording the same target. | | `invalid_action` | The bulk action is unknown. | Use `relabel` or `delete`. | | `invalid_against` | `against` is not an integer id. | Send the id of the model version to compare with. | | `invalid_api_key` | The API key is unknown or has been revoked. | Create a new key under Account → API keys and send it as a Bearer token. | | `invalid_archived` | `archived` must be true or false. | Send exactly `true` to archive or `false` to restore. | | `invalid_bench_key` | The hardware bench key is unknown or revoked. | Issue a new bench key and restart the bench client with it. | | `invalid_body_shape` | The JSON body is not an object. | Wrap the fields in a single JSON object `{...}`. | | `invalid_box` | The box geometry is not valid. | Use fractions 0–1 with a positive size inside the image. | | `invalid_budget` | The GPU-minute budget is not above zero. | Set a positive budget. | | `invalid_bytes` | `bytes` is negative. | Send the real object size in bytes. | | `invalid_class_counts` | Class counts must be positive integers. | Send positive integers. | | `invalid_cleaning` | A dataset cleaning step is unknown or has a wrong setting. | Fix the step the message names; the screen shows the allowed steps. | | `invalid_client_name` | The app name is not plain text of at most 80 characters. | Send a short name such as `Upstratus desktop`. | | `invalid_combination` | Two filters contradict each other. | Remove `label` or `unlabeled=true`. | | `invalid_copy` | Cannot copy boxes from the same image. | Choose a different source image. | | `invalid_credentials` | The username or password was wrong. | Check both and try again; passwords are case-sensitive. | | `invalid_cursor` | The paging cursor is malformed. | Start again without a cursor and follow `next_cursor` from each response. | | `invalid_description` | The description is not text, or is too long. | Send text of at most the stated length. | | `invalid_device_code` | The device code is unknown or was already exchanged for a token. | Start again from `/auth/device/code`; each code works once. | | `invalid_dsp_params` | A feature parameter is out of range. | Use values within the ranges shown on Design. | | `invalid_email` | The address is not a valid email address, or is too long. | Send a single address such as `name@example.com`. | | `invalid_ended` | `ended` must be true or false. | Send exactly `true` when the session is over. | | `invalid_event` | That funnel event cannot be reported by a client. | Report only an event listed in the message; the server records the others itself. | | `invalid_fit_params` | The Fit request is missing required fields. | Send `model_block` and `labels`. | | `invalid_ids` | `ids` is not a list. | Send a JSON list of sample ids. | | `invalid_include_recordings` | `include_recordings` must be true or false. | Send `true` to copy the recordings too, or `false`. | | `invalid_join_code` | The join code is wrong or was already used. | Check the code on the printed sheet; ask the teacher for a new roster import if it was used. | | `invalid_json` | The request body is not valid JSON. | Send well-formed JSON with `Content-Type: application/json`. | | `invalid_label` | The label is empty or not text. | Send a non-empty string label. | | `invalid_limit` | `limit` is below 1. | Send 1 or more. | | `invalid_modality` | The modality is not one this product supports. | Use one of the values listed in `detail.known_modalities`. | | `invalid_model_hparams` | A model setting is out of range. | Use values within the ranges shown on Design. | | `invalid_name` | The name is empty or not text. | Send a non-empty string. | | `invalid_notes` | The notes are not text, or are too long. | Send text of at most the stated length. | | `invalid_offset` | `offset` is negative. | Send 0 or more. | | `invalid_org` | `org_id` is missing, is not a number, or is not an organisation you belong to. | List yours with `GET /orgs/mine` and send one of those ids. | | `invalid_percentile` | The threshold percentile is out of range. | Choose a percentile within the range in the message. | | `invalid_project` | The project id is not an integer. | Send the numeric project id. | | `invalid_qat_flag` | `qat` is not true or false. | Send a JSON boolean. | | `invalid_rejected` | `rejected` must be `true` or `false`. | Send exactly `true` or `false`. | | `invalid_result` | A bench result was not a JSON object. | Report a JSON object. | | `invalid_results` | The Monitor results were malformed or too many at once. | Send up to the stated number of results with index, expected, got and latency_ms. | | `invalid_role` | The role is not one this action can set. | Invite as `admin` or `member`; an owner can set `owner`, `admin` or `member` on an existing member. | | `invalid_serial_number` | The serial number is not text. | Send the serial number as text, or leave it out. | | `invalid_signature` | The webhook signature did not verify. | Resend from the payment provider with the correct signing secret. | | `invalid_since_seq` | `since_seq` is negative. | Send 0 or more. | | `invalid_spec` | The project document could not be applied. | Fix the line named in `detail.line`; nothing was saved. | | `invalid_state` | The deployment state is not one of planned, packaged, flashed, verified or failed. | Send one of those states. | | `invalid_target` | The target is not a finite number. | Send a number, or null to clear it. | | `invalid_token` | The app sign-in token is unknown or has been revoked. | Sign the app in again; the person can also revoke it from Account. | | `invalid_unlabeled` | `unlabeled` must be `true` or `false`. | Send exactly `true` or `false`. | | `invitation_required` | Creating an account needs an access key. | Request an invitation at /alpha/request/; the key is emailed to the address you request it with. | | `invite_closed` | That invitation was already accepted or revoked. | Invite the person again if they still need to join. | | `invite_expired` | That invitation expired (they last 7 days). | Ask an owner or admin to resend it; the old link then stops working. | | `invite_limit` | Too many invitations, or this one was sent a moment ago. | Revoke old invitations, or wait the time in the message, then try again. | | `invite_revoked` | That invitation was withdrawn by the organisation. | Ask them to send a new invitation. | | `invite_used` | That invitation has already been used. | The person is already in the organisation; sign in to continue. | | `job_not_claimed` | The bench no longer holds this job's lease. | Stop work; the job was reassigned. | | `job_not_done` | The training job has not finished. | Wait until it is Done. | | `last_owner` | An organisation must keep at least one owner. | Make someone else an owner first, then try again. | | `lease_lost` | The lease expired before the result arrived. | Stop work and claim a new job. | | `missing_against` | `?against=` is required. | Add `?against=`. | | `missing_bench_key` | A bench request carried no key. | Send `Authorization: Bearer `. | | `missing_field` | A required field is missing or empty. | Add the field named in the message. | | `missing_user_agent` | The connecting client sent no User-Agent. | Open the link in a normal browser. | | `modality_mismatch` | The block does not support this project's data type. | Choose a block marked for the project's modality. | | `monitor_not_available` | Live checking is not available for this model version. | Read the message: some model types answer with a score or map the check cannot compare yet. | | `no_axis_columns` | No axis columns were chosen. | Choose at least one numeric column. | | `no_blob` | The sample has no stored recording. | Re-record or re-import it; there is nothing to download. | | `no_boxes_labelled` | Grid Locator needs at least one drawn box. | Open Label, View a photo and draw a box around each object. | | `no_modality` | The project has no data type chosen. | Choose what kind of data the project holds. | | `no_recipe_to_recompute_from` | The version has no recipe to rebuild from. | Train a new version from a recipe. | | `no_samples` | The project has no labelled data. | Record and label data first. | | `no_samples_to_size_from` | There are no complete samples to size from. | Record samples first. | | `no_sizes_to_recompute` | The version has no real training run to size. | Train it first. | | `non_numeric_value` | A row has a non-numeric axis value. | Fix the value on the row named. | | `normal_label_not_found` | No complete recordings carry the chosen normal label. | Pick a label that has recordings. | | `not_a_classifier_job` | Explanations exist only for classifier models. | Use a classifier job. | | `not_a_manager` | Only an owner or admin of the organisation can do that. | Ask an owner or admin, or switch to an organisation you manage. | | `not_a_vision_project` | Bounding boxes exist only on photo projects. | Use a vision project. | | `not_allowed_for_students` | A student account in a classroom cannot change the plan or the roster. | Ask the teacher who manages the classroom. | | `not_an_anomaly_model` | Only anomaly detectors have a threshold. | Use an anomaly model version. | | `not_an_education_org` | This organisation is not a classroom. | Sign up as a classroom to use rosters. | | `not_an_owner` | Only an owner can change roles. | Ask an owner of the organisation to make the change. | | `not_complete` | The import has not finished validating. | Wait for it to complete, then apply it. | | `not_found` | An id does not exist in your organisation. | Reload the list and use ids from it. | | `not_removable` | That member cannot be removed by you. | Admins can remove members, not owners or other admins; classroom seats are managed from the roster. | | `nothing_to_change` | The request named no field to change. | Send at least one of the fields the message lists. | | `nothing_to_copy` | The source image has no boxes. | Pick an image that has boxes. | | `pii_column_refused` | The roster has a column that holds student personal data. | Remove the named column; a roster takes only alias and group. | | `preview_not_supported_for_block` | This block has no preview yet. | Choose a block with a preview, or continue without. | | `qat_not_applicable` | Quantisation-aware retraining does not apply to this model. | Only classifier models have it; omit `qat`. | | `quota_exhausted` | Your plan's allowance for this resource is used up. | Wait for the next period, or upgrade the plan under Account. | | `rate_limited` | Too many attempts from this address in a short window. | Wait the number of seconds in `Retry-After`, then retry. | | `request_required` | No request text for the snippet. | Describe what to add or change. | | `request_too_long` | The request is too long. | Shorten it to the stated limit. | | `reserved_box_class` | A box class uses the reserved name for 'no object'. | Rename those boxes. | | `roster_needs_alias` | The roster has no alias column. | Make the first row `alias,group` (group optional). | | `roster_too_large` | The roster has more students than a class holds. | Import the class in smaller files. | | `run_already_active` | An Autopilot run is already in progress for this project. | Wait for it, or stop it. | | `session_ended` | This Monitor session has already ended. | Start a new session to record more results. | | `short_code_exhausted` | Could not allocate a collect-session code. | Retry in a moment; if it persists, report it. | | `slow_down` | The app polled faster than the allowed interval. | Wait the larger `detail.interval` seconds between polls from now on. | | `spec_changed` | The project changed after you opened the YAML. | Copy your edits, choose Undo my edits to load the latest, and apply them again. | | `state_backwards` | A deployment only moves forward. | Leave it as it is, or start a new deployment with Deploy again. | | `storage_unavailable` | The recordings could not be copied in storage, so nothing was duplicated. | Try again in a minute, or duplicate without recordings. | | `targets_missing` | Some recordings have no target value. | Set a target on each recording on the Label screen, then train. | | `token_expired` | The QR code has expired. | Show a fresh QR code on the desktop and scan it again. | | `token_invalid` | The QR code is no longer valid. | Show a fresh QR code on the desktop and scan it again. | | `too_few_normal_sessions` | Too few separate sessions of normal data. | Record more separate sessions of the normal label. | | `too_few_sessions` | Too few separate sessions to hold one out. | Record more separate sessions. | | `too_many_boxes` | The image already holds the maximum number of boxes. | Delete a box before adding another. | | `too_many_ids` | More ids than one request may carry. | Split the request into batches under the stated maximum. | | `unknown_board` | The board id is not a supported board. | Use an id from the board list. | | `unknown_column` | A named column is not in the uploaded file. | Pick columns from the list the import showed. | | `unknown_device` | That board is not registered in your workspace. | Add it under Devices first, then choose it. | | `unknown_dsp_block` | The feature block name is not known. | Pick one from the Design screen. | | `unknown_model_version` | That model version is not part of this project. | Pick a version listed under Evaluate for this project. | | `unknown_plan` | The plan is not one that can be purchased. | Choose a paid plan from the plan list. | | `unknown_project` | That project is not in your workspace. | Pick a project from your project list. | | `unsupported_anomaly_features` | Anomaly detection needs window-statistics features. | Choose Window statistics. | | `unsupported_board_for_snippet` | Integration examples exist only for some boards. | Choose the Nano 33 BLE or the ESP32-S3. | | `unsupported_kind` | The import kind is not supported. | Use a supported import kind (CSV). | | `unsupported_recipe` | These features and this model cannot be trained together yet. | Pick one of the pairs in `detail.trainable_recipes` in Design. | | `unsupported_regression_features` | Value prediction needs window-statistics features. | Choose Window statistics. | | `username_taken` | That username already exists. | Choose another, or sign in if the account is yours. | | `value_too_long` | A value is longer than the field allows. | Shorten it to the maximum in the message. | | `weak_password` | The password does not meet the password rules. | Use a longer, less common password; the sign-up form lists the rules. | | `webhook_not_configured` | The webhook secret is not configured on the server. | Operator: set STRIPE_WEBHOOK_SECRET. | | `wrong_account` | This invitation was sent to a different email address than the account you are signed in with. | Sign out and sign in (or sign up) with the invited address. |