Skip to content

API Reference

Overview

The LogSentinel SIEM API is a way to both send events and search for events through a RESTful API. The API supports:

  • General logging - just an log body and optional extracted params
  • Audit logs - including actor, action, and optional entity
  • NetFlow - flow logs from network appliances
  • Authentication logs
  • Healthcare-specific logs - including FHIR, DICOM and IHE
  • GDPR and CCPA specific logs

For more details, see full API reference.

Getting started with the API

In order to get started, you can take a look at the RESTful API for sending audit log events. There are four methods, all of which accept an arbitrary request body – it can be JSON, base64-encoded binary format, or (again encoded) encrypted data.

  • /api/log/simple – this endpoint accepts just any request body (using POST) and stores it as a new event entry. It is the recommended endpoint if you want full secrecy of the data – you should encrypt it at your end and just send us the encrypted payload. Note that you won’t be able to use the search capabilities that way.
  • /api/log/{actorId}/{action} - this endpoint accepts an actorId and an action in addition to the request body. The actorId is normally the userId that performed the action, and the action parameter is an action specific to your system that is not about a particular entity – e.g. PERFORM_SEARCH, START_BACKGROUND_PROCESS, etc.
  • /api/log/{actorId}/{action}/{entityType}/{entityId} – this endpoint accepts the actorId, an action (any action, including the recommended INSERT/UPDATE/DELETE/GET and custom ones like CHECKOUT_BASKET, DISCARD_ITEMS, etc.) and the entity type and ID. This is intended to store database-related, entity-oriented events. This is expected to be the most often used endpoint in your application. It is also the most transparent one, since generic functionality can be plugged in to automatically send the events to LogSentinel. Some of the client libraries provide such features. It is recommended to pass “old” and “new” values in the body of UPDATE events in order to be able to reconstruct the whole data modification process, similarly to how event sourcing works.
  • /api/log/{actorId}/auth/{action} – this endpoint is specifically intended for authentication events – it only takes an actorId and authentication action (LOGIN, LOGIN_FAILED, LOGOUT, SIGNUP, LOGIN_AS (for staff acting as user), AUTO_LOGIN (in case of remember-me functionality)). In order to have some additional legal strength you can have the user sign their login event with a password-derived key (e.g. using PBKDF2) and pass the result of the signing using the custom headers Signature and User-Public-Key

If the metadata isn’t critical and doesn’t need to be encrypted, you can only send an encrypted body and still use the more specific endpoints. If bandwidth is an issue, e.g. in IoT context, the body itself can be a hash of the original resource (e.g. a photo)

The response contains the ID of the inserted log entry and the last known entry hash. Since forming the hash chain is sequential, we cannot return the hash of the entry you just inserted, so instead we return the last computed hash. You can choose to store that hash somewhere (or at least log it).

The hash needs to be published/stored in a place other than the audit log server, because if an attacker gets hold of the audit log and tries to re-write it, he won’t be able to do that without being detected – the hash that has been published/stored elsewhere will not be found, which will indicate tampering with the log. LogSentinel has better ways of guaranteeing that a given hash was indeed computed, thus proving the integrity of the whole audit log, but it will be easier to do periodic checks if you store some of the hashes returned.

Authentication

In order to use the API, you have to authenticate your calls. For that you need to login to the dashboard and obtain the organization key and secret and an application id from the “API credentials” menu. Then you should pass two headers for each request:

  • Authorization: Basic<base64(organizationId:secret)>

  • Application-Id:<applicationId>

Client Libraries

Client libraries can be found on GitHub.com/LogSentinel. Currently the following are available:

Code Examples

Below are some code example for basic SentinelTrails functionality:

Inserting a single entry

//credentials obtained after registration
LogSentinelClientBuilder builder = LogSentinelClientBuilder
    .create(applicationId, organizationId, secret);
LogSentinelClient client = builder.build();

try {
    var result = client.getAuditLogActions().log(
       new ActorData().actorId(actorId).actorDisplayName(username).actorRoles(roles), 
       new ActionData().details(details).action(action)
    );
    System.out.println(result);
} catch (ApiException e) {
    // handle exception
}
LogSentinelClientBuilder builder = LogSentinelClientBuilder
    .create(applicationId, organizationId, secret);

builder.setEncryptionKey(encryptionKey); // Optional

LogSentinelClient client = builder.build();    

try
{
    var result = client.getAuditLogActions().LogUsingPOST(
        new ActorData().setActorDisplayName(actorName).setActorRoles(actorRoles)
            .setActorId(actorId),
        new ActionData().setDetails(details).setAction(act)
            .setEntryType(entryType), 
        applicationId);
    Console.WriteLine(result.LogEntryId);
}
catch (ApiException e)
{
    Console.WriteLine("Exception when calling AuditLogControllerApi#logAuthAction");
}
$data = <<<EOT
{
  "detail1": "detail 1",
  "detail2": "detail 2"
}
EOT;

$curl = curl_init();
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

curl_setopt($curl, CURLOPT_URL, 'https://api.logsentinel.com/api/log/' . $actorId . '/' . $action . '/' . $entityType . '/' . $entityId);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
                    'Application-Id: ' . $applicationId;
));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curl, CURLOPT_USERPWD, $ORG_ID . ":" . $SECRET);

// EXECUTE:
$result = curl_exec($curl); 
import requests
url = 'https://api.logsentinel.com/api/log/' + actorId + '/' + action + '/' + entityType + '/' + entityId;
data = '''{
  "detail1": "detail 1",
  "detail2": "detail 2"
}'''

