Call OmniDesk from programs and AI agents. 325 formats, 33 file actions and the company drive, all over the API.
One key opens everything the app can do. It is not a separate product - the same converter, entered through a different door. Available from the Team plan; create keys in Control panel → API keys.
Tell an AI agent to turn a file into a PDF and have it actually happen. Paste this into your MCP config and fill in the key. The agent gets five tools: convert_file, file_action, find_route, list_formats and list_actions.
{
"mcpServers": {
"omnidesk": {
"command": "uvx",
"args": ["omnidesk-mcp"],
"env": { "OMNIDESK_API_KEY": "od_..." }
}
}
}
pypi.org/project/omnidesk-mcp · github.com/work517/omnidesk-mcp
Send the key in the X-API-Key header on every request. A conversion is two steps: upload to create a job, poll until it finishes, then download.
# 1. upload and convert
curl -X POST https://app.omnidesk.win/api/jobs/quick \
-H "X-API-Key: od_..." \
-F "file=@report.docx" -F "dst_fmt=pdf"
# → {"job": {"id": "…", "status": "queued", …}}
# 2. poll
curl https://app.omnidesk.win/api/jobs/JOB_ID -H "X-API-Key: od_..."
# → {"status": "done", "result_node_id": "…", "quality": {"changes": [...]}}
# 3. download
curl https://app.omnidesk.win/api/drive/NODE_ID/download -H "X-API-Key: od_..."
# → {"url": "https://…"} then GET that url
import httpx, time
c = httpx.Client(base_url="https://app.omnidesk.win",
headers={"X-API-Key": "od_..."})
with open("report.docx", "rb") as f:
job = c.post("/api/jobs/quick",
files={"file": f}, data={"dst_fmt": "pdf"}).json()["job"]
while job["status"] not in ("done", "failed"):
time.sleep(2)
job = c.get(f"/api/jobs/{job['id']}").json()
url = c.get(f"/api/drive/{job['result_node_id']}/download").json()["url"]
open("report.pdf", "wb").write(httpx.get(url).content)
for change in job.get("quality", {}).get("changes", []):
print(change["level"], change["text"])
| POST /api/jobs/quick | Upload and convert. Comma-separate dst_fmt for several formats at once |
| POST /api/jobs/action | File actions (tables, redact, OCR, subtitles, …) |
| GET /api/jobs/{id} | Job status and the quality ledger |
| GET /api/catalog/routes?src=&dst= | Conversion routes and alternatives |
| GET /api/catalog/actions | Actions and their options |
| POST /api/drive/upload | Upload to the drive |
| GET /api/drive/{id}/download | Download URL |
| GET /api/drive/list | List the drive |
| POST /api/drive/import-url | Fetch a file from a URL into the drive → convert it with /api/jobs |
| POST /api/jobs | Convert drive files (node_ids) |
Save an https URL under Control Panel → API keys → Webhook and we POST {"event": "job.done"|"job.failed", "job": {...}} when a job finishes or fails — no polling. Verify each call with the X-OmniDesk-Signature header.
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes) -> bool:
# header = "t=1760000000,v1=<hex>"
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300: # 5분 넘은 것은 버린다 / reject replays
return False
mac = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(mac, parts["v1"])
The job response carries quality.changes: what the conversion cost - a lost transparency, a dropped audio track, substituted fonts. Entries marked warn are the ones the recipient would notice.
Conversions made over the API count against the plan's monthly quota. Up to ten keys per company; revoke any time and it stops immediately.