> ## Documentation Index
> Fetch the complete documentation index at: https://swapnice.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Consent and account linking

> How a partner customer is linked to a Swapnice account, which purposes exist, and how revocation works.

Swapnice treats consent as a first-class object, not a banner click. Events are rejected and purpose-scoped reads fail closed unless a receipt still grants that purpose.

## Two identities, one link

```mermaid theme={null}
flowchart LR
    P["Partner customer<br/>you create and own"] --- L["Connection + consent receipt"]
    L --- S["Swapnice account<br/>the user owns"]
```

| Record             | Who creates it                               | What it is                        |
| ------------------ | -------------------------------------------- | --------------------------------- |
| Partner customer   | Your backend, via `POST /v1/customers`       | Your user, keyed by `external_id` |
| Connection session | Your app, via `POST /v1/connection-sessions` | Short-lived link attempt          |
| Swapnice account   | The user, inside Swapnice                    | Their Swapnice identity           |
| Consent receipt    | Recorded after they accept                   | Purpose grants bound to that link |

You never need the user's global Swapnice user id to start. The connection session is the Plaid-Link-style step that binds the two records after the user authorizes.

A Swapnice consumer account can later have wallet or bank linking **inside Swapnice**. That stack is not part of the Partner API and is never returned to partners.

## Connection session

```ts theme={null}
const session = await swapnice.connectionSessions.create({
  customer_id: customer.id,
  requested_purposes: ["personalization", "analytics"],
  redirect_uri: process.env.SWAPNICE_REDIRECT_URI!,
});
// Open session.connection_url. Poll GET /v1/connection-sessions/:id
```

Statuses: `created` → `authorized` → `completed`, or `expired` / `revoked`.

Sessions expire. Do not reuse an expired `session_id`. Create a new one.

## Consent receipt

After the user accepts, persist the receipt. This is what later events must cite.

```json theme={null}
{
  "receipt_id": "cr_44aa01",
  "customer_id": "cust_8f2a1c",
  "purposes": ["personalization", "analytics"],
  "status": "granted",
  "granted_at": "2026-08-26T16:02:12.000Z"
}
```

Include `terms_version` and `privacy_policy_version` so you can prove which policy the user saw.

Inspect current grants with `GET /v1/customers/:id/effective-consent`.

## Purposes in product language

| Purpose              | What the user is allowing                      | Typical partner use            | Default for a conservative app |
| -------------------- | ---------------------------------------------- | ------------------------------ | ------------------------------ |
| `account_linking`    | Connect this app account to Swapnice           | Complete the link              | Yes, during connect            |
| `personalization`    | Use my activity to improve *this* experience   | Home shelf, recommendations    | Yes                            |
| `analytics`          | Use my activity in aggregate product analytics | Funnels, quality dashboards    | Yes                            |
| `support`            | Let this app debug my own connection           | Break-fix                      | Yes                            |
| `event_enrichment`   | Attach catalog context to my events            | SKU / series metadata          | Optional                       |
| `profile_resolution` | Include this signal when facts are resolved    | Winner-vs-alternative profiles | Optional                       |
| `measurement`        | Use my activity to understand product outcomes | Optional product-outcome reads | Explicit opt-in                |

A user who granted `personalization` has not granted every other purpose. The same event can be stored for one purpose and unusable for another.

Pilot recommendation: request `personalization` and `analytics`. Add other purposes only when the product actually uses them.

## Revocation

```ts theme={null}
await swapnice.consentReceipts.revoke(receiptId, {
  purposes: ["personalization"],
  reason: "user_request",
});
```

`reason` is one of `user_request`, `partner_request`, `policy_change`, `account_disconnected`, `other`.

What happens immediately:

* New events for a revoked purpose return `consent_required`
* Profile reads that pass that `consent_purpose` return `403`
* The partner product must stop using that customer's intelligence for the revoked purpose

The API accepts revoke synchronously (`202` plus a `workflow_id`). Archive and workflow records update asynchronously. Poll `GET /v1/workflows/:id`.

You can revoke a subset of purposes or the entire receipt.

## Collection notice

Your connect UI should say, in language the user understands:

1. Which app is asking
2. Which purposes, in the table above — not raw scope strings
3. That they can disconnect later
4. That they can revoke a purpose without deleting the rest of the connection

Swapnice can provide sample consent copy during onboarding. Do not invent a purpose that is not in the documented API enum.

## What consent does not cover

Consent to the Partner API does not grant access to:

* Wallet, Dwolla, or Plaid credentials
* Government identification
* Precise home address
* Another partner's users or raw events
* The right to resell the consolidated Swapnice profile

See [data collection](/partners/data-collection) for what is transmitted after a grant.
