# File connector

{{% notice style="note" title="Limited access" icon="lock" %}}
{{% include file="include/restricted_access.md" hidefirstheading="true"  %}}
{{% /notice %}}

## What it is

Every organization has a **file space** on the platform: a directory tree of its own that the file connector
manages over HTTP, and that the AI services see as a mounted volume. It is where a trainer's input file lives,
where the time series export writes its Parquet file, where the image plan reads its photographs and writes its
heat maps. Nothing outside the organization can reach it.

| To | Call |
|---|---|
| Upload a file or an archive | `POST /fileConnector/organizations/{organizationId}/upload` |
| List a directory | `GET /fileConnector/organizations/{organizationId}/list?path=…` |
| Download a file | `GET /fileConnector/organizations/{organizationId}/download?path=…` |
| Delete a file or a directory | `POST /fileConnector/organizations/{organizationId}/delete` |

All paths are **relative to the organization's root**. Wildcards are not supported. Authentication is the
`X-ApiKey` header.

## Uploading

```bash
curl --request POST \
     --header "X-ApiKey: <your-api-key>" \
     --form 'meta={"destinyPath": "radius", "overwriteFiles": true};type=application/json' \
     --form "file=@sessions-2026-q2.parquet" \
     https://api.opengate.es/fileConnector/organizations/acme/upload
```

A multipart request with two parts:

| Part | Content |
|---|---|
| `meta` | JSON with `destinyPath`, the directory to write into (created if missing), and `overwriteFiles`, whether existing files may be replaced. Default `true` |
| `file` | The file itself |

**Archives are unpacked, not stored.** A `.zip`, `.tar`, `.tar.gz` or `.tar.bz2` — recognised by its MIME type —
is extracted into `destinyPath`, which is how the image plan's `correct/` and `incorrect/` folders are uploaded in
one request. Any other file is stored as it is.

`204` means everything was written. `200` with an error list means a **partial** upload: `overwriteFiles` was
`false` and some files already existed; the list names them.

## Listing

```bash
curl --request GET \
     --header "X-ApiKey: <your-api-key>" \
     "https://api.opengate.es/fileConnector/organizations/acme/list?path=radius"
```

```json
[
  { "name": ".", "isDir": true, "size": 4096, "modificationTime": "2026-09-01T10:12:00Z", "mode": "drwxr-xr-x" },
  { "name": "sessions-2026-q2.parquet", "isDir": false, "size": 48219833, "modificationTime": "2026-09-01T10:12:00Z", "mode": "-rw-r--r--" }
]
```

For a directory, the first entry named `.` is the directory itself, followed by its children. For a file, just
that file. A `path` that matches nothing is a `400`.

## Downloading

```bash
curl --request GET \
     --header "X-ApiKey: <your-api-key>" \
     --output part-0412_heatmap.jpg \
     "https://api.opengate.es/fileConnector/organizations/acme/download?path=line-3/2026-09-03/part-0412_heatmap.jpg"
```

Returns the file's bytes. This is how you retrieve a heat map the image plan wrote next to a photograph.

## Deleting

```bash
curl --request POST \
     --header "X-ApiKey: <your-api-key>" \
     --header "Content-Type: application/json" \
     --data '{ "destinyPath": "radius", "fileName": "sessions-2026-q2.parquet" }' \
     https://api.opengate.es/fileConnector/organizations/acme/delete
```

With `fileName`, deletes that file inside `destinyPath`. With `fileName` omitted or `null`, deletes the whole
`destinyPath` directory. `204` on success, `404` if there was nothing to delete.

## How the AI services see it

| Service | Sees the file space as |
|---|---|
| A training job | `/data`. A trainer's `source.path` of `radius/sessions.parquet` becomes `dataSourcePath=/data/radius/sessions.parquet` |
| A time-series trainer | The export writes `<model>-<plan>-<time series>.parquet` at the root, and the job reads it from `/data/` |
| An inferencer | `/data`. The image plan's `image_route` is resolved against it, and the heat map is written beside the image |

So the console's file browser in the trainer wizard, the `list` endpoint and the paths a rule sends to an
inferencer all name the same files.

## API specification

{{< openapi src="file_connector.yaml" >}}
