Welcome to the PhishShield-Engine API reference. This platform provides a suite of RESTful endpoints for email security, phishing intelligence, and model management.
Users must register and login to receive a JSON Web Token (JWT), which must be provided in the Authorization header for all protected endpoints.
| Control | Detail |
|---|---|
| Password hashing | bcrypt with auto work-factor (gensalt()) |
| Bcrypt DoS protection | Password length capped at 128 characters |
| Username regex | Strict pattern ^[a-zA-Z0-9_-]{3,50}$ (alphanumeric, hyphens, underscores only) |
| Text sanitization | Null bytes (\0) stripped; HTML tags escaped (html.escape) to eliminate XSS |
| JWT lifetime | 1 hour (configurable via JWT_EXPIRY_HOURS) |
| JWT claims | sub, iat, exp, jti (unique ID per token) |
| Account lockout | After 5 consecutive failures, locked for 15 minutes |
| Rate limit — login | 5 requests / minute per IP |
| Rate limit — register | 3 requests / minute per IP |
| Rate limit — reset | 3 requests / minute per IP |
| User enumeration | All auth failure responses are identical — no distinction between "wrong password" and "no such user" |
| API key comparison | Constant-time via hmac.compare_digest() — no timing oracle |
| Bot detection | Empty User-Agent blocked on all API routes; malicious scanners blacklisted |
| Honeypot traps | Hidden bot_trap field in request payloads; populated values → HTTP 422 |
- URL:
POST /auth/register - Rate limit: 3 / minute per IP
- Payload:
{
"username": "alice",
"password": "Secure@Pass1!",
"email": "alice@example.com"
}Password policy (enforced server-side):
- Minimum 8 characters
- At least one digit
- At least one special character (
!@#$%^&*etc.)
Response (200 OK):
{
"username": "alice",
"api_key": "pse_<48-char hex>"
}
⚠️ Theapi_keyis returned once only at registration. Store it securely — it cannot be retrieved again.
- URL:
POST /auth/login - Rate limit: 5 / minute per IP
- Payload:
{ "username": "alice", "password": "Secure@Pass1!" }- Response (
200 OK):
{
"token": "<JWT>",
"username": "alice",
"expires_in": 3600
}- Failure (
401 Unauthorized): Returns"Invalid credentials"for wrong password, unknown user, or locked account — no distinction is made.
- URL:
POST /auth/password-reset-request - Rate limit: 3 / minute per IP
- Payload:
{ "username": "alice" } - Response (
200 OK, always — even if username does not exist):
{
"detail": "If that username exists, a password-reset token has been issued.",
"_dev_token": "<raw token>"
}
⚠️ _dev_tokenis present in development mode only. In production, remove this field and deliver the token via email out-of-band.
Token properties:
- Generated with
secrets.token_urlsafe(32)(256-bit entropy) - Valid for 1 hour (configurable via
RESET_TOKEN_EXPIRY_HOURS) - SHA-256 hash stored in DB — raw token is never persisted
- Single-use: invalidated immediately after first successful
password-reset
- URL:
POST /auth/password-reset - Rate limit: 5 / minute per IP
- Payload:
{
"username": "alice",
"token": "<raw token from email>",
"new_password": "NewSecure@Pass2!"
}- Response (
200 OK):{ "detail": "Password updated successfully." } - Failure (
400):{ "detail": "Invalid or expired reset token." }
The following endpoints allow authenticated users to view and manage their own resources. Every query strictly verifies that resource.user_id == current_user.id. Access attempts on another user's resources return 404 Not Found (preventing IDOR attacks).
- URL:
GET /auth/me - Auth: Required (
Bearer <JWT>orX-API-Key) - Response (
200 OK):{ "id": 1, "username": "alice", "email": "alice@example.com", "is_email_verified": false, "created_at": "2026-09-02T18:00:00+00:00" }
- URL:
GET /auth/logs - Auth: Required (
Bearer <JWT>orX-API-Key) - Response (
200 OK): List of usage log entries belonging to the authenticated user.
- URL:
GET /auth/logs/{log_id} - Auth: Required (
Bearer <JWT>orX-API-Key) - Response:
200 OKif owned by user;404 Not Foundif not owned or missing.
- URL:
GET /auth/feedback/{feedback_id} - Auth: Required (
Bearer <JWT>orX-API-Key) - Response:
200 OKif owned by user;404 Not Foundif not owned or missing.
- URL:
DELETE /auth/feedback/{feedback_id} - Auth: Required (
Bearer <JWT>orX-API-Key) - Response:
200 OKif owned by user;404 Not Foundif not owned or missing.
For protected endpoints, provide credentials using one of:
Authorization: Bearer <JWT token>
X-API-Key: pse_<api key>
All API endpoints are protected by multi-layer abuse controls applied at the middleware level (src/api/app.py).
| Endpoint | Limit |
|---|---|
POST /predict |
15 / minute |
POST /analyze-security |
15 / minute |
POST /predict/batch |
5 / minute |
POST /export-report |
10 / minute |
POST /feedback |
10 / minute |
POST /auth/login |
5 / minute |
POST /auth/register |
3 / minute |
POST /auth/password-reset-request |
3 / minute |
POST /auth/password-reset |
5 / minute |
| Global default | 60 / minute |
Exceeded limits return HTTP 429 with { "detail": "Rate limit exceeded. Please slow down your requests." } and are logged to logs/security_audit.log.
Every request to API paths (/predict, /auth/*, /analyze-security, /export-report, /feedback) passes through anti_bot_middleware:
- Honeypot Header Check — Requests containing
X-Honeypot-TraporX-Bot-Checkheaders are immediately rejected (HTTP 403). Bots that blindly populate all available headers trigger this trap. - User-Agent Enforcement — Requests with an empty or missing
User-Agentheader are rejected (HTTP 403). - Scanner Blacklist — Requests from known malicious tools are blocked (HTTP 403):
sqlmap,nikto,nmap,masscan,bytespider,zgrab,libwww-perl,python-urllib,dirbuster,w3af,acunetix,nessus,openvas.
All blocked requests log a BOT_ATTACK_BLOCKED, SCRAPER_BLOCKED, or BOT_HONEYPOT_TRIGGERED event to logs/security_audit.log.
PredictRequest and RegisterRequest include a hidden bot_trap field that must always be empty or absent. Automated scripts that populate all fields will fail schema validation:
// ✅ Legitimate request — bot_trap absent or empty
{ "text": "Email content here" }
// ❌ Bot request — bot_trap populated → HTTP 422
{ "text": "Email content here", "bot_trap": "auto-filled" }Calculates both ML probability and heuristic security risk.
- URL:
POST /predict - Payload:
{
"text": "Your account has been compromised. Log in here: http://192.168.1.1",
"headers": "From: amazon-security@attacker.com\nReturn-Path: evil@hacker.io",
"model": "ensemble"
}Deep forensic analysis without ML overhead.
- URL:
POST /analyze-security - Payload:
{ "text": "...", "headers": "..." } - Response: Detailed JSON including
risk_score,risk_level, and specificsecurity_flags(10 flags).
Process up to 50 emails in a single request (capped to prevent bulk data scraping).
- URL:
POST /predict/batch - Rate limit: 5 / minute per IP
- Auth required: Bearer token or X-API-Key
- Payload:
{ "emails": ["text1", "text2"...], "model_name": "svm" }
Retrieve current performance metrics for all registered models.
- URL:
GET /analytics
Submit ground-truth corrections to trigger automated retraining.
- URL:
POST /feedback - Payload:
{
"email_text": "...",
"predicted_label": "ham",
"correct_label": "spam",
"model_used": "naive_bayes"
}Full forensic breakdown of an email's threat indicators.
- URL:
POST /export-report - Payload:
{ "text": "...", "headers": "..." } - Response: Full JSON forensic mapping with timestamps, extracted URLs, and risk breakdown.
- URL:
GET /ab/summary - Response:
{ "naive_bayes": {"count": 10}, "svm": {"count": 12} }
GET /health— Core system status.GET /health/ready— Checks model readiness and DB connections.GET /metrics— Prometheus metrics endpoint (request counts, latency histograms).
GET /docs— Swagger UI for interactive testing.GET /redoc— ReDoc clean documentation view.
| HTTP Code | Meaning |
|---|---|
400 |
Invalid or expired password reset token |
401 |
Invalid credentials, locked account, or expired/invalid token |
403 |
Bot detected — empty User-Agent, blacklisted scanner, or honeypot header triggered |
409 |
Username or email already exists |
422 |
Validation error (password policy, field length, bot_trap populated, etc.) |
429 |
Rate limit exceeded — slow down requests |
503 |
Models not yet trained / service not ready |
Last Updated: 2026-09-02