DislifyDislify

Command Palette

Search for a command to run...

API reference

Authenticate. Submit. Receive the result.

Store Files, run processing jobs, retrieve durable outputs and receive signed events. Existing media compression APIs remain available.

01

Reference

Files and processing API

Every current browser file tool uses this shared model. SDK 0.5.0 is prepared in the repository; npm publication remains a separate release step.

File-processing quickstart

One lifecycle for every tool.

Store a source File, submit an operation, then poll or continue from a signed webhook. Developer API access requires Unlimited.

Authenticate

Use one server-side API key.

Store a File

Upload once and keep the File ID.

Process + retrieve

Run a typed job, then fetch generated Files.

SDK example

Upload → process → download → inspect lineage.

Server-side
file-processing.ts
View on npm
// Repository SDK 0.5.0; npm publication is a separate release step.
// npm install @dislify/sdk (use 0.5.0 once it is published)
import { readFile, writeFile } from "node:fs/promises"
import { DislifyClient } from "@dislify/sdk"

const client = new DislifyClient({ apiKey: process.env.DISLIFY_API_KEY! })

const source = await client.files.upload(
  new Blob([await readFile("proposal.docx")]),
  {
    fileName: "proposal.docx",
    contentType:
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  }
)

const submitted = await client.files.convert(source.id, { format: "pdf" })
const result = await client.processing.wait(submitted.id)

for (const output of result.generatedFiles) {
  const bytes = await client.files.download(output.id)
  await writeFile(output.name, Buffer.from(await bytes.arrayBuffer()))
}

const history = await client.files.get(result.generatedFiles[0].id)
console.log(history.parents.map((file) => file.id))
Running this from the CLI? Use node --env-file=.env app.mjs, or load dotenv/config.

For upload limits, use client.processing.compressPdf(fileId, { targetSizeBytes: 2 * 1024 ** 2, idempotencyKey: "pdf-limit-1" }) or compressImage for JPG/PNG. Choose a target or preset; unreachable targets fail without output.

Office batches return independent child job IDs. Split PDF and PDF-to-images can return multiple generated Files. Sources remain unchanged and retention follows the account plan or connected bucket.

01

Authenticate

POST /api/v1/developers/token with your API key, then keep the returned Bearer token server-side.

02

Create a source File

Prepare a signed upload with POST /api/v1/uploads, upload the bytes, then finalize it with POST /api/v1/files.

03

Submit an operation

Send owned File IDs to POST /api/v1/processing/jobs with one supported operation. The response includes job IDs and an optional batch ID.

04

Read the result

Poll GET /api/v1/processing/jobs/:id or receive a signed job event. Generated Files are durable outputs with source lineage.

05

Download safely

Read File metadata with GET /api/v1/files/:id, then request a short-lived download URL from the File download endpoint.

Processing operations

Browser tools, REST jobs, and SDK helpers share the same operation model.

23 operations

Watermark image

watermark_image

One still JPG / PNG / WebP → One same-format image with a text overlay

Options

text: 1–120 printable ASCII characters; fontSize: 8–512 source pixels (32); margin: 0–1024 (16); opacity: 0.05–1 (0.5); color: #RRGGBB (#000000); nine positions (bottom_right)

Independent batch/retry/ZIP; same-format workflow step. 50 MiB source/output plus plan limits; 8192px side; 20M pixels; bundled Bitstream Vera; no private metadata copied; original unchanged.

SDK
client.processing.watermarkImage(id, { text: "DRAFT" })

Crop / rotate image

crop_rotate_image

One still JPG / PNG → One same-format cropped/rotated File

Options

rotation: 0 | 90 | 180 | 270 clockwise; optional x, y, width, height integer pixels after orientation normalization and rotation; omit all crop fields for full image

Independent batch/retry/ZIP; workflow. Same pixel rectangle per image; out-of-bounds items fail individually. 50 MiB source/output plus plan limits; 8192px side; 20M pixels; no private metadata copied; JPG re-encoded at quality 95.

SDK
client.processing.cropRotateImage(id, { rotation: 90 })

Remove PDF metadata

remove_pdf_metadata

One verified static unsigned PDF per item; comments/layers/external content excluded → One newly written PDF without supported document metadata

Options

