Stream logs to a SIEM

Send tool call, API request, and LLM call logs to Datadog, Splunk, Microsoft Sentinel, Elastic, Cribl, Sumo Logic, or any OTLP backend

Workforce has three logs you can send to a SIEM. Tool calls and API requests are pulled: your platform polls the log endpoints. LLM calls are pushed: AI Gateway sends each model request to a destination you configure.

LogDeliverySet up in
Tool callsPull from GET /api/v1/logs/tool-calls/Your SIEM, with a Workforce production API key
API requestsPull from GET /api/v1/logs/api-calls/Your SIEM, with a Workforce production API key
LLM callsPush over OTLP/HTTPGateway console, Settings → Telemetry export

Tool call and API request logs

Two cursor-paginated endpoints let your SIEM pull logs on a schedule. Full schemas are in the API reference.

The log endpoints are an Enterprise feature. If they’re not enabled for your organization, the endpoints return 403. Contact your account team to enable them.

How the endpoints work

Both endpoints behave identically:

AspectDetail
AuthAuthorization: Bearer <PRODUCTION_KEY>, a production API key from Settings → API keys
OrderOldest-first, so you can tail the log forward without gaps.
PaginationCursor-based. Each response returns next_cursor; pass it back as ?cursor=<…> for the next page. Stop when has_more is false (then next_cursor is null). Persist next_cursor between polls to resume exactly where you left off.
Filterspage_size (1–1000, default 100), created_after / created_before (ISO 8601), plus tool_name / status / connector_id (tool calls) and method / response_status / connector_id / toolcall_id (API requests).
RetentionOnly the last 30 days are available. If a collector is down longer than 30 days, the oldest records age out and can’t be recovered, so alert on collector staleness.
RedactionCredential values in headers (Authorization, API keys, cookies) and secret-named body fields are redacted before they leave the platform.

A poll loop is two shapes of the same request. The first starts from the beginning of the retention window:

curl -s "https://ah-api.merge.dev/api/v1/logs/tool-calls/?page_size=1000&created_after=2026-05-13T00:00:00Z" \
-H "Authorization: Bearer $PRODUCTION_KEY"

It answers with results, a next_cursor, and has_more. Every later poll passes that cursor back:

curl -s "https://ah-api.merge.dev/api/v1/logs/tool-calls/?page_size=1000&cursor=eyJ0…" \
-H "Authorization: Bearer $PRODUCTION_KEY"

Repeat until has_more is false, persist the last next_cursor, and resume from it on the next run.

Native pull vs. forwarder

Platforms fall into two camps:

  • Native pull: the platform has a built-in REST/API poller that follows cursor pagination for you, so configuring it is the whole job. Microsoft Sentinel, Elastic, Cribl Stream, and Sumo Logic are in this camp.
  • Forwarder: the platform is push-only (it expects you to send logs to its intake), so you run a small scheduled job that walks the cursor loop above and posts batches to the platform. Datadog and Splunk are most reliably integrated this way.

Either way, the source side is identical: page through the endpoint with cursor, persist next_cursor, stop on has_more: false.

Platform setup

Forwarder. Datadog log collection is push-based, and there is no native input that follows cursor pagination against an arbitrary REST API. Run a scheduled job (Lambda, container, cron, or a Datadog Agent custom check) that pulls from the endpoint and POSTs to the Datadog Logs intake API.

  1. Create a Datadog API key (Organization Settings → API Keys). This is the DD-API-KEY header value, an API key rather than an application key.
  2. Pick the intake host for your Datadog site (mismatched site is the most common failure):
    • US1: https://http-intake.logs.datadoghq.com
    • US3: https://http-intake.logs.us3.datadoghq.com
    • US5: https://http-intake.logs.us5.datadoghq.com
    • EU1: https://http-intake.logs.datadoghq.eu
    • AP1: https://http-intake.logs.ap1.datadoghq.com
  3. Store the Merge PRODUCTION_KEY and the Datadog API key in a secret manager, and persist the cursor in durable storage (DynamoDB, SSM, Redis, a DB row).
  4. Implement the pull→push loop and schedule it (e.g. every 1–5 minutes). Persist next_cursor after a page ships successfully so a crash re-ships at most one page.
