Skip to content

Step attachment images

Test case steps can include multiple images (PNG, JPEG, or WebP). Images are processed server-side to WebP (full size, longest edge ≤ 2048px, aspect ratio preserved) plus a thumbnail (longest edge ≤ 320px).

Access matches case step editing: any authenticated organization member who can save steps on the case may stage, commit, and delete attachments. This is not restricted to project owners/admins (unlike project avatars).

  1. StagePOST /api/v1/test-cases/{caseUlid}/step-attachments:stage with multipart/form-data and one or more file parts. The server writes objects under staging/<orgUlid>/ and returns staged refs (no database row yet).
  2. Commit — Include those refs on each step in POST /api/v1/projects/{projectUlid}/test-cases (create) or PUT /api/v1/test-cases/{caseUlid}/steps (replace). The server copies staging → permanent keys, inserts rows, and deletes staged objects.
  3. ReadGET /api/v1/test-cases/{caseUlid}/steps (and consolidated case GET) returns each step’s attachments[] with permanent keys. Thumbnails are served at GET /__assets/{thumbKey} (same public asset route as avatars).

Abandoned staged objects expire automatically via an R2 lifecycle rule on the staging/ prefix (~48 hours).

Terminal window
curl -sS -X POST \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
-F "file=@screenshot-a.png" \
-F "file=@screenshot-b.png" \
"https://probara.net/api/v1/test-cases/01J...CASE/step-attachments:stage"

200 example:

{
"attachments": [
{
"ulid": "01J...ATT",
"objectKey": "staging/01J...ORG/01J...UPLOAD.webp",
"thumbKey": "staging/01J...ORG/01J...UPLOAD_thumb.webp",
"mime": "image/webp",
"byteSize": 8420,
"width": 1200,
"height": 800,
"originalFilename": "screenshot-a.png"
}
]
}

Limits:

  • Up to 10 file parts per request
  • 10 MiB per file before processing
  • Allowed MIME types: image/png, image/jpeg, image/webp

422 when too many files, disallowed MIME, or invalid image data. 404 when the case is not in the active organization.

Each step in the replace payload may include attachments:

{
"steps": [
{
"ulid": "01J...STEP",
"position": 1,
"action": "Open settings",
"attachments": [
{
"ulid": "01J...ATT",
"position": 0,
"objectKey": "staging/01J...ORG/01J...UPLOAD.webp",
"thumbKey": "staging/01J...ORG/01J...UPLOAD_thumb.webp",
"mime": "image/webp",
"byteSize": 8420,
"width": 1200,
"height": 800,
"originalFilename": "screenshot-a.png"
}
]
}
]
}

Reconciliation by attachment ulid:

PayloadServer action
New ULID + staging/<this-org>/ keysCopy to attachments/<orgUlid>/<projectUlid>/steps/<stepUlid>/<attUlid>.webp, insert row, delete staged objects
Existing ULID (already stored)Retain objects; update position only
Row exists but ULID omittedDelete row, delete permanent objects, decrement org storage counter

The server does not trust client byteSize, width, or height on commit — it re-measures stored R2 objects. Staging keys must match the requesting organization’s prefix; cross-tenant refs are rejected.

POST /api/v1/projects/{projectUlid}/test-cases accepts the same per-step attachments shape on create.

Terminal window
curl -sS -X DELETE \
-H "Authorization: Bearer $PROBARA_API_TOKEN" \
"https://probara.net/api/v1/test-cases/01J...CASE/step-attachments/01J...ATT"

204 on success. 404 when the attachment or case is unknown.

Committed attachment bytes (full + thumbnail) increment organization_storage_usage for category step_attachment. Staging bytes are not counted. Quota enforcement is not implemented yet; the counter exists for future tiers.