> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nikaplanet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect Pipelines, FME Flow and ArcGIS to NIKA GeoEngine

> Call NIKA GeoEngine from your pipelines, scripts or FME Flow, and share results with ArcGIS Image Server within each vendor's licence terms.

Keep the tools you already run. Call NIKA GeoEngine from your pipelines, scripts or FME Flow, and share results with imagery servers such as ArcGIS Image Server, for the steps that need GPUs, high memory or controlled access.

## Integrate the way each vendor allows

Every pattern on this page keeps each product inside its own licence terms.

> [card] Each product's own installer
>
> FME Flow with Safe Software's Helm chart, ArcGIS Enterprise on Kubernetes with Esri's deployment tools. NIKA GeoEngine never repackages, redistributes or wraps another vendor's software.

> [card] Your licence, your seat count
>
> Each product runs under the licence you hold for it, and the vendor's own engine and core limits keep applying.

> [card] Documented interfaces only
>
> Public REST APIs, webhooks and shared storage formats such as Cloud-Optimised GeoTIFF. No private endpoints, and no automating desktop products.

> [card] Separate namespaces and nodes
>
> Each product gets its own namespace, quota and node pool, so neither starves the other.

> [!WARNING]
>
> **FME: use FME Flow for automation.** Safe Software's licence does not allow FME Form (formerly FME Desktop) to be used as a stand-alone translation service for others. Server-side automation belongs on FME Flow. Never run FME Form inside a worker. Confirm your use with Safe Software if it is unusual.

## The integration contract

Every integration does the same three things. Only the tool that makes the calls changes.

> [!NOTE]
>
> **Illustrative API shape.** Paths and fields show the pattern. The released job API gives the final names; the surrounding pipeline or FME logic does not change.

**1. Submit**

Send the worker, exact version, machine class and inputs, plus the item list for a batch.

```http theme={null}
POST https://<geoengine-host>/v1/jobs
Authorization: Bearer <service-account-token>
Content-Type: application/json

{
  "worker": "cloud-removal-ndvi",
  "version": "2.3.1",
  "machine_class": "highmem",
  "inputs": { "output-dir": "s3://results/2026-11/flevoland" },
  "items": [
    { "scene": "s3://scenes/S2B_31UFU_20261102.tif" },
    { "scene": "s3://scenes/S2B_31UFU_20261105.tif" }
  ]
}
```

```json theme={null}
{ "job_id": "j-7q2c9xk4", "status": "queued", "tasks": 2 }
```

**2. Poll**

`GET /v1/jobs/j-7q2c9xk4` until `completed` or `failed`.

**3. Read the outputs**

Read results from shared storage. Later steps, maps and imagery servers read from the same place.

### Rules that keep integrations robust

* **Service account per pipeline,** granted only the workers it needs, restricted ones included.
* **Pin the worker version.** Never "latest" in production.
* **Fail the step when the job fails,** set a timeout longer than your longest job, and cancel the job if the step gives up waiting.
* **Keep the step thin.** It submits and waits; the compute runs on NIKA GeoEngine's pools.
* **Keep the token in a secret,** never in a pipeline definition.

## Choose your tool

> [tab] Your pipelines and scripts
>
> Anything that can make an HTTPS call can submit a job: a scheduler, a CI job, a cron entry or a script. Your tool keeps ownership of ordering, retries and triggers; NIKA GeoEngine runs the geoprocessing step. This small client uses only the Python standard library, so it runs in any step or container.
>
> ```python geoengine_job.py theme={null}
> """Submit a NIKA GeoEngine job, wait for it, and exit non-zero if it fails.
>
> Usage: GEOENGINE_URL=https://<geoengine-host> GEOENGINE_TOKEN=... \
>        python geoengine_job.py payload.json [timeout_seconds]
> """
> import json, os, sys, time, urllib.error, urllib.request
>
> BASE = os.environ["GEOENGINE_URL"].rstrip("/")
> HEADERS = {"Authorization": "Bearer " + os.environ["GEOENGINE_TOKEN"],
>            "Content-Type": "application/json"}
>
>
> def call(method, path, body=None):
>     data = json.dumps(body).encode() if body is not None else None
>     req = urllib.request.Request(BASE + path, data=data, method=method, headers=HEADERS)
>     with urllib.request.urlopen(req, timeout=30) as resp:
>         return json.load(resp)
>
>
> def run(payload, timeout_seconds=7200):
>     job_id = call("POST", "/v1/jobs", payload)["job_id"]
>     print("submitted " + job_id, file=sys.stderr)
>     deadline, errors = time.time() + timeout_seconds, 0
>     while time.time() < deadline:
>         try:
>             status = call("GET", "/v1/jobs/" + job_id)["status"]
>         except urllib.error.HTTPError as exc:
>             if exc.code < 500:                # auth or request problem: retrying will not help
>                 sys.exit("gateway returned HTTP %d for job %s" % (exc.code, job_id))
>             status = None                     # 5xx: treat like a network blip
>         except OSError:                       # network blip while polling
>             status = None
>         if status is None:
>             errors += 1
>             if errors > 5:
>                 sys.exit("lost contact with the gateway while polling job " + job_id)
>         else:
>             errors = 0
>             if status == "completed":
>                 return job_id
>             if status == "failed":
>                 sys.exit("job " + job_id + " failed")
>         time.sleep(15)
>     try:                                      # nobody is waiting any more: stop the job
>         call("POST", "/v1/jobs/" + job_id + "/cancel")
>     except OSError:
>         pass
>     sys.exit("job " + job_id + " timed out and was cancelled")
>
>
> if __name__ == "__main__":
>     with open(sys.argv[1]) as f:
>         payload = json.load(f)                # worker, version, machine_class, inputs, items
>     timeout = int(sys.argv[2]) if len(sys.argv) > 2 else 7200
>     print(run(payload, timeout))              # the job ID, for later steps
> ```
>
> Pass the printed job ID to later steps, and keep the token in your tool's secret store.
>
> > [!WARNING]
> >
> > **Retries submit a new job.** If your tool retries a failed step, the step submits a fresh job. Retry only infrastructure errors, not a job that failed because of the worker's own code, which would fail again and spend compute twice. Keep workers safe to rerun.
>
> **Fan out with one batch job.** Send all items in one request when they share a worker and machine class, so you track one job ID and one resource record. Submit separate jobs only when items need different workers or versions.

