# Get a spam sender by ID

> GET /v1/spam/senders/{id} · Core resources API

Source: https://developer.kustomer.com/api/core-resources/get-a-spam-sender-by-id

```http
GET https://{orgname}.api.kustomerapp.com/v1/spam/senders/{id}
Authorization: Bearer <API key>
```

## Authorization

Requires any one of these roles: `org.permission.spam.read`; or legacy `org.admin.spam.read`. The API key or user must hold at least one of them. Legacy roles predate permission sets and still work.

Retrieves a spam sender based on the unique sender ID. Deleted senders are still returned, with `deleted` set to `true`.

## About Spam senders

Spam senders are your org's allow and block lists for inbound email. Each entry is one email address marked `whitelist` (never treated as spam) or `blacklist` (always treated as spam), and it overrides Kustomer's automatic spam detection for that sender.

Kustomer checks inbound email for spam automatically. Detection isn't perfect, so each org keeps a list of senders whose mail should always or never be treated as spam. Admins edit the list in the admin settings or through this API, and spam detection can add entries itself, in which case `automated` is `true` and `createdBy` is `null`.

#### Key fields

| Field | Meaning |
| --- | --- |
| `sender` | The email address. Kustomer stores it lowercased and trimmed, the same way it compares inbound mail. |
| `channel` | Always `email`. |
| `list` | `whitelist` (never spam) or `blacklist` (always spam). |
| `automated` | `true` if spam detection added the entry rather than a person. |
| `deleted`, `deletedAt`, `deletedBy` | Set when the entry has been removed. |

When an entry is created, Kustomer links it to the customer with that email address, if there is one, so you can list a customer's entries with the `customer` filter.

#### One entry per address

There's only ever one entry per address, and removing it is a soft delete. Deleted entries drop out of lists unless you pass `includeDeleted=true`, but `GET /v1/spam/senders/{id}` still returns them with `deleted: true`. Because of this, the usual way to add or change a sender is the upsert, `PUT /v1/spam/senders` with `sender`, `channel` and `list`. It creates the entry with `201`, or updates the existing one with `200`, switching its list and restoring it if it was deleted.

#### Which endpoint to use

