# Create asset

`POST /asset.create`

## Description

Creates a file Asset and returns instructions for uploading its bytes directly
to the resumable-uploads service. Unlike [item.upload](/docs/chat-api/item-upload),
the file bytes never pass through the API itself, so this endpoint supports much
larger files (up to 5 GiB).

## Upload Flow

1. Call this endpoint with the file's `name` (and optionally its `size` in bytes).
2. Upload the raw file bytes in a single request: replay `uploadMethod` against
   `uploadUrl`, send every entry of `uploadHeaders`, and put the file in the request body.
3. Processing starts automatically once the bytes land — there is no separate
   "complete" call.
4. Reference the `assetId`:
   - `purpose: "file"` (default) — attach to a chat message via
     [chat.post](/docs/chat-api/chat-post)'s `assetIds` field.
   - `purpose: "story"` — post as a story via [story.post](/docs/roam-api/post-story).

A freshly-uploaded asset may take a few seconds to process (videos take longer).
Endpoints that consume the asset return a 400 with a "still processing" message
until processing completes.

## Purposes

| Purpose | Use | Access |
|---------|-----|--------|
| `file` (default) | Chat message attachments | Organization and Personal |
| `story` | Story media (photo or video) | Personal only |

Story assets are owned by the authenticated user (stories are posted as you, not as
a bot) and expire about 48 hours after creation. Because the media must outlive the
story's 24-hour lifetime, call [story.post](/docs/roam-api/post-story) within about
23 hours of creating the asset; after that the asset is rejected and a new one must
be created.

**Access:** Organization and Personal. `purpose: "story"` is Personal only.

**Required scope:** `item:write` (for `purpose: "file"`), `chat:send_message` or `chat:write` (for `purpose: "story"`)

---

**OpenAPI Spec:** [openapi.json](https://developer.ro.am/openapi.json)

## Authentication

```
Authorization: Bearer YOUR_API_KEY
```

## Request Body

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | string | Yes | File name, including its extension. Processing determines the media type from the extension. |
| `size` | integer | No | Size of the file in bytes, if known. Maximum 5 GiB. |
| `purpose` | "file" | "story" | No | What the asset will be used for. `file` (default) for chat message attachments; `story` for story media (Personal tokens only).  |

## Responses

### 200 - Upload instructions for the created asset.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `assetId` | string | No | ID of the created asset. |
| `uploadUrl` | string | No | URL to upload the file bytes to. |
| `uploadMethod` | string | No | HTTP method to use for the upload request. |
| `uploadHeaders` | object | No | Headers to send verbatim with the upload request. |


#### Example Response

```json
{
  "assetId": "019be84b-0fa8-788f-8850-96de4cc39130",
  "uploadUrl": "https://uploads.ro.am/uploads/",
  "uploadMethod": "POST",
  "uploadHeaders": {
    "Authorization": "Bearer UPLOAD_TOKEN",
    "Upload-Draft-Interop-Version": "6",
    "Upload-Complete": "?1",
    "Upload-Length": "20971520"
  }
}
```

### 400 - Bad request. Common causes:
- Missing or over-long `name`
- `size` negative or over 5 GiB
- Unknown `purpose`


### 401 - Presented invalid authentication credentials.

### 403 - Forbidden. `purpose: "story"` was requested with an organization token,
or the token lacks the required scope.


### 405 - An unsupported method was requested.

### 500 - An internal error occured.

---

*Machine-readable API documentation.*
*Full documentation: https://developer.ro.am/docs/roam-api/create-asset*
