Skip to main content
Solved

REST Collector State Tracking Fails To Assign NewerThan Parameter Correctly

  • February 25, 2026
  • 15 replies
  • 11 views

RSharp
  • Participating Frequently
This message originated from Cribl Community Slack.
Click here to view the original link.

I am trying to implement state tracking for a rest collector. How do I reference the state in the collect parameters trying to assign the parameter 'newerThan' an integer with the javascript prevState.lastActivityId == null ? 7369027 : prevState.lastActivityId When I run a preview it just passes url encoded of my javascript, but the expression should set to 7369027

Best answer by Jon Rust

Not Robert's fault. Quirk in the way we handle JS fields that may be plain text To be sure: Start all JS entries with backticks () and use template expressions (${}`) inside to enclose the JS bits. And for text always start with a single or double quote

15 replies

Jon Rust
Forum|alt.badge.img
  • Employee
  • February 25, 2026
A preview run won't reference state. You'll need a full run i believe

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
the get url cribl is sending should just send that integer 7369027 right ?

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026


Links for this message:
image.png

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
but it's sending the text of the javascript, that is what I am trying to figure out at least initially.

Jon Rust
Forum|alt.badge.img
  • Employee
  • February 25, 2026
Looks like you're missing some backticks or related in the config. can you grab a screencap and post here or DM me?

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
``jon helped I had to wrap it in ${myjs}````

Jon Rust
Forum|alt.badge.img
  • Employee
  • Answer
  • February 25, 2026
Not Robert's fault. Quirk in the way we handle JS fields that may be plain text To be sure: Start all JS entries with backticks () and use template expressions (${}`) inside to enclose the JS bits. And for text always start with a single or double quote

Randy Corelli
One other detail with the state value references is to make sure you increment the value by 1 second (or 1 millisecond depending on timestamp precision) to avoid duplicates

Randy Corelli
i was just helping a customer with NinjaOne a few mins ago and happy to help with this one if needed

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
Hey Randy I was trying to use the lastActivityId to track state, seemed simpler

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
since the events all have an id field and the API gives you a lastActivityID as part of the top level payload (before you break the events)

Randy Corelli
That's a good callout. If it's an ID vs. timestamp then you don't need to increment it. It's just a detail I see lots of people miss with time based state tracking so I like to mention it

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
``{ "type": "collection", "ttl": "4h", "ignoreGroupJobsLimit": false, "removeFields": [], "resumeOnBoot": false, "schedule": { "cronSchedule": "/5 *", "maxConcurrentRuns": 1, "skippable": true, "run": { "rescheduleDroppedTasks": true, "maxTaskReschedule": 1, "logLevel": "debug", "jobTimeout": "60m", "mode": "run", "timeRangeType": "relative", "timeWarning": {}, "expression": "true", "minTaskSize": "1MB", "maxTaskSize": "10MB", "stateTracking": { "stateUpdateExpression": "{lastActivityId}", "stateMergeExpression": "(prevState.lastActivityId || 0) > newState.lastActivityId ? prevState : newState", "enabled": true } }, "enabled": true }, "streamtags": [], "workerAffinity": false, "collector": { "conf": { "discovery": { "discoverType": "none", "discoverMethod": "post_with_body", "pagination": { "type": "none" }, "enableStrictDiscoverParsing": false, "enableDiscoverCode": true, "discoverUrl": "'https://REDACTED.ninjarmm.com/ws/oauth/token'", "discoverRequestParams": [], "discoverRequestHeaders": [ { "name": "Content-Type", "value": "REDACTED" } ], "discoverBody": "{\n \"grant_type\": \"client_credentials\",\n \"client_id\": \"REDACTED\",\n \"client_secret\": \"REDACTED\",\n \"scope\": \"monitoring\"\n}", "formatResultCode": "const json = pm.response.json();\n\nif (json && json.access_token) {\n pm.environment.set(\"ninja_access_token\", json.access_token);\n}\n\nif (json && json.expires_in) {\n // store an approximate expiry timestamp (ms since epoch)\n pm.environment.set(\"access_token_expires_at\", (Date.now() + (json.expires_in * 1000)).toString());\n}\n\npm.test(\"Got access_token\", function () {\n pm.expect(json).to.have.property(\"ninja_access_token\");\n});" }, "collectMethod": "get", "pagination": { "type": "none", "pageField": "after", "sizeField": "pageSize", "size": 200, "maxPages": 50, "zeroIndexed": false }, "authentication": "login", "timeout": 0, "useRoundRobinDns": false, "disableTimeFilter": false, "decodeUrl": false, "rejectUnauthorized": true, "captureHeaders": true, "stopOnEmptyResults": true, "safeHeaders": [], "retryRules": { "type": "backoff", "interval": 1000, "limit": 5, "multiplier": 2, "maxIntervalMs": 20000, "codes": [ 429, 503 ], "enableHeader": true, "retryConnectTimeout": false, "retryConnectReset": false, "retryHeaderName": "retry-after" }, "__scheduling": { "stateTracking": {} }, "loginUrl": "https://REDACTED.ninjarmm.com/ws/oauth/token", "loginBody": "client_id=${username}&client_secret=${password}&grant_type=client_credentials&scope=monitoring", "getAuthTokenFromHeader": false, "authHeaderKey": "Authorization", "authHeaderExpr": "Bearer ${token}", "clientSecretParamName": "client_secret", "collectUrl": "'https://REDACTED.ninjarmm.com/v2/activities'", "collectRequestHeaders": [], "clientSecretParamValue": "REDACTED", "username": "REDACTED", "password": "REDACTED", "tokenRespAttribute": "access_token", "authRequestHeaders": [ { "name": "Content-Type", "value": "application/x-www-form-urlencoded" } ], "collectRequestParams": [ { "name": "newerThan", "value": "${state.lastActivityId == null ? 0 : state.lastActivityId}" }, { "name": "pageSize", "value": "1000" } ] }, "destructive": false, "encoding": "utf8", "type": "rest" }, "input": { "type": "collection", "staleChannelFlushMs": 10000, "sendToRoutes": true, "preprocess": { "disabled": true }, "throttleRatePerSec": "0", "breakerRulesets": [ "Ninjaone_Activities_Array" ] }, "savedState": { "restCollectorState": { "data": { "lastActivityId": 0 } } }, "id": "NinjaOne_Activities" }``

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
{
  "id": "Ninjaone_Activities_Array",
  "minRawLength": 256,
  "rules": [
    {
      "condition": "true",
      "type": "json_array",
      "timestampAnchorRegex": "/^/",
      "timestamp": {
        "type": "auto",
        "length": 150
      },
      "timestampTimezone": "local",
      "timestampEarliest": "-420weeks",
      "timestampLatest": "+1week",
      "maxEventBytes": 51200,
      "disabled": false,
      "parserEnabled": false,
      "shouldUseDataRaw": false,
      "jsonExtractAll": true,
      "eventBreakerRegex": "/[\\n\\r]+(?!\\s)/",
      "name": "NinjaActivitiesArray",
      "jsonArrayField": "activities",
      "parentFieldsToCopy": [
        "lastActivityId"
      ]
    }
  ]
}

RSharp
  • Author
  • Participating Frequently
  • February 25, 2026
I believe I got the state tracking via the lastactivityid working. I can't seem to crack the pagination, despite having a "pageSize" parameter for the endpoint it returns no other data to help manage pages or point to the next page.