---
title: "Buy, stop or budget a metered add-on"
description: "Buys the add-on with a monthly budget (null for no ceiling) and what happens when it is spent, or stops it when enabled is false. Only the organisation holding the billing account may call this. Audited.\n\n### Quire permissions\n\nRequired capability: `billing/manage`.\n\nCredential scopes: `tenants:write`.\n\nThe acting subject must also be allowed to perform this action in the organisation."
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/llms.txt
> Use this file to discover all available pages before exploring further.

Path: Quire API › tenants

`POST /addons/actions/set`

Buys the add-on with a monthly budget (null for no ceiling) and what happens when it is spent, or stops it when enabled is false. Only the organisation holding the billing account may call this. Audited.

\### Quire permissions

Required capability: `billing/manage`.

Credential scopes: `tenants:write`.

The acting subject must also be allowed to perform this action in the organisation.

## Authentication

Requires one of the following:

- `apiKey`, http, header `Authorization`, scopes: `tenants:write`
- `oauth2`, oauth2, scopes: `tenants:write`

## Header parameters

- `setTenantAddon.header.Quire-Version` (string, optional) — The dated API revision to serve this request at. Omitted, the request is served at the revision the credential was issued against. The response echoes the revision actually applied.
  - format `date`
- `setTenantAddon.header.Idempotency-Key` (string, optional) — A caller-chosen key making this request safe to retry. A replay of a completed request returns the recorded response with `Idempotency-Replayed: true`. Reusing a key for a different request is refused.
  - maxLength 255

## Request body

_Required._

- `setTenantAddon.code` (string, required)
  - one of `"video_minutes"`, `"ai_tokens"`
- `setTenantAddon.enabled` (boolean, required)
- `setTenantAddon.budget_minor` (any of, required)
  - any of: `string`, `null`
- `setTenantAddon.on_exhaustion` (string, required)
  - one of `"block"`, `"warn"`; default `"block"`

## Example request

```json
{
  "code": "video_minutes",
  "enabled": true,
  "budget_minor": "string",
  "on_exhaustion": "block"
}
```

## Code samples

### cURL

```curl
curl --request POST \
  --url https://your-organisation.quirelms.com/api/v1/addons/actions/set \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "code": "video_minutes",
  "enabled": true,
  "budget_minor": "string",
  "on_exhaustion": "block"
}
'
```

### TypeScript

```typescript
const url = 'https://your-organisation.quirelms.com/api/v1/addons/actions/set';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json', Authorization: 'Bearer <token>'},
  body: JSON.stringify({
    code: 'video_minutes',
    enabled: true,
    budget_minor: 'string',
    on_exhaustion: 'block'
  })
};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error(err));
```

### Python

```python
import requests

url = "https://your-organisation.quirelms.com/api/v1/addons/actions/set"

payload = {
    "code": "video_minutes",
    "enabled": True,
    "budget_minor": "string",
    "on_exhaustion": "block"
}
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer <token>"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
```

## Responses

### 200

Success.

#### Example

```json
{
  "data": [
    {
      "code": "video_minutes",
      "label": "string",
      "unit_price_minor": "string",
      "unit_size": "string",
      "currency": "string",
      "held": true,
      "included_quantity": "string",
      "budget_minor": "string",
      "on_exhaustion": "block",
      "period_start": "2019-08-24T14:15:22Z",
      "spent_minor": "string",
      "reserved_minor": "string",
      "percent": -9007199254740991,
      "state": "unlimited"
    }
  ]
}
```

- `setTenantAddon.response.200.data` (array<object>, required)
  - `setTenantAddon.response.200.data.code` (string, required)
    - one of `"video_minutes"`, `"ai_tokens"`
  - `setTenantAddon.response.200.data.label` (string, required)
  - `setTenantAddon.response.200.data.unit_price_minor` (string, required)
  - `setTenantAddon.response.200.data.unit_size` (string, required)
  - `setTenantAddon.response.200.data.currency` (string, required)
  - `setTenantAddon.response.200.data.held` (boolean, required)
  - `setTenantAddon.response.200.data.included_quantity` (string | null, required)
  - `setTenantAddon.response.200.data.budget_minor` (string | null, required)
  - `setTenantAddon.response.200.data.on_exhaustion` (any of, required)
    - any of: `string`, `null`
  - `setTenantAddon.response.200.data.period_start` (string, required)
    - format `date-time`; pattern `^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z))$`
  - `setTenantAddon.response.200.data.spent_minor` (string, required)
  - `setTenantAddon.response.200.data.reserved_minor` (string, required)
  - `setTenantAddon.response.200.data.percent` (integer, required)
    - min -9007199254740991; max 9007199254740991
  - `setTenantAddon.response.200.data.state` (string, required)
    - one of `"unlimited"`, `"ok"`, `"warn"`, `"exhausted"`

