Skip to content

Latest commit

 

History

History
473 lines (350 loc) · 13.8 KB

File metadata and controls

473 lines (350 loc) · 13.8 KB

Apache Pony Mail (Foal) — API Documentation

This document describes the HTTP API for Pony Mail Foal. All endpoints accept JSON request bodies (POST) and return JSON unless otherwise noted.

The formal OpenAPI 3.0 specification is available at server/openapi.yaml.


Table of Contents


Authentication

Foal uses cookie-based sessions via OAuth. The session cookie is named ponymail. Most read endpoints work without authentication for public lists. Private list access and write operations (compose, management) require an authenticated session via an authoritative OAuth provider.


Endpoints

stats.json

Search the archives and return matching results.

POST /api/stats.json

Request Parameters

Parameter Type Required Description
list string yes List name prefix (e.g. dev). Use * for wildcard.
domain string yes List domain (e.g. httpd.apache.org). Use * for wildcard.
d string no Date/timespan (see below)
s string no Start month (yyyy-mm)
e string no End month (yyyy-mm)
dfrom string no Start date as days ago
dto string no Number of days to include from dfrom
q string no Free-text search query (see syntax)
header_from string no Filter by From: header
header_to string no Filter by To: header
header_subject string no Filter by Subject: header
header_body string no Filter by message body
header_messageid string no Filter by Message-ID: header
quick (presence) no Return statistics only (omit emails, thread_struct, word cloud, participants)
emailsOnly (presence) no Return email summaries only (omit thread_struct, participants, word cloud)
since integer no UNIX epoch; returns {"changed": false} if no emails are newer

Response (StatsResponse)

{
  "hits": 134,
  "numparts": 28,
  "no_threads": 35,
  "firstYear": 2018,
  "firstMonth": 1,
  "lastYear": 2021,
  "lastMonth": 11,
  "name": "dev",
  "domain": "lists.example.org",
  "list": "dev@lists.example.org",
  "searchlist": "<dev.lists.example.org>",
  "active_months": [{"2021-01": 15}, {"2021-02": 23}],
  "emails": [ /* array of CompactEmailResponse */ ],
  "thread_struct": [ /* threaded representation */ ],
  "participants": [
    {"email": "jane@example.org", "name": "Jane Doe", "count": 10, "gravatar": "..."}
  ],
  "cloud": {"word1": 25, "word2": 10},
  "searchParams": {"list": "dev", "domain": "lists.example.org", "d": "gte=2018-01"},
  "unixtime": 1506761839
}

Example

curl -X POST https://lists.apache.org/api/stats.json \
  -H "Content-Type: application/json" \
  -d '{"list": "dev", "domain": "ponymail.apache.org", "d": "lte=3M"}'

email.json

Fetch a single email by permalink ID or Message-ID.

POST /api/email.json

Request Parameters

Parameter Type Required Description
id string yes Email permalink ID or Message-ID header value
listid string conditional Required when looking up by Message-ID (for disambiguation)
attachment boolean no Set to true to fetch an attachment
file string no Attachment hash (required when attachment=true)

Response (SingleEmailResponse)

{
  "id": "r8cmj7vm5n8z5r3xda5ebd",
  "mid": "r8cmj7vm5n8z5r3xda5ebd",
  "dbid": "08c4e61930db221d...",
  "message-id": "<521062724.28.1506761839312.JavaMail.jenkins@host>",
  "from": "Jane Doe <jane@example.org>",
  "from_raw": "Jane Doe <jane@example.org>",
  "to": "dev@example.org",
  "cc": "announce@example.org",
  "subject": "Re: weekly meeting",
  "date": "2017/09/30 08:57:19",
  "epoch": 1506761839,
  "list": "<dev.example.org>",
  "list_raw": "<dev.example.org>",
  "body": "Full message body...",
  "body_short": "Truncated to 201 chars...",
  "private": false,
  "references": "<parent-message-id>",
  "in-reply-to": "<parent-message-id>",
  "attachments": [],
  "permalinks": ["r8cmj7vm5n8z5r3xda5ebd", "..."],
  "gravatar": "69eea47c5083c2e4945a2704fc7b658c"
}

Notes:

  • date and epoch are in UTC.
  • When attachment=true and a matching file hash is found, the raw attachment binary is returned with appropriate Content-Type and Content-Disposition headers.

thread.json

Fetch a complete email thread starting from a given email.

POST /api/thread.json

Request Parameters

Parameter Type Required Description
id string yes Email permalink ID or Message-ID
listid string no List-ID for disambiguation when using Message-ID
find_parent boolean no If true, navigate up to the thread root before fetching

Response (ThreadResponse)

{
  "thread": {
    "from": "...",
    "subject": "...",
    "id": "...",
    "epoch": 1506761839,
    "children": [ /* nested CompactEmailResponse objects */ ]
  },
  "emails": [ /* flat array of all emails in the thread */ ]
}

source.json

Fetch the raw mbox source of an email.

POST /api/source.json

Request Parameters

Same as email.json (id, optional listid).

Response

Returns the raw RFC 2822 email source as text/plain. This includes all original headers and the unmodified message body.

