---
source_url: https://www.pubnub.com/docs/general/files
title: File Sharing
updated_at: 2026-05-19T12:10:50.725Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# File Sharing

PubNub makes it easy to add file sharing alongside messages and signals.

Use file sharing to share images in social apps or send medical records in healthcare apps. You can easily upload and share videos, images, or documents up to 5 MB per file.

Turn on File Sharing to start sending files with your keyset.

###### Deliver files through the message system

PubNub delivers files as messages with internal type `4`. Each file message carries the sending client's [User ID](https://www.pubnub.com/docs/general/setup/users-and-devices) as the publisher, letting you identify who uploaded a file when retrieving history. Subscribers receive file events on the same channel listeners they use for regular messages. File messages support [Message Persistence](https://www.pubnub.com/docs/general/storage) (retrievable alongside text messages), [custom message types](https://www.pubnub.com/docs/general/messages/type) for categorization, and can trigger [Mobile Push Notifications](https://www.pubnub.com/docs/general/push/send) to alert users about shared files when they are away from the app.

## Configuration

Enable and configure File Sharing for your app's keyset in the [Admin Portal](https://admin.pubnub.com/).

:::tip Public Admin Portal demo
Want to browse through the Admin Portal without creating an account? Explore it through the [Public Demo](https://demo-admin.pubnub.com/) that shows examples of most PubNub features for transport and logistics use case.
:::

The Admin Portal prompts you to enable File Sharing when you create a keyset (unless you choose **Choose later** for **Region**).

[Testing keysets](https://www.pubnub.com/docs/general/setup/account-setup#environment-aligned-keys) default to the `US East` region with `7‑day` retention. Retention is how long PubNub keeps files before deletion.

![File Sharing settings in Admin Portal showing bucket region and retention controls](https://www.pubnub.com/assets/images/file-sharing-0f5874bab827c363fcb9e25711f909ae.png)

| Option | Description |
| --- | --- |
| **Bucket Region** | The geographical region where PubNub stores and manages your files. Align file retention with your [Message Persistence](https://www.pubnub.com/docs/general/storage) settings to keep messages and files for the same duration. |
| **Retention** | The duration that PubNub keeps files. Free accounts support 1 or 7 days. Paid accounts support higher limits. |

###### Secure file operations with Access Manager

When Access Manager is enabled, file operations require channel-level permissions: `write` to send a file, `read` to list or download files, and `delete` to remove a file. These map to the same channel permissions used for messaging, so a token that grants `write` on a channel also authorizes file uploads on that channel. See the [File sharing permissions table](https://www.pubnub.com/docs/general/security/access-control#file-sharing) for the full mapping.

## Send files

To send a file, choose the channel and the file. You can add a text caption.

You can encrypt files using Advanced Encryption Standard ([AES-256](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)) (see [Data Security](https://www.pubnub.com/docs/general/setup/data-security)). Set a cipher key in the client configuration or pass it per request. Clients that download the file must use the same key.

The following code sends the file `cat_picture.jpg` of the custom type `file-message` on the channel `my_channel`.

:::note Custom message types
Not all SDKs support sending messages with a custom message type. For details, see [Message Types](https://www.pubnub.com/docs/general/messages/type).
:::

###### JavaScript

```javascript
// web
const input = document.querySelector('input[file]');

input.addEventListener('change', async () => {
  const file = input.files[0];

  const result = await pubnub.sendFile({
    channel: 'my_channel',
    file: file,
    customMessageType: 'file-message'
  });
});

// Node.js
import fs from 'fs';

const myFile = fs.createReadStream('./cat_picture.jpg');

const result = await pubnub.sendFile({
  channel: 'my_channel',
  file: { stream: myFile, name: 'cat_picture.jpg', mimeType: 'image/jpeg' },
});

// React
const result = await pubnub.sendFile({
  channel: 'my_channel',
  message: {
    test: 'message',
    value: 42
  },
  file: {
    uri: imageUri,
    name: 'cat_picture.jpg',
    mimeType: 'image/jpeg',
  },
});
```

###### C#

```csharp
PNResult<PNFileUploadResult> fileUploadResponse = await pubnub.SendFile()
    .Channel("my_channel")
    .File("cat_picture.jpg") // checks the bin folder if no path is provided
    .Message("Look at this photo!")
    .ExecuteAsync();
PNFileUploadResult fileUploadResult = fileUploadResponse.Result;
PNStatus fileUploadStatus = fileUploadResponse.Status; // contains troubleshooting info
if (!fileUploadStatus.Error && fileUploadResult != null) // checks if successful
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileUploadResult));
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileUploadStatus));
}
```

###### Go

```go
file, err := os.Open("cat_picture.jpg")
defer file.Close()
if err != nil {
    panic(err)
}
resSendFile, statusSendFile, errSendFile := pn.SendFile().
  Channel("my_channel").
  Message("Look at this photo!").
  Name("cat_picture.jpg").
  File(file).Execute()
fmt.Println(resSendFile, statusSendFile, errSendFile)
fmt.Println(resSendFile.Data.ID)
```

###### Objective-C

```objectivec
NSURL *localFileURL = ...;
PNSendFileRequest *request = [PNSendFileRequest requestWithChannel:@"my_channel"
                                                           fileURL:localFileURL];
request.customMessageType = @"file-message";

[self.client sendFileWithRequest:request completion:^(PNSendFileStatus *status) {
    if (!status.isError) {
        /**
         * File upload successfully completed.
         * Uploaded file information is available here:
         *   status.data.fileIdentifier is the unique file identifier
         *   status.data.fileName is the name used to store the file
         */
    } else {
        /**
         * Handle send file error. Check the 'category' property for reasons
         * why the request may have failed.
         *
         * Check 'status.data.fileUploaded' to determine whether to resend the
         * request or if only file message publish should be called.
         */
    }
}];
```

###### Java

```java
pubnub.sendFile()
    .channel("my_channel")
    .fileName("cat_picture.jpg")
    .customMessageType("file-message")
    .inputStream(inputStream)
    .message("Look at this photo!")
    .async(result -> {
        result.onSuccess(res -> {
                System.out.println("send timetoken: " + res.getTimetoken());
                System.out.println("send status: " + res.getStatus());
                System.out.println("send fileId: " + res.getFile().getId());
                System.out.println("send fileName: " + res.getFile().getName());
        }).onFailure(exception -> {
            exception.printStackTrace();
        });
    });
```

###### Python

```python
# synchronous
with open("cat_picture.jpg", "rb") as fd:
    envelope = pubnub.send_file() \
        .channel("my_channel") \
        .file_name("cat_picture.jpg") \
        .message({"test_message": "test"}) \
        .custom_message_type('file-message') \
        .should_store(True) \
        .ttl(222) \
        .file_object(fd) \
        .cipher_key("secret") \
        .sync()

# multithreaded asynchronous
def callback(response, status):
    pass

fd = open("cat_picture.jpg", "rb") as fd:
    pubnub.send_file() \
        .channel("my_channel") \
        .file_name("cat_picture.jpg") \
        .message({"test_message": "test"}) \
        .custom_message_type('file-message') \
        .should_store(True) \
        .ttl(222) \
        .file_object(fd.read()) \
        .cipher_key("secret") \
        .pn_async(callback)
```

###### PHP

```php
pubnub.sendFile()
    ->message("Hey, this is the requested file.")
    ->channel("channel_1")
    ->fileId("p1n4ppl3p1zz4")
    ->fileName("pinapplePizza.jpg")
    ->customMessageType("file-message")
    ->sync();
```

###### Swift

```swift
pubnub.send(
  .file(url: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("cat_picture.jpg")),
  channel: "my_channel",
  remoteFilename: "cat_picture.jpg",
  publishRequest: .init(additionalMessage: ["text": "Look at this photo!"], customMessageType: "file-message")
) { (fileTask) in
  print("The task \(fileTask.urlSessionTask.taskIdentifier) has started uploading; no need to call `resume()`")
  print("If needed, the `URLSessionUploadTask` can be accessed with `fileTask.urlSessionTask`")
  print("You can use `fileTask.progress` to populate a  `UIProgressView`/`ProgressView` ")
} completion: { (result) in
  switch result {
  case let .success((task, file, publishedAt)):
    print("The file with an ID of \(file.fileId) was uploaded at \(publishedAt) timetoken) ")
  case let .failure(error):
    print("An error occurred while uploading the file: \(error.localizedDescription)")
  }
}
```

###### Dart

```dart
import 'dart:io';
import 'package:pubnub/pubnub.dart';

void main() async {
  var pubnub = PubNub(
    defaultKeyset: Keyset(
      subscribeKey: 'sub-c-abc-123',  // Replace with your subscribe key
      publishKey: 'pub-c-def-456'     // Replace with your publish key
    ),
  );

  var inputFile = File('cat_picture.jpg').readAsBytesSync();

  try {
    var result = await pubnub.files.sendFile(
      'my_channel',
      'cat_picture.jpg',
      inputFile,
      fileMessage: 'Look at this photo!',
      customMessageType: 'file-message'   // Example with custom message type
    );

    print('File uploaded - timetoken ${result.timetoken}');
  } catch (e) {
    print('Failed to send file: $e');
  }
}
```

###### Retrieve shared files by timetoken

A successful file upload returns a timetoken, just like a standard [publish](https://www.pubnub.com/docs/general/messages/publish#send-messages) call. This timetoken identifies the file message in [Message Persistence](https://www.pubnub.com/docs/general/storage#retrieve-messages) and lets you retrieve it alongside text messages using the same `start` and `end` parameters. File messages appear in history with internal type `4`, so you can filter them from regular messages during retrieval.

The client uploads files, and the storage service stores and manages the files for each keyset.

After upload, channel subscribers receive a file message with a description, file name, and file ID. Use an SDK method to build a URL to view or download the file. For details, see [Retrieve files](#retrieve-files).

:::note Enable the feature
File Sharing is an optional feature that you must enable for your keys in the [Admin Portal](https://admin.pubnub.com).
:::

## File operations

You can list files in a channel or delete uploaded files. If the keyset has Message Persistence, you can search historical file messages with the Message Persistence API. Use the results to display or download files.

Refer to the File Sharing section in the [SDK documentation](https://www.pubnub.com/docs/sdks) for details about file operations in each SDK.

## Receive files

To receive a file, subscribe to the channel and listen for [file events](https://www.pubnub.com/docs/general/basics/receive-messages). No special subscription is required.

## Retrieve files

Retrieving files takes two steps. Use the [Message Persistence API](https://www.pubnub.com/docs/sdks/rest-api/message-persistence-introduction) and File Sharing SDK methods. To retrieve a file:

1. Get the file message by using the Message Persistence API or the `listFiles` method.
2. Display or download the file by using dedicated SDK methods and the returned file information.

:::note File URLs
The Message Persistence API and the `listFiles` method return secure file metadata (such as file name and ID). Use SDK methods to generate file URLs for viewing or downloading.
:::

When you fetch file messages with the Message Persistence API, the response includes the file ID, file name, and message type `4` (file message). Use this data to view or download the file.

### Display and download files

Apart from the optional description, a file message contains the file ID and file name.

A file message has no URL. Use an SDK method to build the file URL to view or download the file.

The following code returns the URL of the `cat_picture.jpg` file sent to the channel `my_channel`:

#### JavaScript

```javascript
const result = pubnub.getFileUrl({ channel: 'my_channel', id: 'd9515cb7-48a7-41a4-9284-f4bf331bc770', name: 'cat_picture.jpg' });
```

#### C#

```csharp
PNResult<PNFileUrlResult> getFileUrlResponse = await pubnub.GetFileUrl()
    .Channel("my_channel")
    .FileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
    .FileName("cat_picture.jpg")
    .ExecuteAsync();
PNFileUrlResult getFileUrlResult = getFileUrlResponse.Result;
PNStatus getFileUrlStatus = getFileUrlResponse.Status;
if (getFileUrlResult != null)
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(getFileUrlResult));
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(getFileUrlStatus));
}
```

#### Go

```go
resGetFile, statusGetFile, errGetFile := pn.GetFileURL().
  Channel("my_channel").
  ID("d9515cb7-48a7-41a4-9284-f4bf331bc770").
  Name("cat_picture.jpg").Execute()
fmt.Println(resGetFile, statusGetFile, errGetFile)
fmt.Println(resGetFile.URL)
```

#### Objective-C

```objectivec
NSURL *url = [self.client downloadURLForFileWithName:@"cat_picture.jpg"
                                          identifier:@"<file-identifier>"
                                           inChannel:@"my_channel"];
```

#### Java

```java
pubnub.getFileUrl()
        .channel("my_channel")
        .fileName("cat_picture.jpg")
        .fileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
        .async(result -> {
            result.onSuccess(res -> {
                System.out.println("getUrl fileUrl: " + res.getUrl());
            }).onFailure(exception -> {
                exception.printStackTrace();
            });
        });
```

#### Python

```python
# Synchronous:
envelope = pubnub.get_file_url().
    channel("my_channel").
    file_id("fileID").
    file_name("cat_picture.jpg").sync()

# Multithreaded asynchronous:
def callback(response, status):
    pass

pubnub.get_file_url().
    channel("my_channel").
    file_id("fileID").
    file_name("cat_picture.jpg").sync()
```

#### Swift

```swift
do {
  let downloadURL = try pubnub.generateFileDownloadURL(channel: "my_channel", fileId: "fileID", filename: "cat_picture.jpg")
} catch {
  print("An error occurred generating the URL: \(error.localizedDescription)")
}
```

#### Dart

```dart
import 'package:pubnub/pubnub.dart';

void main() async {
  var pubnub = PubNub(
    defaultKeyset: Keyset(
      subscribeKey: 'sub-c-abc-123',  // Replace with your subscribe key
      publishKey: 'pub-c-def-456'     // Replace with your publish key
    ),
  );

  var fileUrl = pubnub.files.getFileUrl(
    'my_channel', 
    'd9515cb7-48a7-41a4-9284-f4bf331bc770', 
    'cat_picture.jpg'
  );

  print('File URL: $fileUrl');
}
```

The call returns a file URL. Use it to view the file in the browser.

Use the same file information to download the file. The following example downloads `cat_picture.jpg` from `my_channel`:

##### JavaScript

```javascript
// web
const file = await pubnub.downloadFile({
  channel: 'my_channel',
  id: '...',
  name: 'cat_picture.jpg',
});

const myImageTag = document.createElement('img');
myImageTag.src = URL.createObjectURL(await file.toFile());

document.body.appendChild(myImageTag);

// Node.js using streams
import fs from 'fs'

const file = await pubnub.downloadFile({
  channel: 'my_channel',
  id: 'd9515cb7-48a7-41a4-9284-f4bf331bc770',
  name: 'cat_picture.jpg',
});

const output = fs.createWriteStream('./cat_picture.jpg');
const fileStream = await file.toStream();

fileStream.pipe(output);

output.once('end', () => {
  console.log('File saved to ./cat_picture.jpg');
});

// React
const file = await pubnub.downloadFile({
  channel: 'my_channel',
  id: imageId,
  name: 'cat_picture.jpg'
});

let fileContent = await file.toBlob();
```

##### C#

```csharp
PNResult<PNDownloadFileResult> fileDownloadResponse = await pubnub.DownloadFile()
    .Channel("my_channel")
    .FileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
    .FileName("cat_picture.jpg")
    .ExecuteAsync();
PNDownloadFileResult fileDownloadResult = fileDownloadResponse.Result;
PNStatus fileDownloadStatus = fileDownloadResponse.Status;
if (fileDownloadResult != null)
{
    fileDownloadResult.SaveFileToLocal(downloadUrlFileName); // saves to bin folder if no path is provided
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileDownloadResult.FileName));
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(fileDownloadStatus));
}
```

##### Go

```go
resDLFile, statusDLFile, errDLFile := pn.DownloadFile().
  Channel("my_channel").
  ID("d9515cb7-48a7-41a4-9284-f4bf331bc770").
  Name("cat_picture.jpg").Execute()
if resDLFile != nil {
    filepathOutput := "cat_picture.jpg"
    out, _ := os.Create(filepathOutput)
    _, err := io.Copy(out, resDLFile.File)

    if err != nil {
        fmt.Println(err)
    }
}
```

##### Objective-C

```objectivec
PNDownloadFileRequest *request = [PNDownloadFileRequest requestWithChannel:@"my_channel"
                                                                identifier:@"<file-identifier>"
                                                                      name:@"cat_picture.jpg"];
request.targetURL = ...;

[self.client downloadFileWithRequest:request
                          completion:^(PNDownloadFileResult *result, PNErrorStatus *status) {
    if (!status.isError) {
        /**
         * File successfully has been downloaded.
         *   status.data.location - location where downloaded file can be found
         *   status.data.temporary - whether file has been downloaded to temporary storage and
         *                           will be removed on completion block return.
         */
    } else {
        /**
         * Handle file download error. Check 'category' property to find out possible issue
         * because of which request did fail.
         *
         * Request can be resent using: [status retry]
         */
    }
}];
```

##### Java

```java
pubnub.downloadFile()
    .channel("my_channel")
    .fileName("cat_picture.jpg")
    .fileId("d9515cb7-48a7-41a4-9284-f4bf331bc770")
    .async(result -> {
        result.onSuccess(res -> {
            System.out.println("getFile fileName: " + res.getFileName());
            System.out.println("getFile byteStream: " + res.getByteStream());
        }).onFailure(exception -> {
            exception.printStackTrace();
        });
    });
```

##### Python

```python
# synchronous:
download_envelope = pubnub.download_file().
    channel("my_channel").
    file_id("fileId").
    file_name("cat_picture.jpg").sync()

# Multithreaded asynchronous:
def callback(response, status):
    pass

pubnub.download_file().
    channel("my_channel").
    file_id("fileID").
    file_name("cat_picture.jpg").pn_async(callback)
```

##### Swift

```swift
let requestFile = PubNubLocalFileBase(
  fileURL: URL(fileURLWithPath: "cat_picture.jpg"),
   channel: "my_channel",
   fileId: "fileId",
   remoteFilename: "cat_picture.jpg"
)

pubnub.download(
  file: requestFile, toFileURL: requestFile.fileURL
) { (fileTask: HTTPFileDownloadTask) in

  print("The task \(fileTask.taskIdentifier) has started downloading; no need to call `resume()`")
  print("If needed, the `URLSessionUploadTask` can be accessed with `fileTask.urlSessionTask`")
  print("You can use `fileTask.progress` to populate a  `UIProgressView`/`ProgressView` ")

} completion: { result in
  switch result {
  case let .success(task, newFile):
    print("The file task \(task.taskIdentifier) downloaded successfully to \(newFile.fileURL), which might be different than \(requestFile.fileURL)")
    print("This also might mean that \(newFile.filename) could be different from \(newFile.remoteFilename)")

  case let .failure(error):
    print("An error occurred while downloading the file: \(error.localizedDescription)")
  }
}
```

##### Dart

```dart
import 'dart:io';
import 'package:pubnub/pubnub.dart';

void main() async {
  var pubnub = PubNub(
    defaultKeyset: Keyset(
      subscribeKey: 'sub-c-abc-123',  // Replace with your subscribe key
      publishKey: 'pub-c-def-456'     // Replace with your publish key
    ),
  );

  try {
    var result = await pubnub.files.downloadFile(
      'my_channel',
      'd9515cb7-48a7-41a4-9284-f4bf331bc770',
      'cat_picture.jpg'
    );

    await File('downloaded_cat_picture.jpg').writeAsBytes(result.fileContent);
    print('File downloaded successfully.');
    
  } catch (e) {
    print('Failed to download file: $e');
  }
}
```

### List files

To list files (without file messages), request up to 100 files for a channel. If more files exist, the response includes `more` for pagination.

The following example returns the name, ID, size, and created timestamp for up to 100 files sent to the channel `my_channel`:

#### JavaScript

```javascript
const result = await pubnub.listFiles({ channel: 'my_channel' });
```

#### C#

```csharp
PNResult<PNListFilesResult> listFilesResponse = await pubnub.ListFiles()
    .Channel("my_channel")
    .ExecuteAsync();
PNListFilesResult listFilesResult = listFilesResponse.Result;
PNStatus listFilesStatus = listFilesResponse.Status;
if (listFilesResult != null)
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(listFilesResult));
}
else
{
    Console.WriteLine(pubnub.JsonPluggableLibrary.SerializeToJsonString(listFilesStatus));
}
```

#### Go

```go
resListFile, statusListFile, errListFile := pn.ListFiles().
  Channel("my_channel").Execute()
fmt.Println(resListFile, statusListFile, errListFile)
if resListFile != nil {
    for _, m := range resListFile.Data {
        fmt.Println(m.ID, m.Created, m.Name, m.Size)
    }
}
```

#### Objective-C

```objectivec
PNListFilesRequest *request = [PNListFilesRequest requestWithChannel:@"my_channel"];
request.limit = 100;
request.next = ...;

[self.client listFilesWithRequest:request
                       completion:^(PNListFilesResult *result, PNErrorStatus *status) {
    if (!status.isError) {
        /**
         * Uploaded files list successfully fetched.
         *   result.data.files - List of uploaded files (information).
         *   result.data.next - Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off.
         *   result.data.count - Total number of files uploaded to channel.
         */
    } else {
        /**
         * Handle fetch files list error. Check 'category' property to find out
         * possible issue because of which request failed.
         *
         * Request can be resent using: [status retry]
         */
    }
}];
```

#### Java

```java
pubnub.listFiles()
        .channel("my_channel")
        .async(result -> {
            result.onSuccess(res -> {
                    System.out.println("files status: " + res.getStatus());
                    System.out.println("files status: " + res.getNext());
                    System.out.println("files status: " + res.getCount());
                    System.out.println("files status: " + res.getCount());
                    for (PNUploadedFile file : res.getData()) {
                        System.out.println("files fileId: " + file.getId());
                        System.out.println("files fileName: " + file.getName());
                        System.out.println("files fileSize: " + file.getSize());
                        System.out.println("files fileCreated: " + file.getCreated());
                    }
            }).onFailure(exception -> {
                exception.printStackTrace();
            });
        });
```

#### Python

```python
# synchronous
pubnub.list_files().channel("my_channel").sync()

# multithreaded asynchronous
def callback(response, status):
    pass

pubnub.list_files().channel("my_channel").pn_async(callback)
```

#### Swift

```swift
pubnub.listFiles(
  channel: "my_channel"
) { result in
  case let .success(response):
    print("There are \(response.files.count) file(s) found")

    if let nextPage = response.next {
      print("The next page used for pagination: \(nextPage)")
    }
  case let .failure(error):
    print("An error occurred while fetching the file list: \(error.localizedDescription)")
}
```

#### Dart

```dart
import 'package:pubnub/pubnub.dart';

void main() async {
  var pubnub = PubNub(
    defaultKeyset: Keyset(
      subscribeKey: 'sub-c-abc-123',  // Replace with your subscribe key
      publishKey: 'pub-c-def-456'     // Replace with your publish key
    ),
  );

  try {
    var files = await pubnub.files.listFiles('my_channel');

    print('Number of files uploaded: ${files.count}');
    for (var file in files.filesDetail) {
      print('File name: ${file.name}, ID: ${file.id}, Size: ${file.size}, Created: ${file.created}');
    }
  } catch (e) {
    print('Failed to list files: $e');
  }
}
```

## Terms in this document

* **Channel** - A pathway for sending and receiving messages between devices, created automatically when you first use it, that can handle any number of users and messages for different communication needs, like 1-1 text chats, group conversations, and other data streaming.
* **Channel pattern** - A way to group and analyze channel data to track performance metrics like message counts and user engagement over time with PubNub Insights.
