Quickstart · Media Encoding · 10 minutes
Add Media Encoding to your Lovable, Replit, or Cursor app in 10 minutes.
Video becomes HLS with multiple renditions and a poster frame. Audio becomes AAC. Images become WebP. Managed temporary storage means no bucket to configure: presigned upload in, presigned outputs out.
1 — Get a key · 1 minute
Sign in with your email at majr.app/keys, name your product, copy the key. It is shown once. One key works on every MAJR Agent Service. Free during beta, no card.
export MAJR_API_KEY=rn_... # the key from majr.app/keys
2 — Connect your agent · 2 minutes
The service speaks MCP over Streamable HTTP at https://encoding.majr.app/mcp, so any MCP client connects with the same URL and header. Pick your tool:
Cursor
Add to Cursorinstalls the server below; it reads MAJR_API_KEY from your environment.
Or add it to .cursor/mcp.json yourself:
{
"mcpServers": {
"majr-media-encoding": {
"url": "https://encoding.majr.app/mcp",
"headers": {
"Authorization": "Bearer ${env:MAJR_API_KEY}"
}
}
}
}Claude Code
The plugin brings all three services and a skill that knows how to use them:
/plugin marketplace add majrdotapp/agent-services /plugin install majr-services@agent-services
Or register just this server:
claude mcp add --transport http majr-media-encoding https://encoding.majr.app/mcp \ --header "Authorization: Bearer $MAJR_API_KEY"
Lovable, Replit, or any chat-driven builder
No MCP needed. Put the key in your project's secrets as MAJR_API_KEY, then paste this prompt into the chat. The agent reads the contract and writes the integration in your app's own language.
Add media encoding to this app using the MAJR Media Encoding service. Read the contract first: https://encoding.majr.app/llms.txt.
Auth: send "Authorization: Bearer <key>" with the key from the MAJR_API_KEY secret. Never hardcode it.
When a user uploads a video, audio file, or image:
1. POST https://encoding.majr.app/v1/uploads with the file's contentType, then PUT the bytes to the returned presigned URL.
2. POST https://encoding.majr.app/v1/encode with a fresh UUID jobId, the contentType, and the "source" from step 1. Omit destination and callback.
3. Poll GET https://encoding.majr.app/v1/jobs/{jobId} until status is SUCCEEDED or FAILED.
4. On success download every entry in "outputs[]" and store it in our own storage, keeping the relative paths, before "objectExpiresAt". Serve "primaryPlayback.path" from our storage; never hand a player the presigned URL.
Handle a 503 with Retry-After by waiting and retrying; it is not a bad key.3 — Encode your first file · 3 minutes
Over MCP the tools are create_upload, encode_media, and get_encoding_job; over HTTP:
# 1. Ask for a managed upload slot (no bucket of your own needed)
curl -sS -X POST https://encoding.majr.app/v1/uploads \
-H "Authorization: Bearer $MAJR_API_KEY" -H "Content-Type: application/json" \
-d '{"contentType": "video/mp4"}'
# -> 201 {"uploadId": ..., "source": {"bucket": ..., "key": ...},
# "upload": {"url": "https://...", "method": "PUT", "headers": {...}, "urlExpiresAt": ...}}
# 2. PUT the bytes straight to the presigned URL (they never transit the API)
curl -sS -T ./clip.mp4 -H "Content-Type: video/mp4" "<upload.url>"
# 3. Enqueue the encode: a fresh UUID per job; omit destination (managed outputs) and callback (poll)
curl -sS -X POST https://encoding.majr.app/v1/encode \
-H "Authorization: Bearer $MAJR_API_KEY" -H "Content-Type: application/json" \
-d '{"jobId": "<uuid>", "contentType": "video/mp4", "source": {"bucket": "<source.bucket>", "key": "<source.key>"}}'
# -> 202 {"jobId": "<uuid>", "status": "QUEUED"}
# 4. Poll until SUCCEEDED (or FAILED)
curl -sS https://encoding.majr.app/v1/jobs/<uuid> -H "Authorization: Bearer $MAJR_API_KEY"A finished managed job returns the whole inventory:
{
"jobId": "<uuid>", "status": "SUCCEEDED", "storage": "managed-temporary",
"primaryPlayback": {"kind": "hls", "path": "master.m3u8"},
"poster": {"path": "poster.jpg"},
"outputs": [
{"path": "master.m3u8", "bytes": 512, "url": "https://...", "urlExpiresAt": "...", "objectExpiresAt": "..."},
{"path": "720p/index.m3u8", ...}, {"path": "720p/seg-000.ts", ...}, ...
]
}4 — Keep the outputs · 3 minutes
Managed storage is temporary, not archival. Download every outputs[] URL and copy it into your own storage or CDN, preserving the relative layout, beforeobjectExpiresAt (about 72 hours). Presigned URLs expire after about an hour; re-poll the job for fresh ones.
HLS playlists reference their segments by relative path, so re-host the whole prefix and point your player at your copy of master.m3u8. Four rules keep the integration correct: a fresh UUID per encode, retry only an identical request, never reuse a jobId while its result is live, copy outputs out before they expire.
5 — Ship · 1 minute
Serve primaryPlayback.path from your storage and you are done. Encodes show up under your product at majr.app/keys.
Reference
- Contract for agents (llms.txt)
- https://encoding.majr.app/llms.txt
- MCP endpoint
- https://encoding.majr.app/mcp
- Your keys and usage
- majr.app/keys
Errors are RFC 7807 problem+json. A 401 means the key is wrong. A 503 with Retry-After is never a verdict on your key: wait and retry, do not rotate it.