import requests
cursor = state.load()
ah = {"Authorization": f"Bearer {AH_PRODUCTION_KEY}"}
dd_url = "https://http-intake.logs.datadoghq.com/api/v2/logs"
dd = {"Content-Type": "application/json", "DD-API-KEY": DD_API_KEY}
while True:
params = {"page_size": 1000}
if cursor:
params["cursor"] = cursor
else:
params["created_after"] = "2026-05-13T00:00:00Z"
body = requests.get(
"https://ah-api.merge.dev/api/v1/logs/tool-calls/", headers=ah, params=params, timeout=30,
).json()
batch = [
{"ddsource": "agent-handler", "service": "ah-tool-calls",
"ddtags": "env:prod", "message": rec}
for rec in body["results"]
]
if batch:
requests.post(dd_url, headers=dd, json=batch, timeout=30).raise_for_status()
cursor = body["next_cursor"]
state.save(cursor)
if not body["has_more"]:
break

Gotchas

  • The intake auth header is DD-API-KEY with an API key, not Authorization: Bearer. The Bearer token is only for the Merge side.
  • A single log is capped at ~1 MB (≤ 25 KB recommended). Chunk batches conservatively.
  • Datadog intake doesn’t dedup. Persist the cursor only after a successful POST, and index a stable record ID so you can dedup downstream if a retry re-sends a page.
  • Set each log’s timestamp (epoch ms) from the record’s created time, or logs land at receive-time and look mis-ordered.
  • Don’t use the Observability Pipelines HTTP/S Client source: it polls a fixed URL and can’t carry a cursor.

Docs: Send logs (HTTP intake) & sites · Log collection · Agent custom check send_log

LLM calls from AI Gateway

LLM calls have no pull endpoint. Gateway’s Telemetry export pushes each model request as an OpenTelemetry span over OTLP/HTTP, so there is no polling or cursor to manage. Export covers only requests made after you create a destination.

Each span carries the model, provider, token counts, and cost. See the full attribute list.

Pick a destination

Set up destinations in the Gateway console under Settings → Telemetry export. The Workforce dashboard doesn’t have this setting.

Your platformDestination
Datadog with LLM ObservabilityDatadog
Datadog with APM onlyDatadog (APM)
Grafana CloudGrafana Cloud (traces only, no metrics)
Splunk, Microsoft Sentinel, Elastic, Cribl, Sumo Logic, or another OTLP backendCustom OTLP endpoint (Enterprise)

Send to any other SIEM

Point a custom endpoint at your platform’s OTLP/HTTP receiver if it has one. If it doesn’t, run an OpenTelemetry Collector in between and use the matching contrib exporter, such as splunk_hec or elasticsearch. This Collector forwards Gateway spans to Splunk HEC:

otel-collector.yaml
extensions:
bearertokenauth:
token: ${env:GATEWAY_EXPORT_TOKEN}
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
auth:
authenticator: bearertokenauth
exporters:
splunk_hec:
endpoint: https://splunk.acme.com:8088/services/collector
token: ${env:SPLUNK_HEC_TOKEN}
index: merge_gateway
sourcetype: "merge:gateway:llm_calls"
service:
extensions: [bearertokenauth]
pipelines:
traces:
receivers: [otlp]
exporters: [splunk_hec]

In the Gateway console, choose Custom OTLP endpoint, enter the Collector’s base URL (https://otel.acme.com), and use GATEWAY_EXPORT_TOKEN as the bearer token.

Gotchas

  • Enter the base URL only. Gateway appends /v1/traces, which matches the Collector’s default path.
  • The endpoint must be public HTTPS with no redirects. Gateway rejects private, loopback, link-local, and cloud metadata addresses.
  • Auth is Authorization: Bearer <token> only. Put a Collector in front of platforms that need another scheme.
  • Don’t sample a destination you use as a security record. Sampling drops some successful requests.

Prompts and completions

Privacy mode is on for new destinations and strips prompt and completion content. Turn it off only if your SIEM is approved to hold prompts. It’s separate from payload logging, so a SIEM can receive prompts that Merge doesn’t store.

Delivery and failures

Gateway retries transient failures and suspends a destination after repeated failures. Export never slows model calls, but it fails silently, so alert in your SIEM when LLM call spans stop arriving.

Next

See what the tool call rows you are forwarding contain in Tool call logs.