Skip to content

Introduction

ServiceLabs is Finnoto's single front door for document and file utilities. Before it, every utility was its own AWS Lambda behind its own API Gateway URL, each with its own JWT secret, its own request shape and no shared view of who was calling what. Now there is one base URL, one API key, one request envelope and one result shape — plus a console that shows every call.

  • One auth layer. Services authenticate with a scoped, revocable API key (du_live_…). People sign in to the console with Google (finnoto.com) or a password, with optional two-factor.
  • Sync or async. Every call becomes a job with a stable id. Wait for the result in the same request, or get the job id back immediately and fetch the result later — by polling or through a signed callback.
  • Tracked. Requests, success rate and latency (end-to-end and inside the utility) per utility and per API key, kept after the raw jobs age out.
  • Files on R2. Every file a utility produces is copied to Cloudflare R2; you always get R2 links back.

The utilities

UtilitySlugInputOutputTime limit
HTML → PDFhtml-pdfHTML document URL (http(s)://, s3://) + page options1 PDF15 min
Split PDFsplit-pdfPDF URL (≤ 150 MB, ≤ 500 pages)1 PDF per page15 min
Zip fileszip-filesUp to 500 { url, fileName }1 .zip15 min
Unzip archiveunzip-files.zip URL (http(s)://, s3://)1 file per archive entry15 min
XLS → XLSXxls-to-xlsxLegacy .xls URL1 .xlsx5 min

GET /v1/utilities returns the same list for your key, each with its full input JSON Schema and an example — see List utilities.

How a call flows

 your service
     │  POST /v1/utilities/split-pdf        Authorization: Bearer du_live_…

 ┌──────────────────────────┐   validate input · check key scope + rate limit
 │  API gateway             │   create job  job_01K…
 └──────────┬───────────────┘
   sync     │            async  (POST …/jobs, or Prefer: respond-async)
   run it   │            ──────────▶ queue ──▶ worker picks it up
   in the   │                                  │
   request  ▼                                  ▼
 ┌──────────────────────────────────────────────────────┐
 │  invoke the utility (AWS Lambda, up to 15 min)       │  retries only transient failures
 │  → it reads your input URL and writes files to S3    │
 └──────────┬───────────────────────────────────────────┘

   copy every output file  S3 → Cloudflare R2

   job succeeded / failed ──▶ response (sync) · GET /v1/jobs/:id · signed callback (async)

   usage + latency rollups ──▶ console dashboard

What a job is

A job is one call to one utility. Sync and async calls produce exactly the same object — the only difference is whether the request waited for it to finish.

json
{
  "id": "job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3",
  "object": "job",
  "utility": "split-pdf",
  "mode": "async",
  "status": "succeeded",
  "timings": { "queueMs": 41, "upstreamMs": 2140, "storageMs": 180, "totalMs": 2372 },
  "result": {
    "files": [
      {
        "url": "https://files.servicelabs.dev/split-pdf/2026-09-10/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3/page-1.pdf",
        "key": "split-pdf/2026-09-10/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3/page-1.pdf",
        "name": "page-1.pdf",
        "size": 48213,
        "contentType": "application/pdf",
        "pageNumber": 1
      }
    ],
    "pageCount": 1
  },
  "error": null
}

The id is the handle for everything: fetch it with GET /v1/jobs/{id}, find it in the console, quote it to support. Details in Jobs, sync & async.

Where to next

ServiceLabs · a Finnoto company