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.
- Spam memory: filled by Report Spam or Not Spam.
- Classification memory: filled by Report a Classification Fix.
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
/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
/personalization/dataDeletes 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
/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
/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
/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()