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.

StepOperationSide-effectScope
Find capturescatalog.searchreadcatalog:read
Preview priceorders.archive.estimatereadorders:read
Place orderorders.archive.placespendorders:write
Track orderorders.get / orders.listreadorders:read
Cancelorders.cancelspendorders: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 creditstop up 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).

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.