---
title: "Depot software"
description: "Every software operation in the Depot API, with parameters, responses and code samples in six languages."
url: "https://support.outpostplatform.com/api/generated/depot/software/"
product: "depot"
type: "reference"
status: "reviewed"
last_reviewed: 2026-11-06
source: "https://gitlab.com/outpostplatform/docs/-/edit/main/src/content/docs/api/generated/depot/software.mdx"
license: "CC BY 4.0"
---

# Depot software

Registers the catalog CRUD and the installer upload.

This page is generated from the Depot route table. Every operation carries the permission it needs and the anchor an agent can link to.

### GET `/api/v1/software/packages` {#depot.software.listPackages}

**List software packages**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `category` | query | string | No |  |
| `dir` | query | string, one of asc, desc | No | Sort direction. |
| `page` | query | integer | No | One-based page number. Defaults to 1. |
| `pageSize` | query | integer | No | Rows per page, 1 to 200. Defaults to 50. |
| `q` | query | string | No | Free-text search term. |
| `query` | query | string | No | Older spelling of q, kept so saved links keep working. |
| `sort` | query | string, one of category, created, name, publisher | No | Sort key. |
| `source` | query | string | No |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagedResultSoftwarePackageSummaryView | Success. |
| `400` | `application/problem+json`, ProblemDetails | One or more validation errors occurred. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold software:read. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/software/packages?category=<category>&dir=asc&page=0&pageSize=0&q=<q>&query=<query>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -b 'outpost_console=<console-session>'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/v1/software/packages?category=<category>&dir=asc&page=0&pageSize=0&q=<q>&query=<query>' `
    -Headers $headers

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

import requests

url = "https://depot.example.com/api/v1/software/packages"
params = {
    "category": "<category>",
    "dir": "asc",
    "page": "0",
    "pageSize": "0",
    "q": "<q>",
    "query": "<query>",
}
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.get(url, params=params, headers=headers, cookies=cookies, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "https://depot.example.com/api/v1/software/packages?category=<category>&dir=asc&page=0&pageSize=0&q=<q>&query=<query>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
// The session cookie is set by signing in. A script cannot set it itself.

const url = new URL("https://depot.example.com/api/v1/software/packages");
url.searchParams.set("category", "<category>");
url.searchParams.set("dir", "asc");
url.searchParams.set("page", "0");
url.searchParams.set("pageSize", "0");
url.searchParams.set("q", "<q>");
url.searchParams.set("query", "<query>");

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
	},
	credentials: "include",
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Get, "https://depot.example.com/api/v1/software/packages?category=<category>&dir=asc&page=0&pageSize=0&q=<q>&query=<query>");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### POST `/api/v1/software/packages` {#depot.software.createPackage}

**Create software package**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, SoftwarePackageDetailView | Created. |
| `400` | `application/problem+json`, ProblemDetails | The request body is not valid JSON. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold software:manage. |
| `404` | `application/problem+json`, ProblemDetails | Package not found. |
| `409` | `application/problem+json`, ProblemDetails | That application is already in the catalog. |
| `500` | `application/problem+json`, ProblemDetails | Could not create the package. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/v1/software/packages' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$body = @'
{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/software/packages' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.

import requests

url = "https://depot.example.com/api/v1/software/packages"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "approvalStatus": "string",
    "autoUpdate": False,
    "category": "string",
    "defaultInstallArgs": "string",
    "defaultUninstallArgs": "string",
    "description": "string",
    "detection": [
        {
            "expected": "string",
            "kind": "string",
            "path": "string",
            "valueName": "string",
        },
    ],
    "iconText": "string",
    "licenseType": "string",
    "name": "string",
    "pinnedVersion": "string",
    "publisher": "string",
    "requiresApproval": False,
    "selfServiceVisible": False,
    "sourceRef": "string",
    "sourceSha256": "string",
    "sourceType": "string",
    "successExitCodes": [
        0,
    ],
    "tags": [
        "string",
    ],
    "timeoutSeconds": 0,
}

