Skip to main content
Applies to:
  • Plan -
  • Deployment -

Summary

Goal: Delete project logs programmatically and have them actually disappear from the UI and BTQL queries.

How deletion works

Deleting a log is done by re-inserting the event with _object_delete: true for each target row id:
The _object_delete marker matches on the row id only. It does not match on span_id or root_span_id. Because the insert endpoint accepts any submitted id and echoes it back in row_ids, a 200 response means Braintrust recorded a delete tombstone for the IDs you sent, even when those IDs don’t correspond to any visible log row. That’s why a delete keyed on span_id or root_span_id “succeeds” but changes nothing. id and span_id are different values: id is the system-assigned unique row identifier, while span_id is an arbitrary value you can set and reuse across projects.

Solution: resolve row IDs first, then delete

Use BTQL to look up the actual row ids, then submit those ids as delete events.

Important behaviors

  • Delete the whole trace, not just the root span. Deleting a root span deletes only that one span. The child spans remain orphaned and still show up in the UI and BTQL. To fully remove a trace, query for all ids under its root_span_id and delete each one (as shown above).
  • Deletions are eventually consistent. Rows may briefly reappear after a delete and take a few seconds plus a page refresh to disappear. This is more noticeable right after deletion, not a sign the delete failed.
  • Use the correct API URL. For hosted Braintrust, use https://api.braintrust.dev. For self-hosted or hybrid deployments, use the data plane API URL from Settings > Data plane. Being able to insert logs confirms the URL only for writes; deletion uses the same endpoint but still requires valid row ids.
  • {project_id} must be the UUID, not the project name (e.g., 914981cd-29bc-4c94-936e-74bd91d8bef2). Passing the project name fails the request.

Verify a deletion

Re-run the BTQL lookup after deleting. If the delete succeeded, the query returns no rows for those IDs (allow a few seconds for consistency):
If rows still persist after deleting by verified row ids, collect the delete response row_ids, a before/after BTQL result for one root_span_id, the request timestamp, the project ID, and your SDK version, and escalate.

Notes

  • For recurring cleanup instead of one-off deletion, use a data retention automation to delete logs older than a configured window.

References