> ## Documentation Index
> Fetch the complete documentation index at: https://grow-hub.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Asset tools

> Upload media and attach it to a post.

See [Assets](/concepts/assets) for kinds, size limits, and which upload path to
pick.

## create\_upload\_session

Mints a browser link for a specific post. The user opens it and drag-and-drops
files of any size, which upload straight to storage and attach themselves to the
post.

<ParamField body="postId" type="string" required>
  Post the uploads attach to.
</ParamField>

```json Result theme={null}
{ "url": "https://studio.growhub.ai/u/<token>", "expiresInMinutes": 30 }
```

The link carries its own signed token — the user does not need to be signed in to
use it, and it stops working after 30 minutes. After they upload, call
`get_post` to see the attached assets.

## request\_asset\_upload

Returns a presigned URL for a direct `PUT` of the raw bytes.

<ParamField body="kind" type="enum" required>
  `image`, `video`, or `document`.
</ParamField>

<ParamField body="mimeType" type="string" required>
  Must be allowed for the kind, e.g. `image/png`.
</ParamField>

<ParamField body="filename" type="string">
  Original filename, kept for display.
</ParamField>

```json Result theme={null}
{ "assetId": "…", "uploadUrl": "https://…", "expiresAt": "2026-08-18T12:10:00.000Z" }
```

```bash Upload the bytes theme={null}
curl -X PUT --data-binary @cover.png "$uploadUrl"
```

Then call `attach_asset` with the `assetId`. The URL is valid for 10 minutes.

## upload\_asset\_inline

Uploads a small file by passing its base64 contents in the tool call.

<ParamField body="kind" type="enum" required>
  `image`, `video`, or `document`.
</ParamField>

<ParamField body="mimeType" type="string" required>
  Must be allowed for the kind.
</ParamField>

<ParamField body="base64" type="string" required>
  File contents, base64-encoded. Decoded size must be under 5 MB — larger files
  return `inline_too_large_use_presign`.
</ParamField>

<ParamField body="filename" type="string">
  Original filename.
</ParamField>

Returns `{ "assetId": "…", "bytes": 48213 }`. The asset is `ready` right away;
still call `attach_asset` to link it to a post.

## attach\_asset

Links an uploaded asset to a post. This is where Studio confirms the bytes
landed, enforces the size ceiling, and enforces the per-post count.

<ParamField body="postId" type="string" required>
  Post to attach to.
</ParamField>

<ParamField body="assetId" type="string" required>
  Asset to attach.
</ParamField>

Returns the asset view: `{ id, postId, kind, mimeType, filename, bytes, status }`.

<Note>
  An oversized file uploaded through a presigned URL is rejected here — the
  stored object and its record are deleted and the call fails with `too_large`.
</Note>

## detach\_asset

Removes an asset from a post. The stored file is kept, so it can be attached
elsewhere.

<ParamField body="postId" type="string" required>
  Post to detach from.
</ParamField>

<ParamField body="assetId" type="string" required>
  Asset to detach.
</ParamField>

Returns `{ "detached": "<assetId>" }`.
