# Send a parquet file

> For AI agents: the complete documentation index is at
> [https://docs.retrocast.com/llms.txt](https://docs.retrocast.com/llms.txt) — every page is also available as
> markdown by appending `.md` to its URL.

Inline rows travel inside the JSON body, which is fine for a few hundred rows
and wasteful for a real table. Upload the file instead: send the request as
`multipart/form-data`, with the query in a part named `query` and the data in
a part of its own.

This is the one case where the request isn't plain JSON, so here is the whole
`curl` call:

```bash
curl -X POST "https://api.retrocast.com/v1-beta/forecast?model=t0-alpha" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "query=@query.json;type=application/json" \
  -F "sales=@sales.parquet"
```

`query.json` is the same request body you would have posted as JSON, except
that `source` points at the uploaded part with a `uri` instead of carrying
rows:

```json
{
  "input": {
    "source": { "uri": "inline://sales" },
    "columns": {
      "date":  { "kind": "time", "frequency": "1d" },
      "store": "identifier",
      "sales": { "kind": "target", "aggregate": "sum" }
    }
  },
  "prediction_length": "2w"
}
```

`model` names the forecasting model and is required on every call to this
endpoint.

The part name is yours to choose. `inline://sales` means the part sent as
`-F "sales=@sales.parquet"` — the name in the URL is the part name, not a
file name, so `inline://sales.parquet` would not resolve.

Two errors tell you the two halves are out of step: `the query reads
inline://x, but no part was uploaded under that name`, and `part x is not
read by the query`.

## One part per source

A `uri` names a single part; it does not take a list. To forecast several
files as one table, concatenate them before uploading.

Several parts are for several *sources*. A covariate has a source of its own,
so it can come from its own part:

```bash
curl -X POST "https://api.retrocast.com/v1-beta/forecast?model=t0-alpha" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "query=@query.json;type=application/json" \
  -F "sales=@sales.parquet" \
  -F "weather=@weather.parquet"
```

```json
"covariates": [
  {
    "schema": {
      "version": "0.3",
      "source": { "uri": "inline://weather" },
      "columns": {
        "date":        { "kind": "time", "frequency": "1d" },
        "temperature": "historical"
      }
    },
    "on": [{ "base": "date", "attached": "date" }]
  }
]
```

Parts can be Parquet or Arrow IPC files. Write Arrow uncompressed — an
LZ4-compressed Arrow file is rejected with `lz4 IPC decompression requires
the lz4 feature`.

## What comes back

The same response as any other request. One row per (store, time step), since
`store` is an identifier and nothing aggregates it away:

```json
{
  "data": {
    "time":      ["2024-02-10T00:00:00Z", "2024-02-11T00:00:00Z"],
    "cutoff":    ["2024-02-09T00:00:00Z", "2024-02-09T00:00:00Z"],
    "lead_time": ["PT0S", "P1D"],
    "span":      ["P1D", "P1D"],
    "identifiers": { "store": ["S1", "S1"] },
    "targets": {
      "sales": {
        "quantiles": {
          "0.1":  [143.5, 144.1],
          "0.5":  [144.8, 145.7],
          "0.9":  [145.8, 146.7],
          "mean": [144.7, 145.6]
        }
      }
    }
  }
}
```

It is JSON either way — uploading a file changes how the input arrives, not
how the forecast comes back.