response = requests.post(url, headers=headers, cookies=cookies, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	body := []byte(`{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/software/packages", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/software/packages";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"approvalStatus": "string",
		"autoUpdate": false,
		"category": "string",
		"defaultInstallArgs": "string",
		"defaultUninstallArgs": "string",
		"description": "string",
		"detection": [
			{
				"expected": "string",
				"kind": "string",
				"path": "string",
				"valueName": "string"
			}
		],
		"iconText": "string",
		"licenseType": "string",
		"name": "string",
		"pinnedVersion": "string",
		"publisher": "string",
		"requiresApproval": false,
		"selfServiceVisible": false,
		"sourceRef": "string",
		"sourceSha256": "string",
		"sourceType": "string",
		"successExitCodes": [
			0
		],
		"tags": [
			"string"
		],
		"timeoutSeconds": 0
	}),
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.

using System;
using System.Net.Http;
using System.Text;

using var client = new HttpClient();

const string body = """
{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/software/packages");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### GET `/api/v1/software/packages/{packageId}` {#depot.software.getPackage}

**Get software package**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `packageId` | path | string | Yes | A uuid. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SoftwarePackageDetailView | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold software:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | Could not read the package. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/software/packages/<packageId>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -b 'outpost_console=<console-session>'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/v1/software/packages/<packageId>' `
    -Headers $headers

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

import requests

url = "https://depot.example.com/api/v1/software/packages/<packageId>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.get(url, headers=headers, cookies=cookies, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "https://depot.example.com/api/v1/software/packages/<packageId>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/software/packages/<packageId>";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
	},
	credentials: "include",
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Get, "https://depot.example.com/api/v1/software/packages/<packageId>");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### PUT `/api/v1/software/packages/{packageId}` {#depot.software.updatePackage}

**Update software package**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `packageId` | path | string | Yes | A uuid. |

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SoftwarePackageDetailView | Success. |
| `400` | `application/problem+json`, ProblemDetails | The request body is not valid JSON. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold the required permission. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `409` | `application/problem+json`, ProblemDetails | What a package IS cannot be changed after it is created, because every deployment that already named it would silently start installing something else. Retire this entry and add a new one. |
| `500` | `application/problem+json`, ProblemDetails | Could not read the package. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X PUT 'https://depot.example.com/api/v1/software/packages/<packageId>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$body = @'
{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}
'@

$response = Invoke-RestMethod `
    -Method Put `
    -Uri 'https://depot.example.com/api/v1/software/packages/<packageId>' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

import requests

url = "https://depot.example.com/api/v1/software/packages/<packageId>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "approvalStatus": "string",
    "autoUpdate": False,
    "category": "string",
    "defaultInstallArgs": "string",
    "defaultUninstallArgs": "string",
    "description": "string",
    "detection": [
        {
            "expected": "string",
            "kind": "string",
            "path": "string",
            "valueName": "string",
        },
    ],
    "iconText": "string",
    "licenseType": "string",
    "name": "string",
    "pinnedVersion": "string",
    "publisher": "string",
    "requiresApproval": False,
    "selfServiceVisible": False,
    "sourceRef": "string",
    "sourceSha256": "string",
    "sourceType": "string",
    "successExitCodes": [
        0,
    ],
    "tags": [
        "string",
    ],
    "timeoutSeconds": 0,
}

