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

312 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`:
```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**
```json
{ "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**
```json
{
"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**
```json
{
"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**
```json
{
"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.
```json
{ "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**
```json
{
"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**
```json
{
"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**
```json
{
"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.
```json
{
"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
```python
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",
},
)
```