# Assets

Twilio Assets is a static file-hosting service that lets you quickly upload and serve files needed to support your applications. You can host your files that support web, voice, and messaging workflows. Common use cases include:

* Hosting `.mp3` audio files used in TwiML and Studio Flows.
* Serving images sent through MMS.
* Storing configuration files used by Twilio Functions.

## How Assets work

Twilio provides you with three different types of Assets: **public**, **protected**, and **private**. The primary difference between the types is how they are accessed:

* Public Assets are served over HTTPS from the Twilio CDN to ensure high availability and security, and are accessible to anyone with the Asset URL. Ensure that you carefully consider the data you include in a public Asset.
* Protected Assets are also accessible through the Twilio CDN by URL. However, they require a valid Twilio request signature. If the request doesn't have a valid signature, the service returns a `403` error.
* Private Assets are not accessible by URL or exposed to the web. Instead, they're packaged with your Twilio Functions at build time.

For more details on Asset visibility, see the [Visibility documentation](/docs/serverless/functions-assets/visibility), which explains the differences between public, protected, and private Assets.

> \[!WARNING]
>
> When you upload Asset files, all the metadata is saved along with them. For example, if your image files have EXIF metadata, this information stays with the image. If you share these files, the metadata remains intact and is not altered or removed by the Assets product.

## Getting started

In the Twilio Console, open one of your existing Services or [creating a new one](/docs/serverless/functions-assets/functions/create-service). When you are in the Functions Editor UI for your Service, note the following three areas:

1. The **Assets** pane: This lists your Assets, their Visibility, a context menu for adjusting visibility, and another menu for other actions.
2. The **Add +** button: This lets you upload files as Assets or create a new Asset.
3. The **Deploy All** button: This uploads and deploys your Assets (along with any Functions).

To upload an Asset, click **Add +** then **Upload File**. Once you've selected a file, set the visibility of each Asset. Click **Upload** to upload the selected file(s) with your desired settings.

As the Asset uploads, it is not immediately accessible through a URL or in your Functions yet. A gray circle next to the Asset's name indicates this state. To deploy the Asset (and the rest of your service), click **Deploy All**. Once deployed, a green check icon appears next to each deployed Asset.

### Get the URL of an Asset

To construct an Asset URL, take the service URL, for example `https://example-1234.twil.io`, and append the name of your Asset. An Asset such as, `example.json`, is accessible at `https://example-1234.twil.io/example.json`.

To copy an Asset URL to your clipboard:

* Click **⋮**, then **Copy URL**.
* Open the Asset in the Functions Editor, and click **Copy URL**.

### Using public Assets

Public Assets are accessible to anyone with the Asset URL once deployed. For example, if your service is hosted at `https://example-1234.twil.io` and has a public Asset named `ahoy.mp3`, the Asset is available at `https://example-1234.twil.io/ahoy.mp3`.

### Using protected Assets

The URL of a protected Asset is created the same way as a that of a public Asset. However, a protected Asset is only accessible from Twilio code, such as a Function, a Studio Flow, or Flex.

For example, you deploy an image named `grumpy.jpg` to `https://twilio-assets-1967.twil.io/grumpy.jpg`, and want to send this image to users as part of an MMS, but don't want it to be accessible via URL. When you deploy following code to a function in the same service and execute it, only the intended recipients receive the image. Anyone who tries to access the file through the URL receives a `403 Forbidden` error instead.

```js title="Use a protected Asset in a Function"
exports.handler = (context, event, callback) => {
  // Access the NodeJS SDK by calling context.getTwilioClient()
  const client = context.getTwilioClient();
  // Query parameters or values sent in a POST body can be accessed from `event`
  const from = event.From || "+15017122661";
  const to = event.To || "+15558675310";
  const body = event.Body || "Ahoy, World!";

  client.messages
    .create({
      to,
      from,
      body,
      // You will get a 403 if you try to view this image, but Twilio will
      // be able to access it and send it as part of the outgoing MMS
      mediaUrl: "https://twilio-assets-1967.twil.io/grumpy.jpg",
    })
    .then((message) => {
      console.log(`Success! MMS SID: ${message.sid}`);
      return callback(null, message.sid);
    })
    .catch((error) => {
      console.error(error);
      return callback(error);
    });
};
```

If you have an audio file to include in a Studio Flow, but don't want the file to be publicly accessible, you can upload it as a protected Asset. Reference the URL in a Studio Say/Play widget to play the audio only for users that reach that part of your Flow. An example protected Asset deployed at `https://twilio-assets-1967.twil.io/sensitive-message.mp3` can be referenced in your Studio Flow as follows:

