> This page is for version v1.
> For other versions, use one of these documentation indexes:
> - v2 (default): https://docs.pinnacle.sh/v-2/llms.txt
> - v1: https://docs.pinnacle.sh/v1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.pinnacle.sh/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.pinnacle.sh/_mcp/server.

# Upload local files

## Python

Our Python SDK `rcs-py` provides both a synchronous and an asynchronous method to upload files to Pinnacle. The `upload(self, file_path: str)` takes in a local file path that is uploaded and return a download URL that can be used as the media url for messages. The `upload` method
can be used in both synchronous and asynchronous mode depending on the type of `PinnacleClient` that is used.

### Synchronous

```python
import os
from rcs.client import Pinnacle, AsyncPinnacle

client = Pinnacle(api_key="YOUR_API_KEY")

test_file_path = os.path.join(
    os.path.dirname(__file__), "test.csv"
)

download_url = client.upload(test_file_path)
client.send.mms(from_="FROM_NUMBER", to="TO_NUMBER", media_urls=[download_url])
```

### Asynchronous

```python
client = AsyncPinnacle(api_key="YOUR_API_KEY")

test_file_path = os.path.join(
    os.path.dirname(__file__), "test.csv"
)

download_url = await client.upload(test_file_path)
await client.send.mms(
    from_="FROM_NUMBER", to="TO_NUMBER", media_urls=[download_url]
)
```

## TypeScript

Our TypeScript SDK `rcs-js` provides a method to upload files to Pinnacle. The async `upload` method takes in a `File` object and return a download URL that can be used as the media url for messages.

```typescript
import { PinnacleClient, Pinnacle } from "rcs-js";
import fs from "fs";
import path from "path";

// Create a File object from a local file
const filePath = path.join(__dirname, "test.csv");
const fileContent = fs.readFileSync(filePath);
const file = new File([fileContent], "test.csv", {
  type: "text/csv",
  lastModified: Date.now(),
});

const client = new PinnacleClient({
  apiKey: "YOUR_API_KEY",
});

const downloadUrl = await client.upload(file);
await client.send.mms({
  from: "FROM_NUMBER",
  to: "TO_NUMBER",
  mediaUrls: [downloadUrl],
});
```

## Other languages

At this time, this upload functionality is not natively supported in other languages (notably Ruby). However, you can still use the [Create Upload URL](api-reference/tools/upload-url) which generates a presigned upload URL (to upload your media asset) and also a download URL (to use as the media URL in your message).

To do so, first create the upload and download URLs using the `POST /tools/upload-url` endpoint.

```ruby
require 'rest-client'
require 'json'

response = RestClient.post(
    'https://www.trypinnacle.dev/api/tools/uploadUrl',
    {
        contentType: "image/png",
        size: 1024,
        name: "example.png"
    }.to_json,
    {
        "PINNACLE-API-Key": "<apiKey>",
        content_type: :json,
        accept: :json
    }
)
upload_url = JSON.parse(response.body)["upload"]
download_url = JSON.parse(response.body)["download"]
```

Then, make a PUT request to the upload URL with the proper `content_type` header to upload the file to Pinnacle.

```ruby
response = RestClient.put(
    upload_url,
    File.read("example.png"),
    {
        content_type: "image/png",
        accept: :json
    }
)
```

Finally, use the download URL as the media URL in your message.

```ruby
require "rcs"
api = Pinnacle::Client.new(
  api_key: "YOUR_API_KEY"
)
api.send.mms(
  to: "TO_NUMBER",
  from: "FROM_NUMBER",
  media_urls: [download_url]
)
```