No options. Info, reachable Metadata/XMP, editing history, thumbnails and source IDs removed; original revisions discarded.

Independent batch/retry/ZIP; single-input PDF workflow step. 2,000 pages; existing plan/250 MiB bounds; visible text, image/font assets and retained functional content are not anonymized; source unchanged.

SDK
client.processing.removePdfMetadata(id, { idempotencyKey: "pdf-metadata-1" })

Add page numbers

add_pdf_page_numbers

One verified static, unsigned PDF per item → One new vector-preserving numbered PDF

Options

format: number | page_number | number_of_total | page_number_of_total; six top/bottom positions; startNumber (1–999999); pages (empty = all); fontSize (8–24pt); #RRGGBB color; opacity (0.3–1); margin (12–72pt)

Independent batch/retry/ZIP; single-input PDF workflow step. Number selected physical pages consecutively; N is the final assigned number.. Embedded Bitstream Vera; 2,000 pages; existing plan/250 MiB bounds; signed/forms/active PDFs rejected; overlays existing numbers.

SDK
client.processing.addPdfPageNumbers(id, { format: "number", position: "bottom_center", startNumber: 1, pages: [], fontSize: 12, color: "#294B3D", opacity: 1, margin: 24 })

Watermark PDF

watermark_pdf

One verified static, unsigned PDF per item → One new vector-preserving PDF with a text overlay

Options

mode: text, text, fontSize (8–96pt), opacity (0.05–0.6), rotation (-180–180), #RRGGBB color, position, pages (empty = all)

Independent batch/retry/ZIP; single-input PDF workflow step. Printable ASCII / embedded Bitstream Vera; 120 characters; 2,000 pages; existing plan/250 MiB bounds; signed/forms/active PDFs rejected; logo input deferred.

SDK
client.processing.watermarkPdf(id, { mode: "text", text: "DRAFT", fontSize: 36, opacity: 0.2, rotation: 45, color: "#294B3D", position: "center", pages: [] })

Remove background

remove_background

Still JPG / PNG / WebP; up to 12 million pixels and existing plan limits → Transparent-capable RGBA PNG at oriented source dimensions

Options

No options

Independent jobs, retry and ZIP; workflows. Local CPU inference; 50 MiB maximum source/output, 8192px per side, 12 million pixels; plan limits apply.

SDK
client.processing.removeBackground(id, { idempotencyKey: 'remove-bg-v1' })

Remove image metadata

strip_image_metadata

One still JPG / PNG / WebP → One sanitized File in the same format

Options

No options; orientation normalized; metadata removed

Independent jobs, retry and ZIP; workflows. 50 MiB source/output and plan cap; 20M pixels; 8192 px side; image queue.

SDK
client.processing.stripImageMetadata(id)

Convert image

convert_image

One still JPG / PNG / WebP → One JPG / PNG / WebP File in the selected different format

Options

format: jpg | png | webp; same-format conversion rejected

Independent jobs, retry and ZIP; workflows. 50 MiB source/output and plan cap; 20M pixels; 8192 px side; image queue.

SDK
client.processing.convertImage(id, { format: "webp" })

Resize image

resize_image

One still JPG / PNG → One same-format resized File

Options

width and/or height: 1–8192 whole pixels; OR percent: 1–400 integer; aspect-preserving fit; half-pixels round up

Independent jobs, batch retry and ZIP; workflows. 50 MiB source/output + plan cap; 20M pixels; 8192 side; no-op is an error; image queue.

SDK
client.processing.resizeImage(id, { width: 1200 })

Extract text

extract_text

Static PDF / JPG / PNG scan → One UTF-8 TXT File per source

Options

language: eng; existing PDF text first, local OCR fallback; LF and form-feed page boundaries

Independent jobs for ordered sources; shared batch/retry/ZIP; terminal workflow step. 20 MiB source; 2 MiB TXT; Free/Pro/Unlimited 3/10/20 PDF pages, 3/30/60 batch pages; OCR queue.

SDK
client.processing.extractText(id, { language: "eng" })

OCR searchable PDF

ocr_pdf

Scanned or hybrid static PDF → One searchable PDF per source

Options

language: eng (English); already-searchable pages retained

Independent jobs for ordered sources; shared batch/retry/ZIP. 20 MiB source; Free/Pro/Unlimited 3/10/20 pages per source, 3/30/60 batch pages; 300 DPI; isolated OCR queue.