![Twilio Respond widget configured to play an audio message from a URL.](https://docs-resources.prod.twilio.com/d25f02ae0afd338d456b2b3473a10bcac5475d4e954ebc9b8d114acb4ffa61aa.png)

### Using private Assets

When Twilio builds your function for deployment, it bundles every private Asset you've uploaded. This makes private Assets well-suited for storing sensitive configuration files, templates, and shared code that supports your application.

To access a private Asset, use the [`Runtime.getAssets`](/docs/serverless/functions-assets/client#getassets) method and open the file with either the `open` helper method or the `fs` module, as shown in the following examples.

```js title="Read the content of a Private Asset" description="Example of how to read the contents of a private Asset."
exports.handler = (context, event, callback) => {
  // Access the open helper method for the Asset
  const openFile = Runtime.getassets()["/my_file.txt"].open;

  // Open the Private Asset and read the contents.
  // Calling open is equivalent to using fs.readFileSync(asset.filePath, 'utf8')
  const text = openFile();
  console.log("Your file contents: " + text);

  return callback();
};
```

```js title="Serve an audio file from a private Asset" description="Example of how to serve an audio file from a private Asset."
// Load the fs module
const fs = require("fs");

exports.handler = (context, event, callback) => {
  // Get the path to the Private Asset
  const mp3Path = Runtime.getassets()["/audio.mp3"].path;

  // Read the file into the buffer and get its metadata
  const buffer = fs.readFileSync(mp3Path);
  const stat = fs.statSync(mp3Path);
  // Create a new Response object
  const response = new Twilio.Response();
  // Send the audio file in the response
  response.setBody(buffer);
  response.appendHeader("Content-Type", "audio/mpeg");
  response.appendHeader("Content-Length", stat.size);

  return callback(null, response);
};
```

## Hosting a root Asset

In some cases, such as hosting an app with a landing page, you want the root URL of your service to return an Asset in the browser. For example, [root-asset-5802.twil.io/](https://root-asset-5802.twil.io/) serves static HTML solely from Assets.

To reproduce this behavior, use one of two special paths for a **public** Asset for your file (HTML, an image, or any other file type).

### `/` \[#root-asset]

If you rename an Asset's path to `/`, the service delivers the Asset when a user requests the root URL.

### `/assets/index.html`

This path only applies to HTML files. Renaming an HTML file's path to `/assets/index.html` instructs the service to return that page when a user requests the root URL of your service.

Try this with the following `index.html` sample:

```html title="Root Asset index.html"
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Hello Twilio Serverless!</title>
  </head>
  <body>
    <header><h1>Hello from Twilio Serverless!</h1></header>
    <main>
      <p>
        This page is a public Asset that can be accessed from the root URL of this service.
      </p>
    </main>
    <footer>
      <p>
        Made with 💖 by your friends at
        <a href="https://www.twilio.com">Twilio</a>
      </p>
    </footer>
  </body>
</html>
```

> \[!NOTE]
>
> To deploy a root Asset using the [Serverless Toolkit](/docs/labs/serverless-toolkit) instead, you need to create the `/assets/index.html` path *inside* the existing `assets/` folder that exists for your project.
>
> The resulting path in your local filesystem will be `/assets/assets/index.html`, and the folder hierarchy will be as follows:
>
> ```bash
> .
> ├── assets
> │   └── assets
> │       └── index.html
> ├── functions
> └── package.json
> ```

### Limitations

All [builds](/docs/serverless/api/resource/build) have limits on the maximum file size and quantity of each Asset type:

| Asset type | Maximum size | Quantity    |
| :--------- | :----------- | :---------- |
| Public     | 25 MB        | 1000 assets |
| Private    | 10 MB        | 50 assets   |
| Protected  | 25 MB        | 1000 assets |

These limits only apply to individual Builds. Assets used in a Build by a [subaccount](/docs/iam/api/subaccounts) don't affect the limits of other subaccounts or the main account.

Using [Environments](/docs/serverless/api/resource/environment) further expands your options, as you can create Builds that contain different sets of Assets and [deploy](/docs/serverless/api/resource/deployment) them to separate Environments.

> \[!WARNING]
>
> When you use the Serverless API (either directly through the [SDKs](/docs/libraries), or through the [CLI](/docs/twilio-cli/quickstart)), you can create more Assets and Asset versions within a service. For example, to have different Assets in different Environments. A specific Build, however, can only include so many Assets, as described in the [Limitations table](#limitations).
>
> The Console UI doesn't support this flexibility. Every Asset listed in the UI is included in the deployed Build, and you cannot upload Assets that exceed the stated limits.
