Query Event History

Overview

The event history contains all access control events including card reads, door access grants/denials, alarms, and system events. This guide covers querying, filtering, and analyzing historical events for reporting, auditing, and troubleshooting purposes.


Event Query Parameters

Parameter Type Description
forKeys string[] Filter by specific keys using OR logic (any one must match)
forAllKeys string[] Filter by specific keys using AND logic (all must match)
page int Page number for pagination (0-based)
pageSize int Number of events per page (max 1000)
startDate DateTime Events after this date
endDate DateTime Events before this date
includeSubFolders bool Include events from child instances
priorityThreshold int Minimum priority level
requiresAck bool Only events requiring acknowledgment

Key Differences:

  • forKeys uses OR logic - event matches if it contains ANY of the specified keys (e.g., AccessGranted OR AccessDenied)
  • forAllKeys uses AND logic - event matches only if it contains ALL specified keys (e.g., AccessGranted AND specific Reader)

Common Event Types

Event Type Description Priority
Access Granted Card read accepted Low
Access Denied Card read rejected Medium
Door Held Open Door exceeded hold time High
Door Forced Door opened without valid read High
Alarm Active Input triggered alarm state High

Query Access Granted Events

// Step 1: Get the Mercury application and event types
var apps = await client.GetAppsAsync(currentInstance);
var mercuryApp = apps.Single(a => a.CommonName == "Mercury Driver Service");
var eventTypes = await client.GetEventTypesAsync(mercuryApp);

// Step 2: Find specific event type
var accessGranted = eventTypes.Single(x => x.CommonName == "Access Granted");

// Step 3: Query events of this type
var events = await client.GetEventsAsync(
    currentInstance, 
    forKeys: new[] { accessGranted.Key }
);

// Step 4: Process event data
foreach (var evt in events)
{
    // Decode the event data from Base64 BSON
    var bytes = Convert.FromBase64String(evt.EventDataBsonBase64);
    var eventData = bytes.FromBson<ExpandoObject>();
    
    Console.WriteLine($"Event at {evt.OccurredOn}:");
    foreach (var kv in eventData)
    {
        Console.WriteLine($"  {kv.Key}: {kv.Value}");
    }
}

Query Events by Date Range

// Get events for the last 24 hours
var events = await client.GetEventsAsync(
    currentInstance,
    startDate: DateTime.UtcNow.AddHours(-24),
    endDate: DateTime.UtcNow
);

Console.WriteLine($"Found {events.Count()} events in the last 24 hours");

Query Multiple Event Types

// Find Access Denied and Granted event types
var accessDenied = eventTypes.Single(x => x.CommonName == "Access Denied");
var accessGranted = eventTypes.Single(x => x.CommonName == "Access Granted");

// Query both event types
var securityEvents = await client.GetEventsAsync(
    currentInstance,
    forKeys: new[] { accessDenied.Key, accessGranted.Key },
    startDate: DateTime.UtcNow.AddDays(-7)
);

Console.WriteLine($"Found {securityEvents.Count()} security events this week");

Paginated Event Retrieval

For large event sets, use pagination:

public async Task<List<EventInfo>> GetAllEventsAsync(
    InstanceInfo instance, 
    string[] eventTypeKeys,
    DateTime startDate,
    DateTime endDate)
{
    var allEvents = new List<EventInfo>();
    int page = 0;
    int pageSize = 1000;
    bool hasMore = true;
    
    while (hasMore)
    {
        var events = await client.GetEventsAsync(
            instance,
            forKeys: eventTypeKeys,
            startDate: startDate,
            endDate: endDate,
            page: page,
            pageSize: pageSize
        );
        
        allEvents.AddRange(events);
        hasMore = events.Count() == pageSize;  // More pages if full page returned
        page++;
        
        Console.WriteLine($"Retrieved page {page}: {events.Count()} events");
    }
    
    return allEvents;
}

Extract Event Details

Access events contain detailed information in BSON format:

public class AccessEventDetails
{
    public string CardNumber { get; set; }
    public string PersonName { get; set; }
    public string ReaderName { get; set; }
    public DateTime EventTime { get; set; }
}

