---
title: "Process inbound emails into tickets (GET)"
method: GET
path: "/cron/inbound-email"
tags: ["Cron"]
---

# Process inbound emails into tickets (GET)

`GET /cron/inbound-email`

## Request

- Base URL: `https://api.apphandoff.com/api`
- URL: `https://api.apphandoff.com/api/cron/inbound-email`
- Auth: none required

Added 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).

## Response `200`

Inbound sweep result

- object
  - `ok` boolean, required: 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.
  - `sweep` object, required
    - `scanned` integer, required: Eligible ledger rows the selection returned.
    - `claimed` integer, required: Rows this sweep took the five-minute lease on.
    - `ticket_created` integer, required
    - `routing_failed` integer, required
    - `processing_failed` integer, required: Rows terminalized because the three-attempt ceiling was passed.
    - `skipped_in_progress` integer, required: Rows another worker holds a LIVE lease on.
    - `skipped_duplicate` integer, required: Rows already terminal.
    - `receipts` object, required: Receipt outcomes by name (sent, failed, delivery_unknown, skipped, errored). A bad receipt never undoes ticket_created.
    - `errors` integer, required
    - `truncated` boolean, required: The batch cap or the internal deadline stopped the sweep with work left over.
  - `bounds` object, required
    - `limit` integer, required
    - `deadline_ms` number, required

## Other responses

- `401`: Missing or invalid CRON_SECRET

---

[API](https://skmtc.dev/apphandoff/apis/apphandoff-api.md) · [All operations](https://skmtc.dev/apphandoff/apis/apphandoff-api/llms.txt) · [OpenAPI document](https://skmtc.dev/apphandoff/apis/apphandoff-api/revisions/9f138fe46d56?raw)
