URL: /code/apps/mailboxes/unsubscribe · Id: `page:/code/apps/mailboxes/unsubscribe`

# Contact.unsubscribe

> Stops commercial email to a person in one click, for one topic or all, at once.

POST https://api.sb/&#123;your-domain&#125;/contacts/&#123;contact&#125;/unsubscribe

Stops commercial email to a person in one click, for one topic or all, at once.

- **Action class:** None
- **Approval:** Runs on its own
- **Emits:** `Contact.unsubscribed`
- **Venues:** Gmail, Outlook
- **Price:** Free

```text title="Run it · Ask"
Rosa asked for no more newsletters, only replies to her own emails.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Contact.unsubscribe({ contact: 'https://api.sb/leaseline.app/contacts/contact_Ro2aL8vZ5x', topic: 'newsletter' })
```

```json title="Run it · Response"
{
  "api": "https://api.sb/leaseline.app",
  "contact": {
    "$id": "https://api.sb/leaseline.app/contacts/contact_Ro2aL8vZ5x",
    "$type": "Contact",
    "unsubscribed": [
      "newsletter"
    ]
  },
  "action": {
    "operation": "Contact.unsubscribe",
    "mode": "try",
    "status": "done"
  }
}
```

Its Inputs, Outputs, Effects and guards quote [the hub’s `Contact.unsubscribe`](https://startupbuilder.dev/business/nouns/contact/unsubscribe); this page adds what each Venue honors, its limits and a worked call. It is one of the [Verbs](https://startupbuilder.dev/business/nouns/verb) of [Mailboxes](/code/apps/mailboxes#verbs).

## Inputs
| Name | Type | Meaning |
| --- | --- | --- |
| `contact` | `Contact` | Required. The Contact. |
| `topic` | `string` | One topic; omitted, every commercial topic. Optional. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `contact` | `Contact` | Never null: the Contact, unsubscribed. |

## Effects
**Action class**

None: no approval rule names it, so it runs within its seat’s Permissions and Limits

**Modes**

- `try`: Runs every check and returns the record as it would be; nothing is stored.
- `send`: Returns a pending Action at once; `Contact.unsubscribed` fires when it is done.
- `do`: Waits for the Action to finish and returns the record.

**Idempotency**

Only with a key: the same `Idempotency-Key` and input return the first Action, and the same key with other input returns `409`.

**Saved or shared**

Writes a Suppression when no topic is named. Never asks for sign-in or confirmation, and never waits.

**Emits**

`Contact.unsubscribed` with `{ contact }`

**Performed by**

Whichever seat calls it

**Price**

Free on your own account: the platform charges nothing for the call.

## On each Venue
| Venue | Formats |
| --- | --- |
| [Gmail](/code/integrations/gmail#limits) | None. |
| [Outlook](/code/integrations/outlook#limits) | None. |

Each Venue’s caps, review and rate limits are on its integration page, linked from its row. A field marked Not sent is one that Venue has no place for; the call never fails on it.

## What try does
| Account | What try does |
| --- | --- |
| [Gmail](/code/integrations/gmail#try) | Renders the exact email, headers included, and returns it with the time it would send; nothing reaches Gmail and no draft is saved. |
| [Outlook](/code/integrations/outlook#try) | Renders the exact email or invite and returns it with the time it would go; nothing reaches Outlook and no draft or event is saved. |

## Refuses when
The hub sets no guard of its own on this call: it refuses only on the shared checks every write meets (a missing record, a Permission the seat lacks).

Through a connected account it can also meet [the shared account refusals](/code/refusals#accounts).

## When it waits
Returns in one response; nothing waits.

## Example
In `send` mode the call returns its pending Action at once, and `Contact.unsubscribed` delivers the result.

**Ask**

```text
Rosa asked for no more newsletters, only replies to her own emails.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/contacts/contact_Ro2aL8vZ5x/unsubscribe?mode=send" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "topic": "newsletter"
}'
```

**CLI**

```bash
npx startup-builder do Contact.unsubscribe --send --data '{
  "topic": "newsletter"
}'
```

`--send` returns the pending Action at once.

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "send",
    "code": "return await $.Contact.unsubscribe({ topic: 'newsletter' })"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const result = await $.send.Contact.unsubscribe({
  contact: 'https://api.sb/leaseline.app/contacts/contact_Ro2aL8vZ5x',
  topic: 'newsletter',
})
```


```json title="Response, in send mode"
{
  "mode": "send",
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/action_Sd3kQ9vT2w",
    "operation": "Contact.unsubscribe",
    "status": "pending",
    "reason": "running"
  }
}
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

## Used by
- **Integrations:** [Gmail](/code/integrations/gmail) · [Outlook](/code/integrations/outlook)

## Related
[`Message.broadcast`](/code/apps/mailboxes/broadcast) · [`Message.schedule`](/code/apps/mailboxes/schedule) · [`Mailbox.warm`](/code/apps/mailboxes/warm) · [`Mailbox.rest`](/code/apps/mailboxes/rest) · [`Domain.authenticate`](/code/apps/mailboxes/authenticate) · [`Mailbox.retire`](/code/apps/mailboxes/retire) · [`Contact.suppress`](/code/apps/mailboxes/suppress) · [`Consent.withdraw`](/code/apps/mailboxes/withdraw)