Overview
The OTLP logs endpoint accepts OpenTelemetry Protocol (OTLP) log data in protobuf or JSON format. Logs capture structured log messages with severity levels, trace context, and custom attributes.
Endpoint
Authentication
This endpoint requires Bearer token authentication using an Agent API key.
Include your agent API key in the Authorization header:
Authorization: Bearer mnfst_your_agent_api_key_here
How to get an API key:
Create an agent in the Manifest dashboard or via the API
The agent API key is automatically generated with the mnfst_ prefix
Retrieve it using the Get Agent Key endpoint
Local Mode Authentication
In local mode (MANIFEST_MODE=local), loopback connections (127.0.0.1) bypass authentication. This is useful for development with the OpenClaw plugin in dev mode.
Content Types
The endpoint accepts two content types:
application/x-protobuf (recommended) - Binary protobuf format, more efficient
application/json - JSON format for easier debugging
Set the Content-Type header accordingly.
Request Body
The request body follows the OTLP logs specification. The top-level structure is ExportLogsServiceRequest.
Structure
JSON Format
TypeScript Interface
{
"resourceLogs" : [
{
"resource" : {
"attributes" : [
{ "key" : "service.name" , "value" : { "stringValue" : "my-agent" } },
{ "key" : "agent.name" , "value" : { "stringValue" : "my-agent" } }
]
},
"scopeLogs" : [
{
"scope" : {
"name" : "openclaw-instrumentation" ,
"version" : "1.0.0"
},
"logRecords" : [
{
"timeUnixNano" : "1609459200000000000" ,
"observedTimeUnixNano" : "1609459200100000000" ,
"severityNumber" : 9 ,
"severityText" : "INFO" ,
"body" : {
"stringValue" : "Agent turn completed successfully"
},
"attributes" : [
{ "key" : "agent.name" , "value" : { "stringValue" : "my-agent" } },
{ "key" : "session.id" , "value" : { "stringValue" : "sess_abc123" } }
],
"traceId" : "5b8aa5a2d2c872e8321cf37308d69df2" ,
"spanId" : "051581bf3cb55c13"
}
]
}
]
}
]
}
Severity Levels
OTLP defines 24 severity levels. Common levels:
severityNumber severityText Description 1-4 TRACE Trace-level debugging 5-8 DEBUG Debug information 9-12 INFO Informational messages 13-16 WARN Warning messages 17-20 ERROR Error messages 21-24 FATAL Fatal errors
Standard mapping:
1 = TRACE
5 = DEBUG
9 = INFO
13 = WARN
17 = ERROR
21 = FATAL
You can provide either severityNumber or severityText (or both). Manifest uses severityText if provided, otherwise maps severityNumber to a text level.
Attributes
Resource Attributes
Attribute Type Description service.namestring Service identifier (fallback for agent name) agent.namestring Agent name (overrides service.name)
Log Record Attributes
Attribute Type Description agent.namestring Agent name (overrides resource attribute)
Any additional attributes are stored as JSON in the attributes column.
Body Types
The body field can contain:
String - Simple log message
Object - Structured log data (stored as JSON)
Other types - Converted to JSON string
Response
Partial success information (omitted if all log records accepted) Number of rejected log records (0 if all accepted)
200 - Success
401 - Unauthorized
Examples
cURL with JSON
curl -X POST https://manifest.build/otlp/v1/logs \
-H "Authorization: Bearer mnfst_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"resourceLogs": [{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "my-agent" } }
]
},
"scopeLogs": [{
"scope": { "name": "my-instrumentation" },
"logRecords": [
{
"timeUnixNano": "1609459200000000000",
"severityText": "INFO",
"body": { "stringValue": "Agent processing request" },
"attributes": [
{ "key": "user.id", "value": { "stringValue": "user_123" } }
]
},
{
"timeUnixNano": "1609459201000000000",
"severityText": "ERROR",
"body": { "stringValue": "Failed to connect to LLM provider" },
"traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
"spanId": "051581bf3cb55c13"
}
]
}]
}]
}'
OpenTelemetry SDK (Node.js)
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http' ;
import { LoggerProvider , BatchLogRecordProcessor } from '@opentelemetry/sdk-logs' ;
import { Resource } from '@opentelemetry/resources' ;
import { SeverityNumber } from '@opentelemetry/api-logs' ;
const exporter = new OTLPLogExporter ({
url: 'https://manifest.build/otlp/v1/logs' ,
headers: {
'Authorization' : 'Bearer mnfst_your_api_key'
}
});
const loggerProvider = new LoggerProvider ({
resource: Resource . default (). merge ( new Resource ({
'service.name' : 'my-agent' ,
'agent.name' : 'my-agent'
}))
});
loggerProvider . addLogRecordProcessor ( new BatchLogRecordProcessor ( exporter ));
const logger = loggerProvider . getLogger ( 'my-instrumentation' );
// Emit logs
logger . emit ({
severityNumber: SeverityNumber . INFO ,
severityText: 'INFO' ,
body: 'Agent turn completed successfully' ,
attributes: {
'session.id' : 'sess_abc123' ,
'duration_ms' : 1500
}
});
logger . emit ({
severityNumber: SeverityNumber . ERROR ,
severityText: 'ERROR' ,
body: 'Failed to parse response' ,
attributes: {
'error.type' : 'ParseError'
}
});
Structured Logs
curl -X POST https://manifest.build/otlp/v1/logs \
-H "Authorization: Bearer mnfst_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"resourceLogs": [{
"resource": {
"attributes": [
{ "key": "agent.name", "value": { "stringValue": "my-agent" } }
]
},
"scopeLogs": [{
"scope": { "name": "my-app" },
"logRecords": [{
"timeUnixNano": "1609459200000000000",
"severityNumber": 17,
"body": {
"kvlistValue": {
"values": [
{ "key": "message", "value": { "stringValue": "API call failed" } },
{ "key": "status_code", "value": { "intValue": 500 } },
{ "key": "retry_count", "value": { "intValue": 3 } }
]
}
}
}]
}]
}]
}'
When using application/x-protobuf, the request body must be a binary-encoded protobuf message following the OTLP logs specification.
Protobuf message definition:
message ExportLogsServiceRequest {
repeated ResourceLogs resource_logs = 1 ;
}
message ResourceLogs {
Resource resource = 1 ;
repeated ScopeLogs scope_logs = 2 ;
}
message ScopeLogs {
InstrumentationScope scope = 1 ;
repeated LogRecord log_records = 2 ;
}
message LogRecord {
fixed64 time_unix_nano = 1 ;
fixed64 observed_time_unix_nano = 11 ;
SeverityNumber severity_number = 2 ;
string severity_text = 3 ;
AnyValue body = 5 ;
repeated KeyValue attributes = 6 ;
uint32 dropped_attributes_count = 7 ;
bytes trace_id = 8 ; // 16 bytes
bytes span_id = 9 ; // 8 bytes
}
enum SeverityNumber {
SEVERITY_NUMBER_UNSPECIFIED = 0 ;
SEVERITY_NUMBER_TRACE = 1 ;
SEVERITY_NUMBER_TRACE2 = 2 ;
SEVERITY_NUMBER_TRACE3 = 3 ;
SEVERITY_NUMBER_TRACE4 = 4 ;
SEVERITY_NUMBER_DEBUG = 5 ;
SEVERITY_NUMBER_DEBUG2 = 6 ;
SEVERITY_NUMBER_DEBUG3 = 7 ;
SEVERITY_NUMBER_DEBUG4 = 8 ;
SEVERITY_NUMBER_INFO = 9 ;
SEVERITY_NUMBER_INFO2 = 10 ;
SEVERITY_NUMBER_INFO3 = 11 ;
SEVERITY_NUMBER_INFO4 = 12 ;
SEVERITY_NUMBER_WARN = 13 ;
SEVERITY_NUMBER_WARN2 = 14 ;
SEVERITY_NUMBER_WARN3 = 15 ;
SEVERITY_NUMBER_WARN4 = 16 ;
SEVERITY_NUMBER_ERROR = 17 ;
SEVERITY_NUMBER_ERROR2 = 18 ;
SEVERITY_NUMBER_ERROR3 = 19 ;
SEVERITY_NUMBER_ERROR4 = 20 ;
SEVERITY_NUMBER_FATAL = 21 ;
SEVERITY_NUMBER_FATAL2 = 22 ;
SEVERITY_NUMBER_FATAL3 = 23 ;
SEVERITY_NUMBER_FATAL4 = 24 ;
}
See the OpenTelemetry Protocol Specification for the complete protobuf schema.
Data Processing
When logs are ingested, Manifest:
Decodes the protobuf or JSON payload
Extracts log records with severity, body, and attributes
Correlates logs with traces using traceId and spanId (if provided)
Stores logs in the agent_logs table
Emits real-time events to connected dashboard clients via SSE
Stored Fields
Each log record is stored with:
tenant_id - Extracted from the authenticated agent API key
agent_id - Extracted from the authenticated agent API key
agent_name - From resource or log record attributes
timestamp - Log timestamp from timeUnixNano
severity - Severity text (e.g., “INFO”, “ERROR”)
body - Log message body (string or JSON)
trace_id - Trace ID for correlation (if provided)
span_id - Span ID for correlation (if provided)
attributes - Custom attributes as JSON (if any)
Trace Correlation
Logs can be correlated with traces by including traceId and spanId:
// When inside a traced operation
const span = trace . getActiveSpan ();
const spanContext = span . spanContext ();
logger . emit ({
severityNumber: SeverityNumber . INFO ,
body: 'Processing request' ,
traceId: spanContext . traceId ,
spanId: spanContext . spanId
});
This allows you to:
View logs in the context of a specific trace
Debug issues by following log trails within traces
Correlate errors across traces, spans, and logs
Rate Limiting
OTLP endpoints use in-memory API key caching (5-minute TTL) to minimize database lookups. Each successful authentication is cached for 5 minutes.
Error Handling
Status Code Description 200 Success (all log records accepted) 401 Unauthorized (invalid or expired API key) 415 Unsupported Media Type (invalid Content-Type) 500 Internal Server Error
Use Cases
Debugging Agent Behavior
Log detailed information during agent execution:
logger . emit ({
severityNumber: SeverityNumber . DEBUG ,
body: 'Tool execution started' ,
attributes: {
'tool.name' : 'web_search' ,
'tool.input' : JSON . stringify ( input )
}
});
Error Tracking
Capture errors with context:
try {
await executeTool ( toolName , input );
} catch ( error ) {
logger . emit ({
severityNumber: SeverityNumber . ERROR ,
body: `Tool execution failed: ${ error . message } ` ,
attributes: {
'error.type' : error . constructor . name ,
'error.stack' : error . stack ,
'tool.name' : toolName
}
});
}
Audit Trails
Log important events for compliance:
logger . emit ({
severityNumber: SeverityNumber . INFO ,
body: 'User data accessed' ,
attributes: {
'user.id' : userId ,
'access.type' : 'read' ,
'data.classification' : 'pii'
}
});
See Also