---
title: "Email to Web App Actions"
summary: "Order emails become actions in a web app that has no API."
date: 2026-06-06
links: []
---

<!-- DRAFT: Axel to rewrite -->

## Problem

Orders arrive by email with a PDF notice, and each must quickly become a job in a third-party web app. The sender cannot change the format, and the app has no API.

## Approach

The integration sends the same HTTP requests as the app's own pages, under the account's login. The mailbox is the queue, and an unread email is an order not yet submitted. The email is marked read right after the job is created, so a retry never submits an order twice.

## Solution

```mermaid
flowchart LR
  EM[order email, unread, with a PDF] --> EX[read the PDF's own text]
  EX --> VAL{fields valid?}
  VAL -->|no| ISS[stop, open an issue]
  VAL -->|yes| CK[session, cached up to 48 h]
  CK --> DP[create the call, as the page would]
  DP --> MR[mark the email read]
  MR --> CF[confirm to the sender]
  CF --> LG[log a row, once per call number]
  LG --> TR[watch the call]
```

```python
for mail in unread_orders():
    order = validate(read_notice(mail.pdf))   # raise before anything irreversible
    call = dispatch(order)
    mark_read(mail)                           # commit point: retries after this never re-dispatch
    confirm(mail.sender, call); log(call); track(call)
```

I created, searched and deleted a job in the browser and copied each request as a curl command. Replaying them from a shell showed the required headers and fields. A discovery script created a test job for each dropdown code, read it back through the search endpoint, and deleted it. The read-back found 22 values where the captured traffic showed 6.

Notice fields come from the PDF's text layer and are validated before any request.

```mermaid
sequenceDiagram
  participant F as Flow
  participant V as Web app
  participant S as Secret store
  F->>S: session younger than 48 h?
  alt no
    F->>V: get the login page
    V-->>F: form and anti-forgery token
    F->>V: post credentials and token
    V-->>F: session cookie
    F->>S: keep the cookie
  end
  F->>V: create the call, as the page's own request
  V-->>F: call id
```

The login loads the form, copies its hidden state and anti-forgery token, and posts them with the credentials. The session cookie is reused for up to 48 hours, the account owner's limit.

```python
def session():
    c = secrets.get("app/session")
    if c and age(c) < hours(48):            # account owner's limit
        return c
    page = http.get(LOGIN)
    token = find_input(page, "anti-forgery-token")
    r = http.post(LOGIN, data={"user": U, "pass": P, "token": token})
    return secrets.put("app/session", r.cookies["session"])

def dispatch(order):
    return http.post(CALLS, json=order,       # bare: a wrapped body made an empty call
                     cookies=session(),
                     headers={"x-requested-with": "XMLHttpRequest"})
```

A body wrapped in an extra JSON object returned success but created an empty job, so the body is sent bare. A tracker polls each job every 5 minutes and sends a final email when the job is complete.

```mermaid
flowchart LR
  T[(calls being watched)] --> M[every 5 minutes, look at each]
  M --> C{can it still be completed?}
  C -->|yes| T
  C -->|no| E[final email, team copied] --> D[stop watching]
  M -->|error| T
```

## Outcome

| Measure                                  | Value                |
| ---------------------------------------- | -------------------- |
| Double dispatches found, then closed     | 36 of 295            |
| Earlier dispatches with the wrong ID     | 294                  |
| Retry time per notice in a reader outage | 46 s                 |
| Longest time between logins              | 48 h                 |
| Checks for new orders, for completion    | every 1 min, every 5 min |

Two overlapping runs had read the same unread email before either marked it read. One run at a time removed the duplicates. Turnaround was not measured.

## Applicability

The approach fits any web app without an API, such as scheduling tools, property management systems and supplier portals.
