# Suppressions

A suppression is an address your account will not send to. Sending to one is rejected before anything leaves, which protects your sending reputation and keeps you from mailing people who bounced or complained.

## How addresses get suppressed

| Reason | Added when |
| --- | --- |
| `bounce` | A hard bounce: the mailbox does not exist or the domain rejects mail permanently. Soft bounces are not suppressed. |
| `complaint` | The recipient marked your email as spam. |
| `manual` | You added it through the API or the dashboard, for example after an unsubscribe. |

Suppressions are per account. Test-mode sends never add suppressions, and simulator addresses on the sandbox domain are never suppressed in either mode, so `bounced@` and `complained@` can be used as often as you like.

An address erased with `POST /v1/recipients/erase` stays on the list as a `manual` entry with `email_address: null`. It still blocks sends.

Listing, adding and removing need the `suppressions:manage` scope. Adding and removing also need a live key: a test key gets `403 forbidden`.

## What happens on send

A request with a suppressed recipient anywhere in `to`, `cc` or `bcc` returns `422 recipient_suppressed` and nothing is sent, not even to the other recipients.

```json
{
  "error": {
    "code": "recipient_suppressed",
    "message": "Recipient suppressed: old@example.com. Previous bounce or complaint. Remove the address from suppressions to send again.",
    "details": { "suppressed": ["old@example.com"] }
  }
}
```

## List

**curl**

```bash
curl "https://api.avelto.dev/v1/suppressions?limit=100" \
  -H "Authorization: Bearer av_live_..."
```

**Node**

```ts
import { Avelto } from "@avelto/sdk";

const avelto = new Avelto(process.env.AVELTO_API_KEY!);

const page = await avelto.suppressions.list({ limit: 100 });
for (const s of page.data) console.log(s.email_address ?? "(erased)", s.reason); // "bounce" | "complaint" | "manual"
```

**Python**

```python
import os, requests

r = requests.get(
    "https://api.avelto.dev/v1/suppressions?limit=100",
    headers={"Authorization": f"Bearer {os.environ['AVELTO_API_KEY']}"},
)
r.raise_for_status()
print(r.json())
```

**Go**

```go
package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.avelto.dev/v1/suppressions?limit=100", nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AVELTO_API_KEY"))

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	fmt.Println(res.Status)
}
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://api.avelto.dev/v1/suppressions?limit=100")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV["AVELTO_API_KEY"]}"

res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
puts res.code, res.body
```

**PHP**

```php
<?php

require "vendor/autoload.php";

$client = new GuzzleHttp\Client(["base_uri" => "https://api.avelto.dev"]);

$res = $client->request("GET", "/v1/suppressions?limit=100", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("AVELTO_API_KEY"),
    ],
]);

echo $res->getStatusCode(), "\n", $res->getBody();
```

**C#**

```csharp
using System.Net.Http.Headers;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AVELTO_API_KEY"));

var res = await client.GetAsync("https://api.avelto.dev/v1/suppressions?limit=100");
Console.WriteLine((int)res.StatusCode);
Console.WriteLine(await res.Content.ReadAsStringAsync());
```

```json
{
  "data": [
    {
      "id": "3e5f7a9b-1c2d-4e6f-8a0b-2c4d6e8f0a1b",
      "email_address": "old@example.com",
      "reason": "bounce",
      "created_at": "2026-09-10T08:30:00.000Z"
    }
  ],
  "next_cursor": null
}
```

Cursor-paginated like the email and delivery lists: pass `next_cursor` back as `cursor`.

## Add

Add an address when someone unsubscribes or asks not to be contacted. Addresses are stored lower-cased. Returns `201` with the suppression object. Adding one that is already suppressed returns `409 conflict`.

**curl**

```bash
curl -X POST https://api.avelto.dev/v1/suppressions \
  -H "Authorization: Bearer av_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email_address": "unsubscribed@example.com"
  }'
```

