---
title: "Depot sales"
description: "Every sales operation in the Depot API, with parameters, responses and code samples in six languages."
url: "https://support.outpostplatform.com/api/generated/depot/sales/"
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/sales.mdx"
license: "CC BY 4.0"
---

# Depot sales

Registers the whole authenticated sales surface.

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/sales/activities` {#depot.sales.listActivities}

**List activities**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `assignee` | query | string | No |  |
| `client` | query | string | No |  |
| `kind` | query | string | No |  |
| `limit` | query | string | No |  |
| `opportunity` | query | string | No |  |
| `prospect` | query | string | No |  |
| `quote` | query | string | No |  |
| `sort` | query | string, one of due, occurred | No | Sort key. |

#### 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/activities?assignee=<assignee>&client=<client>&kind=<kind>&limit=<limit>&opportunity=<opportunity>&prospect=<prospect>' \
  -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/sales/activities?assignee=<assignee>&client=<client>&kind=<kind>&limit=<limit>&opportunity=<opportunity>&prospect=<prospect>' `
    -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/sales/activities"
params = {
    "assignee": "<assignee>",
    "client": "<client>",
    "kind": "<kind>",
    "limit": "<limit>",
    "opportunity": "<opportunity>",
    "prospect": "<prospect>",
}
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/sales/activities?assignee=<assignee>&client=<client>&kind=<kind>&limit=<limit>&opportunity=<opportunity>&prospect=<prospect>", 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/sales/activities");
url.searchParams.set("assignee", "<assignee>");
url.searchParams.set("client", "<client>");
url.searchParams.set("kind", "<kind>");
url.searchParams.set("limit", "<limit>");
url.searchParams.set("opportunity", "<opportunity>");
url.searchParams.set("prospect", "<prospect>");

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/sales/activities?assignee=<assignee>&client=<client>&kind=<kind>&limit=<limit>&opportunity=<opportunity>&prospect=<prospect>");
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/sales/activities` {#depot.sales.createActivity}

**Create activity**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, SalesActivityView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/activities' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}'
```

```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 = @'
{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/activities' `
    -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/sales/activities"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "assigneeUserPublicId": "string",
    "body": "string",
    "clientPublicId": "string",
    "dueOn": "string",
    "kind": "string",
    "occurredAt": "string",
    "opportunityPublicId": "string",
    "prospectPublicId": "string",
    "quotePublicId": "string",
}

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(`{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/activities", 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/sales/activities";

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({
		"assigneeUserPublicId": "string",
		"body": "string",
		"clientPublicId": "string",
		"dueOn": "string",
		"kind": "string",
		"occurredAt": "string",
		"opportunityPublicId": "string",
		"prospectPublicId": "string",
		"quotePublicId": "string"
	}),
});

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 = """
{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/activities");
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());
```

### PATCH `/api/v1/sales/activities/{id}` {#depot.sales.patchActivity}

**patchActivity edits what was written, when it happened, when it is due and who it is for.**

It does NOT move the activity to another subject: a note written about one deal is a note about that deal.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SalesActivityView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/sales/activities/<id>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}'
```

```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 = @'
{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Patch `
    -Uri 'https://depot.example.com/api/v1/sales/activities/<id>' `
    -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/sales/activities/<id>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "assigneeUserPublicId": "string",
    "body": "string",
    "clientPublicId": "string",
    "dueOn": "string",
    "kind": "string",
    "occurredAt": "string",
    "opportunityPublicId": "string",
    "prospectPublicId": "string",
    "quotePublicId": "string",
}

response = requests.patch(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(`{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}`)

	req, err := http.NewRequest(http.MethodPatch, "https://depot.example.com/api/v1/sales/activities/<id>", 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/sales/activities/<id>";

const response = await fetch(url, {
	method: "PATCH",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"assigneeUserPublicId": "string",
		"body": "string",
		"clientPublicId": "string",
		"dueOn": "string",
		"kind": "string",
		"occurredAt": "string",
		"opportunityPublicId": "string",
		"prospectPublicId": "string",
		"quotePublicId": "string"
	}),
});

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 = """
{
  "assigneeUserPublicId": "string",
  "body": "string",
  "clientPublicId": "string",
  "dueOn": "string",
  "kind": "string",
  "occurredAt": "string",
  "opportunityPublicId": "string",
  "prospectPublicId": "string",
  "quotePublicId": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Patch, "https://depot.example.com/api/v1/sales/activities/<id>");
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/sales/activities/{id}` {#depot.sales.deleteActivity}

**Delete activity**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/activities/<id>' \
  -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/sales/activities/<id>' `
    -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/sales/activities/<id>"
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/sales/activities/<id>", 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/sales/activities/<id>";

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/sales/activities/<id>");
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/sales/activities/{id}/complete` {#depot.sales.completeActivity}

**completeActivity ticks a follow-up off.**

Only a TASK can be completed, because nothing else has anything to be done about it, and the store says so rather than this handler guessing from a kind it would have to read first.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SalesActivityView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/activities/<id>/complete' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/activities/<id>/complete' `
    -Headers $headers

$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/sales/activities/<id>/complete"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/activities/<id>/complete", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/activities/<id>/complete";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/activities/<id>/complete");
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/sales/catalog-items` {#depot.sales.listCatalogItems}

**List catalog items**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `kind` | query | string | No |  |
| `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. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagedResultCatalogItemView | Success. |
| `400` | `application/problem+json`, ProblemDetails | One or more validation errors occurred. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/catalog-items?kind=<kind>&page=0&pageSize=0&q=<q>' \
  -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/sales/catalog-items?kind=<kind>&page=0&pageSize=0&q=<q>' `
    -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/sales/catalog-items"
params = {
    "kind": "<kind>",
    "page": "0",
    "pageSize": "0",
    "q": "<q>",
}
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/sales/catalog-items?kind=<kind>&page=0&pageSize=0&q=<q>", 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/sales/catalog-items");
url.searchParams.set("kind", "<kind>");
url.searchParams.set("page", "0");
url.searchParams.set("pageSize", "0");
url.searchParams.set("q", "<q>");

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/sales/catalog-items?kind=<kind>&page=0&pageSize=0&q=<q>");
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/sales/catalog-items` {#depot.sales.createCatalogItem}

**Create catalog item**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, CatalogItemView | 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 psa:admin. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/catalog-items' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 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 = @'
{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 0
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/catalog-items' `
    -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/sales/catalog-items"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "active": False,
    "agreementLineKind": "string",
    "billOnMilestones": False,
    "category": "string",
    "description": "string",
    "fulfillable": False,
    "includedMinutes": 0,
    "kind": "string",
    "name": "string",
    "projectTemplateFamilyPublicId": "string",
    "sku": "string",
    "sortOrder": 0,
    "stockItemId": "string",
    "taxable": False,
    "unit": "string",
    "unitCostCents": 0,
    "unitPriceCents": 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(`{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 0
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/catalog-items", 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/sales/catalog-items";

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({
		"active": false,
		"agreementLineKind": "string",
		"billOnMilestones": false,
		"category": "string",
		"description": "string",
		"fulfillable": false,
		"includedMinutes": 0,
		"kind": "string",
		"name": "string",
		"projectTemplateFamilyPublicId": "string",
		"sku": "string",
		"sortOrder": 0,
		"stockItemId": "string",
		"taxable": false,
		"unit": "string",
		"unitCostCents": 0,
		"unitPriceCents": 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 = """
{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/catalog-items");
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/sales/catalog-items/{id}` {#depot.sales.getCatalogItem}

**Get catalog item**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, CatalogItemView | Success. |
| `400` | `application/problem+json`, ProblemDetails | One or more validation errors occurred. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/catalog-items/<id>' \
  -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/sales/catalog-items/<id>' `
    -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/sales/catalog-items/<id>"
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/sales/catalog-items/<id>", 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/sales/catalog-items/<id>";

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/sales/catalog-items/<id>");
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());
```

### PATCH `/api/v1/sales/catalog-items/{id}` {#depot.sales.patchCatalogItem}

**patchCatalogItem edits one item.**

The SKU is absent from the update deliberately: changing it would orphan every quote line that names the item under its old code.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, CatalogItemView | 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 psa:admin. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | Could not update the catalog item. |
| `501` | `application/problem+json`, ProblemDetails | This server cannot mark catalog items fulfillable. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/sales/catalog-items/<id>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 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 = @'
{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 0
}
'@

$response = Invoke-RestMethod `
    -Method Patch `
    -Uri 'https://depot.example.com/api/v1/sales/catalog-items/<id>' `
    -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/sales/catalog-items/<id>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "active": False,
    "agreementLineKind": "string",
    "billOnMilestones": False,
    "category": "string",
    "description": "string",
    "fulfillable": False,
    "includedMinutes": 0,
    "kind": "string",
    "name": "string",
    "projectTemplateFamilyPublicId": "string",
    "sku": "string",
    "sortOrder": 0,
    "stockItemId": "string",
    "taxable": False,
    "unit": "string",
    "unitCostCents": 0,
    "unitPriceCents": 0,
}

