Processing & analytics
Geopera turns imagery into answers two ways:
- Processing jobs produce new data — a clipped raster, a derived product — written
back into your project as items + assets. These are
external_spendoperations (they reserve credits) and run on registered workers. - Analytics computes results over existing items — statistics, indices, change detection — and returns the numbers (and any rendered output). Analytics is a single extensible operation, so new methods appear without changing the API surface.
All of the heavy geospatial computation runs in the backend. The client sends parameters; the platform produces the correct, reproducible result — the same answer whether the caller is the portal, an SDK, or an AI agent.
Processing jobs
| Operation | Side-effect | Scope | Use |
|---|---|---|---|
clip.create_from_item | spend | processing:process | Clip a source item to an AOI |
processing.create | spend | processing:process | Create a job (dispatch later) |
processing.create_and_dispatch | spend | processing:process | Create + dispatch atomically |
processing.dispatch | spend | processing:process | Dispatch a pending job |
processing.jobs.list / processing.jobs.get | read | processing:read | Track jobs |
Clip an item to an AOI
The most common job: cut a source item down to your area of interest as a new item.
curl -s -X POST https://api.geopera.com/v1/op/clip.create_from_item \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: c0ff...-unique" \
-d '{
"source_item_id": "it_3a9c...",
"project_id": "your-project-id",
"aoi_geometry": { "type": "Polygon", "coordinates": [ ... ] },
"asset_keys": ["data"],
"output_name": "harbour_clip"
}'{
"job": { "id": "job_77a1...", "status": "pending", "...": "..." },
"assets_to_clip": 1,
"asset_keys": ["data"]
}The job reserves credits on creation, runs on a worker, and registers its outputs back into your project — each output item carries a provenance edge to the job and to the source item it was clipped from.
Create and dispatch a job
For arbitrary registered job types (job_type_id), create and dispatch in one atomic,
idempotent call:
curl -s -X POST https://api.geopera.com/v1/op/processing.create_and_dispatch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: d15a...-unique" \
-d '{
"project_id": "your-project-id",
"job_type_id": "bathymetry",
"input_params": { "source_item_id": "it_3a9c...", "...": "job-specific params" },
"target_collection_id": "optional-collection-id"
}'{
"job": { "id": "job_91bd...", "status": "dispatched", "...": "..." },
"dispatch": { "accepted": true, "...": "worker dispatch result" }
}Use processing.create instead when you want to create the job (reserving credits) but
dispatch it later with processing.dispatch — a deliberate two-step path for clients
that stage work.
How a job runs (and reports back)
A dispatched job runs on a registered worker. The worker is handed a scoped,
job-bound token — a worker principal whose only authority is to register its own job’s outputs, nothing else. When it finishes, it calls back through the kernel to
register the produced items, and provenance is recorded at that write. (See the principal model.) This is why a delivered
output always has a complete lineage chain — there is no privileged side door that
skips it.
COG conventions
For outputs to render and analyse efficiently, processing writes Cloud-Optimized
GeoTIFFs with overviews and embedded statistics (ZSTD compression, internal tiling, STATISTICS=YES). Uploads you bring in should follow the same convention — the
platform then auto-computes render rescaling from the embedded stats, so every client
gets correct visuals with no per-client logic.
Analytics
Analytics runs through one operation:
POST /v1/op/analytics.executeThe body names the method and passes its parameters:
{
"operation": "<method name>",
"params": { "...": "method-specific parameters" }
}The parameters are validated against that method’s own schema, so a bad request fails
with 422 carrying the expected schema. Discover the available methods and their
schemas with the read operations analytics.operations.list and analytics.operations.get.
Built-in methods include:
| Method | What it does |
|---|---|
zonal_stats | Summary statistics (min/max/mean/percentiles) of a band or index over an AOI |
time_series | A value (e.g. an index) sampled across an item’s time dimension |
change_detection | The change in a spectral index between a before and after item |
Change detection over two items
Change detection compares a spectral index between two acquisitions — the canonical “what changed here between these dates” question:
curl -s -X POST https://api.geopera.com/v1/op/analytics.execute \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operation": "change_detection",
"params": {
"before_item_id": "it_jan...",
"after_item_id": "it_jun...",
"index_name": "NDVI",
"change_threshold": 0.2,
"geometry": { "type": "Polygon", "coordinates": [ ... ] }
}
}'band_mapping is auto-detected from the items’ band names when omitted; pass it
explicitly (e.g. { "RED": 1, "NIR": 4 }) to override. The response carries the change
statistics and a reference to the rendered change raster.
Running over many items
To analyse a stack — many dates over the same AOI — call analytics.execute per pair
(or per item) and aggregate, or use time_series to sample one item’s time dimension
in a single call. Because each call is independent and stateless, fanning out across
items is just concurrent requests; there is no special batch endpoint to learn.
Extending analytics — adding a new “process”
Analytics methods are pluggable. A method is a small class that declares its name,
its typed input_model, and an execute() — for example change_detection and zonal_stats are each one such class. Registering a new one makes it:
- callable immediately through the same
analytics.executeoperation (no new route), - discoverable through
analytics.operations.list/.get(its schema is published), - available to every client at once — REST, the Python and TypeScript SDKs, and the MCP agent tools — because they’re all projections of the same registry.
So adding a new process to apply over one or many assets (a new index, a new detector, a classifier) is a localized change: write the method, declare its schema, and the entire multi-client surface gains it with the same auth, validation, and result contract. This is the practical payoff of the operation model — capability grows without growing the API’s shape.