SDK
client.processing.ocrPdf(id, { language: "eng" })

Image to searchable PDF

image_to_searchable_pdf

JPG / PNG still scan → One searchable PDF per source

Options

language: eng (English); EXIF orientation normalized

Independent jobs for ordered sources; shared batch/retry/ZIP. 20 MiB source; 16M pixels and 6,000 px side; Free/Pro/Unlimited 1/10/20 sources; isolated OCR queue.

SDK
client.processing.imageToSearchablePdf(id, { language: "eng" })

Office to PDF

office_to_pdf

DOCX / XLSX / PPTX → PDF per source

Options

No options

Independent jobs for ordered sources. 50 MiB per source; plan batch cap; 250 MiB combined.

SDK
client.files.convert(ids, { format: "pdf" })

Images to PDF

images_to_pdf

JPG / PNG → One PDF

Options

No options; source order is page order

Ordered sources → one job. 20M pixels per image; 40M total; Free one image, Pro 25, Unlimited 100.

SDK
client.files.convert(ids, { format: "pdf", operation: "images_to_pdf" })

Merge PDF

merge_pdf

Two or more PDFs → One PDF

Options

No options; preserve input order

Ordered sources → one job. Free 2, Pro 25, Unlimited 100 inputs; 2,000 pages; 250 MiB combined.

SDK
client.processing.mergePdf(ids)

Split / extract PDF

split_pdf

One PDF → One or more PDFs

Options

mode: extract + pages: "1,3,5-8"; ranges + ranges: ["1-3","4-6"]; every_page

One job → multiple Files. 2,000 source pages; Free/Pro/Unlimited 10/50/100 outputs.

SDK
client.processing.splitPdf(id, { mode: "extract", pages: "1,3-5" })

Organize PDF

organize_pdf

One PDF → One PDF

Options

pages: [{ page: 4, rotation: 90 }, { page: 1, rotation: 0 }]; one-based source pages; rotation 0/90/180/270

Ordered page plan → one File. 1–2,000 unique source pages; omitted pages removed.

SDK
client.processing.organizePdf(id, { pages })

PDF to JPG / PNG

pdf_to_images

One PDF → JPG / PNG per page

Options

format: jpg | png; pages: [1,3]; server sorts into source order

One job → multiple Files. 144 DPI; 4,096 px side; 12M pixels per page; 10/50/100 outputs; aggregate output cap.

SDK
client.processing.pdfToImages(id, { format: "png", pages: [1,3] })

Compress PDF

compress_pdf

One PDF → One smaller PDF

Options

preset: best_quality | balanced | smaller_file OR targetSizeBytes: integer maximum output bytes; mutually exclusive

Independent jobs for ordered sources; one File per source. Static PDF; 2,000 pages; target 16 KiB minimum and 5–95% of source; plan byte caps; strict target or safe error.

SDK
client.processing.compressPdf(id, { targetSizeBytes: 5 * 1024 * 1024 })

Compress image

compress_image

One JPG / PNG still → One smaller JPG, or PNG preserving transparency

Options

preset: best_quality | balanced | smaller_file OR targetSizeBytes: integer maximum; mutually exclusive

Independent jobs for ordered sources; one File per source. 50 MiB + plan cap; 20M pixels; 8,192 px side; target 16 KiB minimum and 5–95% of source; image queue.

SDK
client.processing.compressImage(id, { targetSizeBytes: 2 * 1024 * 1024 })

CSV to Excel

csv_to_xlsx

UTF-8 CSV → One XLSX

Options

delimiter: auto (default) | comma | semicolon | tab | pipe

Independent jobs for ordered sources; one File per source. 50 MiB and plan cap; values stored as literal text.

SDK
client.processing.csvToXlsx(id, { delimiter: "auto", idempotencyKey: "export-1" })

Excel to CSV

xlsx_to_csv

One XLSX worksheet → UTF-8 BOM / CRLF CSV

Options

sheet: exact worksheet name; delimiter: comma (default) | semicolon | tab | pipe; omit sheet only for one visible worksheet

Independent jobs for ordered sources; one File per source. 50 MiB and plan cap; cached formula values only; inspect files.worksheets(id).

