Logging using the REST Log API
Your own applications, scripts, and integration platforms can send Log Events straight to the Nodinite Log API. Each REST call posts one Log Event, or a batch, as JSON, and Nodinite stores it in the Log Databases where your Log Views find it.
This page is the starting point for logging from code. It explains the base URL, how the Log API is protected, how to explore the operations in Scalar or Swagger, and gives samples in PowerShell, curl, C#, Python, and Node.js.
- ✅ Post single Log Events or batches over REST with a JSON payload
- ✅ Validate a Log Event before you send it
- ✅ Protect the Log API with OAuth 2.0 client credentials when you need to
- ✅ Explore and test every operation in Scalar or Swagger
Tip
For high volumes, or when the Log API must not slow down your application, write the Log Events to a folder or queue and let the Pickup Log Events Service deliver them. It retries automatically.
Understanding Logging over REST
or script"] IdP["fa:fa-id-card Identity provider
only when protected"] API["fa:fa-cloud-arrow-down Log API
/LogAPI/api/LogEvent"] DB["fa:fa-database Log Databases"] LV["fa:fa-search Log Views"] App -->|1. Get token| IdP App -->|2. POST JSON Log Event| API API --> DB DB --> LV style API fill:#2678BD,color:#fff style IdP fill:#9E59BB,color:#fff style DB fill:#3F827B,color:#fff
Your application posts JSON Log Events to the Log API (blue). When the Log API is protected, the application first gets an access token from the identity provider (purple). The events are stored in the Log Databases (green) and shown in Log Views.
Key Components
| Component | Role |
|---|---|
| Log API | Receives Log Events over REST and stores them. Each environment has its own Log API web site and port. |
| JSON Log Event | The payload. See JSON formatted Log Event for every field. |
| Protection | Off by default: the Log API uses Anonymous Authentication. When you turn on Enable Authentication in the Portal, every request needs an OAuth 2.0 bearer token. |
| Scalar and Swagger | Interactive API references at the root of the Log API web site. Use them to find operations, see the models, and test calls. |
Find the Base URL
In Nodinite v7, each environment's Log API is its own IIS web site with its own port. The Log API operations are under /LogAPI/ on that site. The port is set for each environment in the Nodinite Portal, under Core platform services in the Servers step, so yours probably differs from the examples.
| What | URL | Example |
|---|---|---|
| Log API base URL | https://{host}:{port}/LogAPI/ |
https://nodinite.contoso.com:40002/LogAPI/ |
| Post a Log Event | <base URL>api/LogEvent |
https://nodinite.contoso.com:40002/LogAPI/api/LogEvent |
| Post a batch | <base URL>api/LogEvent/Batch |
https://nodinite.contoso.com:40002/LogAPI/api/LogEvent/Batch |
| Validate without storing | <base URL>api/LogEvent/Validate and api/LogEvent/Batch/Validate |
https://nodinite.contoso.com:40002/LogAPI/api/LogEvent/Validate |
| Scalar (recommended) | https://{host}:{port}/scalar |
https://nodinite.contoso.com:40002/scalar |
| Swagger | https://{host}:{port}/swagger |
https://nodinite.contoso.com:40002/swagger |
Note
Log agents and the Pickup Service ask for the Log API base URL, ending in
/LogAPI/. Scalar and Swagger are at the root of the site, without/LogAPI/. Always use HTTPS in production.
Explore Operations in Scalar or Swagger
Both API references list every operation with its request and response models, and let you send test requests. Scalar also generates code snippets in many languages; add authentication yourself when the Log API is protected. Swagger may be removed in a future version, so prefer Scalar.

Scalar shows the Log Event operation with a sample payload, a generated code snippet, and the response for each status code.

