# Sessions

> A session is one uploaded recording. Create it, upload its parts, complete it, then follow processing.

Source: https://rayform.world/docs/api/sessions
Last updated: 2026-10-18
Status: Rayform is in private preview. APIs, formats and the SDK described here may change before general availability.

## The session object

```json
{
  "id": "ses_01JA8K2M9Q",
  "object": "session",
  "site": "lab-2",
  "device": "arm-cell-1",
  "filename": "kitchen_2026-10-14.mcap",
  "bytes": 4512876544,
  "sha256": "e2d9c1...",
  "status": "processed",
  "recorded_at": "2026-10-14T13:02:11Z",
  "duration_s": 612.4,
  "channels": ["cam0/rgb", "cam0/depth", "cam1/rgb", "lidar/points", "imu", "joints"],
  "segment_count": 14,
  "issues": [
    { "code": "clock_offset_estimated", "severity": "info", "channel": "cam1/rgb", "detail": "Shifted by +18.4 ms" }
  ],
  "notes": "afternoon light, two people",
  "created_at": "2026-10-14T13:20:03Z",
  "processed_at": "2026-10-14T13:58:47Z"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `status` | string | `uploading`, `uploaded`, `processing`, `processed` or `failed` |
| `site` | string | The place the recording was made; sessions at one site share a world frame |
| `sha256` | string | Checksum of the whole file, verified on completion |
| `channels` | array | Channels found in the recording, with Rayform names |
| `issues` | array | Problems found during processing; `severity` is `info`, `warning` or `error` |

## Create a session

```endpoint
POST /v1/sessions
```

Starts an upload and returns signed URLs for each part. Parts must be between 8 MB and 512 MB, except the last.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `site` | string | yes | Site name; created on first use |
| `filename` | string | yes | Original file name, used to detect the format |
| `bytes` | integer | yes | Total size in bytes |
| `sha256` | string | yes | Hex SHA-256 of the whole file |
| `part_size` | integer | no | Part size in bytes; default 64 MB |
| `device` | string | no | Device name, to apply a stored calibration |
| `notes` | string | no | Free text kept with the session |

#### Request

```bash
curl https://api.rayform.world/v1/sessions \
  -H "Authorization: Bearer $RAYFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"site": "lab-2", "filename": "kitchen.mcap", "bytes": 4512876544, "sha256": "e2d9c1...", "part_size": 67108864}'
```

#### Response

```json
{
  "id": "ses_01JA8K2M9Q",
  "object": "session",
  "status": "uploading",
  "upload": {
    "part_size": 67108864,
    "expires_at": "2026-10-15T13:20:03Z",
    "parts": [
      { "number": 1, "url": "https://upload.rayform.world/ses_01JA8K2M9Q/1?sig=..." },
      { "number": 2, "url": "https://upload.rayform.world/ses_01JA8K2M9Q/2?sig=..." }
    ]
  }
}
```

## Upload a part

```endpoint
PUT {upload.parts[n].url}
```

Send the raw bytes of part `n`. Include its checksum so a corrupted part is rejected immediately. The response's `ETag` header is needed to complete the session.

```bash
curl -X PUT "$PART_URL" \
  -H "x-amz-checksum-sha256: $(openssl dgst -sha256 -binary part-1 | base64)" \
  --data-binary @part-1 -i
```

To resume an interrupted upload, call [retrieve a session](#retrieve-a-session): `upload.parts` lists only the parts not yet received, with fresh URLs.

## Complete a session

```endpoint
POST /v1/sessions/{id}/complete
```

Verifies the whole-file checksum and starts processing.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `parts` | array | yes | `{ "number", "etag" }` for every part, in order |

```bash
curl https://api.rayform.world/v1/sessions/ses_01JA8K2M9Q/complete \
  -H "Authorization: Bearer $RAYFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parts": [{"number": 1, "etag": "\"9b2cf5...\""}, {"number": 2, "etag": "\"41a7e0...\""}]}'
```

Returns the session with `status: "processing"`. If the checksum does not match, the response is `409` with code `checksum_mismatch` and the parts are kept so you can re-send the wrong ones.

## Retrieve a session

```endpoint
GET /v1/sessions/{id}
```

```python
session = client.sessions.get("ses_01JA8K2M9Q")
print(session.status, session.issues)
```

## List sessions

```endpoint
GET /v1/sessions
```

| Query | Type | Description |
| --- | --- | --- |
| `site` | string | Only sessions at this site |
| `status` | string | Only sessions in this state |
| `created` | object | `created[gte]` and `created[lt]` as ISO 8601 |
| `limit`, `cursor` | | [Pagination](https://rayform.world/docs/api#pagination) |

## Delete a session

```endpoint
DELETE /v1/sessions/{id}
```

Deletes the original recording and everything derived from it: segments, assets and cached exports. Slices that included its segments return `410 Gone` for those segments. Deletion cannot be undone.

```bash
curl -X DELETE https://api.rayform.world/v1/sessions/ses_01JA8K2M9Q \
  -H "Authorization: Bearer $RAYFORM_API_KEY"
```

```json
{ "id": "ses_01JA8K2M9Q", "object": "session", "deleted": true }
```