Returns HTTP 404 if the email is not found.


mbox.json

Download a set of emails in mbox format.

POST /api/mbox.json

Request Parameters

Same as stats.json — all search/date parameters apply.

Response

Returns the matching emails as a single mbox-format file (text/plain).


compose.json

Compose and send an email to a list. Requires authentication via an authoritative OAuth provider.

POST /api/compose.json

Request Parameters

Parameter Type Required Description
to string yes Recipient address (must match sender_domains config)
subject string yes Email subject
body string yes Email message body
references string no Message-ID reference (if not a direct reply)
in-reply-to string no Message-ID of the email being directly replied to

Response (ActionResponse)

{"okay": true, "message": "Email dispatched"}

Note: The sender_domains configuration controls which recipient domains are permitted. See INSTALL.md.


preferences.json

Fetch user preferences, list overview, and OAuth provider configuration.

POST /api/preferences.json

Request Parameters

Parameter Type Required Description
oauth boolean no If true, return only OAuth provider configuration

Response

{
  "login": {
    "credentials": {"fullname": "Jane Doe", "email": "jane@example.org"}
  },
  "lists": {
    "httpd.apache.org": {"dev": 1523, "users": 890},
    "ponymail.apache.org": {"dev": 36}
  },
  "versions": {
    "foal": "abc123",
    "server": "def456",
    "elasticsearch_engine": "8.11.0",
    "elasticsearch_library": "8.11.0"
  }
}

Notes:

  • versions.server, elasticsearch_engine, and elasticsearch_library are only returned for authenticated users (admin-only for OpenSearch versions).
  • When oauth=true, returns the configured OAuth providers for the login UI.

mgmt.json

Administrative endpoint for email management (GDPR operations). Requires admin authentication.

POST /api/mgmt.json

Request Parameters

Parameter Type Required Description
action string yes One of: log, delete, hide, unhide, edit
document string no Single document permalink ID
documents array no Array of document permalink IDs (batch operations)
size integer no Number of audit log entries (for action=log, default: 50)
page integer no Page offset for audit log
filter string no Filter audit log by action type

Actions:

  • log — View the audit log of past admin actions
  • delete — Permanently delete emails (if allow_delete is configured) or hide them
  • hide — Hide emails from public view (recoverable)
  • unhide — Restore previously hidden emails
  • edit — Edit email metadata (list-id, etc.)

Response

Varies by action. For log:

{"entries": [ /* audit log entries */ ]}

For mutations: returns an ActionResponse with okay and message.


pminfo.json

Return server activity statistics. No authentication required.

POST /api/pminfo.json

Returns the server's gathered activity data (list counts, processing stats).


gravatar.json

Caching proxy for Gravatar images.

POST /api/gravatar.json

Request Parameters

Parameter Type Required Description
md5 string yes MD5 hash of the email address (lowercased)

Returns a image/png response with 24-hour cache headers. Falls back to a default avatar if the hash is unknown.


plain.json

Plain HTML rendering for search engine indexing.

This endpoint serves publicly available lists and threads as simple HTML with canonical link elements, enabling search engines to index the archive content and link to the standard JS-based UI URLs.


Common Parameters

Date/Timespan Parameters

The d parameter supports several formats:

Format Meaning Example
yyyy-mm Specific month 2021-06
lte=N[wMyd] Less than N weeks/Months/years/days ago lte=3M
gte=N[wMyd] More than N weeks/Months/years/days ago gte=1y
dfr=yyyy-mm-dd|dto=yyyy-mm-dd Date range (inclusive) dfr=2021-09-01|dto=2021-09-30

The s and e parameters provide an alternative way to specify a month range: s=2021-01&e=2021-06.

The dfrom/dto pair specifies days: dfrom=31 (31 days ago) with dto=10 (10 days of data starting from that point).

Units: w = weeks, M = Months, y = years, d = days.

lte and gte are mutually exclusive. dfr and dto are normally used together.

Search Query Syntax

The q parameter supports:

Syntax Meaning Example
word Must contain word apples
+word Word must be present +oranges
-word Word must NOT be present -bananas
"phrase" Exact phrase match "weekly meeting"

Additional filters can narrow results:

  • header_from — match sender address
  • header_to — match recipient address
  • header_subject — match subject line
  • header_body — match message body only
  • header_messageid — match Message-ID header

Differences from Legacy PonyMail API

Foal's API is largely compatible with the original Lua-based PonyMail, with the following notable differences:

Change Details
Endpoint suffix Foal uses .json (e.g. /api/stats.json) instead of .lua
Method All endpoints use POST with JSON body (legacy used GET with query params)
notifications.lua Not available in Foal
atom.lua Not available in Foal
Additional email fields dbid, permalinks, body_short, from_raw, list_raw are new in Foal
find_parent New parameter on thread.json to navigate to thread root
versions in preferences New — shows Foal, server, and OpenSearch version info
mgmt.json New — admin/GDPR management endpoint (not in legacy PM)
gravatar.json New — caching proxy (legacy embedded gravatar handling differently)
plain.json New — search engine indexing support

Related Resources