# Register files and get each one's id and upload URL.

`POST /o/{org}/files`

Writes one file row per entry (owned by the caller, bound to no Project yet) and
returns its id with a presigned single-shot `PUT`, so one call registers a whole drop:
send each file's bytes to its own `url` with the `headers` given, then pass each id as a
`{"type": "file", "file_id": id}` source in a session's or a follow-up's `input`. Bytes go
straight to storage, so no file size is refused: this is the way to send a large private
file. Each `url` lasts 60 seconds and is bound to that one file; re-sign an expired one,
or upload a file in parts, with `POST /o/{org}/files/{id}/sign`.
Files are returned in request order. Rate-limited to 30 calls a minute per caller
(60 for admins).

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `org` | path | string | yes | The org's slug or id. A caller with no role in the org gets `404`. |

## Request body

- `files` (array of object, required)
  - `name` (string, required): File name. No `/`, `\`, `..`, leading `.`, or control characters.
  - `mime` (string, required): A media type; parameters are dropped and the lowercased `type/subtype` is stored. Any type is accepted.
  - `size` (integer, required): Exact byte size; the signed PUT refuses a body of any other length.

## Example request

```bash
curl -X POST "https://api.prix.dev/o/$RUSH_ORG/files" \
  -H "Authorization: Bearer $RUSH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "files": [
      {
        "name": "string",
        "mime": "string",
        "size": 1
      }
    ]
  }'
```

```typescript
const org = process.env.RUSH_ORG;

const res = await fetch(`https://api.prix.dev/o/${org}/files`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RUSH_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    files: [
      {
        name: "string",
        mime: "string",
        size: 1,
      },
    ],
  }),
});

console.log(res.status, await res.json());
```

```python
import os

import requests

org = os.environ["RUSH_ORG"]

res = requests.post(
    f"https://api.prix.dev/o/{org}/files",
    headers={"Authorization": f"Bearer {os.environ['RUSH_TOKEN']}"},
    json={
        "files": [
            {
                "name": "string",
                "mime": "string",
                "size": 1,
            },
        ],
    },
)

print(res.status_code, res.json())
```

## Responses

### 200

One ready-to-upload file per request entry, in request order.

- `files` (array of object, required)
  - `id` (string, required)
  - `url` (string, required): Presigned single-shot `PUT` for this file's bytes. Valid 60 seconds.
  - `headers` (object, required): The exact headers the `PUT` must send. The declared byte count is signed in too, so a body of any other length is refused.
    - `content-type` (string, required)
  - `view_url` (string, required): Absolute `GET /o/{org}/files/{id}`, which redirects to a freshly signed download on every request. Readable once the bytes are uploaded.

### 400

Invalid JSON, empty `files`, an invalid entry, or more than 50 files (`TOO_MANY_FILES`; the other cases carry no `code`).

### 401

No bearer, an unknown, revoked or expired one, or an API key on a route keys cannot call.

### 404

No such resource for the caller, including one that exists in an org the caller cannot see.

### 429

Presign rate limit exceeded. `Retry-After` is set.

## Example response (200)

```json
{
  "files": [
    {
      "id": "3f6c1e2a-8b4d-4c1e-9a7f-2d5b6e8c0a14",
      "url": "https://example.com",
      "headers": {
        "content-type": "string"
      },
      "view_url": "https://example.com"
    }
  ]
}
```