response = requests.patch(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(`{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 0
}`)

	req, err := http.NewRequest(http.MethodPatch, "https://depot.example.com/api/v1/sales/catalog-items/<id>", 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/sales/catalog-items/<id>";

const response = await fetch(url, {
	method: "PATCH",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"active": false,
		"agreementLineKind": "string",
		"billOnMilestones": false,
		"category": "string",
		"description": "string",
		"fulfillable": false,
		"includedMinutes": 0,
		"kind": "string",
		"name": "string",
		"projectTemplateFamilyPublicId": "string",
		"sku": "string",
		"sortOrder": 0,
		"stockItemId": "string",
		"taxable": false,
		"unit": "string",
		"unitCostCents": 0,
		"unitPriceCents": 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 = """
{
  "active": false,
  "agreementLineKind": "string",
  "billOnMilestones": false,
  "category": "string",
  "description": "string",
  "fulfillable": false,
  "includedMinutes": 0,
  "kind": "string",
  "name": "string",
  "projectTemplateFamilyPublicId": "string",
  "sku": "string",
  "sortOrder": 0,
  "stockItemId": "string",
  "taxable": false,
  "unit": "string",
  "unitCostCents": 0,
  "unitPriceCents": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Patch, "https://depot.example.com/api/v1/sales/catalog-items/<id>");
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/sales/catalog-items/{id}` {#depot.sales.deleteCatalogItem}

**deleteCatalogItem RETIRES an item.**

There is no delete, deliberately: a quote line names its item for provenance and a year-old quote has to keep resolving, so the row stays and Active goes false. The verb is DELETE because that is what the console's button means.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 psa:admin. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/catalog-items/<id>' \
  -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/sales/catalog-items/<id>' `
    -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/sales/catalog-items/<id>"
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/sales/catalog-items/<id>", 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/sales/catalog-items/<id>";

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/sales/catalog-items/<id>");
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/sales/dashboard` {#depot.sales.getDashboard}

**getDashboard answers the tiles for THE CALLER, which is what makes myOverdueTasks mean anything: "my follow-ups" resolved for whoever is asking, not a practice-wide count of everybody's.**

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SalesDashboardView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/dashboard' \
  -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/sales/dashboard' `
    -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/sales/dashboard"
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/sales/dashboard", 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/sales/dashboard";

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/sales/dashboard");
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/sales/opportunities` {#depot.sales.listOpportunities}

**List opportunities**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `client` | query | string | No |  |
| `kind` | query | string | No |  |
| `outcome` | query | string | No |  |
| `owner` | query | string | No |  |
| `page` | query | integer | No | One-based page number. Defaults to 1. |
| `pageSize` | query | integer | No | Rows per page, 1 to 200. Defaults to 50. |
| `prospect` | query | string | No |  |
| `q` | query | string | No | Free-text search term. |
| `sort` | query | string, one of expected, name, stage, updated, value | No | Sort key. |
| `stage` | query | string | No |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagedResultOpportunityView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/opportunities?client=<client>&kind=<kind>&outcome=<outcome>&owner=<owner>&page=0&pageSize=0' \
  -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/sales/opportunities?client=<client>&kind=<kind>&outcome=<outcome>&owner=<owner>&page=0&pageSize=0' `
    -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/sales/opportunities"
params = {
    "client": "<client>",
    "kind": "<kind>",
    "outcome": "<outcome>",
    "owner": "<owner>",
    "page": "0",
    "pageSize": "0",
}
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/sales/opportunities?client=<client>&kind=<kind>&outcome=<outcome>&owner=<owner>&page=0&pageSize=0", 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/sales/opportunities");
url.searchParams.set("client", "<client>");
url.searchParams.set("kind", "<kind>");
url.searchParams.set("outcome", "<outcome>");
url.searchParams.set("owner", "<owner>");
url.searchParams.set("page", "0");
url.searchParams.set("pageSize", "0");

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/sales/opportunities?client=<client>&kind=<kind>&outcome=<outcome>&owner=<owner>&page=0&pageSize=0");
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/sales/opportunities` {#depot.sales.createOpportunity}

**Create opportunity**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, OpportunityView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `409` | `application/problem+json`, ProblemDetails | This board has no open stage to put a deal in. Add one on the sales settings page. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/opportunities' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 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 = @'
{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 0
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/opportunities' `
    -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/sales/opportunities"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "clientPublicId": "string",
    "expectedCloseOn": "string",
    "kind": "string",
    "name": "string",
    "notes": "string",
    "ownerUserPublicId": "string",
    "probability": 0,
    "prospectPublicId": "string",
    "stagePublicId": "string",
    "valueCents": 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(`{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 0
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/opportunities", 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/sales/opportunities";

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({
		"clientPublicId": "string",
		"expectedCloseOn": "string",
		"kind": "string",
		"name": "string",
		"notes": "string",
		"ownerUserPublicId": "string",
		"probability": 0,
		"prospectPublicId": "string",
		"stagePublicId": "string",
		"valueCents": 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 = """
{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/opportunities");
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/sales/opportunities/{id}` {#depot.sales.getOpportunity}

**Get opportunity**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, OpportunityView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/opportunities/<id>' \
  -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/sales/opportunities/<id>' `
    -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/sales/opportunities/<id>"
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/sales/opportunities/<id>", 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/sales/opportunities/<id>";

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/sales/opportunities/<id>");
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());
```

### PATCH `/api/v1/sales/opportunities/{id}` {#depot.sales.patchOpportunity}

**Patch opportunity**

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, OpportunityView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/sales/opportunities/<id>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 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 = @'
{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 0
}
'@

$response = Invoke-RestMethod `
    -Method Patch `
    -Uri 'https://depot.example.com/api/v1/sales/opportunities/<id>' `
    -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/sales/opportunities/<id>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "clientPublicId": "string",
    "expectedCloseOn": "string",
    "kind": "string",
    "name": "string",
    "notes": "string",
    "ownerUserPublicId": "string",
    "probability": 0,
    "prospectPublicId": "string",
    "stagePublicId": "string",
    "valueCents": 0,
}

response = requests.patch(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(`{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 0
}`)

	req, err := http.NewRequest(http.MethodPatch, "https://depot.example.com/api/v1/sales/opportunities/<id>", 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/sales/opportunities/<id>";

const response = await fetch(url, {
	method: "PATCH",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"clientPublicId": "string",
		"expectedCloseOn": "string",
		"kind": "string",
		"name": "string",
		"notes": "string",
		"ownerUserPublicId": "string",
		"probability": 0,
		"prospectPublicId": "string",
		"stagePublicId": "string",
		"valueCents": 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 = """
{
  "clientPublicId": "string",
  "expectedCloseOn": "string",
  "kind": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "probability": 0,
  "prospectPublicId": "string",
  "stagePublicId": "string",
  "valueCents": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Patch, "https://depot.example.com/api/v1/sales/opportunities/<id>");
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/sales/opportunities/{id}` {#depot.sales.deleteOpportunity}

**Delete opportunity**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/opportunities/<id>' \
  -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/sales/opportunities/<id>' `
    -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/sales/opportunities/<id>"
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/sales/opportunities/<id>", 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/sales/opportunities/<id>";

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/sales/opportunities/<id>");
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/sales/opportunities/{id}/move` {#depot.sales.moveOpportunity}

**moveOpportunity is the board's drop and the detail page's "mark won" and "mark lost", which are the same act: entering the won stage stamps WonAt, entering the lost stage stamps LostAt and the reason, and entering an open stage clears both.**

THERE IS NO SEPARATE WIN OR LOSE ROUTE, and there is no quote "lose" route
either: marking a deal lost is moving it to the lost column.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, OpportunityView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/opportunities/<id>/move' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "lostReason": "string",
  "stagePublicId": "string"
}'
```

```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 = @'
{
  "lostReason": "string",
  "stagePublicId": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/opportunities/<id>/move' `
    -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/sales/opportunities/<id>/move"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "lostReason": "string",
    "stagePublicId": "string",
}

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(`{
  "lostReason": "string",
  "stagePublicId": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/opportunities/<id>/move", 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/sales/opportunities/<id>/move";

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({
		"lostReason": "string",
		"stagePublicId": "string"
	}),
});

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 = """
{
  "lostReason": "string",
  "stagePublicId": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/opportunities/<id>/move");
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/sales/prospects` {#depot.sales.listProspects}

**List prospects**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `owner` | query | string | No |  |
| `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. |
| `sort` | query | string, one of created, name, updated | No | Sort key. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagedResultProspectView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects?owner=<owner>&page=0&pageSize=0&q=<q>&sort=created' \
  -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/sales/prospects?owner=<owner>&page=0&pageSize=0&q=<q>&sort=created' `
    -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/sales/prospects"
params = {
    "owner": "<owner>",
    "page": "0",
    "pageSize": "0",
    "q": "<q>",
    "sort": "created",
}
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/sales/prospects?owner=<owner>&page=0&pageSize=0&q=<q>&sort=created", 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/sales/prospects");
url.searchParams.set("owner", "<owner>");
url.searchParams.set("page", "0");
url.searchParams.set("pageSize", "0");
url.searchParams.set("q", "<q>");
url.searchParams.set("sort", "created");

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/sales/prospects?owner=<owner>&page=0&pageSize=0&q=<q>&sort=created");
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/sales/prospects` {#depot.sales.createProspect}

**createProspect writes a new one, owned by whoever created it unless they said otherwise.**

The default matters: a pipeline of unowned rows is a pipeline nobody chases,
and the person typing a company in is the person who just spoke to them.

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, ProspectView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}'
```

```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 = @'
{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/prospects' `
    -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/sales/prospects"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "addressLine1": "string",
    "addressLine2": "string",
    "city": "string",
    "country": "string",
    "industry": "string",
    "name": "string",
    "notes": "string",
    "ownerUserPublicId": "string",
    "phone": "string",
    "postalCode": "string",
    "region": "string",
    "source": "string",
    "website": "string",
}

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(`{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/prospects", 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/sales/prospects";

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({
		"addressLine1": "string",
		"addressLine2": "string",
		"city": "string",
		"country": "string",
		"industry": "string",
		"name": "string",
		"notes": "string",
		"ownerUserPublicId": "string",
		"phone": "string",
		"postalCode": "string",
		"region": "string",
		"source": "string",
		"website": "string"
	}),
});

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 = """
{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/prospects");
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/sales/prospects/{id}` {#depot.sales.getProspect}

**Get prospect**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, ProspectView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects/<id>' \
  -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/sales/prospects/<id>' `
    -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/sales/prospects/<id>"
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/sales/prospects/<id>", 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/sales/prospects/<id>";

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/sales/prospects/<id>");
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());
```

### PATCH `/api/v1/sales/prospects/{id}` {#depot.sales.patchProspect}

**patchProspect edits the fields a salesperson types.**

It CANNOT change the status: conversion and archiving each have their own preconditions and their own route, and a PATCH that could write "converted" would let a console mark a prospect as a customer that does not exist.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, ProspectView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/sales/prospects/<id>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}'
```

```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 = @'
{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Patch `
    -Uri 'https://depot.example.com/api/v1/sales/prospects/<id>' `
    -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/sales/prospects/<id>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "addressLine1": "string",
    "addressLine2": "string",
    "city": "string",
    "country": "string",
    "industry": "string",
    "name": "string",
    "notes": "string",
    "ownerUserPublicId": "string",
    "phone": "string",
    "postalCode": "string",
    "region": "string",
    "source": "string",
    "website": "string",
}

response = requests.patch(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(`{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}`)

	req, err := http.NewRequest(http.MethodPatch, "https://depot.example.com/api/v1/sales/prospects/<id>", 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/sales/prospects/<id>";

const response = await fetch(url, {
	method: "PATCH",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"addressLine1": "string",
		"addressLine2": "string",
		"city": "string",
		"country": "string",
		"industry": "string",
		"name": "string",
		"notes": "string",
		"ownerUserPublicId": "string",
		"phone": "string",
		"postalCode": "string",
		"region": "string",
		"source": "string",
		"website": "string"
	}),
});

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 = """
{
  "addressLine1": "string",
  "addressLine2": "string",
  "city": "string",
  "country": "string",
  "industry": "string",
  "name": "string",
  "notes": "string",
  "ownerUserPublicId": "string",
  "phone": "string",
  "postalCode": "string",
  "region": "string",
  "source": "string",
  "website": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Patch, "https://depot.example.com/api/v1/sales/prospects/<id>");
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());
```

### POST `/api/v1/sales/prospects/{id}/archive` {#depot.sales.archiveProspect}

**archiveProspect hides a row.**

It is a SOFT DELETE and not a fourth status: "we lost this deal" and "hide this row" are different facts, and one column holding both would make the pipeline report answer a question nobody asked.

A converted prospect refuses: that row is the provenance of a live customer.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects/<id>/archive' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/prospects/<id>/archive' `
    -Headers $headers

$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/sales/prospects/<id>/archive"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/prospects/<id>/archive", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/prospects/<id>/archive";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/prospects/<id>/archive");
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/sales/prospects/{id}/contacts` {#depot.sales.listContacts}

**List contacts**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects/<id>/contacts' \
  -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/sales/prospects/<id>/contacts' `
    -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/sales/prospects/<id>/contacts"
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/sales/prospects/<id>/contacts", 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/sales/prospects/<id>/contacts";

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/sales/prospects/<id>/contacts");
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/sales/prospects/{id}/contacts` {#depot.sales.createContact}

**Create contact**

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, ProspectContactView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects/<id>/contacts' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}'
```

```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 = @'
{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/prospects/<id>/contacts' `
    -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/sales/prospects/<id>/contacts"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "email": "string",
    "name": "string",
    "phone": "string",
    "primary": False,
    "title": "string",
}

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(`{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/prospects/<id>/contacts", 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/sales/prospects/<id>/contacts";

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({
		"email": "string",
		"name": "string",
		"phone": "string",
		"primary": false,
		"title": "string"
	}),
});

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 = """
{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/prospects/<id>/contacts");
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());
```

### PATCH `/api/v1/sales/prospects/{id}/contacts/{cid}` {#depot.sales.patchContact}

**Patch contact**

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, ProspectContactView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/sales/prospects/<id>/contacts/<cid>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}'
```

```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 = @'
{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Patch `
    -Uri 'https://depot.example.com/api/v1/sales/prospects/<id>/contacts/<cid>' `
    -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/sales/prospects/<id>/contacts/<cid>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "email": "string",
    "name": "string",
    "phone": "string",
    "primary": False,
    "title": "string",
}

response = requests.patch(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(`{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}`)

	req, err := http.NewRequest(http.MethodPatch, "https://depot.example.com/api/v1/sales/prospects/<id>/contacts/<cid>", 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/sales/prospects/<id>/contacts/<cid>";

const response = await fetch(url, {
	method: "PATCH",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"email": "string",
		"name": "string",
		"phone": "string",
		"primary": false,
		"title": "string"
	}),
});

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 = """
{
  "email": "string",
  "name": "string",
  "phone": "string",
  "primary": false,
  "title": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Patch, "https://depot.example.com/api/v1/sales/prospects/<id>/contacts/<cid>");
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/sales/prospects/{id}/contacts/{cid}` {#depot.sales.deleteContact}

**deleteContact soft-deletes a person.**

A quote already sent SNAPSHOTTED the address it went to, so removing the contact cannot orphan a record.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/prospects/<id>/contacts/<cid>' \
  -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/sales/prospects/<id>/contacts/<cid>' `
    -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/sales/prospects/<id>/contacts/<cid>"
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/sales/prospects/<id>/contacts/<cid>", 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/sales/prospects/<id>/contacts/<cid>";

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/sales/prospects/<id>/contacts/<cid>");
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/sales/quotes` {#depot.sales.listQuotes}

**listQuotes answers one page WITHOUT lines, and it is what the approvals badge polls: ?status=pending_approval&pageSize=1 is an index-only count on IX_Quote_Status, which is why the total is a separate COUNT under the same predicate rather than len(items).**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `client` | query | string | No |  |
| `isTemplate` | query | string | No |  |
| `opportunity` | query | string | No |  |
| `owner` | query | string | No |  |
| `page` | query | integer | No | One-based page number. Defaults to 1. |
| `pageSize` | query | integer | No | Rows per page, 1 to 200. Defaults to 50. |
| `prospect` | query | string | No |  |
| `q` | query | string | No | Free-text search term. |
| `sort` | query | string, one of expires, number, requested, total, updated | No | Sort key. |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagedResultQuoteSummaryView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes?client=<client>&isTemplate=<isTemplate>&opportunity=<opportunity>&owner=<owner>&page=0&pageSize=0' \
  -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/sales/quotes?client=<client>&isTemplate=<isTemplate>&opportunity=<opportunity>&owner=<owner>&page=0&pageSize=0' `
    -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/sales/quotes"
params = {
    "client": "<client>",
    "isTemplate": "<isTemplate>",
    "opportunity": "<opportunity>",
    "owner": "<owner>",
    "page": "0",
    "pageSize": "0",
}
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/sales/quotes?client=<client>&isTemplate=<isTemplate>&opportunity=<opportunity>&owner=<owner>&page=0&pageSize=0", 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/sales/quotes");
url.searchParams.set("client", "<client>");
url.searchParams.set("isTemplate", "<isTemplate>");
url.searchParams.set("opportunity", "<opportunity>");
url.searchParams.set("owner", "<owner>");
url.searchParams.set("page", "0");
url.searchParams.set("pageSize", "0");

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/sales/quotes?client=<client>&isTemplate=<isTemplate>&opportunity=<opportunity>&owner=<owner>&page=0&pageSize=0");
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/sales/quotes` {#depot.sales.createQuote}

**Create quote**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}'
```

```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 = @'
{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes' `
    -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/sales/quotes"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "agreementStartsOn": "string",
    "agreementTemplatePublicId": "string",
    "billingDay": 0,
    "clientPublicId": "string",
    "contactPublicId": "string",
    "coverLetter": "string",
    "depositBasisPoints": "string",
    "expiresOn": "string",
    "invoiceMode": "string",
    "isTemplate": False,
    "opportunityPublicId": "string",
    "ownerUserPublicId": "string",
    "prospectPublicId": "string",
    "sections": [
        {
            "description": "string",
            "kind": "string",
            "lines": [
                {
                    "adjustable": False,
                    "agreementLineKind": "string",
                    "catalogItemPublicId": "string",
                    "defaultSelected": False,
                    "description": "string",
                    "discountBp": 0,
                    "includedMinutes": 0,
                    "kind": "string",
                    "maxQuantity": "string",
                    "minQuantity": "string",
                    "optional": False,
                    "publicId": "string",
                    "quantity": "string",
                    "taxable": False,
                    "unit": "string",
                    "unitCostCents": 0,
                    "unitPriceCents": 0,
                },
            ],
            "publicId": "string",
            "title": "string",
        },
    ],
    "taxRateBasisPoints": 0,
    "templateName": "string",
    "termMonths": 0,
    "terms": "string",
    "timeCoverage": "string",
    "title": "string",
}

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(`{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes", 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/sales/quotes";

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({
		"agreementStartsOn": "string",
		"agreementTemplatePublicId": "string",
		"billingDay": 0,
		"clientPublicId": "string",
		"contactPublicId": "string",
		"coverLetter": "string",
		"depositBasisPoints": "string",
		"expiresOn": "string",
		"invoiceMode": "string",
		"isTemplate": false,
		"opportunityPublicId": "string",
		"ownerUserPublicId": "string",
		"prospectPublicId": "string",
		"sections": [
			{
				"description": "string",
				"kind": "string",
				"lines": [
					{
						"adjustable": false,
						"agreementLineKind": "string",
						"catalogItemPublicId": "string",
						"defaultSelected": false,
						"description": "string",
						"discountBp": 0,
						"includedMinutes": 0,
						"kind": "string",
						"maxQuantity": "string",
						"minQuantity": "string",
						"optional": false,
						"publicId": "string",
						"quantity": "string",
						"taxable": false,
						"unit": "string",
						"unitCostCents": 0,
						"unitPriceCents": 0
					}
				],
				"publicId": "string",
				"title": "string"
			}
		],
		"taxRateBasisPoints": 0,
		"templateName": "string",
		"termMonths": 0,
		"terms": "string",
		"timeCoverage": "string",
		"title": "string"
	}),
});

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 = """
{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes");
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/sales/quotes/{id}` {#depot.sales.getQuote}

**Get quote**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>' \
  -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/sales/quotes/<id>' `
    -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/sales/quotes/<id>"
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/sales/quotes/<id>", 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/sales/quotes/<id>";

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/sales/quotes/<id>");
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/sales/quotes/{id}` {#depot.sales.putQuote}

**putQuote replaces the WHOLE document.**

The 409 past the three pre-send statuses is the STORE's, decided inside the
transaction that takes the row FOR UPDATE, and not a status check here: a
check here would race a concurrent send and let an edit land on a document
that had already been frozen, numbered and mailed.

An edit to an APPROVED quote drops it back to draft and clears the approval.
An approver said yes to a document, not to a number, and an edit after
approval is a different document.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}'
```

```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 = @'
{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Put `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>' `
    -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/sales/quotes/<id>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "agreementStartsOn": "string",
    "agreementTemplatePublicId": "string",
    "billingDay": 0,
    "clientPublicId": "string",
    "contactPublicId": "string",
    "coverLetter": "string",
    "depositBasisPoints": "string",
    "expiresOn": "string",
    "invoiceMode": "string",
    "isTemplate": False,
    "opportunityPublicId": "string",
    "ownerUserPublicId": "string",
    "prospectPublicId": "string",
    "sections": [
        {
            "description": "string",
            "kind": "string",
            "lines": [
                {
                    "adjustable": False,
                    "agreementLineKind": "string",
                    "catalogItemPublicId": "string",
                    "defaultSelected": False,
                    "description": "string",
                    "discountBp": 0,
                    "includedMinutes": 0,
                    "kind": "string",
                    "maxQuantity": "string",
                    "minQuantity": "string",
                    "optional": False,
                    "publicId": "string",
                    "quantity": "string",
                    "taxable": False,
                    "unit": "string",
                    "unitCostCents": 0,
                    "unitPriceCents": 0,
                },
            ],
            "publicId": "string",
            "title": "string",
        },
    ],
    "taxRateBasisPoints": 0,
    "templateName": "string",
    "termMonths": 0,
    "terms": "string",
    "timeCoverage": "string",
    "title": "string",
}

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(`{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}`)

	req, err := http.NewRequest(http.MethodPut, "https://depot.example.com/api/v1/sales/quotes/<id>", 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/sales/quotes/<id>";

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({
		"agreementStartsOn": "string",
		"agreementTemplatePublicId": "string",
		"billingDay": 0,
		"clientPublicId": "string",
		"contactPublicId": "string",
		"coverLetter": "string",
		"depositBasisPoints": "string",
		"expiresOn": "string",
		"invoiceMode": "string",
		"isTemplate": false,
		"opportunityPublicId": "string",
		"ownerUserPublicId": "string",
		"prospectPublicId": "string",
		"sections": [
			{
				"description": "string",
				"kind": "string",
				"lines": [
					{
						"adjustable": false,
						"agreementLineKind": "string",
						"catalogItemPublicId": "string",
						"defaultSelected": false,
						"description": "string",
						"discountBp": 0,
						"includedMinutes": 0,
						"kind": "string",
						"maxQuantity": "string",
						"minQuantity": "string",
						"optional": false,
						"publicId": "string",
						"quantity": "string",
						"taxable": false,
						"unit": "string",
						"unitCostCents": 0,
						"unitPriceCents": 0
					}
				],
				"publicId": "string",
				"title": "string"
			}
		],
		"taxRateBasisPoints": 0,
		"templateName": "string",
		"termMonths": 0,
		"terms": "string",
		"timeCoverage": "string",
		"title": "string"
	}),
});

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 = """
{
  "agreementStartsOn": "string",
  "agreementTemplatePublicId": "string",
  "billingDay": 0,
  "clientPublicId": "string",
  "contactPublicId": "string",
  "coverLetter": "string",
  "depositBasisPoints": "string",
  "expiresOn": "string",
  "invoiceMode": "string",
  "isTemplate": false,
  "opportunityPublicId": "string",
  "ownerUserPublicId": "string",
  "prospectPublicId": "string",
  "sections": [
    {
      "description": "string",
      "kind": "string",
      "lines": [
        {
          "adjustable": false,
          "agreementLineKind": "string",
          "catalogItemPublicId": "string",
          "defaultSelected": false,
          "description": "string",
          "discountBp": 0,
          "includedMinutes": 0,
          "kind": "string",
          "maxQuantity": "string",
          "minQuantity": "string",
          "optional": false,
          "publicId": "string",
          "quantity": "string",
          "taxable": false,
          "unit": "string",
          "unitCostCents": 0,
          "unitPriceCents": 0
        }
      ],
      "publicId": "string",
      "title": "string"
    }
  ],
  "taxRateBasisPoints": 0,
  "templateName": "string",
  "termMonths": 0,
  "terms": "string",
  "timeCoverage": "string",
  "title": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Put, "https://depot.example.com/api/v1/sales/quotes/<id>");
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/sales/quotes/{id}` {#depot.sales.deleteQuote}

**deleteQuote HARD-DELETES a quote that was never sent.**

A quote is an OFFER, not an accounting record, and an offer that was never
made leaves no hole anybody asks about. That is the deliberate difference from
an invoice number, where a gap is what an auditor asks about. A revision that
was sent and then withdrawn is not a draft and is not deletable.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>' \
  -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/sales/quotes/<id>' `
    -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/sales/quotes/<id>"
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/sales/quotes/<id>", 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/sales/quotes/<id>";

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/sales/quotes/<id>");
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/sales/quotes/{id}/accept-link` {#depot.sales.getAcceptLink}

**getAcceptLink opens the sealed token, and it is the ONLY read path for the plaintext besides the mail composers.**

crm:send and not crm:read, because a link is a bearer credential for a
document: whoever may hand one out is whoever may send the quote. It appears
in no list, detail, event, audit body or log.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, AcceptLinkView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/accept-link' \
  -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/sales/quotes/<id>/accept-link' `
    -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/sales/quotes/<id>/accept-link"
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/sales/quotes/<id>/accept-link", 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/sales/quotes/<id>/accept-link";

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/sales/quotes/<id>/accept-link");
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/sales/quotes/{id}/approve` {#depot.sales.approveQuote}

**Approve quote**

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/approve' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "note": "string"
}'
```

```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 = @'
{
  "note": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/approve' `
    -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/sales/quotes/<id>/approve"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "note": "string",
}

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(`{
  "note": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/approve", 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/sales/quotes/<id>/approve";

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({
		"note": "string"
	}),
});

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 = """
{
  "note": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/approve");
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());
```

### POST `/api/v1/sales/quotes/{id}/convert` {#depot.sales.retryConversion}

**retryConversion puts a FAILED conversion back in the queue.**

crm:write and not crm:approve: retrying is asking the worker to try again at
building what an accepted quote already entitles the customer to, which is
pipeline work rather than a decision about money. The 409 on anything but
failed is the guard that matters: retrying a conversion that is still running
would put two workers on one quote.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/convert' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/convert' `
    -Headers $headers

$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/sales/quotes/<id>/convert"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/convert", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/quotes/<id>/convert";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/convert");
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/sales/quotes/{id}/countersign` {#depot.sales.countersignQuote}

**countersignQuote records the practice's own signature on an accepted quote.**

The executed PDF and the two emails are the POST-COMMIT TAIL, as acceptance's
are, and for the same reason: a render inside the transaction holding the row
would deadlock, and a crash between the commit and the render has to leave a
record that can be completed rather than a countersignature that failed.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/countersign' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "consentText": "string",
  "consentVersion": 0,
  "method": "string",
  "signatureImage": "string",
  "signerName": "string",
  "signerTitle": "string"
}'
```

```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 = @'
{
  "consentText": "string",
  "consentVersion": 0,
  "method": "string",
  "signatureImage": "string",
  "signerName": "string",
  "signerTitle": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/countersign' `
    -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/sales/quotes/<id>/countersign"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "consentText": "string",
    "consentVersion": 0,
    "method": "string",
    "signatureImage": "string",
    "signerName": "string",
    "signerTitle": "string",
}

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(`{
  "consentText": "string",
  "consentVersion": 0,
  "method": "string",
  "signatureImage": "string",
  "signerName": "string",
  "signerTitle": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/countersign", 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/sales/quotes/<id>/countersign";

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({
		"consentText": "string",
		"consentVersion": 0,
		"method": "string",
		"signatureImage": "string",
		"signerName": "string",
		"signerTitle": "string"
	}),
});

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 = """
{
  "consentText": "string",
  "consentVersion": 0,
  "method": "string",
  "signatureImage": "string",
  "signerName": "string",
  "signerTitle": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/countersign");
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/sales/quotes/{id}/events` {#depot.sales.listQuoteEvents}

**listQuoteEvents answers the timeline.**

IP AND USER AGENT ARE RENDERED FROM THE SIGNATURE ROW, not stored on an event:
the one place holding a signer's address is the one place that is sealed. They
appear on accepted, declined and countersigned and are null everywhere else.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/events' \
  -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/sales/quotes/<id>/events' `
    -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/sales/quotes/<id>/events"
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/sales/quotes/<id>/events", 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/sales/quotes/<id>/events";

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/sales/quotes/<id>/events");
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/sales/quotes/{id}/mail` {#depot.sales.listQuoteMail}

**listQuoteMail answers "what was sent about this quote, and did it arrive".**

The outbox row is the delivery record, which is why there is no MarkQuoteSent:
SentAt on the quote is stamped by the send, and whether the message reached
anybody is this list's answer and not that column's.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/mail' \
  -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/sales/quotes/<id>/mail' `
    -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/sales/quotes/<id>/mail"
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/sales/quotes/<id>/mail", 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/sales/quotes/<id>/mail";

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/sales/quotes/<id>/mail");
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/sales/quotes/{id}/pdf/{variant}` {#depot.sales.getQuotePdf}

**Get quote pdf**

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/octet-stream`, string (binary) | Success. The body shape was not resolved; see x-outpost-unresolved. |
| `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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/pdf/<variant>' \
  -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/sales/quotes/<id>/pdf/<variant>' `
    -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/sales/quotes/<id>/pdf/<variant>"
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/sales/quotes/<id>/pdf/<variant>", 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/sales/quotes/<id>/pdf/<variant>";

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/sales/quotes/<id>/pdf/<variant>");
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/sales/quotes/{id}/reject` {#depot.sales.rejectQuote}

**Reject quote**

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/reject' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "note": "string"
}'
```

```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 = @'
{
  "note": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/reject' `
    -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/sales/quotes/<id>/reject"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "note": "string",
}

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(`{
  "note": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/reject", 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/sales/quotes/<id>/reject";

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({
		"note": "string"
	}),
});

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 = """
{
  "note": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/reject");
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());
```

### POST `/api/v1/sales/quotes/{id}/request-approval` {#depot.sales.requestApproval}

**requestApproval moves a draft to pending_approval and mails every holder of crm:approve.**

The APPROVER LIST IS RBAC's and not a settings field. A
SalesSettings.ApproverEmails list would be simpler and would drift from the
roles the first time somebody changed one, which is the sort of drift nobody
notices until an approval request goes to a person who left.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/request-approval' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/request-approval' `
    -Headers $headers

$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/sales/quotes/<id>/request-approval"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/request-approval", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/quotes/<id>/request-approval";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/request-approval");
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/sales/quotes/{id}/resend` {#depot.sales.resendQuote}

**resendQuote re-mails THE SAME LINK and keeps the status.**

It mints nothing, and that is the whole point: a resend that quietly rotated
would kill the link already sitting in the customer's inbox, so the customer
who finally clicks the first email would find it dead. Rotating is a separate,
explicit action.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/resend' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/resend' `
    -Headers $headers

$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/sales/quotes/<id>/resend"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/resend", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/quotes/<id>/resend";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/resend");
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/sales/quotes/{id}/revise` {#depot.sales.reviseQuote}

**reviseQuote writes the NEXT REVISION and supersedes the old row.**

It never edits the old one. That row is what a recipient may already have
signed, it has a rendered PDF whose hash is printed on a certificate, and
"what exactly did we offer them in August" is a question a contract dispute
turns on. The response is the NEW draft, which is what the console opens.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/revise' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/revise' `
    -Headers $headers

$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/sales/quotes/<id>/revise"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/revise", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/quotes/<id>/revise";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/revise");
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/sales/quotes/{id}/rotate-link` {#depot.sales.rotateLink}

**rotateLink kills the old link and mints a new one IN ONE UPDATE, so the old link dies at the same instant the new one is born.**

The new link is mailed, so rotating is never a silent way to lock a customer out.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/rotate-link' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/rotate-link' `
    -Headers $headers

$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/sales/quotes/<id>/rotate-link"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/rotate-link", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/quotes/<id>/rotate-link";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/rotate-link");
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/sales/quotes/{id}/send` {#depot.sales.sendQuote}

**sendQuote freezes the document, mints the link and queues the mail.**

THE ORDER BELOW IS THE CONTRACT'S AND MIRRORS finalizeInvoice. Everything the
store's transaction needs is read first, because the render closure runs
inside that transaction and a database call from it would deadlock against the
row it is rendering:

	sales settings -> billing settings -> logo -> recipient -> agreement template
	-> Price(default selections) -> Approval (409 unless approved)
	-> link base (422) -> NewToken -> compose the mail -> render closure
	-> SendQuote -> SetOpportunityValue

THE TWO REFUSALS COME BEFORE THE TOKEN IS MINTED, deliberately. A quote held
for approval and a Depot with no configured link base must both fail before
the document is frozen, so a misconfigured practice never accumulates sent
quotes nobody can open.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/send' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "expiresOn": "string",
  "to": "string"
}'
```

```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 = @'
{
  "expiresOn": "string",
  "to": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/send' `
    -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/sales/quotes/<id>/send"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "expiresOn": "string",
    "to": "string",
}

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(`{
  "expiresOn": "string",
  "to": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/send", 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/sales/quotes/<id>/send";

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({
		"expiresOn": "string",
		"to": "string"
	}),
});

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 = """
{
  "expiresOn": "string",
  "to": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/send");
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());
```

### POST `/api/v1/sales/quotes/{id}/withdraw` {#depot.sales.withdrawQuote}

**withdrawQuote pulls a sent quote and NULLs both token columns, so the link dies with the offer.**

Terminal: nothing leaves withdrawn except by revising, which writes a new row.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:send. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/<id>/withdraw' \
  -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.

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

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/<id>/withdraw' `
    -Headers $headers

$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/sales/quotes/<id>/withdraw"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.post(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.
package main

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

func main() {
	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/<id>/withdraw", 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.
// The session cookie is set by signing in. A script cannot set it itself.

const url = "https://depot.example.com/api/v1/sales/quotes/<id>/withdraw";

const response = await fetch(url, {
	method: "POST",
	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.

using System;
using System.Net.Http;

using var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/<id>/withdraw");
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/sales/quotes/from-agreement` {#depot.sales.quoteFromAgreement}

**quoteFromAgreement drafts the successor to a running agreement.**

MOUNTED TWICE, at /quotes/renewal and /quotes/from-agreement, and it is one
handler: the console plan and the lead's ruling named different paths and both
are frozen, so neither reader is wrong and a second implementation would be a
second thing to keep in step.

It produces a DRAFT and nothing else. The renewals worker produces one on a
cadence and this route is what an operator uses when a crash left an
opportunity with no quote, or when they want to renew early; in both cases the
answer is a document somebody opens, edits and sends, because renewing a
contract without anybody reading it is not a feature.

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/from-agreement' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}'
```

```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 = @'
{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/from-agreement' `
    -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/sales/quotes/from-agreement"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "agreementPublicId": "string",
    "clientPublicId": "string",
}

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(`{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/from-agreement", 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/sales/quotes/from-agreement";

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({
		"agreementPublicId": "string",
		"clientPublicId": "string"
	}),
});

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 = """
{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/from-agreement");
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());
```

### POST `/api/v1/sales/quotes/renewal` {#depot.sales.quoteFromAgreement.post}

**quoteFromAgreement drafts the successor to a running agreement.**

MOUNTED TWICE, at /quotes/renewal and /quotes/from-agreement, and it is one
handler: the console plan and the lead's ruling named different paths and both
are frozen, so neither reader is wrong and a second implementation would be a
second thing to keep in step.

It produces a DRAFT and nothing else. The renewals worker produces one on a
cadence and this route is what an operator uses when a crash left an
opportunity with no quote, or when they want to renew early; in both cases the
answer is a document somebody opens, edits and sends, because renewing a
contract without anybody reading it is not a feature.

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, QuoteView | 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 crm:write. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/quotes/renewal' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}'
```

```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 = @'
{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/quotes/renewal' `
    -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/sales/quotes/renewal"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "agreementPublicId": "string",
    "clientPublicId": "string",
}

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(`{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/quotes/renewal", 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/sales/quotes/renewal";

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({
		"agreementPublicId": "string",
		"clientPublicId": "string"
	}),
});

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 = """
{
  "agreementPublicId": "string",
  "clientPublicId": "string"
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/quotes/renewal");
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/sales/settings` {#depot.sales.getSettings}

**Get sales settings**

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SalesSettingsView | 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/settings' \
  -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/sales/settings' `
    -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/sales/settings"
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/sales/settings", 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/sales/settings";

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/sales/settings");
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/sales/settings` {#depot.sales.putSettings}

**Put sales settings**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SalesSettingsView | 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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/settings' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "approvals": {
    "lineDiscountAboveBp": 0,
    "marginBelowBp": 0,
    "totalAboveCents": 0
  },
  "automation": {
    "expiringReminderDays": 0,
    "renewalLeadDays": 0,
    "requireCountersign": false,
    "unviewedNudgeDays": 0
  },
  "branding": {
    "accentColor": "string",
    "displayName": "string",
    "replyTo": "string"
  },
  "orders": {
    "defaultDepositBasisPoints": 0,
    "defaultInvoiceMode": "string",
    "salesOrderPrefix": "string"
  },
  "quotes": {
    "defaultCoverLetter": "string",
    "defaultTerms": "string",
    "numberPrefix": "string",
    "validDays": 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 = @'
{
  "approvals": {
    "lineDiscountAboveBp": 0,
    "marginBelowBp": 0,
    "totalAboveCents": 0
  },
  "automation": {
    "expiringReminderDays": 0,
    "renewalLeadDays": 0,
    "requireCountersign": false,
    "unviewedNudgeDays": 0
  },
  "branding": {
    "accentColor": "string",
    "displayName": "string",
    "replyTo": "string"
  },
  "orders": {
    "defaultDepositBasisPoints": 0,
    "defaultInvoiceMode": "string",
    "salesOrderPrefix": "string"
  },
  "quotes": {
    "defaultCoverLetter": "string",
    "defaultTerms": "string",
    "numberPrefix": "string",
    "validDays": 0
  }
}
'@

$response = Invoke-RestMethod `
    -Method Put `
    -Uri 'https://depot.example.com/api/v1/sales/settings' `
    -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/sales/settings"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "approvals": {
        "lineDiscountAboveBp": 0,
        "marginBelowBp": 0,
        "totalAboveCents": 0,
    },
    "automation": {
        "expiringReminderDays": 0,
        "renewalLeadDays": 0,
        "requireCountersign": False,
        "unviewedNudgeDays": 0,
    },
    "branding": {
        "accentColor": "string",
        "displayName": "string",
        "replyTo": "string",
    },
    "orders": {
        "defaultDepositBasisPoints": 0,
        "defaultInvoiceMode": "string",
        "salesOrderPrefix": "string",
    },
    "quotes": {
        "defaultCoverLetter": "string",
        "defaultTerms": "string",
        "numberPrefix": "string",
        "validDays": 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(`{
  "approvals": {
    "lineDiscountAboveBp": 0,
    "marginBelowBp": 0,
    "totalAboveCents": 0
  },
  "automation": {
    "expiringReminderDays": 0,
    "renewalLeadDays": 0,
    "requireCountersign": false,
    "unviewedNudgeDays": 0
  },
  "branding": {
    "accentColor": "string",
    "displayName": "string",
    "replyTo": "string"
  },
  "orders": {
    "defaultDepositBasisPoints": 0,
    "defaultInvoiceMode": "string",
    "salesOrderPrefix": "string"
  },
  "quotes": {
    "defaultCoverLetter": "string",
    "defaultTerms": "string",
    "numberPrefix": "string",
    "validDays": 0
  }
}`)

	req, err := http.NewRequest(http.MethodPut, "https://depot.example.com/api/v1/sales/settings", 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/sales/settings";

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({
		"approvals": {
			"lineDiscountAboveBp": 0,
			"marginBelowBp": 0,
			"totalAboveCents": 0
		},
		"automation": {
			"expiringReminderDays": 0,
			"renewalLeadDays": 0,
			"requireCountersign": false,
			"unviewedNudgeDays": 0
		},
		"branding": {
			"accentColor": "string",
			"displayName": "string",
			"replyTo": "string"
		},
		"orders": {
			"defaultDepositBasisPoints": 0,
			"defaultInvoiceMode": "string",
			"salesOrderPrefix": "string"
		},
		"quotes": {
			"defaultCoverLetter": "string",
			"defaultTerms": "string",
			"numberPrefix": "string",
			"validDays": 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 = """
{
  "approvals": {
    "lineDiscountAboveBp": 0,
    "marginBelowBp": 0,
    "totalAboveCents": 0
  },
  "automation": {
    "expiringReminderDays": 0,
    "renewalLeadDays": 0,
    "requireCountersign": false,
    "unviewedNudgeDays": 0
  },
  "branding": {
    "accentColor": "string",
    "displayName": "string",
    "replyTo": "string"
  },
  "orders": {
    "defaultDepositBasisPoints": 0,
    "defaultInvoiceMode": "string",
    "salesOrderPrefix": "string"
  },
  "quotes": {
    "defaultCoverLetter": "string",
    "defaultTerms": "string",
    "numberPrefix": "string",
    "validDays": 0
  }
}
""";

var request = new HttpRequestMessage(HttpMethod.Put, "https://depot.example.com/api/v1/sales/settings");
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/sales/settings/logo` {#depot.sales.getLogo}

**Get sales logo**

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/octet-stream`, string (binary) | Success. The body shape was not resolved; see x-outpost-unresolved. |
| `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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/settings/logo' \
  -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/sales/settings/logo' `
    -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/sales/settings/logo"
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/sales/settings/logo", 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/sales/settings/logo";

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/sales/settings/logo");
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/sales/settings/logo` {#depot.sales.putLogo}

**putSalesLogo takes one image as the RAW request body.**

Raw body rather than multipart, following branding.go's uploadLogo: there is
one file and no other fields, so multipart would add a parser, a spool and its
own limits to carry no information. The type travels in Content-Type, which is
where an HTTP client already puts it.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, SalesSettingsView | Success. |
| `400` | `application/problem+json`, ProblemDetails | The request body could not be read. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `413` | `application/problem+json`, ProblemDetails | The request was refused. |
| `415` | `application/problem+json`, ProblemDetails | A logo must be a PNG or JPEG image. SVG is not accepted, because it can carry script, and WebP is not accepted, because it cannot be embedded in the quote PDF. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/settings/logo' \
  -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 Put `
    -Uri 'https://depot.example.com/api/v1/sales/settings/logo' `
    -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/sales/settings/logo"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.put(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.MethodPut, "https://depot.example.com/api/v1/sales/settings/logo", 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/sales/settings/logo";

const response = await fetch(url, {
	method: "PUT",
	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.Put, "https://depot.example.com/api/v1/sales/settings/logo");
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());
```

### DELETE `/api/v1/sales/settings/logo` {#depot.sales.deleteLogo}

**Delete sales logo**

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/settings/logo' \
  -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/sales/settings/logo' `
    -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/sales/settings/logo"
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/sales/settings/logo", 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/sales/settings/logo";

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/sales/settings/logo");
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/sales/stages` {#depot.sales.listStages}

**List stages**

#### 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 crm:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/stages' \
  -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/sales/stages' `
    -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/sales/stages"
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/sales/stages", 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/sales/stages";

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/sales/stages");
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/sales/stages` {#depot.sales.createStage}

**Create stage**

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `201` | `application/json`, PipelineStageView | 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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/stages' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "kind": "string",
  "name": "string",
  "probability": 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 = @'
{
  "kind": "string",
  "name": "string",
  "probability": 0
}
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/v1/sales/stages' `
    -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/sales/stages"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "kind": "string",
    "name": "string",
    "probability": 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(`{
  "kind": "string",
  "name": "string",
  "probability": 0
}`)

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/v1/sales/stages", 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/sales/stages";

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({
		"kind": "string",
		"name": "string",
		"probability": 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 = """
{
  "kind": "string",
  "name": "string",
  "probability": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/v1/sales/stages");
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());
```

### PATCH `/api/v1/sales/stages/{id}` {#depot.sales.patchStage}

**patchStage renames a stage or changes its probability.**

THE KIND IS NOT PATCHABLE and its absence from the request type is the whole
statement: "mark won" resolves the terminal stage by kind, so turning an open
column into the won one would move where every future win lands and leave the
old won column holding deals nobody would find again.

#### Parameters

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

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PipelineStageView | 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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### Request samples

```bash
# Sign in first. outpost_console is a session cookie, so send it with every request.
curl -X PATCH 'https://depot.example.com/api/v1/sales/stages/<id>' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "kind": "string",
  "name": "string",
  "probability": 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 = @'
{
  "kind": "string",
  "name": "string",
  "probability": 0
}
'@

$response = Invoke-RestMethod `
    -Method Patch `
    -Uri 'https://depot.example.com/api/v1/sales/stages/<id>' `
    -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/sales/stages/<id>"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "kind": "string",
    "name": "string",
    "probability": 0,
}

response = requests.patch(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(`{
  "kind": "string",
  "name": "string",
  "probability": 0
}`)

	req, err := http.NewRequest(http.MethodPatch, "https://depot.example.com/api/v1/sales/stages/<id>", 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/sales/stages/<id>";

const response = await fetch(url, {
	method: "PATCH",
	headers: {
		"X-CSRF-Token": "<csrf-token>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify({
		"kind": "string",
		"name": "string",
		"probability": 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 = """
{
  "kind": "string",
  "name": "string",
  "probability": 0
}
""";

var request = new HttpRequestMessage(HttpMethod.Patch, "https://depot.example.com/api/v1/sales/stages/<id>");
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/sales/stages/{id}` {#depot.sales.deleteStage}

**deleteStage soft-deletes a stage and moves its open opportunities elsewhere.**

?moveTo= is REQUIRED when the stage holds anything, and the store does the
move in the same transaction as the delete: a two-call version would leave
deals in a column that no longer exists if the second call failed.

#### Parameters

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

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/stages/<id>?moveTo=<moveTo>' \
  -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/sales/stages/<id>?moveTo=<moveTo>' `
    -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/sales/stages/<id>"
params = {
    "moveTo": "<moveTo>",
}
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}

response = requests.delete(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.MethodDelete, "https://depot.example.com/api/v1/sales/stages/<id>?moveTo=<moveTo>", 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/sales/stages/<id>");
url.searchParams.set("moveTo", "<moveTo>");

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/sales/stages/<id>?moveTo=<moveTo>");
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/sales/stages/order` {#depot.sales.reorderStages}

**reorderStages renumbers the board.**

The body is the WHOLE live set in the new order; the store refuses anything else, because a reorder that silently dropped a stage would leave it at its old position, interleaved.

#### Request body

Required, sent as `application/json`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, object | 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 crm:approve. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches. |
| `500` | `application/problem+json`, ProblemDetails | The request failed. |
| `503` | `application/problem+json`, ProblemDetails | Sales is not available on this server. |

#### 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/sales/stages/order' \
  -H 'X-CSRF-Token: <csrf-token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b 'outpost_console=<console-session>' \
  --data '{
  "publicIds": [
    "string"
  ]
}'
```

```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 = @'
{
  "publicIds": [
    "string"
  ]
}
'@

$response = Invoke-RestMethod `
    -Method Put `
    -Uri 'https://depot.example.com/api/v1/sales/stages/order' `
    -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/sales/stages/order"
headers = {
    "X-CSRF-Token": "<csrf-token>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "outpost_console": "<console-session>",
}
payload = {
    "publicIds": [
        "string",
    ],
}

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(`{
  "publicIds": [
    "string"
  ]
}`)

	req, err := http.NewRequest(http.MethodPut, "https://depot.example.com/api/v1/sales/stages/order", 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/sales/stages/order";

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({
		"publicIds": [
			"string"
		]
	}),
});

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 = """
{
  "publicIds": [
    "string"
  ]
}
""";

var request = new HttpRequestMessage(HttpMethod.Put, "https://depot.example.com/api/v1/sales/stages/order");
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());
```
