Process inbound emails into tickets (GET)
const url = 'https://example.com/api/cron/inbound-email';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://example.com/api/cron/inbound-emailAdded by HO-8159. Drives the bounded inbound-email worker: claims
inbound_emails rows that are received or processing with an EXPIRED
five-minute lease, routes each to one accessible project, creates the
ticket and sends the sender a receipt. The Mailgun intake route
(POST /inbound-email) persists an email and returns 200 without routing
it, so nothing else turns a ledger row into a ticket.
At most three attempts per email; past the ceiling the row is terminal
processing_failed. Bounds are overridable per environment with
INBOUND_EMAIL_SWEEP_LIMIT (default/max 25 rows) and
INBOUND_EMAIL_SWEEP_DEADLINE_MS (default 45000).
Protected by CRON_SECRET via the x-cron-secret header ONLY — a token
query parameter is rejected (secrets do not belong in URLs).
Responses
Section titled “ Responses ”Inbound sweep result
object
False when any row errored or the ledger selection itself failed. A truncated batch is NOT a failure — the next hourly fire takes the rest.
object
Eligible ledger rows the selection returned.
Rows this sweep took the five-minute lease on.
Rows terminalized because the three-attempt ceiling was passed.
Rows another worker holds a LIVE lease on.
Rows already terminal.
Receipt outcomes by name (sent, failed, delivery_unknown, skipped, errored). A bad receipt never undoes ticket_created.
object
The batch cap or the internal deadline stopped the sweep with work left over.
object
Missing or invalid CRON_SECRET