### 401

The credential is missing, malformed, expired or revoked.

#### Example

```json
{
  "type": "http://example.com",
  "title": "string",
  "status": 0,
  "code": "string",
  "category": "validation",
  "detail": "string",
  "request_id": "string",
  "errors": [
    {
      "path": "string",
      "code": "string",
      "detail": "string"
    }
  ],
  "retry_after": 0,
  "docs_url": "http://example.com"
}
```

- `setTenantAddon.response.401.type` (string, required)
  - format `uri`
- `setTenantAddon.response.401.title` (string, required)
- `setTenantAddon.response.401.status` (integer, required)
- `setTenantAddon.response.401.code` (string, required)
- `setTenantAddon.response.401.category` (string, required)
  - one of `"validation"`, `"authentication"`, `"authorization"`, `"not_found"`, `"conflict"`, `"precondition"`, `"quota"`, `"rate_limit"`, `"upstream"`, `"internal"`
- `setTenantAddon.response.401.detail` (string, required)
- `setTenantAddon.response.401.request_id` (string, required)
- `setTenantAddon.response.401.docs_url` (string, required)
  - format `uri`
- `setTenantAddon.response.401.errors` (array<object>, optional)
  - `setTenantAddon.response.401.errors.path` (string, required) — RFC 6901 JSON Pointer into the request body.
  - `setTenantAddon.response.401.errors.code` (string, required)
  - `setTenantAddon.response.401.errors.detail` (string, required)
- `setTenantAddon.response.401.retry_after` (integer | null, optional)

### 403

The credential lacks the scope this operation requires, or the acting subject lacks the capability.

#### Example

```json
{
  "type": "http://example.com",
  "title": "string",
  "status": 0,
  "code": "string",
  "category": "validation",
  "detail": "string",
  "request_id": "string",
  "errors": [
    {
      "path": "string",
      "code": "string",
      "detail": "string"
    }
  ],
  "retry_after": 0,
  "docs_url": "http://example.com"
}
```

- `setTenantAddon.response.403.type` (string, required)
  - format `uri`
- `setTenantAddon.response.403.title` (string, required)
- `setTenantAddon.response.403.status` (integer, required)
- `setTenantAddon.response.403.code` (string, required)
- `setTenantAddon.response.403.category` (string, required)
  - one of `"validation"`, `"authentication"`, `"authorization"`, `"not_found"`, `"conflict"`, `"precondition"`, `"quota"`, `"rate_limit"`, `"upstream"`, `"internal"`
- `setTenantAddon.response.403.detail` (string, required)
- `setTenantAddon.response.403.request_id` (string, required)
- `setTenantAddon.response.403.docs_url` (string, required)
  - format `uri`
- `setTenantAddon.response.403.errors` (array<object>, optional)
  - `setTenantAddon.response.403.errors.path` (string, required) — RFC 6901 JSON Pointer into the request body.
  - `setTenantAddon.response.403.errors.code` (string, required)
  - `setTenantAddon.response.403.errors.detail` (string, required)
- `setTenantAddon.response.403.retry_after` (integer | null, optional)

### 409

The request conflicts with the current state, with a previous use of the same idempotency key, or with a concurrent write.

#### Example

```json
{
  "type": "http://example.com",
  "title": "string",
  "status": 0,
  "code": "string",
  "category": "validation",
  "detail": "string",
  "request_id": "string",
  "errors": [
    {
      "path": "string",
      "code": "string",
      "detail": "string"
    }
  ],
  "retry_after": 0,
  "docs_url": "http://example.com"
}
```

- `setTenantAddon.response.409.type` (string, required)
  - format `uri`
- `setTenantAddon.response.409.title` (string, required)
- `setTenantAddon.response.409.status` (integer, required)
- `setTenantAddon.response.409.code` (string, required)
- `setTenantAddon.response.409.category` (string, required)
  - one of `"validation"`, `"authentication"`, `"authorization"`, `"not_found"`, `"conflict"`, `"precondition"`, `"quota"`, `"rate_limit"`, `"upstream"`, `"internal"`
- `setTenantAddon.response.409.detail` (string, required)
- `setTenantAddon.response.409.request_id` (string, required)
- `setTenantAddon.response.409.docs_url` (string, required)
  - format `uri`
- `setTenantAddon.response.409.errors` (array<object>, optional)
  - `setTenantAddon.response.409.errors.path` (string, required) — RFC 6901 JSON Pointer into the request body.
  - `setTenantAddon.response.409.errors.code` (string, required)
  - `setTenantAddon.response.409.errors.detail` (string, required)
- `setTenantAddon.response.409.retry_after` (integer | null, optional)

