Attach media
Turn an image / video into a popkit media URL. Media used for publishing must live in popkit storage (external links expire), so get a mediaUrl here first and put it into media[].dataUrl on create / update. mode=upload returns a direct-upload URL valid for five minutes; PUT the bytes yourself with the returned Content-Type (bytes never touch popkit servers: images 8 MB, videos 500 MB). mode=transload lets popkit fetch and copy a public link — images and covers only, 8 MB max. It only produces a URL and changes nothing else.
/api/v1/mediaAuthorization & guardrails
- Send
Authorization: Bearer <credential>; credentials are issued under Settings → Integrations → Long-lived credentials, see Authentication. - The credential needs scope
posts:write; the issuer's workspace role must also allow the action. - Rate limit: write tier, 10/min per credential; over the cap returns 429 with
Retry-After. - Idempotency: honoured when
Idempotency-Keyis present; optional otherwise.
Request body (application/json)
modeenum: upload · transloadrequiredupload = get a direct-upload URL and PUT yourself; transload = popkit fetches your link.
kindenum: image · cover · videorequiredimage body picture · cover · video.
contentTypestringoptionalRequired for upload: image/jpeg | png | webp or video/mp4 | webm | quicktime.
bytesintegeroptionalRequired for upload: file size in bytes; over the limit is refused.
urlstring (http/https)optionalRequired for transload: the link to copy (images / covers only).
clientRequestIdstringoptionalIdempotency key (8–128 chars of [A-Za-z0-9_.:-]); prefer the Idempotency-Key header, which wins over the body.
Rules
- The upload URL lives 5 minutes; upload right away and re-request if it expires.
- Videos always use upload; transload accepts images and covers only.
Responses
Success is HTTP 201 with a { data, meta } envelope; examples per status are on the right (at the end of the page on narrow screens). Every error is { error: { code, message, params }, meta } — branch on the code, not the message; see Errors.
| Status | Possible codes |
|---|---|
| 400 | invalid_input |
| 401 | unauthenticated |
| 402 | plan_limit |
| 403 | forbidden / workspace_not_found |
| 404 | not_found |
| 409 | no_account / needs_reauth / replay |
| 429 | rate_limited |
| 500 | internal |
| 502 | platform_error |
| 503 | external_writes_off |
Examples
curl -X POST "https://popkit.loxily.com/api/v1/media" \
-H "Authorization: Bearer popkit_it_••••••••" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-2026-09-21-001" \
-d '{
"mode": "upload",
"kind": "video",
"contentType": "video/mp4",
"bytes": 48213000
}'{
"data": {
"mode": "upload",
"kind": "video",
"mediaUrl": "https://storage.googleapis.com/socialhub-media/ws_01J7…/media/01J8ZN….mp4",
"uploadUrl": "https://storage.googleapis.com/socialhub-media/…?X-Goog-Signature=…",
"contentType": "video/mp4",
"expiresInSec": 300,
"next": "PUT the file bytes to uploadUrl with Content-Type: video/mp4 within 300s, then pass mediaUrl in media[].dataUrl."
},
"meta": {
"requestId": "6a1d0c9e-2b7f-4e3a-8c5d-1f0e9b8a7c73"
}
}