# How to post pictures via API?

**URL:** https://meta.discourse.org/t/how-to-post-pictures-via-api/314282
**Category:** Development
**Created:** [June 29, 2024, 6:49am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282 "2024-06-29T06:49:34Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [June 29, 2024, 6:49am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/1 "2024-06-29T06:49:34Z")

</div>

I’ve been working on a [plugin](https://github.com/maxtimbo/discourse-sync) for Obsidian and have been banging my head against the wall trying to get images to upload. This is what I have so far:

```javascript
	async uploadImages(imageReferences: string[]): Promise<string[]> {
		const imageUrls = [];
		for (const ref of imageReferences) {
			const filePath = this.app.metadataCache.getFirstLinkpathDest(ref, this.activeFile.name)?.path;
			if (filePath) {
				const file = this.app.vault.getAbstractFileByPath(filePath) as TFile;
				if (file) {
					try {
						const arrayBuffer = await this.app.vault.readBinary(file);
						const blob = new Blob([arrayBuffer]);
						const boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW';
						let body = '';

						body += `--${boundary}\r\n`;
						body += `Content-Disposition: form-data; name="type"\r\n\r\n`;
						body += "composer\r\n";
						body += `--${boundary}\r\n`;
						body += `Content-Disposition: form-data; name="synchronous"\r\n\r\n`;
						body += "true\r\n";

						body += `--${boundary}\r\n`;
						body += `Content-Disposition: form-data; name="files[]"; filename="${file.name}"\r\n`;
						body += `Content-Type: image/jpg\r\n\r\n`
						body += blob + '\r\n';
						body += `--${boundary}--\r\n`;
						console.log(body)
						const formData = new TextEncoder().encode(body)

						const url = `${this.settings.baseUrl}/uploads.json`;
						const headers = {
							"Api-Key": this.settings.apiKey,
							"Api-Username": this.settings.disUser,
							"Content-Type": `multipart/form-data; boundary=${boundary}`
						};

						const response = await requestUrl({
							url: url,
							method: "POST",
							body: formData,
							throw: false,
							headers: headers,
						});

						//const response = await fetch(url, {
						//	method: "POST",
						//	body: formData,
						//	headers: new Headers(headers),
						//});

						console.log(`Upload Image response: ${response.status}`);
						//if (response.ok) {
						if (response.status == 200) {
							const jsonResponse = response.json();
							console.log(`Upload Image jsonResponse: ${JSON.stringify(jsonResponse)}`);
							imageUrls.push(jsonResponse.url);
						} else {
							new NotifyUser(this.app, `Error uploading image: ${response.status}`).open();
							console.error(`Error uploading image: ${JSON.stringify(response.json)}`);
							//console.error("Error uploading image:", response.status, await response.text());
						}
					} catch (error) {
						new NotifyUser(this.app, `Exception while uploading image: ${error}`).open();
						console.error("Exception while uploading image:", error);
					}
				} else {
					new NotifyUser(this.app, `File not found in vault: ${ref}`).open();
					console.error(`File not found in vault: ${ref}`);
				}
			} else {
				new NotifyUser(this.app, `Unable to resolve file path for: ${ref}`).open();
				console.error(`Unable to resolve file path for: ${ref}`);
			}
		}
		return imageUrls;
	}

```

I’m constructing a `multipart/form-data` because [`requestURL()`](https://docs.obsidian.md/Reference/TypeScript+API/request#request()+function) cannot accept a `formData()` as a parameter. Only [`string` or `arrayBuffer`](https://docs.obsidian.md/Reference/TypeScript+API/RequestUrlParam#RequestUrlParam+interface). I cannot use `fetch()` as I get a CORS error. With this code (and many minor tweaks to the `body`) I’m getting the following error:

> Error uploading image: {“errors”:[“You supplied invalid parameters to the request: Discourse::InvalidParameters”],“error\_type”:“invalid\_parameters”}

---

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [June 30, 2024, 12:07am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/2 "2024-06-30T00:07:53Z")

</div>

Thought I’d go ahead and update as I’m getting a different error massage now:

```javascript
	async uploadImages(imageReferences: string[]): Promise<string[]> {
		const imageUrls = [];
		for (const ref of imageReferences) {
			const filePath = this.app.metadataCache.getFirstLinkpathDest(ref, this.activeFile.name)?.path;
			if (filePath) {
				const file = this.app.vault.getAbstractFileByPath(filePath) as TFile;
				if (file) {
					try {
						const imgfile = await this.app.vault.readBinary(file);
						const boundary = genBoundary();
						const sBoundary = '--' + boundary + '\r\n';
						let body = '';
						body += `${sBoundary}Content-Disposition: form-data; name="type"\r\n\r\ncomposer\r\n`;
						body += `${sBoundary}Content-Disposition: form-data; name="synchronous"\r\n\r\ntrue\r\n`;
						body += `${sBoundary}Content-Disposition: form-data; name="files[]"; filename="${file.name}"\r\nContent-Type: image/jpg`;
						console.log(body);

						const eBoundary = '\r\n--' + boundary + '--\r\n';
						const bodyArray = new TextEncoder().encode(body);
						const endBoundaryArray = new TextEncoder().encode(eBoundary);

						const formDataArray = new Uint8Array(bodyArray.length + imgfile.byteLength + endBoundaryArray.length);
						formDataArray.set(bodyArray, 0);
						formDataArray.set(new Uint8Array(imgfile), bodyArray.length);
						formDataArray.set(endBoundaryArray, bodyArray.length + imgfile.byteLength);

						const url = `${this.settings.baseUrl}/uploads.json`;
						const headers = {
							"Api-Key": this.settings.apiKey,
							"Api-Username": this.settings.disUser,
							"Content-Type": `multipart/form-data; boundary=${boundary}`
						};

						const response = await requestUrl({
							url: url,
							method: "POST",
							body: formDataArray.buffer,
							throw: false,
							headers: headers,
						});

						console.log(`Upload Image response: ${response.status}`);
						if (response.status == 200) {
							const jsonResponse = response.json();
							console.log(`Upload Image jsonResponse: ${JSON.stringify(jsonResponse)}`);
							imageUrls.push(jsonResponse.url);
						} else {
							new NotifyUser(this.app, `Error uploading image: ${response.status}`).open();
							console.error(`Error uploading image: ${JSON.stringify(response.json)}`);
						}
					} catch (error) {
						new NotifyUser(this.app, `Exception while uploading image: ${error}`).open();
						console.error("Exception while uploading image:", error);
					}
				} else {
					new NotifyUser(this.app, `File not found in vault: ${ref}`).open();
					console.error(`File not found in vault: ${ref}`);
				}
			} else {
				new NotifyUser(this.app, `Unable to resolve file path for: ${ref}`).open();
				console.error(`Unable to resolve file path for: ${ref}`);
			}
		}
		return imageUrls;
	}

```

The error message I’m receiving now is:

> Exception while uploading image: SyntaxError: Unexpected token ‘I’, “Invalid request” is not valid JSON

This is confusing to me because the API states that we need to send a `multipart/form-data`, but it’s saying invalid JSON? Maybe it’s related to `requestAPI()`

---

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [July 2, 2024, 10:03pm UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/4 "2024-07-02T22:03:39Z")

</div>

So I thought I’d try a different approach by using the S3 storage. I followed the instructions [here](https://meta.discourse.org/t/set-up-file-and-image-uploads-to-s3/7229) to setup an AWS S3 bucket. Here are my settings:

 ![s3settings](https://global.discourse-cdn.com/meta/original/4X/c/5/e/c5e1bfd3dcdfe83d19fced09c479134f7d811dfd.jpeg)

And here is my code:

```javascript
	async uploadExternalImage(imageReferences: string[]): Promise<string[]> {
		const imageUrls: string[] = [];
		for (const ref of imageReferences) {
			const filePath = this.app.metadataCache.getFirstLinkpathDest(ref, this.activeFile.name)?.path;
			if (filePath) {
				const file = this.app.vault.getAbstractFileByPath(filePath) as TFile;
				if (file) {
					try {
						const url = `${this.settings.baseUrl}/uploads/generate-presigned-put.json`;
						//const imgfile = await this.app.vault.readBinary(file);
						const img = {
							type: "composer",
							file_name: file.name,
							file_size: file.stat.size,
						}
						console.log(JSON.stringify(img));
						const headers = {
							"Content-Type": "application/json",
							"Api-Key": this.settings.apiKey,
							"Api-Username": this.settings.disUser,
						};
						const response = await requestUrl({
							url: url,
							method: "POST",
							body: JSON.stringify(img),
							throw: false,
							headers: headers
						})
						console.log(response.json)
					} catch (error) {
						console.error(`Error uploading: ${error}`);
						//console.log(response.json)
					}
				} else {
					console.error('error')
				}
			} else {
				console.error('error')
			}
		}
		return imageUrls;
	}

```

Now, I realize this currently won’t work because I’m not actually uploading the file yet. From what I read in the [docs](https://docs.discourse.org/#tag/Uploads/operation/generatePresignedPut), I would send a json object containing the type, file\_name, and file\_size. The api should reply with a key and a url for me to use for the actual file transfer. But at this point, I’m getting the following error:

```plaintext
{
    "errors": [
        "The requested URL or resource could not be found."
    ],
    "error_type": "not_found"
}

```

```plaintext
[
    "The requested URL or resource could not be found."
]

```

I looked into my API key to make sure it had permissions, it does. But I created a new one anyways. And a global one. None are working. Same error code. What am I doing wrong?

Edit, here’s the img object:

```plaintext
{"type":"composer","file_name":"face2.jpg","file_size":17342}

```

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [July 2, 2024, 10:59pm UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/5 "2024-07-02T22:59:22Z")

</div>

> [@maxtim](#):
>
> I’ve been working on a [plugin](https://github.com/maxtimbo/discourse-sync) for Obsidian and have been banging my head against the wall trying to get images to upload

I’m not too familiar with that ecosystem, but maybe this helps?

> [@How to upload images via API with Node.js?](https://meta.discourse.org/t/how-to-upload-images-via-api-with-node-js/172605/2):
>
> I got this to work, like this: import Axios from "axios"; import FormData from "form-data"; import fs from "fs"; const http = Axios.create({ baseURL: "https://forum.zeebe.io", headers: { "Api-Key": "...", "Api-Username": "...", "Content-Type": "application/json", Accept: "application/json", }, }); http.interceptors.request.use((config) =\> { if (config.data instanceof FormData) { Object.assign(config.headers, config.data.getHeaders()); } return config; }); …

---

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [July 3, 2024, 12:51am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/6 "2024-07-03T00:51:29Z")

</div>

CORS error…

---

<div class="post-metadata">

### Author: ![thoka](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/thoka/32/115652_2.png) [@thoka](https://meta.discourse.org/u/thoka)
#### Post date: [July 3, 2024, 4:37am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/7 "2024-07-03T04:37:06Z")

</div>

Did you follow

> [@Setup Cross-Origin Resource Sharing (CORS)](https://meta.discourse.org/t/setup-cross-origin-resource-sharing-cors/270819):
>
> notebook_with_decorative_cover This is a #how-to guide that will guide you through the process of setting up Cross-Origin Resource Sharing (CORS) in Discourse. CORS is a mechanism that allows many resources (e.g., fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated. Here’s how you can set up CORS on your Discourse site: Prerequisites Before getting started, the DISCOURSE\_ENABLE\_CORS environmental variable must be …

?

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [July 3, 2024, 6:35am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/8 "2024-07-03T06:35:22Z")

</div>

I’m guessing a bit here, but I don’t think configuring CORS will work for this case. The origin (at least from the Obsidian Desktop app) is `'app://obsidian.md'`. I _think_ CORS can only be configured on Discourse to deal with HTTP requests.

@maxtim, do you need this to work from mobile, or would just being able to post to Discourse from the desktop app be good enough? I’m again guessing a bit, but… my understanding is that the desktop app is an Electron app. It’s running on a combination of Chromium and Node.js. You might be able to use [`node-fetch`](https://www.npmjs.com/package/node-fetch) to make server-side requests to Discourse. If that works, it would take care of the CORS issue and let you use FormData in requests.

---

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [July 3, 2024, 5:20pm UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/9 "2024-07-03T17:20:28Z")

</div>

> [@thoka](#):
>
> Did you follow

I went ahead and tried (proverbial throwing wet noodles to see what sticks). But I was already apprehensive and @simon is correct.

> [@simon](#):
>
> do you need this to work from mobile, or would just being able to post to Discourse from the desktop app be good enough?

Ideally, the plugin would be available on mobile as well. But for now, if Desktop only is what we get, it’s what we get.

Of course, another solution might be: Obsidian Vault on mobile → sync to Desktop → cli to upload to Discourse. But that does seem a little convoluted.

Basically, the ideal situation is that the Discourse forum replaces the Obsidian Vault. That way users who prefer the forum, can use the forum. Users who prefer (or indeed need an off-line solution) can use Obsidian. I already have some ideas on how a bi-directional sync might work. But I think images/files need to handles in some way first.

Edit:

I’m fairly convinced this will work, but I can’t seem to be able to get the parameters correct:

```javascript
					try {
						const imgfile = await this.app.vault.readBinary(file);
						const boundary = genBoundary();
						const sBoundary = '--' + boundary + '\r\n';
						let body = '';
						body += `${sBoundary}Content-Disposition: form-data; name="type"\r\n\r\ncomposer\r\n`;
						body += `${sBoundary}Content-Disposition: form-data; name="synchronous"\r\n\r\ntrue\r\n`;
						body += `${sBoundary}Content-Disposition: form-data; name="files[]"; filename="${file.name}"\r\nContent-Type: image/jpg\r\n`;

						const eBoundary = '\r\n--' + boundary + '--\r\n';
						const bodyArray = new TextEncoder().encode(body);
						const endBoundaryArray = new TextEncoder().encode(eBoundary);

						const formDataArray = new Uint8Array(bodyArray.length + imgfile.byteLength + endBoundaryArray.length);
						formDataArray.set(bodyArray, 0);
						formDataArray.set(new Uint8Array(imgfile), bodyArray.length);
						formDataArray.set(endBoundaryArray, bodyArray.length + imgfile.byteLength);

						const url = `${this.settings.baseUrl}/uploads.json`;
						const headers = {
							"Api-Key": this.settings.apiKey,
							"Api-Username": this.settings.disUser,
							"Content-Type": `multipart/form-data; boundary=${boundary}`
						};

						const response = await requestUrl({
							url: url,
							method: "POST",
							body: formDataArray,
							throw: false,
							headers: headers,
						});

						if (response.status == 200) {
							const jsonResponse = response.json();
							console.log(`Upload Image jsonResponse: ${JSON.stringify(jsonResponse)}`);
							imageUrls.push(jsonResponse.url);
						} else {
							new NotifyUser(this.app, `Error uploading image: ${response.status}`).open();
							console.error(`Error uploading image: ${JSON.stringify(response.json)}`);
						}

```

---

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [July 4, 2024, 6:01pm UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/10 "2024-07-04T18:01:18Z")

</div>

Aye!! I did it!

```javascript
	async uploadImages(imageReferences: string[]): Promise<string[]> {
		const imageUrls = [];
		for (const ref of imageReferences) {
			const filePath = this.app.metadataCache.getFirstLinkpathDest(ref, this.activeFile.name)?.path;
			if (filePath) {
				const file = this.app.vault.getAbstractFileByPath(filePath) as TFile;
				if (file) {
					try {
						const imgfile = await this.app.vault.readBinary(file);
						const boundary = genBoundary();
						const sBoundary = '--' + boundary + '\r\n';
						const imgForm = `${sBoundary}Content-Disposition: form-data; name="file"; filename="${file.name}"\r\nContent-Type: image/${file.extension}\r\n\r\n`;

						let body = '';
						body += `\r\n${sBoundary}Content-Disposition: form-data; name="type"\r\n\r\ncomposer\r\n`;
						body += `${sBoundary}Content-Disposition: form-data; name="synchronous"\r\n\r\ntrue\r\n`;

						const eBoundary = '\r\n--' + boundary + '--\r\n';
						const imgFormArray = new TextEncoder().encode(imgForm);
						const bodyArray = new TextEncoder().encode(body);
						const endBoundaryArray = new TextEncoder().encode(eBoundary);

						const formDataArray = new Uint8Array(imgFormArray.length + imgfile.byteLength + bodyArray.length + endBoundaryArray.length);
						formDataArray.set(imgFormArray, 0);
						formDataArray.set(new Uint8Array(imgfile), imgFormArray.length);
						formDataArray.set(bodyArray, imgFormArray.length + imgfile.byteLength);
						formDataArray.set(endBoundaryArray, imgFormArray.length + bodyArray.length + imgfile.byteLength);

						const url = `${this.settings.baseUrl}/uploads.json`;
						const headers = {
							"Api-Key": this.settings.apiKey,
							"Api-Username": this.settings.disUser,
							"Content-Type": `multipart/form-data; boundary=${boundary}`,
						};

						const response = await requestUrl({
							url: url,
							method: "POST",
							body: formDataArray.buffer,
							throw: false,
							headers: headers,
						});

						if (response.status == 200) {
							const jsonResponse = response.json;
							console.log(`Upload Image jsonResponse: ${JSON.stringify(jsonResponse)}`);
							imageUrls.push(jsonResponse.url);
						} else {
							new NotifyUser(this.app, `Error uploading image: ${response.status}`).open();
							console.error(`Error uploading image: ${JSON.stringify(response.json)}`);
						}
					} catch (error) {
						new NotifyUser(this.app, `Exception while uploading image: ${error}`).open();
						console.error("Exception while uploading image:", error);
					}
				} else {
					new NotifyUser(this.app, `File not found in vault: ${ref}`).open();
					console.error(`File not found in vault: ${ref}`);
				}
			} else {
				new NotifyUser(this.app, `Unable to resolve file path for: ${ref}`).open();
				console.error(`Unable to resolve file path for: ${ref}`);
			}
		}
		return imageUrls;
	}

```

The problem was the order in which I was constructing the form-data. I need it to go:

- img params
- img binary
- params

I was previously putting the params ahead of the img.

I solved this by analyzing a successful upload using python:

```python
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
from requests.models import PreparedRequest

class Discourse:
    def __init__ (self):
        self.base_url = "CENSORED"
        self.api_key = "CENSORED"
        self.api_username = "CENSORED"
        self.category = 2

    def post_uploads(self, file_path):
        headers = {
            "Content-Type": "multipart/form-data",
            "Api-Key": self.api_key,
            "Api-Username": self.api_username
        }

        multi = MultipartEncoder(
            fields = {
                'file': ('filename', open(file_path, 'rb'), 'image/jpg'),
                'type': 'composer',
                'synchronous': 'true'
            }
        )

        headers['Content-Type'] = multi.content_type

        request = requests.Request(
            method = "POST",
            url = f"{self.base_url}/uploads.json",
            headers = headers,
            data = multi
        )
        prepared_request = request.prepare()

        print("Headers:")
        for k, v in prepared_request.headers.items():
            print(f"{k}: {v}")

        print(multi.to_string())

        response = requests.post(
            f"{self.base_url}/uploads.json",
            headers=headers,
            params=params,
            data=m
        )

        return response.json()

if __name__ == " __main__":
    ds = Discourse()
    response = ds.post_uploads("/home/tfinley/Pictures/face2.jpg")
    print(response)

```

Now how do I delete orphaned uploads ^\_o

---

<div class="post-metadata">

### Author: ![thoka](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/thoka/32/115652_2.png) [@thoka](https://meta.discourse.org/u/thoka)
#### Post date: [July 4, 2024, 7:35pm UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/11 "2024-07-04T19:35:29Z")

</div>

> [@maxtim](#):
>
> Now how do I delete orphaned uploads

To my understanding, discourse will handle that for you:

> [@Auto purge uploads from old deleted posts](https://meta.discourse.org/t/auto-purge-uploads-from-old-deleted-posts/51306/1):
>
> Discourse already automatically removes orphan unreferenced uploads.

---

<div class="post-metadata">

### Author: ![maxtim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/maxtim/32/427804_2.png) [@maxtim](https://meta.discourse.org/u/maxtim)
#### Post date: [July 4, 2024, 7:47pm UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/12 "2024-07-04T19:47:08Z")

</div>

> [@thoka](#):
>
> discourse will handle that for you

Yep saw that. GG EZ WP

---

<div class="post-metadata">

### Author: ![Lhc\_fl](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lhc_fl/32/268115_2.png) [@Lhc\_fl](https://meta.discourse.org/u/Lhc_fl)
#### Post date: [July 5, 2024, 2:16am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/13 "2024-07-05T02:16:13Z")

</div>

I’m late to the party, but I’ve written a third-party discourse-api for nodejs before:

> **[DiscourseApi | node-discourse-api](https://lhcfl.github.io/node-discourse-api/classes/api.DiscourseApi.html#createUpload)**
>
> Create a upload | Documentation for node-discourse-api

With this library you can easily create uploads. Just do this:

```js
const { DiscourseApi } = require("node-discourse-api");
const api = new DiscourseApi("https://discourse.example.com");
api.options.api_username = "API_USERNAME";
api.options.api_key = "API_KEY";

api.createUpload(file_path_or_buffer, { filename: "filename" })

```

(Note: This library is not complete)

---

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [August 4, 2024, 2:16am UTC](https://meta.discourse.org/t/how-to-post-pictures-via-api/314282/14 "2024-08-04T02:16:20Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
