Authenticate. Submit. Receive the result.
A compact reference for integrating Dislify media jobs, quotas, and webhook delivery into a server-side application.
Reference
Use @dislify/sdk for media workflows
The official JavaScript/TypeScript SDK v0.2.0 wraps API-key exchange, signed uploads, retries, job and batch polling, typed results, and protected downloads. Use it for image optimization or image, video, and audio batches; the REST endpoints below remain available for lower-level control.
npm install @dislify/sdk
import { readFile } from "node:fs/promises"
import { DislifyClient } from "@dislify/sdk"
const dislify = new DislifyClient({
apiKey: process.env.DISLIFY_API_KEY,
})
const image = new Blob([await readFile("./hero.png")], {
type: "image/png",
})
console.log("Starting image optimization...")
const { shareUrl } = await dislify.optimizeImage(image, {
fileName: "hero.png",
compression: "balanced",
onProgress(job) {
console.log(`${job.status}: ${job.progress ?? 0}%`)
},
})
console.log("Completed:", shareUrl).env with node --env-file=.env app.mjs, or install dotenv and add import "dotenv/config".Reference
Exchange an API key
API keys are long-lived account secrets. Exchange one from your server for a developer token before calling protected job routes.
const tokenRes = await fetch("https://api.dislify.com/v1/developers/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
apiKey: process.env.DISLIFY_API_KEY,
}),
})
const { token } = await tokenRes.json()
console.log(token)Reference
Create a compression job
Request a short-lived upload URL, PUT the media directly to private R2 storage, then create the asynchronous compression job with the returned upload ID.
import { readFile } from "node:fs/promises"
const fileBytes = await readFile("./video.mp4")
const uploadRes = await fetch("https://api.dislify.com/v1/uploads", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
fileName: "video.mp4",
fileSize: fileBytes.byteLength,
contentType: "video/mp4",
}),
})
const upload = await uploadRes.json()
await fetch(upload.uploadUrl, {
method: "PUT",
headers: { "Content-Type": upload.contentType },
body: new Uint8Array(fileBytes),
})
const res = await fetch("https://api.dislify.com/v1/jobs", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
uploadId: upload.uploadId,
compression: "balanced",
}),
})
const job = await res.json()
console.log(job.id)
console.log(job.status)Reference
Choose a preset or target size
Use one of eight named presets, or select custom compression and provide an approximate target in bytes or as a percentage of the original file.
gentle~15% smallerLight optimization for files that are already close to the size you want. Prioritizes visual and audio fidelity.
web~25% smallerA quality-first preset for websites, portfolios, product pages, and everyday sharing.
balanced~35% smallerA practical balance between output quality and file-size reduction for general use.
discordTargets a Discord-friendly output while still keeping the result smaller than the original file.
strong~45% smallerMore aggressive compression for uploads where a noticeably smaller file matters more than perfect fidelity.
maximum~60% smallerPrioritizes a substantially smaller result while keeping the media usable for normal playback and sharing.
tiny~70% smallerPushes hard for a compact result when transfer size is the main concern.
ultra~78% smallerThe most aggressive preset. Best when getting the file as small as practical matters more than fidelity.
Custom target
Set compression: "custom" and send either targetSizeBytes or targetSizePercent. The percentage is the desired output size relative to the source — for example, 55 targets an output around 55% of the original size. Targets must be between 5% and 95% of the source size.
{
"uploadId": "upload_id",
"compression": "custom",
"targetSizePercent": 55
}Reference
Read job status
Check the job while processing or use webhooks to receive completion and failure events automatically. Completed Unlimited jobs also expose the automatic public share URL and its expiration.
const jobId = "job_123"
const res = await fetch(`https://api.dislify.com/v1/jobs/${jobId}`, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
})
const job = await res.json()
console.log(job.id)
console.log(job.status)
console.log(job.downloadUrl)
// Tip:
// You do not need to keep polling this endpoint.
// Register a webhook to receive job.completed and job.failed events automatically.Reference
Download a completed output
The download URL is protected. Send the same temporary Bearer token when requesting the file; the route verifies job ownership before streaming bytes from private R2 storage.
import fs from "node:fs"
const downloadRes = await fetch(job.downloadUrl, {
headers: {
Authorization: `Bearer ${token}`,
},
})
if (!downloadRes.ok) {
throw new Error(`Download failed: ${downloadRes.status}`)
}
const bytes = Buffer.from(await downloadRes.arrayBuffer())
fs.writeFileSync(job.outputName || "dislify-output.bin", bytes)Reference
Register and verify webhooks
Store the signing secret when the endpoint is created, verify the raw request body, and route events by the Dislify event header.
const res = await fetch("https://api.dislify.com/v1/developers/webhooks", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
url: "https://your-app.com/api/dislify/webhook",
events: [
"upload.received",
"quota.updated",
"job.completed",
"job.failed",
],
}),
})
const data = await res.json()
console.log(data.webhook.id)
console.log(data.webhook.secret)import crypto from "node:crypto"
import express from "express"
const app = express()
app.use(
express.json({
verify: (req, _res, buf) => {
req.rawBody = buf.toString("utf8")
},
}),
)
const WEBHOOK_SECRET = process.env.DISLIFY_WEBHOOK_SECRET
app.post("/api/dislify/webhook", (req, res) => {
const signature = req.header("X-Dislify-Signature")
const event = req.header("X-Dislify-Event")
const expectedSignature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(req.rawBody)
.digest("hex")
if (signature !== expectedSignature) {
return res.status(401).json({
error: "Invalid webhook signature",
})
}
if (event === "job.completed") {
console.log("Job completed:", req.body.data)
}
if (event === "job.failed") {
console.log("Job failed:", req.body.data)
}
if (event === "upload.received") {
console.log("Upload received:", req.body.data)
}
if (event === "quota.updated") {
console.log("Quota updated:", req.body.data)
}
return res.json({ received: true })
})
app.listen(3001, () => {
console.log("Webhook server running on port 3001")
})Reference
Default Unlimited API policy
Dislify applies both burst protection and monthly quotas. Completed outputs follow the account retention policy rather than permanent file storage.
60
Job submissions per hour
10,000
Job submissions per month
90 days
Completed-output retention
Reference
Endpoint map
The primary routes used by a standard server-side integration.
/v1/developers/token/v1/developers/token/refresh/v1/jobs/v1/jobs/:id/v1/jobs/:id/download/v1/developers/webhooksKeep credentials server-side
Never expose API keys or webhook secrets in client bundles. Use environment variables and verify webhook signatures against the raw request body.
Ready when you are
Ready to connect the pipeline?
Create an Unlimited account, generate an API key, and submit the first media job from your server.