> [tab] FME Flow
>
> FME Flow is strong at data integration across hundreds of formats; NIKA GeoEngine is strong at heavy imagery processing. A common shape: FME on the edges (ingest, then publish), NIKA GeoEngine in the middle.
>
> **An FME Flow workspace calls NIKA GeoEngine**
>
> | Step | Transformer | What it does |
> | - | - | - |
> | Submit | HTTPCaller | `POST /v1/jobs` with the JSON body and a bearer token from an FME Flow web connection |
> | Read the ID | JSONExtractor | Extracts `job_id` |
> | Wait | HTTPCaller in a looping custom transformer, with a Decelerator | Polls the status every few seconds |
> | Decide | Tester, Terminator | Continues on `completed`, fails the workspace on `failed` |
>
> **NIKA GeoEngine results start FME Flow work.** When a batch completes, your pipeline calls FME Flow's **REST API V4** (`/fmeapiv4/…`) to submit a workspace, or posts to an **Automation webhook** to load results into an enterprise database.
>
> > [!WARNING]
> >
> > **Use REST API V4.** V3 is removed from FME Flow 2026.1, and from 2026.1 a trailing slash on a V4 endpoint can return `404`.
>
> **Both on one cluster**
>
> * Deploy FME Flow with **Safe Software's official Helm chart**, in its own namespace, under your FME Flow licence.
> * Define engine groups in its Helm values, and pin them to FME's own nodes with `nodeSelector` and `tolerations`. Standard and CPU-usage engines stay counted by FME Flow's own licensing.
> * Point both products at the same storage so data flows between them without copies.

> [tab] ArcGIS Image Server
>
> ArcGIS Image Server publishes and analyses imagery for ArcGIS clients. NIKA GeoEngine runs the custom, GPU-heavy or restricted processing around it. They meet in shared storage, not inside each other.
>
> **Pattern: NIKA GeoEngine produces, Image Server serves**
>
> 1. **Write Cloud-Optimised GeoTIFFs** from your workers to an object store bucket.
> 2. **Register that bucket** as a cloud store in ArcGIS Enterprise, using ArcGIS's own data store registration.
> 3. **Create or refresh imagery layers** from the new rasters through ArcGIS's public REST API or ArcGIS API for Python, triggered by your pipeline when the batch completes.
>
> ArcGIS users get the results as hosted imagery layers. OnlyMap apps can stream the same COGs directly with no extra copy.
>
> | Use ArcGIS Image Server for | Use NIKA GeoEngine for |
> | - | - |
> | Serving imagery to ArcGIS clients | Your own Python or R, and open-source models |
> | ArcGIS raster functions and raster analytics your analysts already use | GPU inference that needs specific CUDA libraries |
> | | Workers restricted to cleared roles |
> | | Large fan-out across hundreds of scenes |
>
> **Both on one cluster**
>
> * Deploy ArcGIS Enterprise on Kubernetes with **Esri's deployment tools**, into the dedicated namespace Esri requires, on a supported environment.
> * Image hosting and distributed raster analysis carry their own Esri licences (Image Services, Raster Analytics). They stay with your ArcGIS deployment, not with NIKA GeoEngine.
> * Give each product its own quota and node pool. Both autoscale independently within one cluster.

## Sharing one cluster

| Concern | Recommendation |
| - | - |
| **Namespaces** | One per product, each with its own quota |
| **Node pools** | NIKA GeoEngine's CPU, high-memory and GPU pools tainted and separate from other products' pools |
| **Pipeline step pods** | Thin clients on a general pool, with no GPU or high-memory requests |
| **Storage** | One shared object store or volume, so outputs flow between tools without copies |
| **Network** | Explicit rules for each namespace that calls the gateway |
| **Access** | Cluster RBAC for who administers each product; roles in NIKA GeoEngine for who runs each worker |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.