1Security
Reference

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.

The 1Security REST API lets a SOC, MSSP, or SIEM pull your tenant's activity and detections on a schedule. It is read-only: a key can read the tenant's data and cannot change anything in 1Security or in your Microsoft 365 tenant.

ResourceEndpointWhat it is
Audit logs/logsNormalized M365 activity, enriched by 1Security
Detections/detections1Security's own detection episodes (anomalies today, more to come)
Security alerts/security-alertsMicrosoft Defender / Sentinel-sourced alerts
Notifications/notificationsThe notification log - every decision: sent, suppressed, failed
Policy scans/policy-scansPolicy evaluation snapshots - "at time T, policy P matched N"
Evidence pack/evidence/agentsDated AI agent governance evidence pack for auditors
Actions/actionsThe remediation ledger - staged, approved, executed, failed, reverted
Compliance status/compliance/statusPer-framework compliance readiness - every control with its status and metrics
Compliance snapshots/compliance/snapshotsThe dated snapshot history behind the status - itself evidence of ongoing oversight

This page is the contract: endpoints, parameters, fields, and error codes. A machine-readable version is served at GET /api/v1/openapi.json (OpenAPI 3.1, no authentication required) - import it into Postman or a client generator. For how to wire it into a specific SIEM, see the SIEM integration guide.

Delivery is pull-based: you poll, we do not push. Outbound webhook delivery is planned - until it ships, the polling pattern in the integration guide is the supported approach.

Base URL

The API is served from the same deployment that runs your 1Security instance, under the /api/v1 prefix.

DeploymentBase URL
Cloud (SaaS)https://api.1security.ai/api/v1
BYOC / On-Premisehttps://<your-1security-host>/api/v1

In a BYOC or on-premise deployment the API stays inside your own network perimeter. Everything else on this page is identical across deployments.

All examples below use the SaaS base URL and assume the key is in an environment variable:

export ONESEC_API_KEY="1sec_live_…"

Authentication

Every request is authenticated with an API key. A regular key is bound to exactly one tenant and grants read access to that tenant's data only; an organization-wide key (MSSP) reads any tenant of its organization, selected per request.

Creating a key

In the dashboard, go to Settings → API and choose Create API key. The API keys tab lists every key with its scopes, status, and last use. Key management is admin-only.

You choose a name, the scopes the key should carry, and optionally an expiry date. In a multi-tenant dashboard ("All tenants") you also pick which member tenant the key is for - a key is always bound to one tenant, so a grouped dashboard needs one key per member (or one organization-wide key); the keys table shows every member's keys with a tenant column. The full secret - 1sec_live_… - is displayed once, at creation. 1Security stores only a cryptographic hash of it, so it cannot be shown again or recovered by anyone, including support. Copy it straight into your SIEM's credential store.

Sending the key

Pass it as a bearer token (preferred) or in X-API-Key:

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  https://api.1security.ai/api/v1/ping
curl -H "X-API-Key: $ONESEC_API_KEY" \
  https://api.1security.ai/api/v1/ping

Start with /ping. It confirms the key is valid and shows which tenant and scopes it maps to, which rules out the most common setup mistakes before you build a connector around it.

The Settings → API screen also has a Playground tab: pick an endpoint, fill in parameters, and run the request against your tenant's live data straight from the browser - with the matching curl command ready to copy. It is the fastest way to see real payloads and test filters before writing connector code.

Scopes

Each key carries one or more read scopes. A request to an endpoint whose scope the key lacks returns 403 - grant only what the integration needs.

Prop

Type

Keys created before /policy-scans was renamed from /monitoring-alerts may still carry the monitoring-alerts:read scope. It keeps working - the server treats it as policy-scans:read - but request the new name on new keys.

Organization-wide keys (MSSP)

An MSSP or partner operating several tenants under one organization can create a single organization-wide key instead of one key per tenant: in the create-key dialog, tick Organization-wide key (MSSP) (the option appears only for accounts working inside an organization).

An organization-wide key selects its target tenant per request with the X-Tenant-Id header - either the 1Security tenant id or the Azure tenant id:

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  -H "X-Tenant-Id: <tenant id or Azure tenant id>" \
  "https://api.1security.ai/api/v1/detections"

GET /tenants lists every tenant the key can reach, so an integration iterates: pull the tenant list, then poll each tenant with its id in the header. The rules:

  • A data request without X-Tenant-Id returns 400 TENANT_REQUIRED; only /ping and /tenants work without a selected tenant.
  • A tenant outside the key's organization returns 403 TENANT_NOT_IN_ORGANIZATION - organization membership is the authorization boundary.
  • The plan gate applies per selected tenant, and the rate limit stays per key - all tenants share the one budget.
  • The same header works on the MCP endpoint, so one MCP server entry per tenant, each with its own X-Tenant-Id.

