# Consent purposes

> Part of the Cowliss docs. Markdown for every page: append `index.md` to its URL. Site map: /llms.txt

<!-- GENERATED by apps/docs/scripts/generate-api-reference.ts. Do not edit. Regenerate with `pnpm -F cowliss-docs generate`. -->

Every profile's `consent` map answers its own app's purposes, and every switch that offers one is labelled from here. Two are fixed for every app, the roots `marketing` and `transactional`, and turning a root off turns off every name under it. The rest are the names the app's code declares under `purposes` in [`cow.json`](/guides/repo/), such as `marketing.newsletter` or `transactional.weeklyReport`, so two apps may each declare a key under their own label. Bare `transactional` mail, a receipt or a password reset, is not a purpose anyone can withhold: the `transactional` root turns off only the names under it, never that mail. Purposes are code, like automations and templates, so this resource is read-only: a push is what adds or relabels one, and nothing ever deletes one, because profiles already hold answers against it.

## GET /v1/consent-purposes

List the consent purposes an app declares

**Auth:** Session token.

**Query parameters**

```json
{
  "cursor": "string",
  "limit": 50,
  "appId": "string"
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `cursor` | string | no |  |
| `limit` | integer | no | default `50`; 1 ≤ x ≤ 100 |
| `appId` | string | yes | length 0–200 |

**Response 200**

```json
{
  "data": [
    {
      "key": "string",
      "label": "string",
      "description": "string"
    }
  ],
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object[] | yes |  |
| `data[]` | object | yes |  |
| `data[].key` | string | yes | length 0–64 |
| `data[].label` | string | yes |  |
| `data[].description` | string \| null | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## POST /v1/consent-purposes

Declare a consent purpose on an app

Admin only. `key` is a root, a dot and a camelCase name, like `marketing.newsletter` or `transactional.digest`: a marketing name starts off for every profile and a transactional one starts on. Refused when the app already declares the key; change it with `consent.purposes.update`.

**Auth:** Session token.

**Request body**

```json
{
  "data": {
    "key": "string",
    "label": "string",
    "description": "string",
    "appId": "string"
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.key` | string | yes | length 0–64 |
| `data.label` | string | yes | length 1–50 |
| `data.description` | string | no | length 1–200 |
| `data.appId` | string | yes | length 0–200 |

**Response 201**

```json
{
  "data": {
    "key": "string",
    "label": "string",
    "description": "string"
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.key` | string | yes | length 0–64 |
| `data.label` | string | yes |  |
| `data.description` | string \| null | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## PATCH /v1/consent-purposes/{key}

Relabel a consent purpose

Admin only. Changes what the switch is labelled with and the sentence under it; the key, and every answer against it, stay. `description: null` removes the sentence.

**Auth:** Session token.

**Query parameters**

```json
{
  "appId": "string"
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `appId` | string | yes |  |

**Request body**

```json
{
  "data": {
    "label": "string",
    "description": "string"
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.label` | string | no | length 1–50 |
| `data.description` | string \| null | no |  |

**Response 200**

```json
{
  "data": {
    "key": "string",
    "label": "string",
    "description": "string"
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.key` | string | yes | length 0–64 |
| `data.label` | string | yes |  |
| `data.description` | string \| null | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).

## DELETE /v1/consent-purposes/{key}

Delete a consent purpose and every answer to it

Admin only. Refused while a template is sent under the purpose, a broadcast was drafted or sent to it, or people are subscribed to it as a list, naming each. Otherwise the purpose goes, and so does every profile's answer to it; `answersRemoved` says how many.

**Auth:** Session token.

**Query parameters**

```json
{
  "appId": "string"
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `appId` | string | yes |  |

**Response 200**

```json
{
  "data": {
    "deleted": true,
    "answersRemoved": 42
  },
  "meta": {
    "requestId": "string",
    "nextCursor": "string",
    "hasMore": true,
    "warnings": [
      {
        "code": "string",
        "message": "string",
        "suggestion": "string"
      }
    ]
  }
}
```

| Field | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | object | yes |  |
| `data.deleted` | true | yes |  |
| `data.answersRemoved` | integer | yes |  |
| `meta` | object | no |  |
| `meta.requestId` | string | no |  |
| `meta.nextCursor` | string \| null | no |  |
| `meta.hasMore` | boolean | no |  |
| `meta.warnings` | object[] | no |  |
| `meta.warnings[]` | object | no |  |
| `meta.warnings[].code` | string | yes |  |
| `meta.warnings[].message` | string | yes |  |
| `meta.warnings[].suggestion` | string | no |  |

Errors follow the [shared envelope and code table](/api-conventions/#errors).
