# Ade

> Parse documents and extract schema-shaped data with the ADE (Agentic Document Extraction) v2 APIs through the ade CLI. A local job-item store makes every run idempotent, resumable, and citable — repeat runs are free, interrupted runs resume, and every answer can cite element ids with visual evidence.

- Skill: `landing-ai/ade` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add landing-ai/ade`
- Raw SKILL.md: https://api.skillmd.com/api/skills/landing-ai/ade/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: landing-ai (https://skillmd.com/u/landing-ai)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/landing-ai/ade

---


# ade — agent skill

ade drives the ADE (Agentic Document Extraction) v2 document APIs
(parse, extract) and persists
everything it produces in a local store under `~/.ade`. You are the
judgment; the CLI is deterministic plumbing: it parses, persists,
searches, renders, and totals — it never ranks or selects for you.

## First move

```
ade help --json
```

One call returns the whole shipped surface: every command and flag, each
verb's `--json` result shape, the exit states, and the store layout.
Prefer it over N `--help` round trips. `ade help workflow` (also
`output`, `credentials`, `errors`) is the conceptual page behind it.
Check credentials with `ade auth status --json`; if logged out, set
`ADE_API_KEY`, or pipe a key in (`echo $KEY | ade auth login`) — no
terminal required either way. An API key acts in the organization it
was created in; browser (OAuth) sessions carry their own selection —
`status` reports it, `ade auth org list --json` shows the memberships,
and `ade auth org switch <org> --env <env>` changes it. If a run must
bill to a specific organization, confirm the selection *before*
parsing: re-running the same invocation after a switch joins the
recorded run instead of re-billing under the new organization.

## Conventions

- **Always pass `--json`.** Every command emits one stable JSON
  object/array on stdout — errors and pending payloads included. Human
  text is for terminals, not for parsing. The **whole result is on
  stdout**: the extraction, the matches, the paths. Reading the store is
  a convenience, never a requirement — and `ade help --json` publishes
  each verb's shape, so you never have to discover it by running.
- **`--id-only` when you only need the id.** `parse`, `extract`, and
  `find` take it: the id(s), one per line, nothing else —
  `JOB=$(ade parse -d report.pdf --id-only)`. Errors go to stderr, so a
  captured id is never a sentence.
- **Job item ids are the currency.** Every parse or extract run is a
  *job item*: one folder under `~/.ade/jobs/<job-item-id>/`, keyed by the
  invocation (verb × environment × source × content × params; URL
  sources are keyed by the URL alone — no content component, so remote
  drift does not re-key). Commands accept an
  unambiguous id prefix. Changing the file, its path, or any param mints
  a *sibling* item — nothing is silently replaced.
- **Commands are guarantees.** `parse` means "ensure this exact run
  exists", not "fire a request". Re-running an already-done invocation is
  **free** — served from disk with an `already parsed` notice and
  `"cached": true`. Only `--force` re-bills.

## The loop

1. **Parse** (free if already done):

   ```
   ade parse -d report.pdf --json
   ```

   The payload carries `job_item_id` — hold onto it. Everything else
   keys off it. The markdown and the elements projection stay on disk
   unless you ask for them: `--include markdown`, `--include elements`.

2. **Discover element ids** (local, instant, no API call):

   ```
   ade find JOB_ITEM_ID "total revenue" --json
   ade find JOB_ITEM_ID --type table_cell --page 3 --json
   ```

   (`--job JOB_ITEM_ID` is the equivalent flag spelling — repeat it to search
   several items in one call.)

   Matches are citation records: `{job_item_id, element_id, type, page,
   box, text}`. Filters compose (AND); results come in document order,
   never ranked — selection is your job.

3. **Extract** structured data against a JSON Schema:

   ```
   ade extract JOB_ITEM_ID --schema schema.json --json
   ```

   The result is its own job item. The payload's `extraction` key is the
   schema-shaped result itself — read it there, not off disk. Per-field
   evidence (`evidence`, persisted as `evidence.json`) joins extraction
   spans to element ids, pages, and boxes — non-empty fields the model
   synthesised rather than quoted are flagged `ungroundable`;
   empty-valued fields (blank cells, absent optionals — nothing to
   ground) are labeled `empty`. Neither is ever silently dropped.

4. **Cite and show.** `ade view JOB_ITEM_ID --json` builds a
   self-contained HTML viewer; deep links are the citation contract:

   ```
   ade view JOB_ITEM_ID --element-id ELEMENT_ID --json   # emits view.html#element=ELEMENT_ID
   ade crop JOB_ITEM_ID --element-id ELEMENT_ID --json   # PNG of that element's region
   ade crop JOB_ITEM_ID --type figure --json             # every figure, one command
   ```

   End answers with one deep link per job item, citing element ids.
   Use `crop` when you need to *look at* evidence mid-reasoning — it
   takes `find`'s own filters (`--type`, `--page`, `--all`), so a
   selection crops in one call and returns `crops[]`; never loop `find`
   into `crop` yourself.

## Reuse posture — parse bills once

Every parse the CLI ever runs is a reusable job item. Given a document
path, `extract -d FILE --schema …` reuses the latest completed parse of
that path+content (logged in the summary; no parse billed). If none
exists, it runs a **standalone parse first** — a normal, top-level
parse item, exactly as if you had run `parse -d` — then the extraction
referencing it: two billable runs, both itemised. Repeat `extract -d`
runs of the same file then reuse that parse, so it bills exactly once.

Prefer the explicit two-step (`parse -d`, then `extract JOB_ITEM_ID`) when
you want the same parse to feed several schemas — the id makes the reuse
visible.

## Pending and resume

Wait expiry is a normal outcome, not an error. If the poll budget
(`--wait`, default 600s) runs out, the command exits with code 3 and a
`{"status": "pending", "run_id": …, "job_item_id": …}` payload while the
run continues server-side (submitted work always completes and bills —
there is no cancel). **The recovery gesture is always the same command,
re-run.** A re-run joins the recorded run; it never resubmits and never
re-bills. Interrupts (Ctrl-C) stop the waiting, not the work — same
gesture. `--wait 0` submits and returns immediately.

Submit-and-return, then collect later — the pending payload carries the
id too, so this works in both steps:

```
JOB=$(ade parse -d report.pdf --wait 0 --id-only)   # exit 3, run continuing
ade parse -d report.pdf --json                       # re-run: resumes, never re-bills
```

## Exit codes

| Code | State | Meaning |
|------|-------|---------|
| 0 | ok | Success — the payload is on stdout. |
| 1 | failed | The run failed or the target cannot serve the request. |
| 2 | usage | The invocation itself was wrong; nothing was submitted. |
| 3 | pending | Wait budget expired; the run continues server-side. Re-run the same command to resume. |
| 4 | rate_limited | Submit was rate-limited and the wait budget ran out before a run existed; nothing billed. Re-run to retry. |

## Reading the store directly

Optional, never required: every result is already on stdout. Artifacts
are plain files — `history list --json` gives every item's records, and
the summaries print each item's store path.

- `parse.json` — raw ParseResponse (ground truth, verbatim)
- `parse.md` — the parse markdown extraction spans index
- `elements.json` — flat element records with inline grounding; boxes
  are normalized `{xmin, ymin, xmax, ymax}` fractions of page size in
  `[0, 1]`
- `extract.json` — raw extraction result with per-field spans
- `evidence.json` — the field→box join (element ids, pages, boxes)

One vocabulary note when reading these files: on-disk records
(`meta.json`, `job.json`, `parse/ref.json`) spell the server-side run id
as `job_id` — the wire contract's name for the same value `--json`
payloads report as `run_id`. Neither is ever the job item id.

Prefer `find` over loading `elements.json` into context: it returns
joined records, not lines. `history clear JOB_ITEM_ID` deletes an item;
clearing a parse item cascades to the extractions referencing it.

## Sharp edges

- **`--options` is a verbatim ParseOptions pass-through** — the server
  rejects unknown keys with a 422, so consult `ade help parse` for
  the accepted keys rather than guessing.
- **Parse variants coexist.** The same document parsed with different
  params is a sibling item, not a replacement. An extraction goes stale
  only when its exact parse item is re-run in place with `--force`.
- **Moving or editing a file changes identity** — the next parse of it
  is a new job item that bills. The old item and its evidence stay
  intact and true of the run they came from.
- **`--markdown` extractions have no page evidence** (there is no parse
  to join against) — evidence degrades to spans-only, and `view`
  renders the markdown pane alone.
- **URL parses have no local bytes**, so page imagery renders from an
  attached copy: `parse --document-url … --keep-copy` fetches it at
  parse time (reliable — pre-signed URLs expire); otherwise the first
  `view`/`crop` downloads it automatically (`--no-download` skips;
  the payload records `downloaded`, and on a failed fetch `view`
  degrades to an empty preview with `download_error` while `crop`
  errors). Markdown, elements, and extractions work either way.

