<!-- Source: https://docs.geopera.com/api-reference/guides/ordering · Markdown for LLMs -->

# Ordering archive imagery

An **archive order** buys imagery that already exists in a provider's catalog. The flow
is three operations: search the catalog, estimate the price, place the order. Pricing
is always computed by the backend over the area of interest — the client never decides
the price.

| Step | Operation | Side-effect | Scope |
|---|---|---|---|
| Find captures | `catalog.search` | read | `catalog:read` |
| Preview price | `orders.archive.estimate` | read | `orders:read` |
| Place order | `orders.archive.place` | spend | `orders:write` |
| Track order | `orders.get` / `orders.list` | read | `orders:read` |
| Cancel | `orders.cancel` | spend | `orders:write` |

## 1. Search the catalog

`catalog.search` queries a host's catalog with STAC-style filters and enriches
commercial results with price information.

```bash
curl -s -X POST https://api.geopera.com/v1/op/catalog.search \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "host_name": "the-host",
    "collections": ["..."],
    "bbox": [151.10, -33.92, 151.30, -33.78],
    "datetime": "2024-01-01T00:00:00Z/2024-06-30T23:59:59Z",
    "query": { "eo:cloud_cover": { "lte": 20 } },
    "limit": 25
  }'
```

Key inputs: `host_name` (required), one of `collections` / `ids`, a spatial filter
(`bbox` or `intersects`), `datetime` (an instant or a `start/end` range), `query` for
property filters, and `limit`. Paginate with the `next` token returned in the response.
For very large result sets use `catalog.search_stream`, an NDJSON variant.

Each returned feature is a capture you can order. Note its `id` and `geometry` — those
are what you pass to the next steps.

## 2. Preview the price

`orders.archive.estimate` is a **read** that returns the exact, server-authoritative
price for a cart of captures. Nothing is persisted and no money moves.

```bash
curl -s -X POST https://api.geopera.com/v1/op/orders.archive.estimate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "captures": [
      { "id": "scene-abc", "geometry": { "type": "Polygon", "coordinates": [ ... ] } },
      { "id": "scene-def", "geometry": { "type": "Polygon", "coordinates": [ ... ] } }
    ],
    "splitByDate": false
  }'
```

```json
{
  "groups": [
    { "captures": ["scene-abc", "scene-def"], "aoi_km2": 12.4, "credits": 4200, "aud": 42.00 }
  ],
  "errors": [],
  "total_aud": 42.00,
  "total_credits": 4200
}
```

- **`total_credits`** is an integer credit count (100 credits = A$1). **`total_aud`** is
  the dollar equivalent.
- **`groups`** breaks the cart into priced groups (one per AOI/date depending on
  `splitByDate`).
- **`errors`** lists any captures that couldn't be priced (e.g. an unsupported product),
  so you can fix the cart before placing.

Set `splitByDate: true` to price each acquisition date as its own group.

## 3. Place the order

`orders.archive.place` reserves credits and creates the order. It's an
`external_spend` operation, so:

- it requires the `orders:write` scope (an **API key cannot place archive orders** — this
  is a deliberate guard on the money path; use a user or delegated principal), and
- it accepts an **`Idempotency-Key`** header so a retry after a dropped connection never
  places two orders.

```bash
curl -s -X POST https://api.geopera.com/v1/op/orders.archive.place \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9d1f...-unique-per-order" \
  -d '{
    "projectId": "your-project-id",
    "captures": [
      { "id": "scene-abc", "geometry": { "type": "Polygon", "coordinates": [ ... ] } }
    ],
    "licenseType": "standard",
    "splitByDate": false
  }'
```

```json
{
  "id": "f2b1c0de-...",
  "status": "processing",
  "orderType": "archive",
  "billingMode": "credit",
  "currency": "AUD",
  "totalAud": 42.00,
  "totalCredits": 4200,
  "groups": [ { "...": "the priced groups, as ordered" } ]
}
```

Inputs: `captures` (required, non-empty), `projectId` (the owning workspace),
`licenseType`, and `splitByDate`. The response echoes the final server price and the
new order's `id` and `status`.

### Billing modes

`billingMode` tells you how the order was paid:

- **`credit`** — credits were reserved from the org's prepaid balance.
- **`card`** — a free org without a prepaid wallet authorized its card at checkout (a
  hold that is captured on delivery and voided if the order fails).
- **`enterprise`** — an invoiced enterprise org; the amount accrues to its monthly
  invoice.

### Payment errors

If the order can't be paid, you get `402 Payment Required`:

- **Insufficient credits** — [top up](/api-reference/guides/billing) or enable auto top-up.
- **No / declined card** — attach a payment method.
- **3-D Secure required** — the `402` body carries a `client_secret`; complete the bank
  challenge, then retry with the **same `Idempotency-Key`**.

## 4. Track and cancel

Poll `orders.get` (or list with `orders.list`) until the order is terminal:

```bash
curl -s https://api.geopera.com/v1/op/orders.get \
  -H "Authorization: Bearer $TOKEN" \
  -G --data-urlencode 'id=f2b1c0de-...'
```

When the order is delivered, its imagery lands in your project as STAC items, each
carrying a provenance edge back to this order (see
[Provenance & lineage](/api-reference/guides/provenance)).

To cancel, call `orders.cancel` — it's policy-gated (only cancellable states) and
refunds or voids the reservation:

```bash
curl -s -X POST https://api.geopera.com/v1/op/orders.cancel \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "id": "f2b1c0de-..." }'
```

## Resource-style route

The same capability is exposed on the familiar `POST /v2/orders` route for clients that
follow STAC/UP42 conventions. It runs the identical operation underneath, with the same
scope, pricing, idempotency, and provenance.
