When a route has tracking-mode=simple, POST/PUT/PATCH requests return 202 Accepted with tracking information.
For tracking-mode=attachments, see Attachments API.
POST /api/orders HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json
{"item": "widget", "quantity": 5}HTTP/1.1 202 Accepted
Location: http://gateway:9443/status/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"status": "accepted",
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"_links": {
"status": {
"href": "/status/550e8400-e29b-41d4-a716-446655440000"
}
}
}|
Note
|
The Location header contains an absolute URI. The _links.status.href contains a relative URI (proxy-safe).
|
GET /status/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1{
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"status": "ACCEPTED",
"acceptedAt": "2026-03-13T10:00:00Z",
"updatedAt": "2026-03-13T10:00:00Z"
}Only for routes with tracking-mode=attachments. The parent request is waiting for attachments to arrive (see Attachments API).
{
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"status": "COLLECTING_ATTACHMENTS",
"acceptedAt": "2026-03-13T10:00:00Z",
"updatedAt": "2026-03-13T10:00:00Z"
}While in this status, POST /attachments/{traceId} uploads are accepted. Once the minimum attachment count (attachments-min-count) is reached, the gateway automatically transitions the status to PROCESSED. Attachments are still accepted up to attachments-max-count even after this transition. The attachment window is open only while the status is COLLECTING_ATTACHMENTS or PROCESSED; it closes when the status transitions to PROCESSING, REJECTED, ERROR, or any other status set by downstream flow logic (409 Conflict).
{
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PROCESSING",
"acceptedAt": "2026-03-13T10:00:00Z",
"updatedAt": "2026-03-13T10:00:05Z"
}{
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"status": "PROCESSED",
"acceptedAt": "2026-03-13T10:00:00Z",
"updatedAt": "2026-03-13T10:00:10Z"
}The error object is an RFC 9457 Problem Details object.
It is emitted whenever any error* cache field yields a member, regardless of the entry’s status — REJECTED and ERROR are the common cases, but a PROCESSED entry carrying a warning detail returns the object just the same.
It is absent when no error* field yields a member.
A field that is present but malformed counts as absent for this purpose, so an entry whose only error* fields are malformed returns no error key rather than an empty object.
{
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"status": "REJECTED",
"acceptedAt": "2026-03-13T10:00:00Z",
"updatedAt": "2026-03-13T10:00:10Z",
"error": {
"type": "https://example.com/problems/validation",
"status": 422,
"title": "Validation Failed",
"detail": "The submitted order failed validation",
"instance": "/status/550e8400-e29b-41d4-a716-446655440000",
"violations": [
{
"pointer": "/item",
"detail": "must not be blank"
},
{
"pointer": "/quantity",
"detail": "must be greater than 0"
}
]
}
}Every member is optional and is emitted only when its producing cache field is populated.
status is a JSON number and violations is a JSON array; the remaining members are strings.
violations[].pointer values are RFC 6901 JSON Pointers into the submitted payload, passed through verbatim from the producer.
To correlate related requests, include the X-Parent-Trace-Id header:
POST /api/payments HTTP/1.1
Authorization: Bearer <jwt-token>
Content-Type: application/json
X-Parent-Trace-Id: 550e8400-e29b-41d4-a716-446655440000
{"orderId": "ORD-123", "amount": 99.99}The status response will include parentTraceId:
{
"traceId": "660e8400-...",
"status": "ACCEPTED",
"parentTraceId": "550e8400-e29b-41d4-a716-446655440000",
...
}When request tracking is enabled, FlowFiles carry these additional attributes:
| Attribute | Description |
|---|---|
|
The unique trace ID (UUID) for this request |
|
The parent trace ID (if |
|
ISO 8601 timestamp when the request was accepted (e.g., |
Downstream flows update the request status using NiFi’s standard PutDistributedMapCache processor.
| Property | Value |
|---|---|
Cache Entry Identifier |
|
Cache Entry Value |
JSON status string (see below) |
Distributed Cache Service |
Same |
Update to PROCESSING at the start of your flow:
{"traceId":"${rest.trace.id}","status":"PROCESSING","acceptedAt":"${rest.trace.accepted.at}","updatedAt":"${now():format('yyyy-MM-dd''T''HH:mm:ss''Z''','UTC')}"}|
Note
|
The simplest approach is to use UpdateAttribute to build the JSON, then PutDistributedMapCache.
|
[RestApiGateway] --> [UpdateAttribute: set status=PROCESSING]
--> [PutDistributedMapCache: update status]
--> [Your Processing Logic]
--> [UpdateAttribute: set status=PROCESSED or REJECTED]
--> [PutDistributedMapCache: update final status]Six optional cache fields populate the response’s error object.
Writing any one of them makes the gateway emit error — no particular status value is required.
{
"traceId": "${rest.trace.id}",
"status": "REJECTED",
"acceptedAt": "...",
"updatedAt": "...",
"errorType": "https://example.com/problems/validation",
"errorStatus": "422",
"errorTitle": "Validation Failed",
"errorDetail": "Validation failed: ${error.message}",
"errorInstance": "/status/${rest.trace.id}",
"errorViolations": "[{\"pointer\":\"/item\",\"detail\":\"must not be blank\"}]"
}| Cache field | Response member | Notes |
|---|---|---|
|
|
URI reference identifying the problem type. |
|
|
Written as a String holding an integer; the gateway parses it into a JSON number. The value must fall within the RFC 9457 §3.1.2 range |
|
|
Short, human-readable summary. |
|
|
Explanation specific to this occurrence. |
|
|
URI reference identifying this occurrence. |
|
|
Written as a String holding a serialized JSON array; the gateway parses it into a real JSON array. Entries conventionally carry |
All six are written as JSON string scalars in the cache entry; only errorStatus and errorViolations are re-typed on the way out.
A blank value counts as absent.
|
Important
|
A malformed errorStatus (not an integer, or outside the 100-599 range) or errorViolations (not a serialized JSON array) omits only that one response member, without failing the response.
Any sibling members that are well-formed are still returned, the request never fails, and the gateway records a WARN in the NiFi log — REST-125 for errorStatus, REST-126 for errorViolations.
A malformed field counts as absent rather than as a populated component, so when it is the only error* field present the error key is omitted entirely instead of being returned empty.
Check the NiFi log when a member you expected does not appear in the response.
|
|
Note
|
These six keys are reserved. Unlike free-form top-level cache keys they are no longer echoed back as additional response fields — they feed the error object instead.
|
-
Update to PROCESSING early: immediately after the gateway emits the FlowFile
-
Update to PROCESSED/REJECTED at the end: after all processing is complete
-
Use RETRY sparingly: only for genuinely transient errors that will be retried
-
Populate the error fields meaningfully:
errorTitleanderrorDetailat minimum; adderrorType,errorStatus,errorInstanceand per-fielderrorViolationswhen the failure is structured, so consumers can act on it instead of parsing prose
| Property | Default | Description |
|---|---|---|
Distributed Map Cache Client |
(none) |
Required for request tracking. Points to a |
Status Endpoint Enabled |
|
Whether the |
Status Endpoint Auth Mode |
|
Authentication for the status endpoint |
Status Endpoint Required Roles |
(empty) |
JWT roles required for the status endpoint |
Status Endpoint Required Scopes |
(empty) |
JWT scopes required for the status endpoint |
Route tracking is configured via restapi.<name>.tracking-mode (see Configuration Reference).
For tracking-mode=attachments, see Attachments API.
A DistributedMapCacheServer controller service must be running in NiFi for the cache client to connect to.
-
Add
DistributedMapCacheServercontroller service -
Configure port (default: 4557)
-
Enable the service
-
Add
DistributedMapCacheClientServicecontroller service -
Configure server hostname and port
-
Enable the service
-
Reference it in the gateway processor’s "Distributed Map Cache Client" property
The gateway writes one cache entry per tracked request and sets no expiry on it.
Entries are removed explicitly only when a request never reaches a terminal state (for example, a queue-full 503, or in-flight containers discarded on processor shutdown).
Entries for requests that complete normally — PROCESSED, REJECTED, ERROR — are never removed by the gateway.
The entry count therefore grows with total request volume, and the only bound is the eviction policy of the DistributedMapCacheServer.
Sizing it is an operational decision, not a detail that can be left at its defaults.
Configure these properties on the DistributedMapCacheServer controller service:
| Property | Recommended | Rationale |
|---|---|---|
Eviction Strategy |
|
The default is |
Maximum Cache Entries |
Peak accept rate x longest polling window, plus headroom |
The default is |
Eviction is silent and irreversible. Once an entry is evicted, GET /status/{traceId} cannot distinguish that trace ID from one that never existed: both return the same RFC 9457 404 Problem Detail documented under "Unknown traceId (404)" above.
{
"type": "https://github.com/cuioss/nifi-extensions/blob/main/doc/reference/error-reference.adoc",
"title": "Not Found",
"status": 404,
"detail": "No status found for traceId: 550e8400-e29b-41d4-a716-446655440000"
}|
Important
|
A 404 therefore does not mean the request was never accepted — it may mean the request completed and its entry has since been evicted. Consumers must not treat 404 as proof that a submission failed. Size the cache so that eviction cannot occur inside the window in which consumers are expected to poll.
|
|
Note
|
The same 404 appears after a NiFi restart when the DistributedMapCacheServer has no "Persistence Directory" configured, because the cache is then held in memory only and starts empty.
|