Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/api-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ Choose the tool that best fits your workflow:
- **For Python users**: Begin with the [Python API tool](/api-tools/python-api-tool/)
- **For exploration**: Try the [Web API tool](/api-tools/web-api-tool/) in your browser

All tools use the same underlying [API endpoints](/api/) and support consistent authentication and query patterns.
All tools use the same underlying [API endpoints](/api/) and support consistent authentication and query patterns. Authentication uses Personal Access Tokens obtained via a browser-based device authorization flow — your password is never sent to a script. See the [API documentation](/api/) for full details on token management.
117 changes: 78 additions & 39 deletions docs/api-tools/matlab-api-tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,59 +17,99 @@ nav_order: 1

The BrainSTEM MATLAB API tool (`brainstem_matlab_api_tools`) provides a thin wrapper around the REST endpoints documented in the [STEM API reference]({{ "/api/stem/" | absolute_url }}). This page mirrors the structure of those docs while expanding on hands-on usage. Every snippet below has been tested against the public API; replace placeholders (for example, IDs) with values available to your account.

Helper functions such as `get_token`, `load_model`, `load_project`, `load_session`, and `save_model` streamline typical MATLAB workflows while mapping their arguments directly onto the underlying HTTP parameters.
The `BrainstemClient` class is the recommended entry point. It holds the authentication token and base URL for all subsequent calls, and supports both interactive browser-based login and direct token injection for scripts and HPC workflows.

