Skip to main content
Every API call your agent makes should be logged to Brain as a BackendEvent. Brain is the OTAS service that stores, indexes, and serves analytics for all observed activity. There are two ways to get events into Brain: the server-side SDK middleware (which captures in-domain requests automatically when your agent attaches a session token) and the direct agent log endpoint (which your agent calls manually after making any external API call). Both paths produce the same BackendEvent record — they differ only in the authentication mechanism and who initiates the log call.

Logging modes

This path is used by your project’s server-side middleware to automatically capture events for any request your agent makes within the project domain. Your agent does not call Brain directly — it simply attaches the session token to the in-domain request, and the middleware handles the rest.Endpoint: POST http://localhost:8002/api/v1/backend/log/sdk/Required headers:
HeaderValue
X-OTAS-SDK-KEYThe project’s backend SDK key
X-OTAS-AGENT-SESSION-TOKENThe session JWT obtained from UASAM
Content-Typeapplication/json
Example (server middleware):
curl -X POST http://localhost:8002/api/v1/backend/log/sdk/ \
  -H "X-OTAS-SDK-KEY: sdk_your_backend_key_here" \
  -H "X-OTAS-AGENT-SESSION-TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "550e8400-e29b-41d4-a716-446655440000",
    "path": "/api/v1/some-resource",
    "method": "GET",
    "status_code": 200,
    "latency_ms": 45.2
  }'
Successful response:
{
  "status": 1,
  "status_description": "event_captured",
  "response": {}
}
The SDK log response does not include an event_id. Use the agent log endpoint if you need to track individual event IDs.

Required fields

Every event log request — regardless of which endpoint you use — must include these five fields. Requests missing any of them will be rejected with a 400 missing_required_fields error that lists which fields are absent.
project_id
string
required
UUID of the OTAS project this event belongs to. Must match the project associated with the SDK key or agent key you are using.
path
string
required
The API path that was called, e.g., /api/v1/some-resource. For external calls, use the path portion of the external URL.
method
string
required
The HTTP method used: GET, POST, PUT, PATCH, DELETE, etc.
status_code
integer
required
The HTTP status code returned by the API, e.g., 200, 201, 404, 500.
latency_ms
float
required
The measured round-trip time in milliseconds. This value is used directly in p50/p95/p99 latency analytics — measure it accurately.

Optional fields

These fields are not required but provide richer observability data and enable more detailed filtering in the OTAS dashboard.
FieldTypeDescription
request_size_bytesintegerSize of the request body in bytes
response_size_bytesintegerSize of the response body in bytes
request_headersstringJSON-encoded request headers (e.g., "{\"Content-Type\": \"application/json\"}")
request_bodystringJSON-encoded request body
query_paramsstringQuery string from the request URL
post_datastringForm-encoded POST data, if applicable
response_headersstringJSON-encoded response headers
response_bodystringJSON-encoded response body
request_content_typestringMIME type of the request (e.g., application/json)
response_content_typestringMIME type of the response
custom_propertiesobjectArbitrary JSON object for agent-defined metadata
errorstringError message if the call failed. A non-empty value marks the event as an error in analytics
metadataobjectArbitrary JSON object for additional structured context

Full example request body

The following example shows all fields populated — use this as a reference for out-of-domain log calls:
{
  "project_id": "550e8400-e29b-41d4-a716-446655440000",
  "path": "/api/v1/the-external-path",
  "method": "POST",
  "status_code": 200,
  "latency_ms": 123.45,
  "request_size_bytes": 512,
  "response_size_bytes": 1024,
  "request_headers": "{\"Content-Type\": \"application/json\"}",
  "request_body": "{\"your\": \"request body\"}",
  "query_params": "",
  "post_data": "",
  "response_headers": "{\"Content-Type\": \"application/json\"}",
  "response_body": "{\"your\": \"response body\"}",
  "request_content_type": "application/json",
  "response_content_type": "application/json",
  "custom_properties": {},
  "error": "",
  "metadata": {}
}

Error responses

Statusstatus_descriptionCause
401missing_sdk_key / missing_agent_keyThe required authentication header is absent
401invalid_sdk_key / invalid_or_expired_agent_keyThe key does not exist, is expired, or has been revoked
401missing_agent_session_tokenThe X-OTAS-AGENT-SESSION-TOKEN header is absent
401invalid_or_expired_tokenThe session JWT is malformed, expired, or invalid
403session_agent_mismatchThe agent ID in the JWT does not match the agent key (agent endpoint only)
400missing_required_fieldsOne or more of the five required fields are missing from the request body
400invalid_jsonThe request body is not valid JSON

Best practices

Measure latency_ms using a timer that starts immediately before you send the request and stops as soon as you receive the full response. This value drives p50, p95, and p99 latency analytics — inaccurate measurements will skew your percentile charts.
  • Set error to a non-empty string whenever the call fails or returns an error status code. This is what Brain uses to count errors in the error-rate analytics view.
  • Use custom_properties to attach agent-specific context (e.g., which LLM model was used, which tool was invoked) that is not captured by the standard fields.
  • Log out-of-domain events immediately after each call, not in a batch at the end of the task. Batching risks losing events if the process exits unexpectedly.

Build docs developers (and LLMs) love