# Management SDK - Assets

> Source: https://agilitycms.com/docs/dotNet/management-sdk-dotnet-assets

The assets client uploads and manages media files, folders and galleries. You reach it through `client.Assets` on an `AgilityManagementClient`.

> **Note:** This page covers version 2.0 of the .NET Management SDK. Upgrading from 1.x? See [Migrating to 2.0](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-migrating-to-2).

## Before you start

The examples assume a `client` and an instance GUID (`guid`). To set up the client, see the [Intro](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro).

```csharp
using Agility.Management.Sdk;
using Agility.Management.Sdk.Clients;
using Agility.Management.Sdk.Models;
```

Asset methods take the instance GUID first. They don't take a locale. Every method also takes an optional `CancellationToken cancellationToken` as its last parameter. The signatures below leave it out.

Assets are `AssetMedia` objects (`Media` in 1.x). Asset operations don't run as batches, so there's no batch to wait for.

## Method list

| Method | Description |
|---|---|
| `UploadAsync` | Upload one or more files into a folder |
| `GetMediaListAsync` | List assets, a page at a time |
| `GetAssetAsync` | Get an asset by media ID |
| `GetAssetByUrlAsync` | Get an asset by its URL |
| `GetDefaultContainerAsync` | Get the instance's default asset container |
| `CreateFolderAsync` | Create a folder |
| `RenameFolderAsync` | Rename a folder |
| `DeleteFolderAsync` | Delete a folder |
| `MoveAssetAsync` | Move an asset to another folder |
| `DeleteAssetAsync` | Delete an asset |
| `GetGalleriesAsync` | List galleries |
| `GetGalleryAsync` | Get a gallery by ID |
| `GetGalleryByNameAsync` | Get a gallery by name |
| `SaveGalleryAsync` | Create or update a gallery |
| `DeleteGalleryAsync` | Delete a gallery |

---

## Uploading files

### UploadAsync

Upload one or more files into a folder. Each file is an `AssetUpload`: the file name to store, a `Stream` with the file's contents, and an optional MIME type.

```csharp
await using var file = File.OpenRead("/path/to/hero-image.jpg");

List<AssetMedia> uploaded = await client.Assets.UploadAsync(guid, "images/heroes",
    [new AssetUpload("hero-image.jpg", file, "image/jpeg")]);

Console.WriteLine($"Uploaded: {uploaded[0].FileName} - {uploaded[0].EdgeUrl}");
```

To upload several files in one call, pass more `AssetUpload`s:

```csharp
await using var hero = File.OpenRead("/path/to/hero-image.jpg");
await using var logo = File.OpenRead("/path/to/logo.png");

List<AssetMedia> uploaded = await client.Assets.UploadAsync(guid, "images/heroes",
[
    new AssetUpload("hero-image.jpg", hero, "image/jpeg"),
    new AssetUpload("logo.png", logo, "image/png"),
]);

foreach (var media in uploaded)
{
    Console.WriteLine($"Uploaded: {media.FileName} - {media.EdgeUrl}");
}
```

- `folderPath` is the folder in Agility, such as `images/heroes`. Use `""` or `"/"` for the root.
- `galleryId` also adds the files to a gallery.
- `focalX` and `focalY` set an image's focal point.
- If you leave out the MIME type, the SDK sends `application/octet-stream`.
- The SDK reads your streams but doesn't dispose them. Dispose them yourself, as the `await using` lines do.

> **Note:** In 1.x, `Upload` took a `Dictionary<string, string>` of file names and local directory paths. In 2.0 you pass streams, so the files can come from disk, memory or a download.

**Signature:** `Task<List<AssetMedia>> UploadAsync(string instanceGuid, string folderPath, IReadOnlyCollection<AssetUpload> files, int? galleryId = null, string? focalX = null, string? focalY = null)`

**Returns:** The uploaded assets.

---

## Finding assets

### GetMediaListAsync

List assets, a page at a time.

```csharp
AssetMediaList page = await client.Assets.GetMediaListAsync(guid, pageSize: 50, recordOffset: 0);

Console.WriteLine($"Total assets: {page.TotalCount}");
foreach (var media in page.AssetMedias ?? [])
{
    Console.WriteLine($"{media.FileName} - {media.EdgeUrl}");
}
```

`pageSize` defaults to 20. Pass `updatedSince` to get only assets changed after that time:

```csharp
AssetMediaList changed = await client.Assets.GetMediaListAsync(guid, updatedSince: DateTime.UtcNow.AddDays(-1));
```

**Signature:** `Task<AssetMediaList> GetMediaListAsync(string instanceGuid, int pageSize = 20, int recordOffset = 0, DateTime? updatedSince = null)`

---

### GetAssetAsync

Get an asset by its media ID.

```csharp
AssetMedia asset = await client.Assets.GetAssetAsync(guid, mediaId);
Console.WriteLine($"Asset: {asset.FileName} - {asset.EdgeUrl}");
```

**Signature:** `Task<AssetMedia> GetAssetAsync(string instanceGuid, int mediaId)`

---

### GetAssetByUrlAsync

Get an asset by its URL.

```csharp
AssetMedia asset = await client.Assets.GetAssetByUrlAsync(guid,
    "https://cdn.aglty.io/your-guid/images/heroes/hero-image.jpg");

Console.WriteLine($"Found: {asset.FileName} (ID: {asset.MediaID})");
```