public AccessEventDetails ParseAccessEvent(EventInfo evt)
{
    var bytes = Convert.FromBase64String(evt.EventDataBsonBase64);
    var doc = MongoDB.Bson.BsonDocument.Parse(
        System.Text.Encoding.UTF8.GetString(bytes));
    
    return new AccessEventDetails
    {
        CardNumber = doc.GetValue("CardNumber", "").AsString,
        PersonName = doc.GetValue("PersonName", "").AsString,
        ReaderName = doc.GetValue("ReaderCommonName", "").AsString,
        EventTime = evt.OccurredOn
    };
}

// Usage
foreach (var evt in events)
{
    var details = ParseAccessEvent(evt);
    Console.WriteLine($"{details.EventTime}: {details.PersonName} at {details.ReaderName}");
}

Generate Access Report

public async Task GenerateAccessReportAsync(
    InstanceInfo instance,
    DateTime startDate,
    DateTime endDate)
{
    var apps = await client.GetAppsAsync(instance);
    var mercuryApp = apps.Single(a => a.CommonName == "Mercury Driver Service");
    var eventTypes = await client.GetEventTypesAsync(mercuryApp);
    
    var accessGranted = eventTypes.Single(x => x.CommonName == "Access Granted");
    var accessDenied = eventTypes.Single(x => x.CommonName == "Access Denied");
    
    var events = await client.GetEventsAsync(
        instance,
        forKeys: new[] { accessGranted.Key, accessDenied.Key },
        startDate: startDate,
        endDate: endDate
    );
    
    // Group by event type
    var granted = events.Where(e => e.EventTypeKey == accessGranted.Key);
    var denied = events.Where(e => e.EventTypeKey == accessDenied.Key);
    
    Console.WriteLine($"Access Report: {startDate:yyyy-MM-dd} to {endDate:yyyy-MM-dd}");
    Console.WriteLine($"  Granted: {granted.Count()}");
    Console.WriteLine($"  Denied: {denied.Count()}");
    Console.WriteLine($"  Denial Rate: {(double)denied.Count() / (granted.Count() + denied.Count()):P2}");
}

Query Events by Reader

Filter events for a specific reader using forAllKeys to require both the event type and reader to match:

// Get the reader object
var reader = await client.GetByHrefAsync<MercuryReaderInfo>(readerHref);

// Get access granted event type
var apps = await client.GetAppsAsync(currentInstance);
var mercuryApp = apps.Single(a => a.CommonName == "Mercury Driver Service");
var eventTypes = await client.GetEventTypesAsync(mercuryApp);
var accessGranted = eventTypes.Single(x => x.CommonName == "Access Granted");

// Query events using forAllKeys to match BOTH access granted AND this specific reader
var events = await client.GetEventsAsync(
    currentInstance,
    forAllKeys: new[] { accessGranted.Key, reader.Key },
    startDate: DateTime.UtcNow.AddDays(-1)
);

Console.WriteLine($"Found {events.Count()} Access Granted events at {reader.CommonName}");

Note: forAllKeys enforces that ALL specified keys must match (AND logic), while forKeys uses OR logic (any one must match).


cURL Examples

Query Access Granted Events

curl -X GET \
  "https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/events?forKeys=ACCESS_GRANTED_EVENT_TYPE_KEY&page=0&pageSize=100" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Query Events by Date Range

curl -X GET \
  "https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/events?startDate=2024-01-01T00:00:00Z&endDate=2024-01-31T23:59:59Z&page=0&pageSize=1000" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Get Event Types

curl -X GET \
  "https://api.us.acresecurity.cloud/api/f/INSTANCE_KEY/apps/MERCURY_APP_KEY/eventtypes" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Best Practices

Practice Recommendation
Pagination Always paginate large queries (max 1000 per page)
Date Filtering Use date ranges to limit result sets
Event Type Keys Cache event type keys instead of looking up repeatedly
BSON Parsing Handle missing fields gracefully in event data
Time Zones All timestamps are UTC - convert for display
Rate Limiting Add delays between large paginated requests

Troubleshooting

Issue Cause Solution
Empty results Wrong event type key Verify key matches desired event
Missing events Date range too narrow Expand date range
Timeout Too many events Use pagination, narrow filters
Parse errors Malformed BSON Check Base64 encoding
Wrong event data Different event schema Check event type documentation