Skip to main content
POST
/
v2
/
auto-monitor-setups
/
bulk
Bulk Upsert Auto Monitor Setups
curl --request POST \
  --url https://app.traceloop.com/api/v2/auto-monitor-setups/bulk \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "setups": [
    {}
  ]
}
'
import requests

url = "https://app.traceloop.com/api/v2/auto-monitor-setups/bulk"

payload = { "setups": [{}] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({setups: [{}]})
};

fetch('https://app.traceloop.com/api/v2/auto-monitor-setups/bulk', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://app.traceloop.com/api/v2/auto-monitor-setups/bulk",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'setups' => [
[

]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://app.traceloop.com/api/v2/auto-monitor-setups/bulk"

payload := strings.NewReader("{\n \"setups\": [\n {}\n ]\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://app.traceloop.com/api/v2/auto-monitor-setups/bulk")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"setups\": [\n {}\n ]\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://app.traceloop.com/api/v2/auto-monitor-setups/bulk")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"setups\": [\n {}\n ]\n}"

response = http.request(request)
puts response.read_body
Creates or updates up to 100 auto monitor setups in one request, keyed by external_id. Designed for IaC-style clients that would otherwise fan out one HTTP call per setup. Each item is upserted independently using the same semantics as Update by External ID: if a setup with the given external_id exists in the project it is replaced (status reset to pending, evaluators replaced wholesale); otherwise it is created.
All API requests require authentication. Pass your API key as a Bearer token in the Authorization header. See Authentication for details.

Atomic Semantics

The batch is applied atomically — it either fully succeeds or nothing is written. If any item fails validation, or the database write fails, no setups are persisted. On success the endpoint returns 200 OK with the full list of upserted setups. Because the batch is all-or-nothing, there are no per-item status flags: every item in the response was written successfully. Failures are surfaced as a single request-level error (400 or 500) identifying the first offending item.

Request Body

setups
object[]
required
Array of setups to upsert. Must contain between 1 and 100 items. Each item has the same shape as the Create request body:
FieldTypeRequiredDescription
external_idstringYesUnique identifier for the setup within the project. Must be unique across items in the same batch.
evaluatorsstring[]YesList of evaluator slugs to run on matched spans. See Evaluator Slugs.
selectorobject[]NoArray of filter rules used to match spans. See the Create endpoint for the selector schema.
evaluator_configsobject[]NoOptional per-evaluator configuration overrides.

Example Request

curl -X POST https://api.traceloop.com/v2/auto-monitor-setups/bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "setups": [
      {
        "external_id": "openai-gpt4o-monitor",
        "evaluators": ["answer-relevancy", "toxicity-detector"],
        "selector": [
          {"key": "gen_ai.system", "value": "openai", "source": "span_attributes"},
          {"key": "gen_ai.request.model", "value": "gpt-4o", "source": "span_attributes"}
        ]
      },
      {
        "external_id": "anthropic-monitor",
        "evaluators": ["answer-relevancy"],
        "selector": [
          {"key": "gen_ai.system", "value": "anthropic", "source": "span_attributes"}
        ]
      }
    ]
  }'

Response

200 OK

Returned when the entire batch is upserted successfully. The setups array contains one entry per input item, in the same order as the request. Each entry is a full setup object, matching the Create response shape.
{
  "setups": [
    {
      "id": "cmm...",
      "external_id": "openai-gpt4o-monitor",
      "org_id": "c108269c-...",
      "project_id": "cm9v2g95l...",
      "env_project_id": "cm9v2ga9i...",
      "init_rules": [
        { "key": "gen_ai.system", "value": "openai", "source": "span_attributes", "operator": "equals" },
        { "key": "gen_ai.request.model", "value": "gpt-4o", "source": "span_attributes", "operator": "equals" }
      ],
      "evaluators": [
        { "evaluator_type": "answer-relevancy", "status": "pending" },
        { "evaluator_type": "toxicity-detector", "status": "pending" }
      ],
      "status": "pending",
      "created_at": "2026-05-20T10:30:00Z",
      "updated_at": "2026-05-20T10:30:00Z"
    },
    {
      "id": "cmm...",
      "external_id": "anthropic-monitor",
      "org_id": "c108269c-...",
      "project_id": "cm9v2g95l...",
      "env_project_id": "cm9v2ga9i...",
      "init_rules": [
        { "key": "gen_ai.system", "value": "anthropic", "source": "span_attributes", "operator": "equals" }
      ],
      "evaluators": [
        { "evaluator_type": "answer-relevancy", "status": "pending" }
      ],
      "status": "pending",
      "created_at": "2026-05-20T10:30:00Z",
      "updated_at": "2026-05-20T10:30:00Z"
    }
  ]
}

400 Bad Request

Returned when the batch is rejected without writing any items. The error message identifies the first offending item by index. Causes:
  • Empty setups array — setups must contain at least 1 item
  • More than 100 items — setups exceeds max of 100
  • Any item missing external_idsetups[N]: external_id is required
  • Two items in the batch share the same external_idsetups[N]: duplicate external_id "..." (also at setups[M])
  • Any item fails validation — e.g. an unknown evaluator slug, an invalid selector, or an invalid evaluator config, prefixed with the item index: setups[N]: evaluators[0]: unknown evaluator slug "..."
{
  "error": "setups[2]: duplicate external_id \"openai-gpt4o-monitor\" (also at setups[0])"
}

500 Internal Server Error

Returned when the database write fails. No items are persisted. The underlying cause is logged server-side and not returned to the client.
{
  "error": "Internal server error"
}