ReFile/ Denoise Labs
PricingDocs
Sign inGet started

Start here

  • Overview
  • Getting started

What ReFile does

  • What it can & can't do
  • Supported formats
  • Writing good prompts
  • Voice input

Plans & limits

  • Limits & plans
  • Troubleshooting

Account

  • Account & data

Developers

  • API reference
Docs menu

Start here

  • Overview
  • Getting started

What ReFile does

  • What it can & can't do
  • Supported formats
  • Writing good prompts
  • Voice input

Plans & limits

  • Limits & plans
  • Troubleshooting

Account

  • Account & data

Developers

  • API reference

Reference

API

Submit natural-language file operations from any backend. Same engine as the web app.

Quick start

Base URL: https://refile.denoiselabs.com/api/v1. All requests are HTTPS; responses are JSON.

# 1. Get an upload URL
curl -X POST https://refile.denoiselabs.com/api/v1/uploads \
  -H "Authorization: Bearer rf_live_..."
# → { "uploadUrl": "...", "expiresInSeconds": 1800 }

# 2. Upload your file to that URL
curl -X POST "$UPLOAD_URL" \
  -H "Content-Type: image/png" \
  --data-binary @photo.png
# → { "storageId": "kg2..." }

# 3. Submit the job (waits up to 25s for short jobs)
curl -X POST 'https://refile.denoiselabs.com/api/v1/jobs?wait=true' \
  -H "Authorization: Bearer rf_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "make this black and white",
    "files": [{"storageId":"kg2...","filename":"photo.png"}]
  }'
  • Step 1 returns a short-lived signed URL — no auth on the upload itself.
  • Step 2 POSTs the raw file bytes; the storage layer responds with a storageId.
  • Step 3 creates the job. With ?wait=true the response blocks up to 25s for the finished job; without it, you get status=pending immediately.

→ Get your API key.

Authentication

Send your key as a bearer token on every request: Authorization: Bearer rf_live_.... Keys are server-side credentials — never embed them in browser, mobile, or public code.

Keys can't be retrieved after creation. If you lose one, revoke it at /settings/api and create a new one.

Endpoints

POST /api/v1/uploads

Mint a signed upload URL. No body. You then POST the raw file bytes to uploadUrl; that storage call returns a storageId to pass into a job.

// Request
POST /api/v1/uploads
Authorization: Bearer rf_live_...

// Response
{
  "uploadUrl": "string",          // signed URL — POST your file bytes here
  "expiresInSeconds": 1800
}

POST /api/v1/jobs

Create a job. Pass ?wait=true to long-poll up to 25 seconds for the finished result; omit it to return immediately. The response body is the job object.

POST /api/v1/jobs?wait=true
Authorization: Bearer rf_live_...
Content-Type: application/json

{
  "prompt": "string (required, 1..2000 chars)",
  "files": [
    { "storageId": "string", "filename": "string" }
  ],                                                  // optional
  "chat_id": "string",                                // optional — group jobs into a thread
  "webhook_url": "https://example.com/refile-events"  // optional — POST when job settles
}

GET /api/v1/jobs/:id

Fetch the current state of a job. Use this to poll after creating a job without ?wait=true, or to confirm a webhook delivery. Returns the same job object.

The job object

Every job endpoint returns the same shape:

{
  "id": "string",
  "chat_id": "string | null",
  "status": "pending | generating | running | succeeded | failed",
  "description": "string | null",
  "kind": "command | chat | null",
  "message": "string | null",     // set when kind == "chat"
  "input_files": ["string"],
  "outputs": [
    { "storageId": "string", "filename": "string", "url": "string | null" }
  ],
  "pipeline": [                   // populated for multi-step jobs
    { "description": "string", "status": "pending | running | completed | failed" }
  ] | null,
  "files_expired": "boolean",     // outputs are GC'd after 24h
  "created_at": "number",         // unix ms
  "error": { "code": "string", "message": "string" } | null
}

Convex's internal completed state surfaces as succeeded in the API. Output url is a short-lived signed download link — fetch within the 24-hour retention window or files_expired will flip to true.

Errors

Errors are returned as {"error":{"code":"...","message":"..."}} with the matching HTTP status.

Codes marked job error are job-level failures, not request-level: HTTP 200, with status: "failed" and a populated error field on the job object. The request itself was accepted.

CodeStatusWhen
unauthorized401Missing or invalid API key.
invalid_request400Validation failed on the request body.
not_found404Job id not found, or not yours.
quota_exceeded402Plan limits exceeded.
rate_limited429Too many requests. Retry-After header is set.
unprocessable_request200 (job error)Prompt was too complex to plan.
no_output200 (job error)Job ran but produced no output file.
execution_failed200 (job error)Job ran but the file failed processing.
internal_error500Service problem. Safe to retry.

Webhooks

  • Pass webhook_url on job creation. We POST the full job object to it once the job settles (succeeded or failed).
  • Delivery headers: X-Refile-Event: job.settled, X-Refile-Delivery: <uuid>, X-Refile-Signature: sha256=<hex> (HMAC-SHA256 over the raw body).
  • Retried up to 3× with 1s / 5s backoff on any non-2xx response or network error.
Content-Type:        application/json
X-Refile-Event:      job.settled
X-Refile-Delivery:   <uuid>
X-Refile-Signature:  sha256=<hex>   // HMAC-SHA256 over the raw body
Signature verification requires a shared secret — currently issued via support. Self-serve secret rotation is on the roadmap. Until then, treat the webhook as a delivery hint and re-fetch GET /jobs/:id to confirm state.

Rate limits & quotas

  • 10 requests/sec per key, burst of 20. 429 responses include a Retry-After header in seconds.
  • File size, file count, and pipeline-step limits match your plan — identical to the web app. See /docs/limits-and-plans.
  • Jobs are async by default. Pass ?wait=true to long-poll for up to 25 seconds; after that, poll GET /api/v1/jobs/:id.

Pricing

Pay-as-you-go, no monthly minimum. Usage is metered per job and billed monthly.

  • $0.05 per command job.
  • $0.05 per pipeline step (multi-step jobs are billed per step).

See /pricing for the full breakdown and current regional pricing.

What's next

  • Create a key at /settings/api.
  • See live pricing at /pricing.
  • Read the rest of the docs at /docs.
PreviousAccount & data
ReFile

AI-native file automation. Describe the outcome, drop the file, get the command and the result.

A Denoise Labs product

Product

  • Convert
  • Chat
  • Pricing
  • Changelog

Popular conversions

  • All conversions
  • PDF → JPG
  • HEIC → JPG
  • Word → PDF
  • MOV → MP4
  • Compress PDF
  • Compress video

Resources

  • Docs
  • Developers
  • Community
  • Status
  • GitHub

Legal

  • Terms
  • Privacy
  • Security

Contact

  • Email
  • Support
© 2026 ReFile · A Denoise Labs productBuilt for people who like commands as much as outcomes.