Create a Sign-In Link

Make a one-time link that signs someone in to the SpamFoo Dashboard with the role you choose. A mail server or control panel can use it to send its users to the dashboard without sharing the proxy secret.

POST/generate-login-link

When to Use It

  • Add a "SpamFoo Dashboard" link to your own webmail or control panel, and make a fresh sign-in link each time a user clicks it.
  • Give a domain administrator access to the domains they manage, and nothing else.

To make a link by hand on the server, use spamfoo-client login-link instead. For what each role sees, see Manage Sign-In and Roles.

Request

Send the current proxy secret in X-SpamFoo-Proxy-Auth, even from loopback. Send a JSON body with Content-Type: application/json.

{
    "role": "DomainAdmin",
    "domains": ["example.com", "example.org"],
    "email": "[email protected]",
    "expiresInMinutes": 15,
    "returnPath": "/admin/"
}
Field Required Type Description
role Yes string SystemAdmin, DomainAdmin, or User. Letter case doesn't matter.
domain or domains For DomainAdmin string, or array of strings The domain, or up to 50 domains, a Domain Admin can manage. Send one of the two fields, not both. Don't send either for the other roles.
email For User email address The mailbox a User sees. For a Domain Admin, also shows that mailbox's own pages. Optional for SystemAdmin and DomainAdmin.
expiresInMinutes No integer How long the link can be used, from 1 to 1440. The default is 15. The sign-in it creates lasts 24 hours either way.
returnPath No string A dashboard page to open after sign-in. It must be /admin or start with /admin/, and be at most 1024 characters.

Domains and email addresses are stored in lowercase, and international domain names in their Unicode form, so Example.COM and example.com are the same domain.

Response

{
    "url": "http://spamfoo.example.com:16253/admin/login?t=...",
    "expiresAt": "2026-10-13T14:15:00+00:00"
}
  • url starts with SPAMFOO_PUBLIC_URL in standalone mode when it's set. Otherwise it uses the address your request was sent to, so call SpamFoo Client at the address the user's browser will use.
  • Opening the link shows a Sign in to SpamFoo page. The link is used up only when the person clicks Continue, so a mail scanner that opens links in advance doesn't use it.
  • Each link works once. Changing the proxy secret stops every unused link.
Warning Treat a sign-in link like a password until it's used or expires. Send it only to the person it's for, over HTTPS where you can.

Errors

Status Body Cause
400 {"error": "..."} A field is missing or invalid, such as role must be SystemAdmin, DomainAdmin or User. or expiresInMinutes must be between 1 and 1440. The message names the field.
401 Empty The proxy secret was missing or wrong.
429 {"error": "...", "code": "auth_blocked"} Standalone mode only. Too many wrong secrets came from this address. See Repeated Wrong Secrets.

Example

curl -s -X POST http://localhost:16253/generate-login-link \
    -H "X-SpamFoo-Proxy-Auth: $SPAMFOO_PROXY_SECRET" \
    -H "Content-Type: application/json" \
    -d '{"role":"User","email":"[email protected]"}'
using System.Net.Http.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);

using var response = await http.PostAsJsonAsync("/generate-login-link",
    new { role = "User", email = "[email protected]" });
response.EnsureSuccessStatusCode();
var link = await response.Content.ReadFromJsonAsync<LoginLink>();
Console.WriteLine(link!.Url);

record LoginLink(string Url, DateTimeOffset ExpiresAt);
import os
import requests

response = requests.post(
    "http://localhost:16253/generate-login-link",
    headers={"X-SpamFoo-Proxy-Auth": os.environ["SPAMFOO_PROXY_SECRET"]},
    json={"role": "User", "email": "[email protected]"},
    timeout=10,
)
response.raise_for_status()
print(response.json()["url"])