### 422

The request was understood and its content is not valid. `errors` names each field.

#### Example

```json
{
  "type": "http://example.com",
  "title": "string",
  "status": 0,
  "code": "string",
  "category": "validation",
  "detail": "string",
  "request_id": "string",
  "errors": [
    {
      "path": "string",
      "code": "string",
      "detail": "string"
    }
  ],
  "retry_after": 0,
  "docs_url": "http://example.com"
}
```

- `setTenantAddon.response.422.type` (string, required)
  - format `uri`
- `setTenantAddon.response.422.title` (string, required)
- `setTenantAddon.response.422.status` (integer, required)
- `setTenantAddon.response.422.code` (string, required)
- `setTenantAddon.response.422.category` (string, required)
  - one of `"validation"`, `"authentication"`, `"authorization"`, `"not_found"`, `"conflict"`, `"precondition"`, `"quota"`, `"rate_limit"`, `"upstream"`, `"internal"`
- `setTenantAddon.response.422.detail` (string, required)
- `setTenantAddon.response.422.request_id` (string, required)
- `setTenantAddon.response.422.docs_url` (string, required)
  - format `uri`
- `setTenantAddon.response.422.errors` (array<object>, optional)
  - `setTenantAddon.response.422.errors.path` (string, required) — RFC 6901 JSON Pointer into the request body.
  - `setTenantAddon.response.422.errors.code` (string, required)
  - `setTenantAddon.response.422.errors.detail` (string, required)
- `setTenantAddon.response.422.retry_after` (integer | null, optional)

### 429

A rate limit or a concurrency cap was reached. `Retry-After` says when to try again.

#### Example

```json
{
  "type": "http://example.com",
  "title": "string",
  "status": 0,
  "code": "string",
  "category": "validation",
  "detail": "string",
  "request_id": "string",
  "errors": [
    {
      "path": "string",
      "code": "string",
      "detail": "string"
    }
  ],
  "retry_after": 0,
  "docs_url": "http://example.com"
}
```

- `setTenantAddon.response.429.type` (string, required)
  - format `uri`
- `setTenantAddon.response.429.title` (string, required)
- `setTenantAddon.response.429.status` (integer, required)
- `setTenantAddon.response.429.code` (string, required)
- `setTenantAddon.response.429.category` (string, required)
  - one of `"validation"`, `"authentication"`, `"authorization"`, `"not_found"`, `"conflict"`, `"precondition"`, `"quota"`, `"rate_limit"`, `"upstream"`, `"internal"`
- `setTenantAddon.response.429.detail` (string, required)
- `setTenantAddon.response.429.request_id` (string, required)
- `setTenantAddon.response.429.docs_url` (string, required)
  - format `uri`
- `setTenantAddon.response.429.errors` (array<object>, optional)
  - `setTenantAddon.response.429.errors.path` (string, required) — RFC 6901 JSON Pointer into the request body.
  - `setTenantAddon.response.429.errors.code` (string, required)
  - `setTenantAddon.response.429.errors.detail` (string, required)
- `setTenantAddon.response.429.retry_after` (integer | null, optional)

### 500

Something failed at our end. Quote `request_id` when reporting it.

#### Example

```json
{
  "type": "http://example.com",
  "title": "string",
  "status": 0,
  "code": "string",
  "category": "validation",
  "detail": "string",
  "request_id": "string",
  "errors": [
    {
      "path": "string",
      "code": "string",
      "detail": "string"
    }
  ],
  "retry_after": 0,
  "docs_url": "http://example.com"
}
```

- `setTenantAddon.response.500.type` (string, required)
  - format `uri`
- `setTenantAddon.response.500.title` (string, required)
- `setTenantAddon.response.500.status` (integer, required)
- `setTenantAddon.response.500.code` (string, required)
- `setTenantAddon.response.500.category` (string, required)
  - one of `"validation"`, `"authentication"`, `"authorization"`, `"not_found"`, `"conflict"`, `"precondition"`, `"quota"`, `"rate_limit"`, `"upstream"`, `"internal"`
- `setTenantAddon.response.500.detail` (string, required)
- `setTenantAddon.response.500.request_id` (string, required)
- `setTenantAddon.response.500.docs_url` (string, required)
  - format `uri`
- `setTenantAddon.response.500.errors` (array<object>, optional)
  - `setTenantAddon.response.500.errors.path` (string, required) — RFC 6901 JSON Pointer into the request body.
  - `setTenantAddon.response.500.errors.code` (string, required)
  - `setTenantAddon.response.500.errors.detail` (string, required)
- `setTenantAddon.response.500.retry_after` (integer | null, optional)


Source: https://docs.quirelms.com/api/tenants/setTenantAddon/index.md
