# Headless Delivery Check — read a status that lives only on a JavaScript page

A sanitised reference implementation. Drop it into Claude Code, point it at your own
tracking page, adapt the selectors. Generic on purpose: no carrier specifics, no real
tracking numbers.

## The problem

Plenty of the status you need has no API. It exists only on a page that renders in the
browser with JavaScript, sometimes behind a bot check. You cannot fetch it with a plain
HTTP request; the HTML that arrives is an empty shell and the real content paints later.

This layer drives a real browser headlessly, lets the page render, reads the status, and
writes it back to wherever you track things. No API required.

```
tracking refs ──> [headless browser: load, wait for render] ──> read status ──> write back
                         one fresh session per ref                       to your sheet/DB
```

## Minimal reference implementation

```python
# delivery_check.py — reference only. Swap in your own URL pattern + selectors + store.
from playwright.sync_api import sync_playwright

def check_one(ref, base_url, status_selector):
    """One ref, one fresh context. Return the rendered status text or None."""
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        ctx = browser.new_context()        # fresh context: no cached state bleeds across refs
        page = ctx.new_page()
        try:
            page.goto(base_url.format(ref=ref), wait_until="domcontentloaded")
            page.wait_for_selector(status_selector, timeout=15000)   # wait for the JS paint
            return page.inner_text(status_selector).strip()
        except Exception:
            return None                     # treat unreadable as unknown, never as delivered
        finally:
            browser.close()                 # close fully so the next ref starts clean

def run(refs, base_url, status_selector, write_back):
    for ref in refs:
        status = check_one(ref, base_url, status_selector)
        write_back(ref, status)             # update your sheet / DB row for this ref
```

## Why it works

- **A real browser renders the JavaScript.** You read what a human would see, not the
  empty shell a raw request returns.
- **One fresh session per ref.** Single page apps cache aggressively and reuse state
  across lookups in the same session, which silently gives you the previous ref's
  answer. Closing the browser between refs is the fix. Never batch them in one session.
- **Unreadable is unknown, never delivered.** If the selector never appears, return None
  and leave the row flagged for a human. Fail safe: a missing status must never be
  recorded as a positive outcome.

## Adapt it

- Find the stable selector once by hand in dev tools, then keep it in config so a site
  redesign is a one line change.
- If there is a bot check, slow down, add realistic headers, and respect rate limits.
  Do not hammer it.
- Cache results and only re-check refs that are still in flight, on a sensible cadence
  rather than constantly.

Built for my own logistics flow, runs every couple of days; this is the generalised
version. Take it, point it at your own tracking page, tell me where it breaks.