**Signature:** `Task<AssetMedia> GetAssetByUrlAsync(string instanceGuid, string url)`

---

### GetDefaultContainerAsync

Get the instance's default asset container: the CDN container and its URLs.

```csharp
AssetContainer container = await client.Assets.GetDefaultContainerAsync(guid);
Console.WriteLine($"Container {container.ContainerName}: {container.EdgeUrl}");
```

**Signature:** `Task<AssetContainer> GetDefaultContainerAsync(string instanceGuid)`

---

## Folders

### CreateFolderAsync

Create a folder in the media library.

```csharp
AssetMedia? folder = await client.Assets.CreateFolderAsync(guid, "images/blog/2026");
Console.WriteLine($"Created folder: {folder?.OriginKey}");
```

**Signature:** `Task<AssetMedia?> CreateFolderAsync(string instanceGuid, string originKey)`

---

### RenameFolderAsync

Rename a folder. Pass the folder's current path and its new path.

```csharp
await client.Assets.RenameFolderAsync(guid, "images/blog/2026", "images/blog/archive-2026");
```

You can also pass the folder's media ID, if you know it, as `mediaId`.

**Signature:** `Task RenameFolderAsync(string instanceGuid, string folderName, string newFolderName, int? mediaId = null)`

---

### DeleteFolderAsync

Delete a folder.

```csharp
await client.Assets.DeleteFolderAsync(guid, "images/blog/archive-2026");
```

You can also pass the folder's media ID, if you know it, as `mediaId`.

**Signature:** `Task DeleteFolderAsync(string instanceGuid, string originKey, int? mediaId = null)`

---

### MoveAssetAsync

Move an asset to another folder.

```csharp
AssetMedia? moved = await client.Assets.MoveAssetAsync(guid, mediaId, "images/archive");
Console.WriteLine($"Moved to: {moved?.OriginKey}");
```

**Signature:** `Task<AssetMedia?> MoveAssetAsync(string instanceGuid, int mediaId, string newFolder)`

---

## Deleting assets

### DeleteAssetAsync

Delete an asset. The method returns nothing. A failure throws an exception.

```csharp
await client.Assets.DeleteAssetAsync(guid, mediaId);
```

**Signature:** `Task DeleteAssetAsync(string instanceGuid, int mediaId)`

---

## Galleries

Galleries are `AssetMediaGrouping` objects. A gallery's ID is its `MediaGroupingID`, and its name is `Name`.

### GetGalleriesAsync

List galleries. All the filters are optional.

```csharp
AssetGalleries galleries = await client.Assets.GetGalleriesAsync(guid, search: "team", pageSize: 50, rowIndex: 0);

foreach (var gallery in galleries.AssetMediaGroupings ?? [])
{
    Console.WriteLine($"{gallery.Name} (ID: {gallery.MediaGroupingID})");
}
```

- `search` returns only galleries whose name contains that text.
- `pageSize` is the number of galleries per page.
- `rowIndex` is how many galleries to skip.

**Signature:** `Task<AssetGalleries> GetGalleriesAsync(string instanceGuid, string? search = null, int? pageSize = null, int? rowIndex = null)`

---

### GetGalleryAsync

Get a gallery by ID.

```csharp
AssetMediaGrouping gallery = await client.Assets.GetGalleryAsync(guid, galleryId);
Console.WriteLine($"Gallery: {gallery.Name}");
```

**Signature:** `Task<AssetMediaGrouping> GetGalleryAsync(string instanceGuid, int galleryId)`

---

### GetGalleryByNameAsync

Get a gallery by name. The result can be `null`, so check it before you use it.

```csharp
AssetMediaGrouping? gallery = await client.Assets.GetGalleryByNameAsync(guid, "Team photos");

if (gallery is not null)
{
    Console.WriteLine($"Gallery ID: {gallery.MediaGroupingID}");
}
```

**Signature:** `Task<AssetMediaGrouping?> GetGalleryByNameAsync(string instanceGuid, string galleryName)`

---

### SaveGalleryAsync

Create or update a gallery. Use a `MediaGroupingID` of `-1` to create one.

```csharp
AssetMediaGrouping saved = await client.Assets.SaveGalleryAsync(guid, new AssetMediaGrouping
{
    MediaGroupingID = -1,
    Name = "Team photos",
    GroupingTypeID = 1,
});

Console.WriteLine($"Saved gallery ID: {saved.MediaGroupingID}");
```

Any `MediaGroupingID` of 0 or less creates a new gallery.

**Signature:** `Task<AssetMediaGrouping> SaveGalleryAsync(string instanceGuid, AssetMediaGrouping gallery)`

**Returns:** The saved gallery.

---

### DeleteGalleryAsync

Delete a gallery. The method returns nothing. A failure throws an exception.

```csharp
await client.Assets.DeleteGalleryAsync(guid, galleryId);
```

**Signature:** `Task DeleteGalleryAsync(string instanceGuid, int galleryId)`

---

## Error handling

```csharp
try
{
    var asset = await client.Assets.GetAssetAsync(guid, mediaId);
}
catch (AgilityManagementException ex)
{
    Console.Error.WriteLine($"Error {(int?)ex.StatusCode}: {ex.ApiMessage ?? ex.Message}");
}
```

`AgilityManagementException` covers any error from the API or the network. See [Errors](https://agilitycms.com/docs/dotNet/management-sdk-dotnet-intro#errors). The SDK retries reads that fail with a temporary error, but it never retries an upload, move or delete.
