howl/API.md
lorentz 5af3fef37d Add REST API, DB user management, and API documentation
Adds a JSON REST API at /api/v1/ (Bearer token auth via API_KEYS env var)
exposing the full pipeline — email processing, log querying, sender profiles,
purge rules, whitelist, and analysis — for external consumers like OpenClaw.

Adds a /dbusers web UI for generating PostgreSQL roles with read_only,
modify, or full permission levels; credentials shown once and never stored.

Includes Alembic migration 0005 for the db_api_users tracking table and
API.md with full endpoint documentation and integration examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:47:39 -04:00

6.5 KiB
Raw Permalink Blame History

Howl REST API

Howl exposes a JSON REST API at /api/v1/ for programmatic access by external services such as OpenClaw. All endpoints require a Bearer token.

Authentication

Set one or more API keys in .env:

API_KEYS=your-key-here,another-key

Include the key in every request:

Authorization: Bearer your-key-here

Requests without a valid key return 401 Unauthorized.


Endpoints

Email Processing

POST /api/v1/process

Trigger a pipeline poll — fetches unread email from the last 14 days and runs the full classification pipeline.

Response

{ "processed": 12 }

Email Log

GET /api/v1/log

Query the email processing log.

Query parameters

Parameter Type Description
status string Filter by status: pending, processing, completed, failed, skipped
sender_type string Filter by sender type: customer, vendor, whitelist, unknown
classification string Filter by LLM classification (e.g. vendor_invoice)
q string Search sender address or subject (case-insensitive)
limit int Results per page, max 200 (default 50)
offset int Pagination offset (default 0)

Response

{
  "total": 847,
  "items": [
    {
      "id": "...",
      "sender_address": "billing@vendor.com",
      "subject": "Invoice #1042",
      "llm_classification": "vendor_invoice",
      "llm_confidence": 0.97,
      "final_action": "flag_follow_up",
      "status": "completed",
      ...
    }
  ]
}

GET /api/v1/log/{entry_id}

Retrieve a single log entry by UUID.


Sender Profiles

Sender profiles guide the LLM by providing context about known senders.

GET /api/v1/senders

List all active sender profiles.

Response: { "items": [...] }

POST /api/v1/senders

Create or update a sender profile (upserts by email_address).

Request body

{
  "email_address": "billing@vendor.com",
  "display_name": "Vendor Billing",
  "sender_type": "vendor",
  "email_types": ["invoice", "statement"],
  "processing_instructions": "Always flag for follow-up",
  "notes": null
}

sender_type values: customer, vendor, whitelist, unknown

GET /api/v1/senders/{profile_id}

Retrieve a single profile by UUID.

PUT /api/v1/senders/{profile_id}

Replace a profile's fields (same body as POST).

DELETE /api/v1/senders/{profile_id}

Deactivate a profile (soft delete).


Purge Rules

Purge rules define senders whose mail should be deleted after a retention period.

GET /api/v1/purge-rules

List all purge rules (active and inactive).

POST /api/v1/purge-rules

Create or reactivate a purge rule.

Request body

{
  "email_address": "newsletter@bulk.com",
  "display_name": "Bulk Newsletter",
  "older_than_days": 14,
  "notes": null
}

PUT /api/v1/purge-rules/{rule_id}

Partially update a rule. All fields are optional.

{ "older_than_days": 7, "is_active": false }

DELETE /api/v1/purge-rules/{rule_id}

Deactivate a rule (soft delete).


Purge Execution

POST /api/v1/purge/execute

Preview or execute a purge run against Microsoft 365.

Request body

{
  "mode": "preview",
  "sender": "newsletter@bulk.com",
  "days": 30
}
Field Values Description
mode preview | execute preview reports what would be deleted without deleting
sender string or null Specific sender address; null uses all active rules
days int Retention threshold in days (only applies when sender is set)

Response

{
  "mode": "preview",
  "total": 47,
  "rows": [
    {
      "address": "newsletter@bulk.com",
      "label": "newsletter@bulk.com (>30d)",
      "count": 47,
      "subjects": ["Weekly digest", "Monthly digest", "Special offer"]
    }
  ]
}

Analysis

GET /api/v1/analysis/senders

Aggregated sender statistics from the email log (top 200 by volume).

Response

{
  "items": [
    {
      "sender_address": "billing@vendor.com",
      "count": 143,
      "first_seen": "2025-01-03T10:22:00+00:00",
      "last_seen": "2026-03-28T14:05:00+00:00"
    }
  ]
}

Whitelist

GET /api/v1/whitelist

List all active whitelist entries.

POST /api/v1/whitelist

Add a whitelist entry. At least one of email_address or domain is required.

{
  "email_address": "ceo@partner.com",
  "domain": null,
  "description": "Executive contact",
  "added_by": "openclaw",
  "expires_at": null
}

DELETE /api/v1/whitelist/{entry_id}

Deactivate a whitelist entry.


DB Users

The /dbusers web UI (available in the Howl dashboard sidebar) lets you create PostgreSQL roles for direct database access. This is intended for services that need to query Howl's database directly rather than going through the API.

Permission Levels

Level Permissions
Read Only SELECT on all tables
Modify SELECT + INSERT/UPDATE on customers, vendors, whitelist, sender_profiles, purge_rules
Full SELECT + INSERT/UPDATE/DELETE on all tables

Usage

  1. Navigate to DB Users in the sidebar
  2. Click + Create User
  3. Enter a username (331 chars, lowercase, letters/digits/underscores) — the role will be created as howl_<username>
  4. Select a permission level
  5. Optionally add a description (e.g. "OpenClaw integration")
  6. Click Create User — credentials are shown once and never stored

To revoke access, click Revoke next to the user. This sets the user inactive in Howl and drops the PostgreSQL role.

Connection details

Host:     localhost (or the host running Postgres)
Port:     5434
Database: howl
Username: howl_<username>
Password: (shown at creation time)

Example: OpenClaw integration

import httpx

HOWL_BASE = "https://howl.wulfconsulting.cloud"
HOWL_KEY  = "your-api-key"

headers = {"Authorization": f"Bearer {HOWL_KEY}"}

# Trigger a mail fetch
httpx.post(f"{HOWL_BASE}/api/v1/process", headers=headers)

# Pull recent completed entries
resp = httpx.get(
    f"{HOWL_BASE}/api/v1/log",
    headers=headers,
    params={"status": "completed", "limit": 100},
)
entries = resp.json()["items"]

# Add a sender profile
httpx.post(
    f"{HOWL_BASE}/api/v1/senders",
    headers=headers,
    json={
        "email_address": "alerts@vendor.com",
        "sender_type": "vendor",
        "email_types": ["notification"],
        "processing_instructions": "Route to Infosec if subject contains CVE",
    },
)