Rotating and revoking

Revocation takes effect immediately - the next request with that key returns 401. To rotate without downtime: create the replacement key, deploy it to the connector, confirm traffic on the new key in Settings → API (each key shows when it was last used), then revoke the old one.

Treat an API key like a password. Anyone holding it can read the tenant's activity and alerts. Store it in a secret manager, never in a repository or a connector's plain-text configuration file.

Response format

Every successful list response uses the same envelope:

{
  "data": [
    /* … */
  ],
  "pagination": {
    "nextCursor": "eyJvIjo1MH0",
    "hasMore": true,
    "limit": 50
  }
}

Single-object endpoints (/ping, /security-alerts/{id}) return { "data": { … } } with no pagination block.

Fields are whitelisted per endpoint, so the shape is stable: new fields may be added over time, existing ones are not removed or renamed without notice.

Timestamps

All timestamps are UTC and use full ISO-8601 (2026-06-05T09:12:44Z) on every endpoint. Earlier releases returned policy-scan timestamps without the T separator and Z designator; that is fixed.

Pagination

List endpoints return at most limit items (default 50, max 1000) plus an opaque cursor. Pass the returned nextCursor back as ?cursor= to fetch the next page. When hasMore is false, nextCursor is null and you have reached the end.

Treat the cursor as meaningless - echo it back unchanged. Its encoding is an implementation detail and will change.

Page over a closed time window. Paging a live, open-ended result set is not safe: new events keep arriving at the head of the ordering while you page, which shifts rows between requests. Bound both ends of the window (discoveredFrom and discoveredTo on /logs, from and to on the other feeds) and the set stops changing underneath you while you drain it. The integration guide turns this into a concrete polling loop.

For a fully deterministic drain on /logs, add sort=discoveredAtAsc: that ordering is tie-broken by event id, so every row has exactly one position. On the other feeds, rows sharing an identical sort value have no guaranteed relative order, so keep windows narrow and deduplicate on id.

Rate limits

Keys are limited to 600 requests per minute, applied per key on a best-effort basis. Successful responses carry:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetWhen the window resets, in epoch seconds

Exceeding the limit returns 429 with a Retry-After header. Polling each endpoint once per minute with a large limit sits far inside the budget; the limit exists to blunt runaway loops, not to shape normal integration traffic.

Endpoints

GET /ping

Connection test. Returns the tenant and scopes the key maps to. Requires no particular scope - any valid key works. An organization-wide key without a tenant selector returns tenantId: null.

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  https://api.1security.ai/api/v1/ping
{
  "data": {
    "tenantId": "01H…",
    "keyId": "01J…",
    "name": "Splunk prod",
    "scopes": ["logs:read", "detections:read", "security-alerts:read"],
    "organizationWide": false
  }
}

GET /tenants

The tenants this key can read. A tenant-bound key returns its single tenant; an organization-wide key returns every tenant of its organization - the ids an integration iterates with X-Tenant-Id. Requires no particular scope.

{
  "data": [
    { "id": "01H…", "name": "Contoso", "azureTenantId": "d3adb33f-…" }
  ]
}

GET /logs

Normalized M365 activity events, enriched with the actor, resource, application, device, and location 1Security resolved for each one. Requires logs:read.

Two different times are recorded per event, and the difference matters when polling:

  • occurredAt - when the action happened in Microsoft 365.
  • discoveredAt - when 1Security ingested it. M365 can surface events well after the fact, so this is the one to poll on. An event that arrives late has an old occurredAt but a current discoveredAt.

Query parameters

Prop

Type

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/logs?severity=high,critical&limit=100"
{
  "data": [
    {
      "id": "01J…",
      "occurredAt": "2026-06-05T09:12:44Z",
      "discoveredAt": "2026-06-05T09:13:01Z",
      "action": "FileDownloaded",
      "description": "Downloaded Q3-forecast.xlsx",
      "severity": "high",
      "actorId": "01H…",
      "actorName": "jane@contoso.com",
      "actorType": "user",
      "actorIp": "20.42.0.0",
      "resourceId": "01H…",
      "resourceName": "Q3-forecast.xlsx",
      "resourceType": "file",
      "workload": "SharePoint",
      "sourceType": "azure",
      "sourceName": "Microsoft 365",
      "clientApp": "OneDrive Sync",
      "deviceId": "01H…",
      "deviceName": "LAPTOP-4471",
      "isManagedDevice": false,
      "applicationId": "01H…",
      "applicationClientId": "ab12…",
      "applicationDisplayName": "Microsoft SharePoint",
      "externalEventId": "…"
    }
  ],
  "pagination": { "nextCursor": "eyJvIjoxMDB9", "hasMore": true, "limit": 100 }
}

