SIEM: REST API Poll¶
The REST API Poll test checks your application's log or audit API the way a SIEM uses it. Helena plays the SIEM, or a log collector, in the most common integration: REST API polling. Splunk, Microsoft Sentinel, QRadar and similar products call a log API every few minutes to fetch new events. The Okta System Log API, Microsoft Graph audit logs and Salesforce Event Monitoring all work this way.
Each run:
- Works out the time window to ask for: the last N minutes, or everything since the last passed run.
- Calls the log endpoint with your API key as
Authorization: Bearer {api-key}. - Follows pagination (Link header, a next-page URL, or a cursor) up to a page limit.
- Checks the responses and the collected events: status, JSON shape, event count, required fields, and timestamps inside the window.
- Writes each request, each page's metadata and a sample of the events to the Test Log.
It can also check the opposite: that the API refuses an invalid or revoked key.
sequenceDiagram
participant H as Helena (collector)
participant A as Your log API
H->>H: Resolve window [since, until]
H->>A: GET endpoint?since=…&until=… (Bearer api-key)
A-->>H: 200 [events…] + next page
loop Until no next page, an empty page, or max pages
H->>A: GET next page (same origin only)
A-->>H: 200 [events…]
end
H->>H: Assert status, shape, count, fields, timestamps
Sidebar: SIEM → REST API Poll
Before you begin¶
- You need the log API's endpoint URL, which must be public HTTPS.
- You need an API key or access token that the API accepts as a Bearer token.
Bearer authentication only
Helena always sends Authorization: Bearer {api-key}. APIs that need a different scheme are not supported
yet, such as Okta's SSWS {token} for API tokens. Okta, for example, accepts OAuth access tokens as
Bearer tokens.
Field reference¶
Log API¶
| Field | Key | Required | Default | Supported values / notes |
|---|---|---|---|---|
| Method | http_method |
✅ | GET |
GET sends the parameters and window as a query string. POST sends them as a JSON body, for search-style APIs. |
| Endpoint URL | endpoint_url |
✅ | — | Public HTTPS URL, up to 2048 characters. A query string in the URL is kept. |
| API key | secret api_key |
✅ | — | Up to 4096 characters. Sent as Authorization: Bearer {key}. Encrypted, never shown again. On edit, leave it blank to keep the stored key. |
| Parameters | query_params |
— | Static parameters sent on every request, one name=value per line, up to 20. E.g. limit=100. |
Time window¶
| Field | Key | Required | Default | Supported values / notes |
|---|---|---|---|---|
| Each poll asks for | window_mode |
✅ | lookback |
The last N minutes (lookback): a fixed lookback from the time of the run. Everything since the last passed run (since_last_run): works like a collector checkpoint. See Time windows. |
| Lookback (minutes) | lookback_minutes |
✅ | 5 |
1–10080 (7 days). Also used as the first run's window in since_last_run mode. |
| Time format | time_format |
✅ | iso8601 |
ISO 8601 (2026-10-28T12:00:00Z), Unix seconds (unix), or Unix milliseconds (unix_ms). |
| Window start parameter | since_param |
— | Name of the parameter that carries the window start, e.g. since or startTime. Leave blank to send no time filter. |
|
| Window end parameter | until_param |
— | Name of the parameter that carries the window end, e.g. until. Must differ from the start parameter. |
Parameter names may contain letters, digits and _ . - [ ] $ : @, up to 100 characters.
Response¶
| Field | Key | Required | Default | Supported values / notes |
|---|---|---|---|---|
| Events path | events_path |
— | Where the event array is in the JSON body: a key or dot path, e.g. value (Microsoft Graph), records (Salesforce) or data.events. Leave it blank if the body is the array (Okta). |
|
| Event timestamp field | timestamp_field |
— | Event field to check against the window, e.g. published or createdDateTime. It accepts ISO 8601 or Unix seconds or milliseconds. |
|
| Pagination | pagination |
✅ | none |
See Pagination. |
| Next page URL path | next_url_path |
✅ (body next URL) | — | Where the next-page URL is in the body, e.g. @odata.nextLink or nextRecordsUrl. |
| Cursor path | cursor_path |
✅ (cursor) | — | Where the cursor is in the body, e.g. meta.next_cursor. |
| Cursor parameter | cursor_param |
✅ (cursor) | — | Parameter name used to send the cursor back, e.g. cursor. |
| Maximum pages | max_pages |
✅ | 5 |
1–20. Always 1 when pagination is None. |
Paths try the whole string as a literal key first, then fall back to a dot path. That way keys that contain dots,
such as @odata.nextLink, work as written.
Expected result¶
| Field | Key | Default | Supported values / notes |
|---|---|---|---|
| The log API should | expected_outcome |
success |
Return log events (success) or Refuse the API key (rejection). |
| HTTP status | expected_status |
— | 100–599. Blank: any 2xx on success, 401 or 403 on refusal. |
| Minimum events | min_events |
0 |
0–1,000,000, counted across all pages. 0 accepts an empty window. |
| Maximum events | max_events |
— | Optional, at least Minimum events. |
| Required event fields | required_fields |
— | Field paths every event must have (non-null, non-empty), one per line, up to 20. E.g. uuid, eventType, actor.id. |
| Specification checks | strict_spec_checks |
On | Also check that the response is JSON, and that a 401 includes a WWW-Authenticate: Bearer … challenge (RFC 6750 §3). |
Time windows¶
| Mode | Window sent | Notes |
|---|---|---|
| The last N minutes | now − lookback → now |
Simple and stateless. Consecutive scheduled runs overlap or leave gaps unless the schedule matches the lookback. |
| Everything since the last passed run | The previous passed run's start time → now |
Works like a real collector's checkpoint. A failed or errored run doesn't move the checkpoint, so the next run asks for the missed period again. The first run uses the lookback. The window is capped at 7 days. |
The window is only sent if you set a Window start parameter (and optionally an end parameter). The run's Build log request step records the window it used and where it came from:
window.source |
Meaning |
|---|---|
lookback |
The fixed lookback |
previous_run |
Since the last passed run |
first_run_lookback |
No passed run yet, so the lookback was used |
previous_run_capped |
The last passed run was more than 7 days ago, so the window was cut to 7 days |
When Event timestamp field is set, every event must fall within the window, with ±60 seconds allowed for clock skew.
Pagination¶
| Option (UI label) | pagination |
How the next page is found |
|---|---|---|
| None (single request) | none |
One request only. |
| Link header, rel="next" (RFC 8288) | link_header |
Link: <https://…?after=x>; rel="next". Used by the Okta System Log. |
| Next page URL in the body | body_next_url |
A URL at Next page URL path, which can be absolute or path-absolute. Used by Microsoft Graph (@odata.nextLink) and Salesforce (nextRecordsUrl). |
| Cursor token in the body | cursor |
A token at Cursor path, sent back in Cursor parameter with the original method and parameters. |
Next-page URLs are always fetched with GET. Paging stops at Maximum pages, on an empty page, on a non-2xx
page, or when a next URL or cursor repeats.
Same-origin rule
Your API key is only ever sent to the endpoint's own origin (scheme, host and port). A next-page URL that points anywhere else is not followed, and an assertion fails. This stops a misconfigured or compromised API from redirecting your key elsewhere.
What gets checked¶
When the log API should return events:
| Assertion | When |
|---|---|
HTTP status / HTTP status is 2xx |
Always (first page) |
Content-Type is JSON |
Specification checks on, and a 2xx response |
Events list found |
2xx. The value at Events path, or the root, must be a JSON array. |
Page N returned a list of events |
Each page after the first |
Next page URL stays on the log API origin |
When pagination returned URLs |
Event count |
Minimum events > 0 or Maximum events set |
Every event has `field` |
One per required field, when there are events |
`field` within the poll window |
Event timestamp field set and there are events |
When the log API should refuse the key (only the first request is sent):
| Assertion | When |
|---|---|
HTTP status / API key refused (HTTP 401 or 403) |
Always |
No log events returned |
Always. Fails on a 2xx. |
401 includes a Bearer WWW-Authenticate challenge (RFC 6750 §3) |
Specification checks on, and a 401 |
Reading the results¶
| Step | Shows |
|---|---|
| Build log request | Method, URL, parameters, the API key's fingerprint and the window (since, until, minutes, source) |
| Fetch page N | Status, selected headers (content-type, link, www-authenticate, retry-after, rate-limit headers, x-request-id), event count, and the next page or why it wasn't followed. The body is included when the page failed or in a refusal test. |
| Collected events | total, pages, logged, and a sample of up to 25 events (64 KB at most). The total is always exact. |
Logged events go through the same redaction as the rest of the log.
Examples¶
Okta System Log¶
| Field | Value |
|---|---|
| Endpoint URL | https://acme.okta.com/api/v1/logs |
| API key | An OAuth access token with okta.logs.read |
| Parameters | limit=100 and sortOrder=ASCENDING |
| Each poll asks for | Everything since the last passed run |
| Window start / end parameter | since / until, ISO 8601 |
| Events path | (blank, because the body is an array) |
| Event timestamp field | published |
| Required event fields | uuid, eventType, actor.id |
| Pagination | Link header, 5 pages |
| Cron expression (UTC) | */5 * * * * |
Microsoft Graph sign-in logs¶
| Field | Value |
|---|---|
| Endpoint URL | https://graph.microsoft.com/v1.0/auditLogs/signIns |
| Parameters | $top=50 |
| Events path | value |
| Pagination | Next page URL in the body, path @odata.nextLink |
| Event timestamp field | createdDateTime |
Graph filters by time with $filter=createdDateTime ge …, not a plain parameter. Leave Window start
parameter blank, and add a static $filter parameter if you need one.
Salesforce event log files¶
| Field | Value |
|---|---|
| Endpoint URL | https://acme.my.salesforce.com/services/data/v61.0/query?q=SELECT+Id,EventType,LogDate+FROM+EventLogFile |
| Events path | records |
| Pagination | Next page URL in the body, path nextRecordsUrl |
| Required event fields | Id, EventType |
Checking that a revoked key is refused¶
Store the revoked key, set The log API should to Refuse the API key, and leave Specification checks
on. The run passes on a 401 with WWW-Authenticate: Bearer error="invalid_token". It fails if any events are
returned.
Troubleshooting¶
| Symptom | Likely cause |
|---|---|
Events list found fails |
Events path is wrong. Check the Fetch page 1 body to see where the array is. |
… within the poll window fails |
The API ignores the window parameters, or uses a different name or format for them. Check Time format and the parameter names. |
Next page URL stays on the log API origin fails |
The API returns next links on a different host (e.g. a CDN). Point Endpoint URL at that host, if it serves the first page too. |
| Every run returns 0 events | The window is too short for your event volume. Increase Lookback (minutes), or keep Minimum events at 0. |
401 with a valid key |
The API expects a scheme other than Bearer. See Before you begin. |