response = requests.put(url, headers=headers, cookies=cookies, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	body := []byte(`{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}`)

	req, err := http.NewRequest(http.MethodPut, "https://depot.example.com/api/v1/software/packages/<packageId>", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/software/packages/<packageId>";

const response = await fetch(url, {
	method: "PUT",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"approvalStatus": "string",
		"autoUpdate": false,
		"category": "string",
		"defaultInstallArgs": "string",
		"defaultUninstallArgs": "string",
		"description": "string",
		"detection": [
			{
				"expected": "string",
				"kind": "string",
				"path": "string",
				"valueName": "string"
			}
		],
		"iconText": "string",
		"licenseType": "string",
		"name": "string",
		"pinnedVersion": "string",
		"publisher": "string",
		"requiresApproval": false,
		"selfServiceVisible": false,
		"sourceRef": "string",
		"sourceSha256": "string",
		"sourceType": "string",
		"successExitCodes": [
			0
		],
		"tags": [
			"string"
		],
		"timeoutSeconds": 0
	}),
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.

using System;
using System.Net.Http;
using System.Text;

using var client = new HttpClient();

const string body = """
{
  "approvalStatus": "string",
  "autoUpdate": false,
  "category": "string",
  "defaultInstallArgs": "string",
  "defaultUninstallArgs": "string",
  "description": "string",
  "detection": [
    {
      "expected": "string",
      "kind": "string",
      "path": "string",
      "valueName": "string"
    }
  ],
  "iconText": "string",
  "licenseType": "string",
  "name": "string",
  "pinnedVersion": "string",
  "publisher": "string",
  "requiresApproval": false,
  "selfServiceVisible": false,
  "sourceRef": "string",
  "sourceSha256": "string",
  "sourceType": "string",
  "successExitCodes": [
    0
  ],
  "tags": [
    "string"
  ],
  "timeoutSeconds": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Put, "https://depot.example.com/api/v1/software/packages/<packageId>");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");
request.Content = new StringContent(body, Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### DELETE `/api/v1/software/packages/{packageId}` {#depot.software.deletePackage}

**Delete software package**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `packageId` | path | string | Yes | A uuid. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold the required permission. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `409` | `application/problem+json`, ProblemDetails | The request conflicts with the current state. |
| `500` | `application/problem+json`, ProblemDetails | Could not read the package. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X DELETE 'https://depot.example.com/api/v1/software/packages/<packageId>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -b 'outpost_console=<console-session>'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$response = Invoke-RestMethod `
    -Method Delete `
    -Uri 'https://depot.example.com/api/v1/software/packages/<packageId>' `
    -Headers $headers

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

import requests

url = "https://depot.example.com/api/v1/software/packages/<packageId>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.delete(url, headers=headers, cookies=cookies, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodDelete, "https://depot.example.com/api/v1/software/packages/<packageId>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/software/packages/<packageId>";

const response = await fetch(url, {
	method: "DELETE",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
	},
	credentials: "include",
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Delete, "https://depot.example.com/api/v1/software/packages/<packageId>");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### GET `/api/v1/software/packages/{packageId}/versions` {#depot.software.listVersions}

**List software versions**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `packageId` | path | string | Yes | A uuid. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, object | Success. |
| `400` | `application/problem+json`, ProblemDetails | One or more validation errors occurred. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold software:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | Could not read the package. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X GET 'https://depot.example.com/api/v1/software/packages/<packageId>/versions' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -b 'outpost_console=<console-session>'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/v1/software/packages/<packageId>/versions' `
    -Headers $headers

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

import requests

url = "https://depot.example.com/api/v1/software/packages/<packageId>/versions"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.get(url, headers=headers, cookies=cookies, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "https://depot.example.com/api/v1/software/packages/<packageId>/versions", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/software/packages/<packageId>/versions";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
	},
	credentials: "include",
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Get, "https://depot.example.com/api/v1/software/packages/<packageId>/versions");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### POST `/api/v1/software/packages/{packageId}/versions` {#depot.software.uploadVersion}

**uploadSoftwareVersion takes one installer as a multipart body: a JSON "metadata" part, then the file as "installer".**

MULTIPART AND STREAMED, unlike the ticket attachment route next door, and
both halves are deliberate. Multipart because there are two things to send and
the metadata has to arrive FIRST so a bad version string is refused before two
gigabytes cross the wire; streamed through r.MultipartReader rather than
ParseMultipartForm because that one buffers and spools the whole form before a
handler sees any of it, which for this size is the difference between a temp
file and an out-of-memory.

The bytes are spooled to a temp file all the same, because the destination
needs an exact Content-Length before the first byte goes out and a multipart
part does not declare one. The digest is computed on the way past, so the
spool is read once and hashed once.

A version row may also be written with NO file, which is what pins a winget or
choco package to one version. The store enforces the other half: a
custom_upload package's version cannot exist without kind, digest and key.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `packageId` | path | string | Yes | A uuid. |
| `storageProfileId` | query | string | No |  |

#### Request body

Required, sent as `multipart/form-data`.

The body is `object`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, SoftwareVersionView | Created. |
| `400` | `application/problem+json`, ProblemDetails | One or more validation errors occurred. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold the required permission. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `409` | `application/problem+json`, ProblemDetails | That version is already recorded for this package. |
| `413` | `application/problem+json`, ProblemDetails | The request was refused. |
| `500` | `application/problem+json`, ProblemDetails | Could not read the package. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |
| `507` | `application/problem+json`, ProblemDetails | The storage this catalog writes to is full. Raise the quota or remove old installers. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/v1/software/packages/<packageId>/versions?storageProfileId=<storageProfileId>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: multipart/form-data' \
  -b 'outpost_console=<console-session>' \
  --data '{}'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Content-Type' = 'multipart/form-data'
    'Cookie' = 'outpost_console=<console-session>'
}

$body = @'
{}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/software/packages/<packageId>/versions?storageProfileId=<storageProfileId>' `
    -Headers $headers `
    -ContentType 'multipart/form-data' `
    -Body $body

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.

import requests

url = "https://depot.example.com/api/v1/software/packages/<packageId>/versions"
params = {
    "storageProfileId": "<storageProfileId>",
}
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "multipart/form-data",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {}

response = requests.post(url, params=params, headers=headers, cookies=cookies, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	body := []byte(`{}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/software/packages/<packageId>/versions?storageProfileId=<storageProfileId>", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "multipart/form-data")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// The session cookie is set by signing in. A script cannot set it itself.

const url = new URL("https://depot.example.com/api/v1/software/packages/<packageId>/versions");
url.searchParams.set("storageProfileId", "<storageProfileId>");

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "multipart/form-data",
	},
	credentials: "include",
	body: JSON.stringify({}),
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.

using System;
using System.Net.Http;
using System.Text;

using var client = new HttpClient();

const string body = """
{}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/software/packages/<packageId>/versions?storageProfileId=<storageProfileId>");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");
request.Content = new StringContent(body, Encoding.UTF8, "multipart/form-data");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

### DELETE `/api/v1/software/versions/{versionId}` {#depot.software.deleteVersion}

**deleteSoftwareVersion removes a version row and the bytes it points at.**

THE OBJECT GOES FIRST, which is the opposite order to a ticket attachment, and
the difference is that there is no sweeper on this side. An attachment whose
object outlives its row is reconciled by the storage accountant; an installer
in that state is billed forever with nothing in any database pointing at it,
because SumInstallerBytesByProfile counts ROWS. So a failed object delete is
fatal here: the row stays, the operator can see the version and try again, and
nothing is orphaned.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `versionId` | path | string | Yes | A uuid. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | That installer belongs to the shared catalog, so it is managed for every client at once and cannot be removed here. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | Could not read the version. |
| `502` | `application/problem+json`, ProblemDetails | The stored installer could not be removed, so the version was kept. Try again. |
| `503` | `application/problem+json`, ProblemDetails | The software catalog is not available on this Depot. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.
curl -X DELETE 'https://depot.example.com/api/v1/software/versions/<versionId>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -b 'outpost_console=<console-session>'
```

```powershell
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

$headers = @{
    'X-CSRF-Token' = '<csrf-token>'
    'Accept' = 'application/json'
    'Cookie' = 'outpost_console=<console-session>'
}

$response = Invoke-RestMethod `
    -Method Delete `
    -Uri 'https://depot.example.com/api/v1/software/versions/<versionId>' `
    -Headers $headers

$response | ConvertTo-Json -Depth 10
```

```python
# Sign in first. outpost_console is a session cookie, so send it with every request.
# This operation is idempotent. Repeating it does not repeat its effect.

import requests

url = "https://depot.example.com/api/v1/software/versions/<versionId>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.delete(url, headers=headers, cookies=cookies, timeout=30)
response.raise_for_status()
print(response.json())
```

```go
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodDelete, "https://depot.example.com/api/v1/software/versions/<versionId>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-CSRF-Token", "<csrf-token>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "outpost_console", Value: "<console-session>"})

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	out, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(resp.Status, string(out))
}
```

```ts
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/software/versions/<versionId>";

const response = await fetch(url, {
	method: "DELETE",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
	},
	credentials: "include",
});

if (!response.ok) {
	throw new Error(`Request failed with status ${response.status}`);
}

const data = await response.json();
console.log(data);
```

```csharp
// Sign in first. outpost_console is a session cookie, so send it with every request.
// This operation is idempotent. Repeating it does not repeat its effect.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Delete, "https://depot.example.com/api/v1/software/versions/<versionId>");
request.Headers.TryAddWithoutValidation("X-CSRF-Token", "<csrf-token>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "outpost_console=<console-session>");

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
```
