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

# Depot portal billing

Registers the billing routes behind the org middleware.

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/portal/v1/orgs/{clientID}/autopay` {#depot.portalBilling.getAutopay}

**getAutopay is whether automatic payments are on, with the authorization as agreed, and what enrolling would offer.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalAutopay | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:autopay:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### PUT `/api/portal/v1/orgs/{clientID}/autopay` {#depot.portalBilling.enrollAutopay}

**enrollAutopay creates the authorization the member accepted, with the text they read (a changed text is 409 quote_changed), recording the member as the authorizing person.**

A copy and a one-click revoke link are mailed.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalSetupResult | Success. |
| `201` | `application/json`, PortalEnrolResult | Created. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:autopay:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

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

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

```go
// Sign in first. __Host-portal_session 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(`"string"`)

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

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay";

const response = await fetch(url, {
	method: "PUT",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Put, "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/autopay` {#depot.portalBilling.revokeAutopay}

**revokeAutopay stops automatic payments before the next charge.**

The billing contact is mailed a confirmation. 404 when autopay is off.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalAuthorization | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:autopay:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Delete `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay";

const response = await fetch(url, {
	method: "DELETE",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### POST `/api/portal/v1/orgs/{clientID}/autopay/preview` {#depot.portalBilling.autopayPreview}

**autopayPreview returns the authorization text for the tokenized method, naming the signed-in member as the person authorizing.**

Reading it is its own step, never a checkbox on a payment (NACHA).

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalConsent | Success. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:autopay:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/preview' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/preview' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/preview"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/preview", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay/preview";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/preview");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/autopay/session` {#depot.portalBilling.autopaySession}

**autopaySession is the hosted fields in set-up mode for the method autopay will charge.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalPaySession | Success. |
| `400` | `application/problem+json`, ProblemDetails | The request body is not acceptable. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:autopay:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/session' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/session' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/session"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/session", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/autopay/session";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/autopay/session");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/estimate` {#depot.portalBilling.getEstimate}

**getEstimate is the next bill on the client's active agreement: each line with the devices and people it counts, what changed since the last invoice, when the counts were taken, and the period.**

Devices no line bills are the MSP's concern and are not listed. 404 when the client is on no agreement.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalEstimate | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:estimate:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/estimate' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/estimate' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/estimate"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/estimate", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/estimate";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/estimate");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### GET `/api/portal/v1/orgs/{clientID}/invoices` {#depot.portalBilling.listInvoices}

**listInvoices is the client's invoices, newest first, filtered by status ("open" for what is owed), issue date and number.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `page` | query | string | No |  |
| `pageSize` | query | string | No |  |
| `q` | query | string | No |  |
| `status` | query | string | No |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagePortalInvoice | Success. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices?page=<page>&pageSize=<pageSize>&q=<q>&status=<status>' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices?page=<page>&pageSize=<pageSize>&q=<q>&status=<status>' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices"
params = {
    "page": "<page>",
    "pageSize": "<pageSize>",
    "q": "<q>",
    "status": "<status>",
}
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices?page=<page>&pageSize=<pageSize>&q=<q>&status=<status>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices");
url.searchParams.set("page", "<page>");
url.searchParams.set("pageSize", "<pageSize>");
url.searchParams.set("q", "<q>");
url.searchParams.set("status", "<status>");

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices?page=<page>&pageSize=<pageSize>&q=<q>&status=<status>");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### GET `/api/portal/v1/orgs/{clientID}/invoices/{invoiceID}` {#depot.portalBilling.getInvoice}

**getInvoice is one invoice: its lines with the devices and people each counted line was billed for, its payments (online and recorded by the MSP), credit notes, and any payment still processing.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `invoiceID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalInvoiceDetail | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### GET `/api/portal/v1/orgs/{clientID}/invoices/{invoiceID}/attempts/{attemptID}` {#depot.portalBilling.getPayAttempt}

**getPayAttempt polls one payment attempt of this invoice; while it is in flight the processor is asked, so the page does not wait on a webhook.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `attemptID` | path | string | Yes |  |
| `clientID` | path | string | Yes |  |
| `invoiceID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalAttempt | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/attempts/<attemptID>' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/attempts/<attemptID>' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/attempts/<attemptID>"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/attempts/<attemptID>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/attempts/<attemptID>";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/attempts/<attemptID>");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### POST `/api/portal/v1/orgs/{clientID}/invoices/{invoiceID}/pay` {#depot.portalBilling.payConfirm}

**payConfirm takes the payment: the figures are quoted again (a change is 409 quote_changed), the attempt is written, then the processor is called.**

The member is recorded as who paid.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `invoiceID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalPayResult | Success. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:pay. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/invoices/{invoiceID}/pay/quote` {#depot.portalBilling.payQuote}

**payQuote prices the chosen method: the balance and, for a credit card where the practice allows it, the surcharge on its own line, before the payer confirms.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `invoiceID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalPayQuote | Success. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:pay. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/quote' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/quote' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/quote"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/quote", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/quote";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/quote");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/invoices/{invoiceID}/pay/session` {#depot.portalBilling.paySession}

**paySession starts paying an invoice: the processor's browser configuration, the methods offered, the surcharge rule, and the saved methods that can pay it.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `invoiceID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalPaySession | Success. |
| `400` | `application/problem+json`, ProblemDetails | The request body is not acceptable. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:pay. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/session' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/session' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/session"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/session", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/session";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pay/session");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/invoices/{invoiceID}/pdf` {#depot.portalBilling.invoicePdf}

**invoicePdf is the invoice document as issued, named by its number.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `invoiceID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/octet-stream`, string (binary) | Success. The body shape was not resolved; see x-outpost-unresolved. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pdf' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pdf' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pdf"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pdf", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pdf";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/invoices/<invoiceID>/pdf");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### GET `/api/portal/v1/orgs/{clientID}/payment-methods` {#depot.portalBilling.listPaymentMethods}

**listPaymentMethods is the org's payment methods on file and what can be added.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalMethodsOffer | Success. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:payment_methods:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### POST `/api/portal/v1/orgs/{clientID}/payment-methods` {#depot.portalBilling.addPaymentMethod}

**addPaymentMethod keeps the tokenized method on file.**

It authorizes no automatic payment: autopay is its own consented step.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalSetupResult | Success. |
| `201` | `application/json`, PortalSetupResult | Created. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:payment_methods:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/payment-methods/{methodID}` {#depot.portalBilling.removePaymentMethod}

**removePaymentMethod takes a method off file.**

If automatic payments charge it, they stop too, and the billing contact is told.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `methodID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `204` | None | Success, with no body. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:payment_methods:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods/<methodID>' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Delete `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods/<methodID>' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods/<methodID>"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods/<methodID>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods/<methodID>";

const response = await fetch(url, {
	method: "DELETE",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods/<methodID>");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### POST `/api/portal/v1/orgs/{clientID}/payment-methods/session` {#depot.portalBilling.paymentMethodSession}

**paymentMethodSession starts adding a payment method: the processor's hosted fields in set-up mode.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Request body

Required, sent as `application/json`.

The body is `any`.

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalPaySession | Success. |
| `400` | `application/problem+json`, ProblemDetails | The request body is not acceptable. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:payment_methods:manage. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session is a session cookie, so send it with every request.
curl -X POST 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods/session' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -b '__Host-portal_session=<portal-session>' \
  --data '"string"'
```

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

$headers = @{
    'X-Csrf-Token' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Content-Type' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$body = @'
"string"
'@

$response = Invoke-RestMethod `
    -Method Post `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods/session' `
    -Headers $headers `
    -ContentType 'application/json' `
    -Body $body

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

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

import requests

url = "https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods/session"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
    "Content-Type": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}
payload = "string"

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

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

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

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

	req, err := http.NewRequest(http.MethodPost, "https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods/session", bytes.NewReader(body))
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payment-methods/session";

const response = await fetch(url, {
	method: "POST",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"Accept": "application/json",
		"Content-Type": "application/json",
	},
	credentials: "include",
	body: JSON.stringify("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. __Host-portal_session 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 = """
"string"
""";

var request = new HttpRequestMessage(HttpMethod.Post, "https://depot.example.com/api/portal/v1/orgs/<clientID>/payment-methods/session");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-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/portal/v1/orgs/{clientID}/payments` {#depot.portalBilling.listPaymentHistory}

**listPaymentHistory is the client's payments, newest first: method, amount, status (a bank payment in flight is "processing", never paid), the decline reason of a failure, and whether a receipt can be downloaded.**

?status filters by row status; ?from and ?to bound the invoices' issue dates.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `page` | query | string | No |  |
| `pageSize` | query | string | No |  |
| `status` | query | string | No |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PagePortalPaymentRow | Success. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments?page=<page>&pageSize=<pageSize>&status=<status>' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payments?page=<page>&pageSize=<pageSize>&status=<status>' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments"
params = {
    "page": "<page>",
    "pageSize": "<pageSize>",
    "status": "<status>",
}
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments?page=<page>&pageSize=<pageSize>&status=<status>", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments");
url.searchParams.set("page", "<page>");
url.searchParams.set("pageSize", "<pageSize>");
url.searchParams.set("status", "<status>");

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments?page=<page>&pageSize=<pageSize>&status=<status>");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### GET `/api/portal/v1/orgs/{clientID}/payments/{paymentID}/receipt` {#depot.portalBilling.paymentReceipt}

**paymentReceipt is a PDF receipt for money received, named by the invoice number and the date.**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |
| `paymentID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/octet-stream`, string (binary) | Success. The body shape was not resolved; see x-outpost-unresolved. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:invoices:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments/<paymentID>/receipt' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/payments/<paymentID>/receipt' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments/<paymentID>/receipt"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments/<paymentID>/receipt", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments/<paymentID>/receipt";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/payments/<paymentID>/receipt");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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

### GET `/api/portal/v1/orgs/{clientID}/usage` {#depot.portalBilling.getUsage}

**getUsage is the billable inventory now (the devices and people an agreement can count) and the daily counts between ?from and ?to (default the last 31 days, at most 400).**

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `clientID` | path | string | Yes |  |

#### Responses

| Status | Body | Description |
| --- | --- | --- |
| `200` | `application/json`, PortalUsage | Success. |
| `400` | None | The request was refused. |
| `401` | `application/problem+json`, ProblemDetails | No signed-in identity. |
| `403` | `application/problem+json`, ProblemDetails | The identity does not hold portal:estimate:read. |
| `404` | `application/problem+json`, ProblemDetails | No resource matches that id. |

#### Request samples

```bash
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/usage' \
  -H 'X-Csrf-Token: <portal-csrf>' \
  -H 'Accept: application/json' \
  -b '__Host-portal_session=<portal-session>'
```

```powershell
# Sign in first. __Host-portal_session 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' = '<portal-csrf>'
    'Accept' = 'application/json'
    'Cookie' = '__Host-portal_session=<portal-session>'
}

$response = Invoke-RestMethod `
    -Method Get `
    -Uri 'https://depot.example.com/api/portal/v1/orgs/<clientID>/usage' `
    -Headers $headers

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

```python
# Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/usage"
headers = {
    "X-Csrf-Token": "<portal-csrf>",
    "Accept": "application/json",
}
cookies = {
    "__Host-portal_session": "<portal-session>",
}

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

```go
// Sign in first. __Host-portal_session 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/portal/v1/orgs/<clientID>/usage", nil)
	if err != nil {
		log.Fatal(err)
	}

	req.Header.Set("X-Csrf-Token", "<portal-csrf>")
	req.Header.Set("Accept", "application/json")
	req.AddCookie(&http.Cookie{Name: "__Host-portal_session", Value: "<portal-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. __Host-portal_session 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/portal/v1/orgs/<clientID>/usage";

const response = await fetch(url, {
	method: "GET",
	headers: {
		"X-Csrf-Token": "<portal-csrf>",
		"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. __Host-portal_session 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/portal/v1/orgs/<clientID>/usage");
request.Headers.TryAddWithoutValidation("X-Csrf-Token", "<portal-csrf>");
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("Cookie", "__Host-portal_session=<portal-session>");

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