Request a presigned URL for a media upload (dual-authenticated).
Initiate a media upload for a specific listing. Returns a presigned PUT URL the client uses to stream bytes directly to the staging bucket, plus a Celery task id that will deliver the object to the agency's connected storage (Dropbox / Google Drive) via Composio.
Authentication
Accepts EITHER:
- Session cookie (dashboard): better-auth-session cookie. Viewer role blocked.
- API key (external integrators): X-API-Key header. Agency-scoped at key creation.
API key takes precedence when both are present.
Rate limit
API-key callers: 20 req/min (upload_media bucket, tightest on the platform). Session callers share the dashboard's existing limits. The 202 response is cheap; the expensive work happens asynchronously after the client PUTs bytes to the presigned URL.
Webhook correlation
This endpoint fires NO webhook itself. Integrators must subscribe to media.delivery.completed / .failed (events/02) to know when bytes have actually landed in their storage. The task_id returned here appears verbatim on the later webhook event.
| Method | Path |
|---|---|
POST | /api/v1/media/upload |
Primary host: https://valara.cloud (legacy alias: https://dash.jacoballenmedia.com).
Parameters
(no path or query parameters)
Request body
(no request body)
Response
null
Responses
| Status | Description |
|---|---|
202 | Successful Response |
422 | Validation Error (see error codes) |
Examples
curl
curl -X POST "https://valara.cloud/api/v1/media/upload" \
-H "Authorization: Bearer $VALARA_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)"
Python
import os
import uuid
import httpx
res = httpx.request(
"POST",
"https://valara.cloud/api/v1/media/upload",
headers={
"Authorization": f"Bearer {os.environ['VALARA_API_KEY']}",
"Content-Type": "application/json",
"X-Idempotency-Key": str(uuid.uuid4()),
},
)
res.raise_for_status()
print(res.json())