SDK
client.processing.xlsxToCsv(id, { sheet: "Sales", delimiter: "comma" })

HEIC to JPG / PNG

heic_to_image

HEIC / HEIF primary still → One JPG / PNG

Options

format: jpg | png

Independent jobs for ordered sources; one File per source. 50 MiB source + plan cap; 20M pixels; 8,192 px side; SDR only; native image consumer required.

SDK
client.processing.heicToImage(id, { format: "jpg", idempotencyKey: "photo-1" })

Maximum-size compression

Submit {"fileIds":["source-id"],"operations":[{"type":"compress_pdf","targetSizeBytes":2097152}]}, or use compress_image for JPG/PNG. Choose exactly one of integer targetSizeBytes or preset (best_quality, balanced, smaller_file). Targets are at least 16 KB, 5–95% of the source and within plan/output limits. 1 MB = 1,048,576 bytes. Completion means independently verified stored bytes are at or below the target. An unreachable target or no-savings error creates no output File or output storage charge; try a larger target or preset.

Generic signed events include sanitized target/preset options, source IDs and actual generated File sizes. Failed targets have no successful output. Guests have no account webhook endpoints. SDK 0.5.0 is prepared in the repository; npm publication is a separate release step.

Limits + errors

Developer tokens require Unlimited and the correct scopes. Expect standard 4xx responses for invalid options, ownership, limits, quota, and unsupported formats. Native dispatch failures may transition an accepted job to error.

Idempotency

CSV→XLSX, XLSX→CSV, HEIC→image, Compress PDF and Compress image support Idempotency-Key. Reuse a key only for the same owner, source, and options. Other processing submissions are not retried automatically.

Signed events

Subscribe to job.queued, job.completed, and job.failed. Verify HMAC-SHA256 against the exact raw body and deduplicate by event ID. Delivery is currently best effort without automatic replay.

Current API quotas are 60 job submissions per hour and 10,000 per month for eligible accounts; operation-specific caps and normal storage retention still apply.
02

Reference

Existing media-compression SDK

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.

sdk-quick-start
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)
Running this from the CLI? Load your .env with node --env-file=.env app.mjs, or install dotenv and add import "dotenv/config".
03

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.

auth-token
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)
04

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.

create-job
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)
05

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% smaller

Light optimization for files that are already close to the size you want. Prioritizes visual and audio fidelity.

web~25% smaller

A quality-first preset for websites, portfolios, product pages, and everyday sharing.

balanced~35% smaller

A practical balance between output quality and file-size reduction for general use.

discord

Targets a Discord-friendly output while still keeping the result smaller than the original file.

strong~45% smaller

More aggressive compression for uploads where a noticeably smaller file matters more than perfect fidelity.

maximum~60% smaller

Prioritizes a substantially smaller result while keeping the media usable for normal playback and sharing.

tiny~70% smaller

Pushes hard for a compact result when transfer size is the main concern.

ultra~78% smaller

The 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
}
06

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.

job-status
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.
07

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.

download-output
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)
08

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.

register-webhook
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)
webhook-handler
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")
})
09

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.

Current policy

Unlimited API access

Unlimited

Burst limit

60

Job submissions per hour

Monthly quota

10,000

Job submissions per month

Retention

90 days

Completed-output retention

Hourly and monthly limits reset automatically. Normal operation-specific caps, storage quotas, and retention rules still apply.
10

Reference

Endpoint map

The primary routes used by a standard server-side integration.

POST/api/v1/uploads
Prepare a signed source upload
POST/api/v1/files
Finalize a durable File
GET/api/v1/files/:id
Read File metadata and lineage
GET/api/v1/files/:id/worksheets
Inspect workbook sheet names and bounds
POST/api/v1/processing/jobs
Submit a file operation
GET/api/v1/processing/jobs/:id
Read job status and generated Files
GET/api/v1/files/:id/download
Authorize a signed File download
POST/v1/developers/token
Exchange an API key for an access token
POST/v1/developers/token/refresh
Refresh an authenticated developer token
POST/v1/jobs
Upload media and create a compression job
GET/v1/jobs/:id
Read job state and download information
GET/v1/jobs/:id/download
Download the completed output with the same Bearer token
POST/v1/developers/webhooks
Register a signed webhook endpoint

Keep 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 File operation from your server.