> **Repository & tutorial:** Source code and examples live in [brainstem-org/brainstem_matlab_api_tools](https://github.com/brainstem-org/brainstem_matlab_api_tools). The repository includes the `brainstem_api_tutorial.m` script that mirrors this guide with runnable sections.

## Installation

Clone the MATLAB helper package and add it to your MATLAB path:
Clone the MATLAB helper package and add the root folder to your MATLAB path:

```matlab
git clone https://github.com/brainstem-org/brainstem_matlab_api_tools.git
addpath(genpath('brainstem_matlab_api_tools'))
addpath('/path/to/brainstem_matlab_api_tools')
```

Only the root folder needs to be added. MATLAB automatically discovers the `+brainstem` package folder and the `BrainstemClient` class. Do not add `+brainstem/` itself to the path.

## Authentication

Run `get_token` once to create and store an authentication token. Subsequent API calls reuse the cached credentials.
BrainSTEM enforces two-factor authentication (2FA). To ensure 2FA is always respected, authentication uses a **browser-based device authorization flow** — your password is never sent to a script.

### Recommended: Personal Access Token (scripts, HPC, automation)

Create a token at [brainstem.org/private/users/tokens/](https://www.brainstem.org/private/users/tokens/). Tokens are valid for 1 year (sliding, auto-refresh).

```matlab
get_token
% Option A: environment variable (set once per shell/session)
setenv('BRAINSTEM_TOKEN', '<your_token>')
client = BrainstemClient(); % picks it up automatically

% Option B: pass directly
client = BrainstemClient('token', '<your_token>');
```

Tokens expire periodically; re-run `get_token` if you see HTTP 401 responses.
### Interactive login (device flow, desktop MATLAB)

When neither argument is supplied and the `BRAINSTEM_TOKEN` environment variable is not set, `BrainstemClient` opens a browser window automatically. If you are already logged in to BrainSTEM, simply click **Approve** in the browser. The token is cached and reused in future sessions.

```matlab
client = BrainstemClient(); % opens browser login page
```

If the browser cannot be opened (headless environment), MATLAB prints a short code and URL to enter on any other device.

Re-run `BrainstemClient()` or call `brainstem.get_token()` directly if you see HTTP 401 responses.

## Loading data

`load_model` mirrors the REST endpoints documented under [STEM API]({{ "/api/stem/" | absolute_url }}). All arguments map directly onto their HTTP counterparts, and the helper infers the correct `app` when you omit it.
`client.load` retrieves records from any BrainSTEM endpoint. All arguments map directly onto their HTTP counterparts.

```matlab
out = client.load('session');
disp(numel(out.sessions))
```

Load a single record by UUID:

```matlab
sessions = load_model('model', 'session');
disp(numel(sessions.sessions))
out = client.load('session', 'id', '<id>');
```

Convenience wrappers include the correct `app` and default `include` lists for common models:
Auto-paginate to retrieve all records across multiple pages:

```matlab
out = client.load('session', 'load_all', true);
```

- `load_project`: includes `sessions`, `subjects`, `collections`, `cohorts`
- `load_subject`: includes `procedures`, `subjectlogs`
- `load_session`: includes `dataacquisition`, `behaviors`, `manipulations`, `epochs`
### Convenience loaders

They expose a small set of shorthand arguments (for example, `name`, `projects`, `tags`) that expand into the appropriate filter entries internally.
Convenience loaders include the correct `app` and default `include` lists for common models:

| Method | Default includes | Named filter kwargs |
|--------|-----------------|---------------------|
| `load_project` | sessions, subjects, collections, cohorts | `name`, `id`, `tags` |
| `load_subject` | procedures, subjectlogs | `name`, `id`, `sex`, `strain`, `tags` |
| `load_session` | dataacquisition, behaviors, manipulations, epochs | `name`, `id`, `projects`, `tags` |
| `load_collection` | sessions | `name`, `id`, `tags` |
| `load_cohort` | subjects | `name`, `id`, `tags` |
| `load_behavior` | — | `session`, `id`, `tags` |
| `load_dataacquisition` | — | `session`, `id`, `tags` |
| `load_manipulation` | — | `session`, `id`, `tags` |
| `load_procedure` | — | `subject`, `id`, `tags` |

```matlab
project = load_project('name', 'Allen Institute: Visual Coding – Neuropixels');
subject = load_subject('name', 'Mouse');
session = load_session('name', 'Example Session');
project = client.load_project('name', 'Allen Institute: Visual Coding – Neuropixels');
subject = client.load_subject('name', 'Mouse');
session = client.load_session('name', 'Example Session');
```

## Filtering

Filters accept cell arrays of key–value pairs. The modifier syntax (`.icontains`, `.iexact`, etc.) matches the API documentation.

```matlab
filtered = load_model(
'model', 'project', ...
filtered = client.load('project', ...
'filter', {'name.icontains', 'Allen'});

disp(numel(filtered.projects))
Expand All @@ -78,46 +118,42 @@ disp(numel(filtered.projects))
Multiple filter conditions can be specified as pairs in the same cell array:

```matlab
filtered = load_model(
'model', 'session', ...
filtered = client.load('session', ...
'filter', {'name.icontains', 'Rat', 'description.icontains', 'demo'});
```

The convenience functions also support multiple filter conditions through their shorthand parameters:
The convenience loaders also support shorthand filter parameters:

```matlab
% Filter projects by name and tags
project = load_project('name', 'Allen', 'tags', '1');
project = client.load_project('name', 'Allen', 'tags', '1');

% Filter subjects by name and sex
subject = load_subject('name', 'Mouse', 'sex', 'M');
subject = client.load_subject('name', 'Mouse', 'sex', 'M');
```

## Sorting

Pass a cell array of field names in `sort`. Prefix with `-` for descending order.

```matlab
descending_sessions = load_model(
'model', 'session', ...
'sort', {'-start_time'});
descending_sessions = client.load('session', 'sort', {'-start_time'});
```

## Including related records

Request related models (for example, `projects`, `datastorage`, `dataacquisition`) in a single call by supplying `include`. Each entry automatically expands to `<name>.*`, mirroring the REST include syntax.
Request related models in a single call by supplying `include`. Each entry automatically expands to `<name>.*`, mirroring the REST include syntax.

```matlab
with_related = load_model(
'model', 'session', ...
with_related = client.load('session', ...
'include', {'projects', 'datastorage', 'dataacquisition'});

project_names = arrayfun(@(p) p.name, with_related.projects, 'UniformOutput', false);
```

## Creating records

Use `save_model` with the required fields from the STEM API reference. The example below assumes you have contributor access to the project ID provided. Note that `tags` is a required field and can be set to an empty array if no tags are needed.
Use `client.save` with the required fields from the STEM API reference. The example below assumes you have contributor access to the project ID provided. Note that `tags` is a required field and can be set to an empty array if no tags are needed.

```matlab
payload = struct();
Expand All @@ -126,32 +162,35 @@ payload.description = 'Created via MATLAB API tool';
payload.projects = {'your-project-uuid'};
payload.tags = [];

created = save_model('model', 'session', 'data', payload);
created = client.save(payload, 'session');
session_id = created.session.id;
```

## Updating records

Update existing records by placing the record ID inside the payload struct. `save_model` detects the `id` field, switches to `PUT`, and replaces the stored record with the data you supply. Remember to include the `tags` field even when updating.
Pass a struct with an `id` field and any fields to change. Use `'method', 'patch'` for a partial update (only the supplied fields are modified).

```matlab
update_data = struct(
update_data = struct( ...
'id', 'your-session-uuid', ...
'description', 'Updated via MATLAB API tool', ...
'tags', []);

updated = save_model(
'model', 'session', ...
'data', update_data);
updated = client.save(update_data, 'session', 'method', 'patch');
```

## Deleting records

```matlab
client.delete('your-session-uuid', 'session');
```

## Troubleshooting

- **401 Unauthorized**: Regenerate your token with `get_token`.
- **401 Unauthorized**: Regenerate your token with `brainstem.get_token()` or create a new Personal Access Token at [brainstem.org/private/users/tokens/](https://www.brainstem.org/private/users/tokens/).
- **403 Forbidden**: Check that your user or group has the required permissions listed in the STEM API documentation.
- **404 Not Found**: Confirm the record exists in the selected portal (public/private).
- **Validation errors (400)**: Ensure your payload matches the field definitions in the API reference. Remember that `tags` is a required field for sessions (can be an empty array).
- **Delete operations**: The MATLAB helpers focus on read and write actions. Use the Python client or direct REST calls if you need to delete records.

The repository ships with `brainstem_api_tutorial.m`, a tutorial script demonstrating common workflows.

Expand Down
Loading