**Node**

```ts
await avelto.suppressions.create({ email_address: "unsubscribed@example.com" });
```

**Python**

```python
import os, requests

r = requests.post(
    "https://api.avelto.dev/v1/suppressions",
    headers={"Authorization": f"Bearer {os.environ['AVELTO_API_KEY']}"},
    json={
      "email_address": "unsubscribed@example.com"
    },
)
r.raise_for_status()
print(r.json())
```

**Go**

```go
package main

import (
	"bytes"
	"fmt"
	"net/http"
	"os"
)

func main() {
	body := []byte(`{"email_address":"unsubscribed@example.com"}`)
	req, _ := http.NewRequest("POST", "https://api.avelto.dev/v1/suppressions", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AVELTO_API_KEY"))

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	fmt.Println(res.Status)
}
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://api.avelto.dev/v1/suppressions")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV["AVELTO_API_KEY"]}"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
  email_address: "unsubscribed@example.com"
})

res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
puts res.code, res.body
```

**PHP**

```php
<?php

require "vendor/autoload.php";

$client = new GuzzleHttp\Client(["base_uri" => "https://api.avelto.dev"]);

$res = $client->request("POST", "/v1/suppressions", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("AVELTO_API_KEY"),
    ],
    "json" => [
        "email_address" => "unsubscribed@example.com"
    ],
]);

echo $res->getStatusCode(), "\n", $res->getBody();
```

**C#**

```csharp
using System.Net.Http.Headers;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AVELTO_API_KEY"));

var res = await client.PostAsJsonAsync("https://api.avelto.dev/v1/suppressions", new
{
    email_address = "unsubscribed@example.com"
});
Console.WriteLine((int)res.StatusCode);
Console.WriteLine(await res.Content.ReadAsStringAsync());
```

## Remove

Remove an address to send to it again, for instance after a bounce caused by a full mailbox that has since been cleared. URL-encode the address (`@` becomes `%40`).

**curl**

```bash
curl -X DELETE https://api.avelto.dev/v1/suppressions/unsubscribed%40example.com \
  -H "Authorization: Bearer av_live_..."
```

**Node**

```ts
await avelto.suppressions.delete("unsubscribed@example.com");
```

**Python**

```python
import os, requests

r = requests.delete(
    "https://api.avelto.dev/v1/suppressions/unsubscribed%40example.com",
    headers={"Authorization": f"Bearer {os.environ['AVELTO_API_KEY']}"},
)
r.raise_for_status()
```

**Go**

```go
package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("DELETE", "https://api.avelto.dev/v1/suppressions/unsubscribed%40example.com", nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AVELTO_API_KEY"))

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	fmt.Println(res.Status)
}
```

**Ruby**

```ruby
require "net/http"
require "json"

uri = URI("https://api.avelto.dev/v1/suppressions/unsubscribed%40example.com")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer #{ENV["AVELTO_API_KEY"]}"

res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
puts res.code, res.body
```

**PHP**

```php
<?php

require "vendor/autoload.php";

$client = new GuzzleHttp\Client(["base_uri" => "https://api.avelto.dev"]);

$res = $client->request("DELETE", "/v1/suppressions/unsubscribed%40example.com", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("AVELTO_API_KEY"),
    ],
]);

echo $res->getStatusCode(), "\n", $res->getBody();
```

**C#**

```csharp
using System.Net.Http.Headers;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AVELTO_API_KEY"));

var res = await client.DeleteAsync("https://api.avelto.dev/v1/suppressions/unsubscribed%40example.com");
Console.WriteLine((int)res.StatusCode);
Console.WriteLine(await res.Content.ReadAsStringAsync());
```

Returns `204`; an address that is not on the list is `404 not_found`. Removing a bounced or complained address does not prevent it from being suppressed again the next time it bounces or complains.

---

Rendered page: https://avelto.dev/docs/suppressions