In Swagger, Try it out fills in a sample Log Event that you can edit and send.
Choose How to Protect the Log API
| Unprotected (default) | Protected with OAuth 2.0 | |
|---|---|---|
| Portal setting | Enable Authentication cleared | Enable Authentication checked, with the Log API Audiences |
| What the caller sends | Only the JSON payload | Authorization: Bearer <access token> plus the payload |
| How the caller gets access | Network access to the Log API | A client app registration using the OAuth 2.0 client credentials grant |
| Pickup Service setting | Protected cleared | Protected checked |
| Requires | Nothing | Nodinite running in OpenID Connect / OAuth 2.0 mode |
| Use when | The Log API is only reachable from trusted networks | Callers run outside your trusted network, or your policy requires authenticated logging |
You set both values in the Nodinite Portal, in Step 2: Auth › Log API; the fields are described in OAuth Log API settings.
Set Up a Client for a Protected Log API
The Log API has its own app registration and audience, separate from the Web API. In short:
- Register the Log API (logApi) in Microsoft Entra ID, set its Application ID URI, and set its access token version to
2. - Register one client app per system that sends Log Events.
- In the Portal, add the logApi Application (client) ID (not the
api://URI) to LogAPI.2 – Audiences, and check LogAPI.1 – Enable Authentication. - The logging client requests a token with the scope
api://<logapi-client-id>/.defaultand sends it as a bearer token. - In production, harden the setup: add the
AppRole_LogApi_writeapp role, grant it to each approved client, and set Assignment required on logApi. Without this, any app registration in your tenant can get a Log API token.
Follow the step-by-step guide with screenshots: Protect the Log API with OAuth 2.0 and Entra ID.
Warning
Don't use the installation client (
NodiniteInstallationClient) for your own logging. Give each calling system its own client, so you can rotate or revoke it on its own.
JSON Payload and Response
Post Log Events as JSON formatted Log Events. At minimum, provide the mandatory fields:
{
"LogAgentValueId": 42,
"EndPointName": "INT101: My Integration",
"EndPointUri": "https://api.example.com/orders",
"EndPointDirection": 0,
"EndPointTypeId": 50,
"OriginalMessageTypeName": "Orders.Customer/1.0",
"LogDateTime": "2026-10-11T13:37:00.123Z"
}
A successful call returns 200 with the result for the Log Event:
{
"EventId": "123e4567-e89b-12d3-a456-426614174000",
"EventNumber": 1,
"Status": 0,
"ErrorMessage": null
}
A 400 response means the payload is invalid. Use api/LogEvent/Validate while you develop, to check a payload without storing it. For valid values, see Endpoint Directions, Endpoint Types, and Event Directions.
Code Examples
The examples use these placeholders:
| Placeholder | Value |
|---|---|
<logapi-base-url> |
The Log API base URL, for example https://nodinite.contoso.com:40002/LogAPI/ |
<tenant-id>, <client-id>, <client-secret> |
Your Entra ID tenant and the client app, only when the Log API is protected |
<logapi-client-id> |
The Application (client) ID of the logApi app registration, only when the Log API is protected |
PowerShell
Use Invoke-RestMethod to post the Log Event. Remove the token lines and the -Headers parameter when the Log API is unprotected.
$logApiBaseUrl = '<logapi-base-url>'
# Only when the Log API is protected: get an access token
$token = (Invoke-RestMethod -Method Post `
-Uri 'https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token' `
-Body @{
grant_type = 'client_credentials'
client_id = '<client-id>'
client_secret = $env:NODINITE_CLIENT_SECRET
scope = 'api://<logapi-client-id>/.default'
}).access_token
$headers = @{ Authorization = "Bearer $token" }
$logEvent = @{
LogAgentValueId = 42
EndPointName = 'INT101: REST PowerShell Integration'
EndPointUri = 'https://api.example.com/orders'
EndPointDirection = 0
EndPointTypeId = 50
OriginalMessageTypeName = 'Orders.Customer/1.0'
LogDateTime = (Get-Date).ToUniversalTime().ToString('o')
LogStatus = 0
LogText = 'Order processed successfully'
Body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('<Order>...</Order>'))
Context = @{ OrderId = 'ORD-12345'; CustomerId = 'CUST-001' }
}
$response = Invoke-RestMethod -Method Post -Uri "${logApiBaseUrl}api/LogEvent" `
-Headers $headers -ContentType 'application/json' `
-Body ($logEvent | ConvertTo-Json -Depth 10)
"Logged event $($response.EventId), status $($response.Status)"
Example: Post a Log Event from PowerShell.
curl
# Only when the Log API is protected
TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" \
-d "grant_type=client_credentials" -d "client_id=<client-id>" \
-d "client_secret=$NODINITE_CLIENT_SECRET" -d "scope=api://<logapi-client-id>/.default" | jq -r .access_token)
curl -X POST "<logapi-base-url>api/LogEvent" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"LogAgentValueId":42,"EndPointName":"INT101: REST curl Integration","EndPointUri":"https://api.example.com/orders","EndPointDirection":0,"EndPointTypeId":50,"OriginalMessageTypeName":"Orders.Customer/1.0","LogDateTime":"2026-10-11T13:37:00.123Z"}'
Example: Post a Log Event with curl. Leave out the Authorization header when the Log API is unprotected.
C#
When the Log API is protected, get the token with MSAL as in the Web API OAuth guide, using the scope api://<logapi-client-id>/.default, and set it on the HttpClient.
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using var http = new HttpClient { BaseAddress = new Uri("<logapi-base-url>") };
// Only when the Log API is protected:
// http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var logEvent = new
{
LogAgentValueId = 42,
EndPointName = "INT101: REST C# Integration",
EndPointUri = "https://api.example.com/orders",
EndPointDirection = 0,
EndPointTypeId = 50,
OriginalMessageTypeName = "Orders.Customer/1.0",
LogDateTime = DateTimeOffset.UtcNow,
LogStatus = 0,
LogText = "Order processed",
Body = Convert.ToBase64String(Encoding.UTF8.GetBytes("<Order>...</Order>")),
Context = new Dictionary<string, string> { ["OrderId"] = "ORD-12345", ["CustomerId"] = "CUST-001" }
};
var response = await http.PostAsJsonAsync("api/LogEvent", logEvent);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Example: Post a Log Event from .NET with HttpClient.
Tip
For .NET, you can also use the
Nodinite.Libraries.Contracts.LogRestApiNuGet package for typed Log Event contracts. See the Hello World .NET sample.
Python
Install the dependency first: pip install requests.
import base64
import datetime
import os
import requests
log_api_base_url = "<logapi-base-url>"
headers = {}
# Only when the Log API is protected
if os.environ.get("NODINITE_CLIENT_SECRET"):
token = requests.post(
"https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token",
data={
"grant_type": "client_credentials",
"client_id": "<client-id>",
"client_secret": os.environ["NODINITE_CLIENT_SECRET"],
"scope": "api://<logapi-client-id>/.default",
},
timeout=30,
).json()["access_token"]
headers["Authorization"] = f"Bearer {token}"
log_event = {
"LogAgentValueId": 42,
"EndPointName": "INT101: REST Python Integration",
"EndPointUri": "https://api.example.com/orders",
"EndPointDirection": 0,
"EndPointTypeId": 50,
"OriginalMessageTypeName": "Orders.Customer/1.0",
"LogDateTime": datetime.datetime.now(datetime.timezone.utc).isoformat(),
"Body": base64.b64encode(b"<Order>...</Order>").decode(),
"Context": {"OrderId": "ORD-12345"},
}
response = requests.post(f"{log_api_base_url}api/LogEvent", json=log_event, headers=headers, timeout=30)
response.raise_for_status()
print(response.json())
Example: Post a Log Event from Python.
Node.js
Requires Node.js 18 or later (built-in fetch).
const logApiBaseUrl = '<logapi-base-url>';
const headers = { 'Content-Type': 'application/json' };
// Only when the Log API is protected
if (process.env.NODINITE_CLIENT_SECRET) {
const tokenResponse = await fetch('https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: '<client-id>',
client_secret: process.env.NODINITE_CLIENT_SECRET,
scope: 'api://<logapi-client-id>/.default'
})
});
headers.Authorization = `Bearer ${(await tokenResponse.json()).access_token}`;
}
const response = await fetch(`${logApiBaseUrl}api/LogEvent`, {
method: 'POST',
headers,
body: JSON.stringify({
LogAgentValueId: 42,
EndPointName: 'INT101: REST Node.js Integration',
EndPointUri: 'https://api.example.com/orders',
EndPointDirection: 0,
EndPointTypeId: 50,
OriginalMessageTypeName: 'Orders.Customer/1.0',
LogDateTime: new Date().toISOString(),
Body: Buffer.from('<Order>...</Order>').toString('base64'),
Context: { OrderId: 'ORD-12345' }
})
});
console.log(response.status, await response.json());
Example: Post a Log Event from Node.js.
Important
Read client secrets from a vault or environment variable at run time. Never commit secrets to source control, and never log tokens.
Best Practices
Use Asynchronous Logging for Volume
For high-volume scenarios, or when network reliability is a concern, use the Pickup Log Events Service instead of synchronous REST calls:
- Write JSON Log Events to a file system folder or message queue.
- Configure the Pickup Service to read from that location.
- Let Nodinite handle retries and processing.
This decouples your application from the logging infrastructure and improves resilience; see Asynchronous Logging. To send many events at once over REST, post them to api/LogEvent/Batch.
Include Context Properties
Use the Context field to add searchable business data:
"Context": {
"OrderId": "ORD-12345",
"CustomerId": "CUST-001",
"TotalAmount": "1234.56",
"CorrelationId": "064205E2-F7CF-43A6-B514-4B55536C2B67"
}
This enables self-service Log Views for business users without payload parsing. See Context Options for the reserved keys that change how an event is processed, and Repository Binding for the keys that populate the Repository Model. Context properties that match the ContextPropertiesFilters System Parameter are removed before they are stored; see What happens after the Log API accepts an event.
Handle Errors Gracefully
Always implement error handling and retry logic, and never let a logging failure stop your business process:
try {
Invoke-RestMethod -Method Post -Uri "${logApiBaseUrl}api/LogEvent" -Headers $headers `
-ContentType 'application/json' -Body $jsonBody
} catch {
Write-Warning "Failed to log event: $($_.Exception.Message)"
# Retry, or write the event to a folder for the Pickup Service
}
Base64 Encode Payloads
Always base64 encode message bodies:
$body = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($xmlMessage))
Next Steps
- JSON formatted Log Event
- Pickup Log Events Service
- Log Event Overview
- OAuth Log API settings
- Protect the Log API with OAuth 2.0 and Entra ID
- Log API Scenarios