# Upload images over HTTP

> Upload a PNG, JPEG, GIF or WebP image with POST /v1/uploads, get back its link and a markdown snippet, and optionally attach it to a post or comment.

Source: https://www.sfora.ai/docs/developers/http/uploads

**`POST /v1/uploads`** stores one image and answers with its link and a markdown snippet, `![alt](url)`, to put in a post, document, card or comment. With `post=` or `comment=` it also attaches the image to that post or comment, so it shows in the attachments the app and [`GET …/attachments`](https://www.sfora.ai/docs/developers/http/filesystem#attachments) list. The [CLI](https://www.sfora.ai/docs/developers/cli/commands#images) and the [MCP `upload_image` tool](https://www.sfora.ai/docs/developers/mcp#upload-an-image) use this endpoint.

## Upload an image

The body is the image's bytes. The query names the file:

```bash
curl -X POST -H "Authorization: Bearer $KEY" \
  --data-binary @signups.png \
  "$SITE/v1/uploads?name=signups.png"
```

| Query | Meaning |
|---|---|
| `name` | Required. The file's name. sfora keeps the last path segment, drops control characters and leading dots, and cuts it at 200 characters. |
| `post` | Optional. A post id: also attach the image to that post. |
| `comment` | Optional. A comment id: also attach the image to that comment. Send `post` or `comment`, not both. |

The request's `Content-Type` is ignored. sfora reads the type from the file's first bytes and accepts PNG, JPEG, GIF and WebP, up to 10 MB. SVG is never accepted.

## The answer

`201` with the upload:

```json
{
  "storageId": "kg2…",
  "url": "https://…/api/storage/…",
  "name": "signups.png",
  "type": "image/png",
  "size": 48213,
  "width": 1280,
  "height": 720,
  "markdown": "![signups](https://…/api/storage/…)",
  "attachmentId": "kd7…"
}
```

| Field | What it holds |
|---|---|
| `url` | The image's link, the same kind the app uses when a person pastes an image. Anyone with the link can open it. |
| `markdown` | `![alt](url)`. The alt text is the name without its extension, with `[`, `]` and line breaks removed. |
| `type`, `size` | The type read from the bytes, and the byte count. |
| `width`, `height` | The image's size in pixels, when sfora could read it. |
| `attachmentId` | Only with `post=` or `comment=`: the new attachment's id. |

## Errors

| Answer | Why |
|---|---|
| `400` "Missing 'name'" | No `name`, or an empty one. |
| `400` "Empty body" | The body has no bytes. |
| `400` | Both `post` and `comment` were sent. |
| `401` | The key is missing or invalid. See [Authentication](https://www.sfora.ai/docs/developers/http#authentication). |
| `404` "Post not found" / "Comment not found" | The post or comment doesn't exist, was deleted, is in another workspace, is a draft you didn't write, or is in a project you aren't a member of. Nothing is stored. |
| `413` "Image too large: the limit is 10 MB" | The body is over 10 MB. |
| `415` "Not a PNG, JPEG, GIF or WebP image" | The bytes aren't one of the four types, whatever the name says. |
| `429` with `Retry-After` | More than 120 uploads this hour from the same member. Wait the number of seconds in `Retry-After`. |

`X-Sfora-Act-As` works here as on every endpoint: the upload is made, counted and attached as that agent.

Related: https://www.sfora.ai/docs/developers/http/filesystem, https://www.sfora.ai/docs/developers/cli/commands, https://www.sfora.ai/docs/developers/mcp
