TCM Automation Docs

Attachments

Attach screenshots, logs, and trace files to an execution. The simplest way is a single call; a presigned flow is available for very large files.

One-call upload

POST /executions/{id}/attachments with multipart/form-data and a single file field. The API streams the bytes to object storage (MinIO/S3), links the file to the execution’s screenshots, and returns an attachment ref { key, filename, download_url }. One round trip, nothing else to do.

The SDKs do this for you
Every SDK exposes a single uploadAttachment call that performs this upload and returns the ref. The file is linked to the execution automatically, so you don’t need to pass the ref anywhere else. Use the raw API (the curl tab) if you’re uploading from a language we don’t ship an SDK for.

Example

bytes, _ := os.ReadFile("test-screenshot.png")
ref, err := client.UploadAttachment(ctx, execID, "test-screenshot.png",
    "image/png", bytes)
if err != nil { log.Fatal(err) }
log.Printf("uploaded: %s (%d bytes)", ref.URL, ref.Size)
Record the result and attach in one call
POST /runs/{run_id}/testcases/{test_case_id}/report is a single multipart call that records the result (result, duration_ms, worker_id, …) and links an optional file — handy when your runner only kept the run id + test case id. The SDKs expose it as reportResult.

Large files — presigned upload

With the one-call upload the bytes flow through the API, which ties up a worker for the whole transfer. For very large artifacts (big browser traces, videos) use the presigned flow instead: the API hands back a short-lived URL and the runner PUTs the bytes straight to storage, so they never touch the API. Then link the ref to the execution via the result update.

  1. POST /executions/{id}/attachments/presign { upload_url, key, download_url }.
  2. PUT the bytes to upload_url (direct to MinIO/S3).
  3. PATCH /executions/{id} with a screenshots array to link it.
presigned-upload.sh
EXEC_ID="<execution-id>"
HOST="https://YOUR_HOST"

# 1) Presign — ask the API for a short-lived upload URL.
resp=$(curl -sS -X POST "$HOST/api/v1/executions/$EXEC_ID/attachments/presign" \
  -H "Authorization: Bearer $TCM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filename":"test-screenshot.png","content_type":"image/png"}')

upload_url=$(echo "$resp" | jq -r .upload_url)
key=$(echo "$resp" | jq -r .key)
download_url=$(echo "$resp" | jq -r .download_url)

# 2) PUT the bytes straight to storage (MinIO/S3) — the API never sees them.
#    Content-Type must match what you presigned with.
curl -sS -X PUT "$upload_url" \
  -H "Content-Type: image/png" \
  --data-binary @test-screenshot.png

# 3) Link the file to the execution via the result update.
curl -sS -X PATCH "$HOST/api/v1/executions/$EXEC_ID" \
  -H "Authorization: Bearer $TCM_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"screenshots\":[{\"key\":\"$key\",\"filename\":\"test-screenshot.png\",\"download_url\":\"$download_url\"}]}"

Limits

  • Per-file size limit: 100 MB by default (configurable per tenant).
  • Allowed content types: anything — TCM does not inspect the bytes.
  • Attachments are tied to the execution; revoking the token does not delete the uploaded files.