Quick reference
| Upload API | https://api.telemetry-upload.weka.io |
| Required header | x-weka-dret-telemetry: x-weka-dret-telemetry |
| Max file size | 50 MB |
| Allowed types | application/json (.json) |
| Internal access | @weka.io Google accounts only |
Upload via API — two-step flow
Clients first request a presigned POST URL, then upload the file directly to S3 using that URL.
Step 1 — request the URL
POST https://api.telemetry-upload.weka.io/upload-request
Content-Type: application/json
x-weka-dret-telemetry: x-weka-dret-telemetry
{
"filename": "my-data.json",
"content_type": "application/json"
}
Response (200):
{
"upload_id": "...",
"url": "https://...amazonaws.com/",
"fields": { ...form fields to include in the next POST... },
"expires_in": 900,
"s3_key": "default/<uuid>/my-data.json"
}
Step 2 — POST your file to that URL
Multipart form with all fields + a file part. URL is valid for 15 minutes.
curl
RESP=$(curl -sS -X POST https://api.telemetry-upload.weka.io/upload-request \
-H "Content-Type: application/json" \
-H "x-weka-dret-telemetry: x-weka-dret-telemetry" \
-d '{"filename":"my-data.json","content_type":"application/json"}')
echo '{"hello":"world"}' > /tmp/my-data.json
URL=$(echo "$RESP" | jq -r .url)
ARGS=$(echo "$RESP" | jq -r '.fields | to_entries | map("-F \(.key)=\(.value)") | join(" ")')
eval curl -X POST "$URL" $ARGS -F file=@/tmp/my-data.json
Python (requests)
import requests
API = "https://api.telemetry-upload.weka.io"
H = {"x-weka-dret-telemetry": "x-weka-dret-telemetry"}
r = requests.post(f"{API}/upload-request", headers=H, json={
"filename": "my-data.json",
"content_type": "application/json",
}).json()
with open("my-data.json", "rb") as f:
requests.post(r["url"], data=r["fields"], files={"file": f}).raise_for_status()
print("uploaded as", r["upload_id"])
Node.js (built-in fetch + FormData)
const fs = require("fs");
const API = "https://api.telemetry-upload.weka.io";
const H = { "x-weka-dret-telemetry": "x-weka-dret-telemetry" };
const r1 = await fetch(`${API}/upload-request`, {
method: "POST",
headers: { "Content-Type": "application/json", ...H },
body: JSON.stringify({ filename: "my-data.json", content_type: "application/json" }),
});
const p = await r1.json();
const form = new FormData();
for (const [k, v] of Object.entries(p.fields)) form.append(k, v);
form.append("file", new Blob([fs.readFileSync("my-data.json")]), "my-data.json");
await fetch(p.url, { method: "POST", body: form });
console.log("uploaded as", p.upload_id);
What happens after a successful POST
- File lands in the quarantine S3 bucket (encrypted, never publicly readable).
- GuardDuty Malware Protection scans the bytes (~5 seconds).
- The validator Lambda parses the JSON. If valid, the file moves to the approved bucket and shows up in the file list. If not, it's deleted from S3 and audit shows
REJECTED with the reason.
Total latency from upload to "visible in UI": typically 5–15 seconds.
API errors
| HTTP | error code | Cause |
| 400 | invalid_json_body | Request body wasn't valid JSON |
| 400 | filename_and_content_type_required | Missing field |
| 400 | invalid_filename | Disallowed chars; allowed = [A-Za-z0-9._-]{1,200} |
| 400 | invalid_rule_id | Rule ID format invalid |
| 400 | content_type_not_allowed | MIME not in rule's allowed list |
| 400 | extension_not_allowed | Extension not in rule's allowed list |
| 403 | header_validation_failed | Required header missing or value doesn't match |
| 404 | rule_not_found | Specified rule_id doesn't exist |
| 429 | — | WAF rate limit (>2000 req / 5 min from same IP) |
Using this UI
- List: approved files newest first. Filter by
rule_id (exact match).
- Download: issues a 5-minute presigned GET; browser starts the download immediately.
- Delete: removes the S3 object and hides the row. Audit history is preserved.
- Theme: toggle in the top right (light / dark). Saved per-browser.
- Session: Google ID tokens last 1 hour; you'll be sent back to sign-in afterward.
More
- Full README: github.com/weka/weka-telemetry-upload
- Adding additional upload rules (different size limits, different headers per tenant): see the README's "Configuring upload rules" section.