Skip to main content
POST
/
uploads
/
image-url
# 1. Mint the upload URL.
UPLOAD_URL=$(curl -s -X POST https://teambattles.gg/api/v1/uploads/image-url \
  -H "Authorization: Bearer tb_your_api_key" \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["uploadUrl"])')

# 2. POST the image bytes to the minted URL.
curl -X POST "$UPLOAD_URL" \
  -H "Content-Type: image/png" \
  --data-binary @screenshot.png
# -> { "storageId": "..." }
{
  "uploadUrl": "<string>"
}

Permission Required

This endpoint requires uploads.upload:read-write on your API key, plus the writes feature. The owner is always the API key owner, derived from the key itself - it is never accepted as a body field. A free (api_free) key receives 403 error_api_feature_required; this requires an api_pro+ plan.

Access / Membership Rules

This is a user-self route (binding: "none") - it is not bound to a game or league. The minted URL belongs to the API key owner. The endpoint takes no request body and no parameters.

What’s Returned

Returns { uploadUrl } - a short-lived, single-use Convex storage upload URL. The bytes do not exist yet at mint time, so no validation happens here.

The Upload Flow

The image upload is a two-step, mint-then-upload flow:
1

Mint the URL

Call this endpoint to get a fresh uploadUrl. The URL is single-use and short-lived, so mint it just before you upload.
2

POST the image bytes

POST the raw image bytes to the uploadUrl with the matching Content-Type header. The response body is { "storageId": "..." }.
3

Attach the storageId on a downstream write

Supply the returned storageId on a consuming write - for example a score-ingest screenshotStorageIds, or a support ticket’s images.

Size And Content-Type Limits

This endpoint mints the URL only - it does not validate the bytes. Validation happens on the consuming write when you attach the storageId:
LimitValue
Max size5 MB
Allowed typesimage/png, image/jpeg, image/gif, image/webp, image/avif
An oversized or unsupported blob is rejected by the downstream write with 400 INVALID_INPUT. image/svg+xml is intentionally not allowed (SVG can carry script).
The storageId is bound to you, the uploader. The consuming write rejects a storageId minted for or by another tenant, so you cannot attach someone else’s blob.
# 1. Mint the upload URL.
UPLOAD_URL=$(curl -s -X POST https://teambattles.gg/api/v1/uploads/image-url \
  -H "Authorization: Bearer tb_your_api_key" \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["uploadUrl"])')

# 2. POST the image bytes to the minted URL.
curl -X POST "$UPLOAD_URL" \
  -H "Content-Type: image/png" \
  --data-binary @screenshot.png
# -> { "storageId": "..." }

Authorizations

Authorization
string
header
required

Send your API key as: Authorization: Bearer tb_

Response

A freshly minted upload URL.

A freshly minted Convex storage upload URL. POST the image to it to receive a storageId for use on a downstream write.

uploadUrl
string
required

Short-lived, single-use URL to POST the image bytes to.