Skip to content
IAM built for the AI-Era

Event API

The Specops Event API lets you integrate authentication and security events from Specops services into external systems such as SIEM platforms, monitoring tools, and automation workflows.

The API works by polling. Clients request new events at regular intervals, and the API returns them as a time-ordered stream using cursor-based pagination, one page at a time. There are no webhooks, so a typical setup is a small script or service that runs continuously and forwards each event to downstream systems, such as a SIEM.

Each response includes a cursor that records exactly how far you have read. By sending that cursor with the next request, you can continue streaming events forward without refetching or reprocessing events you have already handled.

The API is designed to support continuous ingestion into external systems, not for ad hoc historical searches or occasional compliance lookups. A typical client is expected to:

  • fetch historical data on initial startup (up to 7 days back)
  • continue polling for new events
  • handle retries and pagination
  • forward events to downstream systems such as a SIEM

Note

Clients must handle rate limiting, or they may receive HTTP 429 responses. Excessive polling can lead to temporary blocking or other protective rate-limiting measures. See Throttling. We also recommend de-duplicating events returned by the Specops Event API using eventRecordId. See If the cursor is rejected or lost.

Prerequisites

Configuration

The following are required in order to start using the API.

  • API Key: From the Admin pages in Specops Authentication Web, generate an API key for the Event API resource. The key is a 72-character string. Store it securely and treat it like a password.
  • Base URL: Use the base URL for the data center your key was issued in.

    Data center Base URL
    EU https://eu.eventapi.specopssoft.com
    US https://eventapi.specopssoft.com

API Headers

The following headers must be included in the API call:

Name Required Description
x-api-key Yes The API key generated from the Admin pages in Specops Authentication Web.
Content-Type Yes Set to application/json. Required on every request that sends a body.

Note

The reachability check (/api/v2/Ping) is the only endpoint that does not require an API key.

Swagger API documentation

Specops provides interactive documentation for the Event API through Swagger UI. Use the link for your region:

US

https://eventapi.specopssoft.com/swagger/

EU

https://eu.eventapi.specopssoft.com/swagger/

Expected Polling Flow

You start a stream by sending a request with a from timestamp. For each subsequent request, send the cursor returned in the previous response. This same polling loop catches up on existing events and then continues fetching new ones, as long as the same from value and cursor are used.

  1. Check reachability (optional)

    Parameter Value
    Method GET
    Endpoint /api/v2/Ping
    Parameters
    • None
    Description Confirm the API is reachable before sending authenticated traffic. No API key required.
  2. Start the stream

    Parameter Value
    Method POST
    Endpoint /api/v2/SearchLogEvents
    Body Parameters
    • from
    • pageSize (optional)
    Description Open the stream from a chosen start time. Send from without a cursor on this first request only. from must be within the last 7 days. The response returns the first events plus a nextCursor.
  3. Continue the stream

    Parameter Value
    Method POST
    Endpoint /api/v2/SearchLogEvents
    Body Parameters
    • from (the same value used in step 2)
    • cursor
    Description Call again with the same from and cursor set to the nextCursor from the previous response. The cursor records your exact position, so each call returns only events you have not seen yet. Repeat this process for each subsequent request.
  4. Stay live

    When a response has hasMoreResults: false you have reached the end of the events currently available. Keep the nextCursor, wait a few seconds, then send another request using the same from value and the same nextCursor. As new events arrive, they resume from the point where the previous response ended. Nothing duplicated, nothing missed. Continue this loop indefinitely.

    Note

    Never change from or drop the cursor while streaming. Because the cursor pins your position, from is allowed to age past 7 days while the stream remains active. The 7-day limit applies only when starting a new stream without a cursor (step 2). Run only a single consumer per API key, because multiple consumers sharing the same key will compete and trip the rate limit.

Example of a complete polling session

The example below walks through a single stream, from the first request to staying live. Cursor values are opaque, so they are shown in abbreviated form.

1. Open the stream: send from with no cursor:

{
  "from": "2026-06-05T08:00:00Z",
  "pageSize": 100
}

