curl --request POST \
--url https://api.egp.scale.com/v5/spans/deletions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"reason_code": "customer_requested_deletion",
"trace_ids": [
"<string>"
],
"reason_details": "<string>",
"group_id": null
}
'import requests
url = "https://api.egp.scale.com/v5/spans/deletions"
payload = {
"reason_code": "customer_requested_deletion",
"trace_ids": ["<string>"],
"reason_details": "<string>",
"group_id": None
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
reason_code: 'customer_requested_deletion',
trace_ids: ['<string>'],
reason_details: '<string>',
group_id: null
})
};
fetch('https://api.egp.scale.com/v5/spans/deletions', 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://api.egp.scale.com/v5/spans/deletions",
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([
'reason_code' => 'customer_requested_deletion',
'trace_ids' => [
'<string>'
],
'reason_details' => '<string>',
'group_id' => null
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$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://api.egp.scale.com/v5/spans/deletions"
payload := strings.NewReader("{\n \"reason_code\": \"customer_requested_deletion\",\n \"trace_ids\": [\n \"<string>\"\n ],\n \"reason_details\": \"<string>\",\n \"group_id\": null\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
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://api.egp.scale.com/v5/spans/deletions")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"reason_code\": \"customer_requested_deletion\",\n \"trace_ids\": [\n \"<string>\"\n ],\n \"reason_details\": \"<string>\",\n \"group_id\": null\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.egp.scale.com/v5/spans/deletions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason_code\": \"customer_requested_deletion\",\n \"trace_ids\": [\n \"<string>\"\n ],\n \"reason_details\": \"<string>\",\n \"group_id\": null\n}"
response = http.request(request)
puts response.read_body{
"deletion_request_id": "<string>",
"status": "accepted",
"object": "trace_deletion",
"trace_count": 123,
"resolved_trace_count": 123,
"tombstoned_span_count": 123,
"soft_deleted_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Create trace deletion request
curl --request POST \
--url https://api.egp.scale.com/v5/spans/deletions \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"reason_code": "customer_requested_deletion",
"trace_ids": [
"<string>"
],
"reason_details": "<string>",
"group_id": null
}
'import requests
url = "https://api.egp.scale.com/v5/spans/deletions"
payload = {
"reason_code": "customer_requested_deletion",
"trace_ids": ["<string>"],
"reason_details": "<string>",
"group_id": None
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
reason_code: 'customer_requested_deletion',
trace_ids: ['<string>'],
reason_details: '<string>',
group_id: null
})
};
fetch('https://api.egp.scale.com/v5/spans/deletions', 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://api.egp.scale.com/v5/spans/deletions",
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([
'reason_code' => 'customer_requested_deletion',
'trace_ids' => [
'<string>'
],
'reason_details' => '<string>',
'group_id' => null
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$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://api.egp.scale.com/v5/spans/deletions"
payload := strings.NewReader("{\n \"reason_code\": \"customer_requested_deletion\",\n \"trace_ids\": [\n \"<string>\"\n ],\n \"reason_details\": \"<string>\",\n \"group_id\": null\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
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://api.egp.scale.com/v5/spans/deletions")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"reason_code\": \"customer_requested_deletion\",\n \"trace_ids\": [\n \"<string>\"\n ],\n \"reason_details\": \"<string>\",\n \"group_id\": null\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.egp.scale.com/v5/spans/deletions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"reason_code\": \"customer_requested_deletion\",\n \"trace_ids\": [\n \"<string>\"\n ],\n \"reason_details\": \"<string>\",\n \"group_id\": null\n}"
response = http.request(request)
puts response.read_body{
"deletion_request_id": "<string>",
"status": "accepted",
"object": "trace_deletion",
"trace_count": 123,
"resolved_trace_count": 123,
"tombstoned_span_count": 123,
"soft_deleted_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Authorizations
Headers
Body
- TraceIdsDeletionCreateRequest
- GroupDeletionCreateRequest
customer_requested_deletion Explicit trace IDs to delete. Limited to 10,000 after deduplication.
1 - 256Optional bounded audit context for the deletion request.
2000Response
Successful Response
accepted, resolving, deleting, completed, failed, canceled Live spans tombstoned in the tracing service, from the awaited propagation acknowledgement. Response-only (the durable record is sgp-traces' deletion audit). Null when propagation was skipped or ran best-effort. The tracing service applies its deletion cutoff per storage engine. On ClickHouse deployments this counts the identities live among the rows ingested at or before the cutoff and masks exactly those rows. On Postgres deployments an identity is one row whose ingested_at its latest write refreshed, so it counts identities whose latest write is at or before the cutoff: a span whose in-progress precursor was ingested before the cutoff and whose completion landed after it counts 1 on ClickHouse and 0 on Postgres. In this order both engines still serve the completion, so only the count differs; the reverse order, a lower-version write landing after the cutoff, also differs in read visibility. The cutoff is the deletion's arrival at the tracing service, so this is a race with an in-flight completion. On ClickHouse deployments a lower-version write of a span that lands after the cutoff is a second row: the deletion removes only the rows ingested at or before the cutoff and point reads then serve the surviving lower-version row. On Postgres deployments the upsert guard rejects a lower-version write while the row is live, so the one row keeps its pre-cutoff ingested_at, is masked, and no read serves the span. Both engines count the span once here. Only a write landing after the mask, when the masked row admits any version, behaves the same on both engines. On ClickHouse deployments a repeat deletion of traces an earlier audit already covers reports 0: its live count excludes every row ingested at or before the earlier audit's reach, whether or not that mask completed. On Postgres deployments the count is the rows still live at or before the cutoff, so a retry after a failed mask reports the rows it masks.