Response fields: id, occurredAt, discoveredAt, action, description, severity, actorId, actorName, actorType, actorIp, resourceId, resourceName, resourceType, workload, sourceType, sourceName, clientApp, deviceId, deviceName, applicationId, applicationClientId, applicationDisplayName, isManagedDevice, externalEventId.

GET /detections

1Security's own detection episodes. Requires detections:read.

Rows use a kind-discriminated envelope: the fields below exist for every detection, and everything specific to one kind rides under details. Today every row is kind: "anomaly"; new kinds (for example impossible travel or phishing verdicts) will arrive as new kind values with their own details shapes - the envelope itself does not change.

By default only alerted-tier episodes are returned - the ones that cleared the tenant's alert line. Pass includeInfoTier=true to also receive informational-tier episodes.

Query parameters

Prop

Type

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/detections?status=open&limit=50"
{
  "data": [
    {
      "id": "01J…",
      "kind": "anomaly",
      "severity": "high",
      "status": "open",
      "stateful": true,
      "openedAt": "2026-06-05T09:12:44Z",
      "lastSeenAt": "2026-06-05T11:40:02Z",
      "resolvedAt": null,
      "summary": "jane@contoso.com performed 412 actions (Permanent deletions) in one day, about 2,475% above their usual 16 per day.",
      "detector": { "id": "01J…", "name": "Mass file deletion" },
      "entity": { "type": "user", "id": "01H…", "name": "jane@contoso.com" },
      "details": {
        "level": "alerted",
        "current": 412,
        "peakCurrent": 412,
        "baseline": 16,
        "score": 5.8,
        "peakScore": 5.8,
        "diffPercent": 2475,
        "actionGroup": "destructive_permanent",
        "activityLabel": "Permanent deletions",
        "activityDescription": "Deletions nothing can bring back: hard-deleted mail, files purged past the recycle bin, wiped version history, deleted sites and lists.",
        "episodeCount": 1
      }
    }
  ],
  "pagination": { "nextCursor": null, "hasMore": false, "limit": 50 }
}

Response fields: id, kind, severity, status, stateful, openedAt, lastSeenAt, resolvedAt, summary, detector (id, name), entity (type, id, name - null for tenant-wide aggregate episodes), and details (level, current, peakCurrent, baseline, score, peakScore, diffPercent, actionGroup, activityLabel, activityDescription, episodeCount).

summary is a ready-made English sentence describing the episode in the entity's own numbers - suitable as an incident title in a SIEM. activityLabel and activityDescription are the human-readable mirror of actionGroup, so consumers never have to decode taxonomy values like privilege_granted themselves.

Detections are stateful - status and resolvedAt change after the episode first appears. Pair the incremental poll with an hourly reconciliation sweep that upserts on id - see the integration guide.

GET /detections/{id}

One detection episode in full. Unlike the list, no tier gating - a by-id lookup answers for info-tier episodes too. Requires detections:read.

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/detections/01J…"

Returns { "data": <detection> } in the same shape as the list items, or 404 NOT_FOUND when the id does not exist in this tenant.

GET /policy-scans

Policy evaluation snapshots: each row records that at scannedAt the policy matched resources resources. Requires policy-scans:read (keys with the legacy monitoring-alerts:read scope keep working).

This is a metric series, not an alert feed - forward it when you want posture counts in the SIEM. A policy whose count crossing a line should notify someone does that through its alert rule, and the decision lands in /notifications.

Query parameters

Prop

Type

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/policy-scans?severity=high&limit=50"

Response fields: id, name, severity, status, isResolved, resourceType, resources, assignedUser, description, createdFrom, scannedAt, resolvedAt.

scannedAt is when the scan ran, and it is the same value that from and to filter on. Use it as the event time in your SIEM.

GET /policy-scans/{id}

One policy scan snapshot in full. On top of the list shape it carries policyId and automations (the JSON-stringified automations payload the scan carried). Requires policy-scans:read.

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/policy-scans/01J…"

Returns { "data": <scan> }, or 404 NOT_FOUND when the id does not exist in this tenant.

GET /notifications

The notification log: one row per notification decision - sent, suppressed with the reason, or failed - across policy digests, instant triggers, and anomaly notifications. Requires notifications:read.

Query parameters

Prop

Type

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/notifications?decision=suppressed&from=$(date -u -d '-1 day' +%FT%TZ)"

Response fields: id, createdAt, source, kind, channel, decision, reason, policyId, policyName, subjectType, subjectId, subjectCount, resourceType, resourceId, severity, recipients, recipientSource, senderMode, mailSubject.

GET /evidence/agents

The AI agent compliance evidence pack: one dated JSON document covering agent inventory, permission grants, activity, lifecycle and threat events, instruction changes, the available-to-install shelf, third-party app access, and a log retention attestation - each section mapped to the articles it evidences across every registered framework (EU AI Act, NIS2, Polish KSC and KRiBSI, GDPR, DORA, ISO/IEC 42001, ISO/IEC 27001, SOC 2, NIST CSF 2.0, HIPAA). Requires evidence:read.

