Manage Personal Data

Delete a user, or count or delete the corrections SpamFoo Client has stored for one user. Delete the user when a mailbox is removed. Delete only the corrections when a user asks to reset their personalization.

SpamFoo Client keeps two separate memories for each user.

All of these endpoints require the proxy secret, even from loopback, and have no license check. The user ID must match the X-User-ID value you send when classifying. Case doesn't matter.

Delete a User

DELETE/integration/users/{userId}

Deletes everything SpamFoo Client keeps for one user: both memories and their rules, the user's folder, the copies of messages the user corrected, the user's classification history and mailbox statistics in the admin console, and the user's own allow and block rules. Put the user ID in the path, URL-encoded, for example /integration/users/user%40example.com. There is no body.

Calling it for a user that has nothing stored still returns 200. If you push an address map, leave the user out of your next push too. If mail for the same address is classified later, SpamFoo Client starts that user again from nothing.

A successful request returns 200 with what was removed:

{
    "userId": "[email protected]",
    "folderDeleted": true,
    "archivedCorrectionsDeleted": 2,
    "classificationsDeleted": 140,
    "mailboxStatsDeleted": 1,
    "overridesDeleted": 0
}

Delete Spam Corrections

DELETE/personalization/data

Deletes the user's spam memory, plus any per-user spam rules stored on disk. After this call, the user's past spam and not-spam corrections no longer affect their mail.

Delete Classification Corrections

DELETE/personalization/tabs/data

Deletes the user's classification memory, plus any per-user classification rules stored on disk. To remove all of a user's corrections, call both DELETE endpoints.

Delete Request

Both DELETE endpoints take the same JSON body and need Content-Type: application/json.

{
    "userId": "[email protected]"
}
Field Required Type Description
userId Yes string The user whose data to delete.

Delete Response

A successful request returns 200. The message says tabs personalization for the classification endpoint.

{
    "status": "success",
    "message": "All personalization data cleared for user [email protected]"
}

What Is Not Deleted

The two correction DELETE calls remove only the two memories and any rule files. They do not remove the items below. Delete a User removes the first two.

  • The compressed copies of corrected messages that SpamFoo Client keeps on its server.
  • Classification history and stored message content shown in the admin console.
  • Anonymized corrections already queued or sent to SpamFoo. These carry a hashed user ID, not the address.

Count Spam Corrections

GET/personalization/knn/stats?userId={userId}

Returns how many entries are in the user's spam memory.

{
    "userId": "[email protected]",
    "totalEntries": 37,
    "userCorrections": "N/A",
    "autoEntries": "N/A"
}
Field Type Description
userId string The user you asked about.
totalEntries integer Number of entries in the user's spam memory.
userCorrections, autoEntries string Always "N/A". SpamFoo Client does not report this split.

Count Classification Corrections

GET/personalization/tabs/knn/stats?userId={userId}

Returns how many entries are in the user's classification memory, grouped by sender.

{
    "userId": "[email protected]",
    "totalEntries": 5,
    "entriesBySender": {
        "[email protected]": 3,
        "[email protected]": 2
    }
}
Field Type Description
userId string The user you asked about.
totalEntries integer Number of entries in the user's classification memory.
entriesBySender object Number of entries for each sender address.

Stats Request

Query Parameter Required Type Description
userId Yes string The user to count. URL-encode it.

Errors

Status Body Cause
400 {"error": "userId is required"} DELETE: userId was missing or blank.
400 {"error": "userId query parameter is required"} GET: userId was blank.
400 Empty GET: the userId parameter was left out. DELETE: the body was empty or was not valid JSON.
401 Empty The proxy secret was missing or wrong.
415 Empty A correction DELETE did not use Content-Type: application/json.

Example

This example counts a user's corrections, then deletes both memories.

curl -G http://localhost:16253/personalization/knn/stats \
    -H "X-SpamFoo-Proxy-Auth: $SPAMFOO_PROXY_SECRET" \
    --data-urlencode "[email protected]"

curl -G http://localhost:16253/personalization/tabs/knn/stats \
    -H "X-SpamFoo-Proxy-Auth: $SPAMFOO_PROXY_SECRET" \
    --data-urlencode "[email protected]"

curl -X DELETE http://localhost:16253/personalization/data \
    -H "Content-Type: application/json" \
    -H "X-SpamFoo-Proxy-Auth: $SPAMFOO_PROXY_SECRET" \
    -d '{"userId": "[email protected]"}'

curl -X DELETE http://localhost:16253/personalization/tabs/data \
    -H "Content-Type: application/json" \
    -H "X-SpamFoo-Proxy-Auth: $SPAMFOO_PROXY_SECRET" \
    -d '{"userId": "[email protected]"}'
using System.Net.Http.Json;
using System.Text.Json;

var secret = Environment.GetEnvironmentVariable("SPAMFOO_PROXY_SECRET")
    ?? throw new InvalidOperationException("Set SPAMFOO_PROXY_SECRET.");

using var http = new HttpClient { BaseAddress = new Uri("http://localhost:16253") };
http.DefaultRequestHeaders.Add("X-SpamFoo-Proxy-Auth", secret);

var userId = "[email protected]";
var query = $"?userId={Uri.EscapeDataString(userId)}";

var spamStats = await http.GetFromJsonAsync<JsonElement>("/personalization/knn/stats" + query);
var tabsStats = await http.GetFromJsonAsync<JsonElement>("/personalization/tabs/knn/stats" + query);
Console.WriteLine($"spam entries: {spamStats.GetProperty("totalEntries").GetInt32()}");
Console.WriteLine($"classification entries: {tabsStats.GetProperty("totalEntries").GetInt32()}");

foreach (var path in new[] { "/personalization/data", "/personalization/tabs/data" })
{
    using var request = new HttpRequestMessage(HttpMethod.Delete, path)
    {
        Content = JsonContent.Create(new { userId })
    };
    using var response = await http.SendAsync(request);
    response.EnsureSuccessStatusCode();
}
import os

import requests

SPAMFOO_URL = "http://localhost:16253"
HEADERS = {"X-SpamFoo-Proxy-Auth": os.environ["SPAMFOO_PROXY_SECRET"]}
user_id = "[email protected]"

for path in ("/personalization/knn/stats", "/personalization/tabs/knn/stats"):
    response = requests.get(
        f"{SPAMFOO_URL}{path}",
        params={"userId": user_id},
        headers=HEADERS,
        timeout=30,
    )
    response.raise_for_status()
    print(path, response.json()["totalEntries"])

for path in ("/personalization/data", "/personalization/tabs/data"):
    response = requests.delete(
        f"{SPAMFOO_URL}{path}",
        json={"userId": user_id},
        headers=HEADERS,
        timeout=30,
    )
    response.raise_for_status()