# List a contact's consent changes

Source: https://kirora.io/docs/api/reference/listContactConsentChanges

`GET /v1/contacts/{id}/consent-changes`

Requires an API key with the `contacts:read` scope. [About keys and scopes](https://kirora.io/docs/api/reference/authentication)

Works with integration, agent and partner keys.

Lists every grant and withdrawal of consent for this contact, per channel or per topic, newest first: where it came from (dashboard, API, import, form, unsubscribe link), who made it, and the IP it came from. Append-only. It tells apart a contact never asked from one who withdrew, which both read as `false` in `consent`.

## Path parameters

- `id` (string, required, 1–255 characters) — Kirora's id for the contact, your own with `idType=externalId`, or its email address with `idType=email`, URL-encoded (`@` as `%40`).

## Query parameters

- `limit` (integer, optional, min > 0, max 100, default `20`) — How many items a page holds: 1 to 100, 20 by default.
- `cursor` (string, optional, 1–2,048 characters) — Where the page starts: the previous page's `meta.pagination.nextCursor`. Omit it for the first page, and send the same filters with it.
- `idType` (string, optional, one of `id`, `externalId`, `email`, default `"id"`) — What `{id}` is: Kirora's id for the contact (`id`, the default), your own (`externalId`, as set with identify) or its email address (`email`, matched trimmed and without case).

## Header parameters

- `Kirora-Workspace` (string, optional, pattern `^[A-Za-z0-9._:-]{1,128}$`) — Partner keys only: the child workspace to act in, by the external id the partner gave it. Without it, a partner key acts in its own workspace. Any other key that sends it is refused with 403, and an unknown workspace is 404.

## Returns

Returns `200 OK` with a list of `ConsentChange` objects in `data`, and the next page's cursor in `meta` (see [pagination](https://kirora.io/docs/api/reference/pagination)). Otherwise returns an error; [Errors](https://kirora.io/docs/api/reference/errors) lists every code and which to retry.

- `id` (string, required, uuid)
- `channel` (string | null, required, one of `email`, `push`) — The channel consented to. With `topicId` null: consent to the channel; with both set: a per-channel topic choice.
- `topicId` (string | null, required, uuid) — The topic consented to, if any.
- `granted` (boolean, required) — True for a grant, false for a withdrawal.
- `source` (string, required, one of `dashboard`, `api`, `import`, `form`, `unsubscribe`, `system`, `sdk`)
- `sourceId` (string | null, required, uuid) — The form, when `source` is `form`.
- `actorType` (string, required, one of `user`, `api_key`, `contact`, `system`)
- `actorId` (string | null, required, uuid) — Member or API key id; null for the contact themselves and the system.
- `ip` (string | null, required) — IP address the change came from.
- `reason` (string | null, required) — Why it changed, when one was given.
- `basis` (string | null, required, one of `opted_in`, `existing_customer`) — How the person agreed, as the workspace stated it; null when nobody did.
- `evidence` (string | null, required) — Where and how they agreed, as stated.
- `consentedAt` (string | null, required, date-time) — When they agreed, as stated; null means at `occurredAt`.
- `wording` (string | null, required) — The words they agreed to, as stated.
- `occurredAt` (string, required, date-time)
- `actorName` (string | null, required) — The member's or API key's name now; null for the contact, the system, or one deleted.

## Example

```bash
curl https://api.kirora.io/v1/contacts/018f2a4e-6b1c-7d3e-9f40-37386ae05a6b/consent-changes \
  -H "Authorization: Bearer $KIRORA_API_KEY"
```

Response `200`:

```json
{
  "data": [
    {
      "id": "0192f3a5-6081-7da2-9f76-9e1f2a3b4c5d",
      "channel": "email",
      "topicId": null,
      "granted": false,
      "source": "unsubscribe",
      "sourceId": null,
      "actorType": "contact",
      "actorId": null,
      "ip": "203.0.113.7",
      "reason": null,
      "basis": null,
      "evidence": null,
      "consentedAt": null,
      "wording": null,
      "occurredAt": "2026-10-05T12:00:00.000Z",
      "actorName": null
    },
    {
      "id": "0192f3a5-5f70-7c91-8e65-8d0e1f2a3b4c",
      "channel": "email",
      "topicId": null,
      "granted": true,
      "source": "api",
      "sourceId": null,
      "actorType": "api_key",
      "actorId": null,
      "ip": null,
      "reason": null,
      "basis": "opted_in",
      "evidence": "Checkout opt-in on shop.example.com",
      "consentedAt": null,
      "wording": "Send me offers and news by email",
      "occurredAt": "2026-09-12T08:15:00.000Z",
      "actorName": "Shop backend"
    }
  ],
  "meta": {
    "pagination": {
      "limit": 20,
      "nextCursor": null
    }
  }
}
```
