SIEM Integration
Forward 1Security detections, security alerts, audit logs, and policy scans into Splunk, Microsoft Sentinel, QRadar, Elastic, or any SIEM that can poll a REST endpoint.
This guide covers wiring 1Security into a SIEM end to end: what to forward, the polling loop that keeps the feed complete, and how to connect it to the common platforms. The API contract itself - every endpoint, parameter, and field - is in the API reference.
The model is pull-based. Your SIEM polls on a schedule and stores a watermark, so each poll fetches only what is new. No inbound connectivity to your network is required.
Before you start
You need an API key with the scopes for the feeds you plan to forward. Create
it in the dashboard under Settings → API (admin only) and copy the
1sec_live_… secret - it is shown once.
Confirm it works before building anything around it:
curl -H "Authorization: Bearer $ONESEC_API_KEY" \
https://api.1security.ai/api/v1/pingThe response echoes the tenant and the scopes the key carries. If a scope you
expected is missing, fix it now rather than debugging a 403 from inside a
connector.
The Playground tab on the same screen runs any endpoint against your tenant's live data from the browser - use it to preview payloads and dial in filters before committing them to connector configuration.
1. Decide what to forward
The feeds answer different questions, and they have very different volumes.
Two of them are alert feeds in the SOC sense - /security-alerts and
/detections - and those are the ones to start with.
| Feed | Volume | Forward it when |
|---|---|---|
/security-alerts | Low | Always. These are Defender / Sentinel detections, already enriched with the users, groups, and apps they touch. |
/detections | Low | Always. 1Security's own detection episodes - anomalous activity your policies and detectors flagged - and they exist nowhere else. Scope detections:read. |
/logs | High - the full activity feed | You want M365 activity in the SIEM with permission, device, and location context attached. |
/notifications | Low | You want an audit trail of what 1Security told whom - every notification decision: sent, withheld with the reason, or failed. Scope notifications:read. |
/policy-scans | Low | You want posture counts as metrics. Each row is a snapshot - "at time T, policy P matched N resources" - not an alert. Scope policy-scans:read. |
/detections returns stateful episodes in a kind-discriminated envelope:
today every row is kind: "anomaly", and the registry will grow (for example
impossible travel or phishing verdicts) without the envelope changing. Each
episode carries status (open · acknowledged · dismissed · resolved),
openedAt / lastSeenAt / resolvedAt, the detector that raised it, the
entity it concerns (null for tenant-wide aggregate episodes), and
kind-specific numbers under details. By default only alerted-tier episodes
are returned; add includeInfoTier=true if you also want the informational
tier.
/notifications is the notification log: one row per decision, with recipients, whether it left from your own mailbox or the platform mailer, the originating policy or anomaly detector, the effective severity, and for anomaly notifications the subject entity (resourceType / resourceId). Filter with source, kind, decision, reason, policyId, resourceType, resourceId, from, to; poll it like the other feeds. It is how "why didn't we get an email about X" is answered from your SIEM.
/policy-scans is deliberately not called an alert feed: a scan row is a point
sample of a policy's match count, useful as a metric series or for posture
reporting, but forwarding every scan as an alert is a straight path to alert
fatigue. If a policy crossing a line should page someone, configure an instant
trigger on the policy - the firing shows up in /notifications.
On a busy tenant /logs is the feed that drives SIEM licence cost. Filter at
the API rather than after ingest: severity=high,critical or a specific
action list keeps the volume proportional to what your detections actually
use. You can always widen it later.
2. The polling loop
Poll a closed time window and move the watermark only after the window has drained completely.
Closing the window is what makes this reliable. An open-ended query keeps growing while you page through it - new events land at the head of the ordering and shift rows between requests. A window with both ends bound is a fixed set: it cannot change while you read it.
Window by ingestion time
For /logs, use discoveredFrom and discoveredTo, not from and to.
discoveredAt is when 1Security ingested the event; occurredAt is when it
happened in M365. Microsoft can surface events hours after the fact, and
only ingestion time is monotonic - so only ingestion time guarantees you
never miss a late arrival.
For /security-alerts and /detections, from and to window on when
the alert or episode was raised; for /policy-scans, on when the scan ran.
Leave a short lag
End the window a minute or two behind the current clock rather than at "now". Events are still committing at the boundary, and a small lag keeps one from landing just after you read past it.
Sort ascending and drain
Add sort=discoveredAtAsc on /logs. That ordering is tie-broken by event
id, so every row has exactly one position in the page sequence. Follow
pagination.nextCursor with ?cursor= until hasMore is false.
Advance only on success
Move the watermark to the end of the window you just drained, and only
after every page succeeded. If any page fails, keep the old watermark and
retry the whole window - re-reading is cheap, and id dedupe absorbs the
overlap.
A complete poller, small enough to read in one sitting:
#!/usr/bin/env bash
set -euo pipefail
STATE_FILE=/var/lib/1security/watermark
LAG_SECONDS=120 # stay slightly behind now, so nothing commits past the edge
SINCE=$(cat "$STATE_FILE" 2>/dev/null || date -u -d '-1 hour' +%FT%TZ)
UNTIL=$(date -u -d "-${LAG_SECONDS} seconds" +%FT%TZ)
BASE="https://api.1security.ai/api/v1/logs"
QUERY="discoveredFrom=$SINCE&discoveredTo=$UNTIL&sort=discoveredAtAsc&limit=1000"
CURSOR=""
while :; do
URL="$BASE?$QUERY"
[ -n "$CURSOR" ] && URL="$URL&cursor=$CURSOR"
RESP=$(curl -sS --fail-with-body \
-H "Authorization: Bearer $ONESEC_API_KEY" "$URL")
echo "$RESP" | jq -c '.data[]' >> /var/log/1security-logs.ndjson
CURSOR=$(echo "$RESP" | jq -r '.pagination.nextCursor // empty')
[ -z "$CURSOR" ] && break
done
# Reached only if every page succeeded.
echo "$UNTIL" > "$STATE_FILE"Run it every one to five minutes. Overlapping windows slightly and relying on
id dedupe in the SIEM is safer than trying to hit exact boundaries.
3. The first run
Backfilling history is the same loop with a fixed sequence of windows instead of one rolling window. 1Security retains activity for up to three years, so decide how far back your SIEM actually needs before you start.
- Walk the range in fixed chunks - one to six hours each, depending on tenant size. Each chunk is a closed window, so each is independently restartable.
- Keep
limit=1000and stay inside the rate limit of 600 requests per minute. - Log which chunk you are on. If the backfill dies, you resume at that chunk rather than from the beginning.
- Run the backfill and the live poll as separate jobs with separate watermarks. Let the live poll start from now, and let the backfill work backwards behind it, so live coverage is never waiting on history.
4. Detections change after they are raised
Logs are immutable: once ingested, an event never changes. Detections are not -
they are stateful. A /detections episode stays open while the behaviour
continues, gets acknowledged or dismissed by an analyst, and eventually
resolves; /security-alerts rows move through Microsoft's own status and
classification lifecycle the same way. The stateful: true flag on a detection
is the explicit signal that a row you already ingested can change.
A watermark-only poll captures the moment an episode opened and never sees what
happened to it afterwards. If your SOC works detections inside the SIEM, add a
reconciliation sweep next to the incremental poll: on a slower schedule,
say hourly, re-fetch episodes raised in the last N days regardless of the
watermark and upsert them on id. State changes then land within one sweep.
# Hourly reconciliation - re-read the last 7 days and upsert on id
curl -H "Authorization: Bearer $ONESEC_API_KEY" \
"https://api.1security.ai/api/v1/detections?from=$(date -u -d '-7 days' +%FT%TZ)&limit=1000"Size the sweep window to how long an episode typically stays open in your
process. status and resolvedAt tell you where each one ended up.
/policy-scans rows are point samples and do not need a sweep - the one
exception is that an admin can edit a scan's status / isResolved in the
dashboard, so include them in the sweep only if you track that workflow.
5. Wire it into your SIEM
Use the Codeless Connector Platform, or a Logic App if you want explicit control over the loop.
- Auth:
Authorization: Bearer <key>as a connection secret. - Paging: follow
pagination.nextCursorinto thecursorquery parameter; stop whenhasMoreisfalse. - Destination: post
data[]to a custom table in Log Analytics through a Data Collection Endpoint and rule. - TimeGenerated: map from
occurredAt(logs),openedAt(detections),scannedAt(policy scans), orfirstActivityDateTime(security alerts). - State: store the window end in the connector's state, and advance it only after a complete drain.
Because 1Security also ingests Defender and Sentinel alerts, forwarding
/security-alerts back into Sentinel can duplicate what is already there.
Most teams forward /detections and /logs to Sentinel and leave
/security-alerts for SIEMs that do not already have that source.
6. Field mapping
| Use for | /logs | /detections | /security-alerts | /policy-scans | /notifications |
|---|---|---|---|---|---|
| Event time | occurredAt | openedAt | firstActivityDateTime | scannedAt | createdAt |
| Ingest time | discoveredAt | - | - | - | - |
| Dedup key | id | id | id | id | id |
| Severity | severity | severity | severity | severity | severity |
| Actor | actorName, actorId, actorIp | entity.name, entity.id | actorDisplayName | assignedUser | recipients |
| Resource | resourceName, resourceType | entity.type | users, groups, apps | resources, resourceType | resourceType, resourceId |
| Rule / title | action | detector.name, kind | title | name | policyName, source, kind |
| Status | - | status, stateful | status, classification | status, isResolved | decision, reason |
Two normalization details worth handling up front:
- Severity vocabularies differ. Logs, detections, policy scans, and
notifications use
info · low · medium · high · critical. Security alerts follow Microsoft's scale:informational · low · medium · high · unknown. Map both into your SIEM's own scale rather than passing the strings through. - Timestamps are ISO-8601 UTC (
2026-06-05T09:12:44Z) on every feed. Earlier releases returned policy-scan timestamps without theTandZ; that is fixed, so a plain ISO parser covers every feed.
7. Operating the integration
What is coming
Planned improvements to this integration path, so you can design around them:
- New detection kinds in
/detections- the envelope is kind-discriminated precisely so additions (impossible travel, phishing verdicts) arrive as newkindvalues with their owndetails, not as new endpoints. - Webhook push delivery, removing the polling loop for the detection feeds.
- Reference connectors for Sentinel and Splunk, so the wiring in
section 5 becomes an import rather than a build. The OpenAPI specification
already ships at
GET /api/v1/openapi.json. - Bidirectional sync, so resolving a detection in the SIEM resolves it in 1Security.
Two items from earlier revisions of this list have shipped: the OpenAPI
specification, and organization-wide keys
for MSSPs - one key covering a whole portfolio of tenants, selected per
request with X-Tenant-Id.
Next
NIS2
Meet NIS2's 24-hour and 72-hour incident reporting deadlines with three years of forensic history, live risk visibility, and evidence for every Article 21 measure - on standard Microsoft 365 licenses.
API Reference
The read-only REST API for pulling 1Security detections, Microsoft security alerts, audit logs, policy scans, and the notification log into your own tools - SIEM, SOC, or MSSP.