response = requests.post(url, auth = HTTPBasicAuth(orgId, secret), data = data, headers = {"Content-Type": "application/json", "Application-Id": "applicationId"})
var https = require('https');
var data = JSON.stringify({
  "detail1": "detail 1",
  "detail2": "detail 2"
});

var auth = 'Basic ' + Buffer.from(ORG_ID + ':' + ORG_SECRET).toString('base64')

var options = {
  host: 'app.logsentinel.com',
  path: '/api/log/' + actorId + '/' + action + '/' + entityType + '/' + entityId,
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset = utf-8',
    'Application-Id': applicationId,
    'Authorization': auth;
  }
};

var req = https.request(options, function(res) {
  var res = JSON.parse(response.body)
  //...
});

req.write(data);
req.end();

Inserting batch entries

//credentials obtained after registration
LogSentinelClientBuilder builder = LogSentinelClientBuilder
    .create(applicationId, organizationId, secret);
LogSentinelClient client = builder.build();

List<BatchLogRequestEntry> batch = new ArrayList<>();
for (int i = 0; i < COUNT; i++) {
    String details = "details" + i;

    BatchLogRequestEntry entry = new BatchLogRequestEntry();
    entry.setActionData(new ActionData(details).setAction(action).setBinaryContent(false));
    entry.setActorData(new ActorData().actorId(actorId).actorDisplayName(username).actorRoles(roles));
    entry.setAdditionalParams(new HashMap<>());

    batch.add(entry);
}

try {
    client.getAuditLogActions().logBatch(batch);
} catch (ApiException e) {
    // handle exception
}
LogSentinelClientBuilder builder = LogSentinelClientBuilder
    .create(applicationId, organizationId, secret);

LogSentinelClient client = builder.build();    

try
{
    List<BatchLogRequestEntry> batch = new List<BatchLogRequestEntry>();
    for (int i = 0; i < COUNT; i++) {
        string details = "details" + i;

        BatchLogRequestEntry entry = new BatchLogRequestEntry(
           new ActorData().setActorDisplayName(actorName).setActorRoles(actorRoles).setActorId(actorId),
           new ActionData().setDetails(details).setAction(act).setEntryType(entryType));

        batch.Add(entry);
    }
    var result = client.getAuditLogActions().LogBatchUsingPOST(batch, applicationId);
    Console.WriteLine(result.LogEntryId);
}
catch (ApiException e)
{
    Console.WriteLine("Exception when calling AuditLogControllerApi#logAuthAction");
}
$data = <<<EOT
[{
  "actorData": {
    "actorId":"actor1",
    "actorDisplayName":"actor 1",
    "department":"IT"
  },
  "actionData": {
    "action":"VIEW",
    "entityId":"123",
    "entityType":"Deposit",
    "details":{
      "detail1": "detail 1",
      "detail2": "detail 2"
    }
},{
  "actorData": {
    "actorId":"actor2",
    "actorDisplayName":"actor 2",
    "department":"IT"
  },
  "actionData": {
    "action":"WITHDRAW",
    "entityId":"123",
    "entityType":"Deposit",
    "details":{
      "detail1": "detail 1",
      "detail2": "detail 2"
    }]
EOT;

$curl = curl_init();
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

curl_setopt($curl, CURLOPT_URL, 'https://app.logsentinel.com/api/log/batch');
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
                    'Application-Id: ' . $applicationId
));

curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curl, CURLOPT_USERPWD, $ORG_ID . ":" . $SECRET);

// EXECUTE:
$result = curl_exec($curl);
import requests
url = 'https://app.logsentinel.com/api/log/batch';
data = '''[{
  "actorData": {
    "actorId":"actor1",
    "actorDisplayName":"actor 1",
    "department":"IT"
  },
  "actionData": {
    "action":"VIEW",
    "entityId":"123",
    "entityType":"Deposit",
    "details":{
      "detail1": "detail 1",
      "detail2": "detail 2"
    }
},{
  "actorData": {
    "actorId":"actor2",
    "actorDisplayName":"actor 2",
    "department":"IT"
  },
  "actionData": {
    "action":"WITHDRAW",
    "entityId":"123",
    "entityType":"Deposit",
    "details":{
      "detail1": "detail 1",
      "detail2": "detail 2"
    }]'''

response = requests.post(url, auth = HTTPBasicAuth(orgId, secret), data = data, headers = {"Content-Type": "application/json", "Application-Id": "applicationId"})
var https = require('https');
var data = JSON.stringify([{
  "actorData": {
    "actorId":"actor1",
    "actorDisplayName":"actor 1",
    "department":"IT"
  },
  "actionData": {
    "action":"VIEW",
    "entityId":"123",
    "entityType":"Deposit",
    "details":{
      "detail1": "detail 1",
      "detail2": "detail 2"
    }
},{
  "actorData": {
    "actorId":"actor2",
    "actorDisplayName":"actor 2",
    "department":"IT"
  },
  "actionData": {
    "action":"WITHDRAW",
    "entityId":"123",
    "entityType":"Deposit",
    "details":{
      "detail1": "detail 1",
      "detail2": "detail 2"
    }]);

var auth = 'Basic ' + (Buffer.from(ORG_ID + ':' + ORG_SECRET).toString('base64'))

var options = {
  host: 'app.logsentinel.com',
  path: '/api/log/batch',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset = utf-8',
    'Application-Id': applicationId,
    'Authorization': auth;
  }
};

var req = https.request(options, function(res) {
  var res = JSON.parse(response.body)
  //...
});

req.write(data);
req.end();  

Merkle proofs

For consistency and inclusion proofs (used to prove the integrity of logs in data sources with enables merkle tree generation), see our example verification application: logsentinel-java-client-verification-ui