The response returns the first events, hasMoreResults: true, and a nextCursor:

{
  "maxResultsPerPage": 100,
  "hasMoreResults": true,
  "nextCursor": "eyJTb3J0Ijpb...abbreviated...Um9sbGluZyJ9",
  "logEvents": [
    {
      "timestamp": "2026-06-05T08:01:12Z",
      "eventId": "UserPasswordReset",
      "eventRecordId": "9f1c8a3e-4b2d-4c7a-9b10-2f5e8d6a1c44",
      "eventDetails": {
        "upn": "jane.doe@contoso.com",
        "ipAddress": "203.0.113.42",
        "resource": "Reset Password",
        "identityServiceId": "Gatekeeper"
      }
    }
  ]
}

eventId identifies the type of event; the value shown here is an example. Any eventDetails fields that do not apply to an event are returned as null.

2. Continue: send the same from and the nextCursor from the previous response:

{
  "from": "2026-06-05T08:00:00Z",
  "cursor": "eyJTb3J0Ijpb...abbreviated...Um9sbGluZyJ9"
}

Keep repeating this. Each response gives you the next batch and a fresh nextCursor — while hasMoreResults is true.

3. Stay live: when you are caught up, the response has hasMoreResults: false and an empty logEvents, but it still includes a nextCursor:

{
  "maxResultsPerPage": 100,
  "hasMoreResults": false,
  "nextCursor": "eyJTb3J0Ijpb...abbreviated2...Um9sbGluZyJ9",
  "logEvents": []
}

Wait a few seconds, then send the same request again using the same from value and the latest nextCursor. As soon as new events are available, they are returned and hasMoreResults becomes true again. Repeat this loop indefinitely.

If the cursor is rejected or lost

You only need a time-based restart when you have lost your place, for example, if the consumer restarted without a stored cursor.

To restart, open a new stream (step 2, no cursor) with from set to the newest event timestamp you have already processed, minus a few seconds of overlap. Then resume the loop from step 3. The overlap can resend a few events you have already seen, so de-duplicate on eventRecordId to keep only one copy of each event. This is a recovery path only. During normal streaming, stay on the cursor and do not reset from.

API parameter description

Parameter name Description
from The start time of the stream. Must be within the last 7 days when starting a new stream (no cursor). When a cursor is supplied, keep sending the same from; it is not re-checked against the 7-day window, so a long-running stream is never forced to reset.
pageSize Optional. Number of events per page (1–1000). Defaults to 100.
cursor The nextCursor value from the previous response. Send it on every follow-up request to continue the stream from your exact position.
hasMoreResults true when more events are available right now, keep calling immediately. false when you are caught up, keep the cursor, wait a few seconds, and poll again.
nextCursor The cursor to send on the next request. Returned on every response, including when hasMoreResults is false, so always carry it forward.
timestamp When an event occurred. Track the newest value you have processed as a restart anchor (see If the cursor is rejected or lost).
eventRecordId A unique identifier for each event. Use it to de-duplicate events after a restart or recovery.

Note

All timestamps, including the from value in the request and the timestamp on each event, use ISO 8601 format with a time zone, for example 2026-05-28T12:34:56Z (UTC) or 2026-05-28T14:34:56+02:00.

Throttling

The API enforces a per-key rate limit. If you call too often, you will receive an HTTP 429 response.

Note

Treat the HTTP 429 response as a signal to slow down, not as a failure. Pause briefly, retry, and space your calls out a little more. A few seconds between the request cycles is plenty.

Important information

  • The API never pushes events to you. There are no webhooks, so nothing arrives on its own. Your client has to keep asking (polling) for new events on a schedule.
  • Normal cursor streaming does not return duplicates. As long as you keep sending the cursor, each event is returned exactly once. Duplicates only appear after a restart or recovery, where a deliberate time overlap may resend a few events. Use the unique eventRecordId to drop them so each event is stored only once.
  • Keep your API key secret. Anyone with the key can read your events. Store it securely, and rotate it regularly or if you suspect it has been exposed.