# Managing unsubscribed contacts

> How a contact becomes unsubscribed and why campaigns skip them.

Every contact carries an **`unsubscribed`** boolean. When it is `true`, the contact has opted out: **campaigns skip them entirely** and they receive no further campaign email to that audience.

Honoring opt-outs is not optional — it keeps your sending compliant and protects your domain reputation.

## Why it matters

Keeping your list up to date as people unsubscribe protects your sender reputation. Specifically, honoring opt-outs:

- reduces the likelihood of your emails being marked as spam, and
- improves deliverability for every other marketing and transactional email you send.

## Subscription statuses

A contact’s status reflects the `unsubscribed` flag:

| Status | Meaning |
| --- | --- |
| **Subscribed** | `unsubscribed: false` — the contact still receives campaigns to this audience. |
| **Unsubscribed** | `unsubscribed: true` — the contact has opted out and is skipped by every campaign. |

The audience-wide `unsubscribed` flag is the **global** subscription state. It is distinct from per-[topic](https://www.mailblastr.com/docs/topics/overview) subscriptions: a contact can stay subscribed to the audience while opting out of an individual topic. The global flag always wins — a globally unsubscribed contact is never emailed even if they are opted in to a topic.

## How a contact becomes unsubscribed

- **Campaign unsubscribe link** — every campaign includes a per-contact unsubscribe link. When a recipient clicks it, that contact is flipped to `unsubscribed: true`.
- **Manual toggle** — you can set `unsubscribed` yourself when [creating](https://www.mailblastr.com/docs/api/contacts-create) a contact or by [updating](https://www.mailblastr.com/docs/api/contacts-update) one with `PATCH`.

## Unsubscribing a contact via the API

Patch the contact (by id or email) with `unsubscribed: true`. Patches are partial, so this changes only the subscribe state and leaves the name fields untouched.

**Node.js**

```js
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr('mb_xxxxxxxxx');

const { data, error } = await mb.contacts.update({
  audienceId: 'AUDIENCE_ID',
  id: 'steve@example.com',
  "unsubscribed": true
});
console.log({ data, error });
```

**Ruby**

```ruby
require 'net/http'
require 'uri'

uri = URI('https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Patch.new(uri)
req['Authorization'] = 'Bearer mb_xxxxxxxxx'
req['Content-Type'] = 'application/json'
req.body = <<~JSON
{ "unsubscribed": true }
JSON
res = http.request(req)
puts res.body
```

**PHP**

```php
<?php
$ch = curl_init('https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer mb_xxxxxxxxx',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => <<<'JSON'
{ "unsubscribed": true }
JSON,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
```

**Python**

```python
import requests

res = requests.patch(
    "https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com",
    headers={"Authorization": "Bearer mb_xxxxxxxxx", "Content-Type": "application/json"},
    json={ "unsubscribed": true },
)
print(res.json())
```

**Go**

```go
package main

import (
    "fmt"
    "io"
    "net/http"
    "strings"
)

func main() {
    req, _ := http.NewRequest("PATCH", "https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com", strings.NewReader(`{ "unsubscribed": true }`))
    req.Header.Set("Authorization", "Bearer mb_xxxxxxxxx")
    req.Header.Set("Content-Type", "application/json")
    res, _ := http.DefaultClient.Do(req)
    defer res.Body.Close()
    out, _ := io.ReadAll(res.Body)
    fmt.Println(string(out))
}
```

**Rust**

```rust
use reqwest::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let res = Client::new()
        .patch("https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com")
        .header("Authorization", "Bearer mb_xxxxxxxxx")
        .header("Content-Type", "application/json")
        .body(r#"{ "unsubscribed": true }"#)
        .send()
        .await?;
    println!("{}", res.text().await?);
    Ok(())
}
```

**Java**

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com"))
    .header("Authorization", "Bearer mb_xxxxxxxxx")
    .header("Content-Type", "application/json")
    .method("PATCH", HttpRequest.BodyPublishers.ofString("""
{ "unsubscribed": true }
"""))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
```

**.NET**

```csharp
using System.Net.Http;
using System.Text;

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Patch, "https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com");
request.Headers.Add("Authorization", "Bearer mb_xxxxxxxxx");
request.Content = new StringContent(@"{ ""unsubscribed"": true }", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());
```

**cURL**

```bash
curl -X PATCH 'https://api.mailblastr.com/audiences/AUDIENCE_ID/contacts/steve@example.com' \
  -H 'Authorization: Bearer mb_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{ "unsubscribed": true }'
```

To re-subscribe a contact (for example, if they opted back in), patch the same contact with `unsubscribed: false`.

> **Note:** Unsubscribed contacts stay on the audience — they are not deleted — so your list size and history are preserved. They are simply excluded from every campaign until re-subscribed.