Optional from and to query parameters (ISO-8601) bound the covered activity period; the default window is the trailing 183 days. An optional framework parameter (a registry key such as dora or iso_27001) keeps only the sections and legend entries for that framework - the same scoped pack the framework view's Export evidence pack button downloads. Returns { "data": <pack> } with no pagination - the pack is one document, identical to the dashboard's download.

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/evidence/agents"

The full pack structure, section list, and article mapping live on the Compliance evidence export page.

GET /compliance/status

Compliance readiness in three layers. requirements are what the organisation does or measures, each evaluated once: status is met, at_risk, not_met or - honestly - not_evaluated, evaluation says where the verdict comes from (measured, no_data, attested, manual, not_applicable), metricsJson carries the numbers, attestation the compliance officer's record for manual duties, and articles every article across every framework that cites it. frameworks lists every registered framework (EU AI Act, NIS2, Polish KSC and KRiBSI, GDPR, DORA, ISO/IEC 42001, ISO/IEC 27001, SOC 2, NIST CSF 2.0, HIPAA) with selected (declared applicable by the tenant), readinessPercent and its controls (articles), each rolled up from the requirements it cites. changes is the per-framework diff against the latest snapshot. Headline counts (metCount and friends) are over requirements cited by the selected frameworks, not over articles. It is the same document the compliance dashboard renders and the weekly snapshot persists. Requires evidence:read.

Takes no parameters. Returns { "data": <status> } with no pagination - one document per call, evaluated live.

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/compliance/status"

GET /compliance/snapshots

The dated snapshot history behind /compliance/status: the weekly cron and every manual snapshot, newest first, each carrying the full status document as it was persisted at the time. A stored series of these is itself evidence of ongoing oversight. Requires evidence:read.

Takes limit and cursor only; items have id, takenAt, trigger (scheduled or manual), and status (the full document).

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/compliance/snapshots?limit=12"

GET /actions

The remediation ledger: every action 1Security staged, an admin approved or rejected, the platform executed, and an independent probe confirmed - with its trigger (policy or human), scope, and current attention state. This is the audit trail behind "what did the platform change in Microsoft 365 and who authorised it". Requires actions:read.

Query parameters

Prop

Type

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/actions?rung=applied&from=$(date -u -d '-30 day' +%FT%TZ)"

Response fields: id, actionName, actionType, policyId, policyName, actorId, actorName, actorKind, rollupRung, itemsTotal, itemsSettled, failedCount, hasFailures, isSimulated, isIrreversible, revertBucket, resourceType, resourceId, resourceName, askerCount, askerNames, actionArguments, createdAt, updatedAt, dueAt, acknowledgedAt.

GET /security-alerts

Microsoft Defender / Sentinel-sourced alerts, linked to the users, groups, emails, and apps 1Security matched them to. Requires security-alerts:read.

Query parameters

Prop

Type

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  "https://api.1security.ai/api/v1/security-alerts?severity=high&status=new"

Response fields: id, title, description, severity, status, classification, category, threatDisplayName, firstActivityDateTime, isResolved, users, groups, emails, apps.

GET /security-alerts/{id}

Full detail for a single security alert. Requires security-alerts:read. Returns 404 if the id does not exist within your tenant.

Use this to enrich an alert already in your SIEM: forward the summary from the list endpoint, then fetch the detail on demand during triage rather than indexing the full payload for every alert.

curl -H "Authorization: Bearer $ONESEC_API_KEY" \
  https://api.1security.ai/api/v1/security-alerts/01J…

Response fields: everything from the list endpoint plus determination, assignedTo, alertWebUrl, incidentWebUrl, serviceSource, detectionSource, createdDateTime, lastUpdateDateTime, resolvedDateTime, lastActivityDateTime, recommendedActions, actorDisplayName, threatFamilyName, and rawData - the original provider payload, unmodified.

Errors

Errors use a consistent JSON shape and standard HTTP status codes:

{
  "error": {
    "code": "UNAUTHENTICATED",
    "message": "Invalid, expired, or revoked API key."
  }
}

Limits of the current API

Being explicit about what is not here yet, so you can design around it:

  • No push delivery. Webhook subscriptions are planned; today you poll.
  • No write access. Alerts cannot be acknowledged or resolved through the API. Bidirectional sync is planned.
  • One tenant per request. An organization-wide key covers all of an organization's tenants, but each request still addresses one tenant via X-Tenant-Id - there is no cross-tenant aggregate endpoint.
  • No entity lookups. Individual users, files, groups, and apps cannot be queried directly yet; detections currently carry the anomaly kind only.

Next

On this page