| To | Call |
| --- | --- |
| Allow or block a sender by email address | [`PUT /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/upsert-spam-sender) |
| Review the list, or one customer's entries | [`GET /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/get-spam-senders), with `customer` or `includeDeleted` |
| Read one entry | [`GET /v1/spam/senders/{id}`](https://developer.kustomer.com/api/core-resources/get-a-spam-sender-by-id) |
| Move an entry to the other list, or delete or restore it | [`PUT /v1/spam/senders/{id}`](https://developer.kustomer.com/api/core-resources/update-a-spam-sender-by-id), with `list` and/or `deleted` |
| Remove an entry | [`DELETE /v1/spam/senders/{id}`](https://developer.kustomer.com/api/core-resources/delete-a-spam-sender) |

A `PUT /v1/spam/senders/{id}` that doesn't change `list` or `deleted` returns the entry unchanged. If someone else changes the same entry at the same moment, a write can fail with `409 Conflict`; read the entry again and retry. Reading and editing the list requires spam permissions, which are usually limited to admins.

Related: [Conversations and messages](https://developer.kustomer.com/docs/concepts/conversations-and-messages), [Customers](https://developer.kustomer.com/api/core-resources#customers)

### Recipes

#### 1. Unblock a customer whose email is going to spam

When a customer says their emails aren't getting through, check whether their address is on the block list and move it to the allow list.

**Step 1.** [`GET /v1/customers/email={email}`](https://developer.kustomer.com/api/core-resources/get-customer-by-email.md). Find the [customer](https://developer.kustomer.com/api/core-resources#customers) by email address.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/customers/email=jane@example.com" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
CUSTOMER_ID=$(kusty api GET "/v1/customers/email=$EMAIL" | jq -r '.data.id')
```

**Step 2.** [`GET /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/get-spam-senders.md). List the spam sender entries linked to that customer. Leave out `includeDeleted`: any value for it, even `false`, includes deleted entries.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/spam/senders?customer=$CUSTOMER_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/spam/senders --query customer=$CUSTOMER_ID --all \
  --fields id,attributes.sender,attributes.list,attributes.automated
```

**Step 3.** [`PUT /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/upsert-spam-sender.md). Put the address on the allow list with the upsert. It updates the existing entry, or creates one with `201` if there wasn't any, so you don't need to know which.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/spam/senders" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"sender": "jane@example.com", "channel": "email", "list": "whitelist"}'
```

```bash title="kusty"
kusty api PUT /v1/spam/senders \
  --field sender=$EMAIL \
  --field channel=email \
  --field list=whitelist
```

#### 2. Review entries added by spam detection

Audit the senders Kustomer blocked automatically and move any false positives, such as a partner's domain, to the allow list.

**Step 1.** [`GET /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/get-spam-senders.md). Page through the list and keep the blocked entries that spam detection added, marked `automated: true`.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/spam/senders" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/spam/senders --all \
  | jq -r 'select(.data.attributes.automated and .data.attributes.list == "blacklist")
      | select(.data.attributes.sender | endswith("@partner.example.com"))
      | .data.id' > false-positives.txt
```

**Step 2.** [`PUT /v1/spam/senders/{id}`](https://developer.kustomer.com/api/core-resources/update-a-spam-sender-by-id.md). Switch each one to `whitelist`. If someone else edits the same entry at the same moment, the update can fail with `409 Conflict`; read the entry again and retry.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/spam/senders/$SENDER_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"list": "whitelist"}'
```

```bash title="kusty"
while read -r id; do
  kusty api PUT /v1/spam/senders/$id --field list=whitelist --fields id,attributes.list
done < false-positives.txt
```

#### 3. Mirror a block list from your own system

Keep Kustomer's block list in step with one maintained elsewhere, such as a fraud tool, adding new addresses and removing ones you've cleared.

**Step 1.** [`PUT /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/upsert-spam-sender.md). Upsert every address on your list as `blacklist`. Repeating the call for an address that's already blocked is harmless, and it restores an entry that was deleted.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/spam/senders" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"sender": "fraud@example.net", "channel": "email", "list": "blacklist"}'
```

```bash title="kusty"
while read -r email; do
  kusty api PUT /v1/spam/senders --field "sender=$email" --field channel=email --field list=blacklist --fields id
done < blocked.txt
```

**Step 2.** [`GET /v1/spam/senders`](https://developer.kustomer.com/api/core-resources/get-spam-senders.md). Read Kustomer's current block list. Addresses come back lowercased and trimmed, so normalize yours the same way before comparing. Skip `automated` entries if you only manage the ones you added.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/spam/senders" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/spam/senders --all \
  | jq -r 'select(.data.attributes.list == "blacklist" and (.data.attributes.automated | not))
      | [.data.id, .data.attributes.sender] | @tsv' > kustomer-blocked.tsv
```

**Step 3.** [`DELETE /v1/spam/senders/{id}`](https://developer.kustomer.com/api/core-resources/delete-a-spam-sender.md). Delete the entries whose address is no longer on your list. Deleting is a soft delete, so a later upsert of the same address brings the entry back.

```bash title="cURL"
curl --request DELETE "https://api.kustomerapp.com/v1/spam/senders/$SENDER_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
tr 'A-Z' 'a-z' < blocked.txt | sort -u > wanted.txt
awk -F'\t' 'NR==FNR {keep[$1]; next} !($2 in keep) {print $1}' wanted.txt kustomer-blocked.tsv \
  | while read -r id; do
      kusty api DELETE /v1/spam/senders/$id --confirm --yes
    done
```

### Example object

A real record from [`GET /v1/spam/senders/{id}`](https://developer.kustomer.com/api/core-resources/get-a-spam-sender-by-id.md):

```json
{
  "type": "sender",
  "id": "5f52b303775dbe9e4ed8f9a3",
  "attributes": {
    "sender": "spammer@example.com",
    "channel": "email",
    "list": "blacklist",
    "createdAt": "2020-09-04T21:34:58.858Z",
    "modifiedAt": "2020-09-04T21:34:58.858Z",
    "updatedAt": "2020-09-04T21:34:58.858Z",
    "deleted": false,
    "deletedAt": null,
    "deletedBy": null,
    "automated": true
  },
  "relationships": {
    "org": {
      "links": {
        "self": "/v1/orgs/5f06187f1adcf30013d1f08a"
      },
      "data": {
        "type": "org",
        "id": "5f06187f1adcf30013d1f08a"
      }
    },
    "createdBy": null,
    "modifiedBy": null
  }
}
```

Fields:

- `type` (string): Always `sender`.
- `attributes.sender` (string): The email address, stored lowercased and trimmed. Kustomer normalizes inbound mail the same way before comparing, so differences in case or surrounding spaces don't matter.
- `attributes.channel` (string): Always `email`.
- `attributes.list` (string): `whitelist` to never treat the sender's mail as spam, or `blacklist` to always treat it as spam. Switch it with either `PUT` endpoint.
- `attributes.deleted` (boolean): Whether the entry has been removed. Lists leave deleted entries out unless `includeDeleted` is present with any value, so even `includeDeleted=false` includes them; omit the parameter to exclude them.
- `attributes.deletedBy` (string): The user who removed the entry, or `null`.
- `attributes.automated` (boolean): `true` when the sender was added automatically by spam detection rather than by a user.
- `relationships.createdBy` (null): `null` for senders added automatically by spam detection.
- `relationships.modifiedBy` (null): `null` when the latest change was made automatically by spam detection.

### Relationships

- Points to **Customers** via `relationships.customer`: The customer whose email address matches the spam sender entry, linked when the entry is created. Filter `GET /v1/spam/senders` by `customer` to see whether a customer's mail is being allowed or blocked.

### Permissions

Fewest roles for a read-only integration: `org.permission.spam.read`. For every endpoint: `org.permission.spam.read`, `org.permission.spam.update`, `org.permission.spam.delete`.

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Unique sender ID. |

## Responses

### 200

Success

- `data` (object, required)
  - `type` (string, required)
  - `id` (string, required)
  - `attributes` (object, required)
    - `sender` (string, required)
    - `channel` (string, required)
    - `list` (string, required)
    - `createdAt` (string, required)
    - `modifiedAt` (string, required)
    - `updatedAt` (string, required)
    - `deleted` (boolean)
    - `deletedAt` (string, nullable)
    - `deletedBy` (string, nullable)
    - `automated` (boolean): `true` when the sender was added automatically by spam detection rather than by a user.
  - `relationships` (object, required)
    - `org` (object, required)
    - `createdBy` (object, required, nullable): `null` for senders added automatically by spam detection.
    - `modifiedBy` (object, required, nullable): `null` when the latest change was made automatically by spam detection.

```json
{
  "data": {
    "type": "sender",
    "id": "5f52b303775dbe9e4ed8f9a3",
    "attributes": {
      "sender": "spammer@example.com",
      "channel": "email",
      "list": "blacklist",
      "createdAt": "2020-09-04T21:34:58.858Z",
      "modifiedAt": "2020-09-04T21:34:58.858Z",
      "updatedAt": "2020-09-04T21:34:58.858Z",
      "deleted": false,
      "deletedAt": null,
      "deletedBy": null,
      "automated": true
    },
    "relationships": {
      "org": {
        "links": {
          "self": "/v1/orgs/5f06187f1adcf30013d1f08a"
        },
        "data": {
          "type": "org",
          "id": "5f06187f1adcf30013d1f08a"
        }
      },
      "createdBy": null,
      "modifiedBy": null
    }
  }
}
```

### 401

Unauthorized

### 403

Forbidden

### 404

Not Found

## Example request

```bash
curl --request GET \
  --url 'https://api.kustomerapp.com/v1/spam/senders/5f52b303775dbe9e4ed8f9a3' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```
