{
  "markdown": "# Convertrilo TypeScript SDK\n\nType-safe client for the Convertrilo API.\n\n## VMAF And Encoding Passes\n\nWhen `optimize: \"vmaf\"` is requested, Convertrilo performs VMAF sampling and one optimized final encode. A supplied `passes: 2` value remains accepted for backward compatibility, but the API uses and bills one effective pass.\n\nPricing responses expose `requestedPasses` and `effectivePasses`. New integrations should use `passes: 1` with VMAF.\n\n## Install\n\n```bash\npnpm add @convertrilo/sdk\n```\n\nThe package currently targets modern Node.js runtimes with global `fetch`. If your runtime does\nnot provide `fetch`, pass `fetchImpl` to the client.\n\n## CLI And MCP Automation\n\nThe package includes two executable automation tools:\n\n```bash\nexport CONVERTRILO_API_KEY=\"cvr_...\"\n\nconvertrilo encode https://example.com/input.mp4 \\\n  --codec h264 \\\n  --resolution 1080p \\\n  --audio-policy transcode-aac \\\n  --frame-rate-policy cap \\\n  --scale-policy no-upscale \\\n  --quality better \\\n  --wait \\\n  --json\n\nconvertrilo-mcp\n```\n\nUse `convertrilo` for scripts, CI, cron, and local operations. Use `convertrilo-mcp`\nas a stdio MCP server for agent and workflow integrations.\n\nRun `convertrilo login` or `convertrilo init` to save an API key locally, or run\n`convertrilo` with no arguments to start the interactive encode wizard. Waiting\ncommands show terminal progress by default and keep `--json` clean for scripts.\n\nShell completion snippets are available with:\n\n```bash\nconvertrilo completion zsh\n```\n\nSee [`docs/CLI-AND-MCP.md`](docs/CLI-AND-MCP.md) for commands, MCP client config,\nS3 output examples, VMAF, two-pass, status, cancel, and balance workflows.\n\nIf you want the simplest terminal walkthrough, start with\n[`docs/CLI-QUICKSTART.md`](docs/CLI-QUICKSTART.md).\n\nFor agent/client setup, use [`docs/MCP-QUICKSTART.md`](docs/MCP-QUICKSTART.md).\n\nMCP registry metadata lives in [`server.json`](server.json).\n\n## Create A Client\n\n```ts\nimport { ConvertriloClient } from \"@convertrilo/sdk\";\n\nconst client = new ConvertriloClient({\n  baseUrl: \"https://api.convertrilo.com\",\n  apiKey: process.env.CONVERTRILO_API_KEY,\n});\n```\n\nUse an API key for server-to-server integrations. Browser apps should call your own backend,\nthen your backend calls Convertrilo.\n\n## Examples\n\nThe `examples/` directory contains starter scripts for the main integration paths:\n\n- `node-url-to-cdn.ts` - encode a public URL and receive a signed CDN download URL\n- `node-url-to-s3.ts` - encode a public URL and upload the result to S3/S3-compatible storage\n- `google-drive-byo-token.ts` - upload output to Google Drive using a customer-owned service account\n- `folder-ingest-s3.ts` - queue one encode job per video in an S3 prefix\n- `idempotency.ts` - safely retry `createJob` and `createJobsBulk`\n- `webhook-receiver-hmac.ts` - verify managed webhook HMAC signatures from a Node receiver\n\nLocal SDK smoke tests use `.env`, but published SDK users should provide credentials through their\nown server environment. Do not put Convertrilo API keys or customer storage tokens in frontend code.\n\nFor a complete server-to-server walkthrough covering URL, S3, folder ingest, Google Drive\nservice accounts, polling, and webhooks, see\n[`docs/API-INTEGRATION-GUIDE.md`](docs/API-INTEGRATION-GUIDE.md).\n\n## Idempotent Job Creation\n\nUse an idempotency key when retrying create calls from your backend. Reusing the same key with the\nsame body returns the original response instead of creating duplicate jobs.\n\n```ts\nconst job = await client.createJob({\n  externalId: \"upload-123\",\n  metadata: { customerId: \"cus_123\" },\n  codec: \"h264\",\n  resolution: \"1080p\",\n  fps: 30,\n}, {\n  idempotencyKey: \"job-upload-123\",\n});\n\nconst batch = await client.createJobsBulk({\n  jobs: [\n    {\n      externalId: \"batch-42:clip-1\",\n      codec: \"h264\",\n      resolution: \"1080p\",\n      fps: 30,\n      sourceS3: { bucket: \"source\", key: \"clip-1.mp4\" },\n    },\n  ],\n  settings: { confirm: true },\n}, {\n  idempotencyKey: \"bulk-batch-42\",\n});\n```\n\n## Job File Cleanup\n\nDelete managed upload and output objects after a job reaches a terminal state:\n\n```ts\nawait client.cancelJob(jobId); // required first for created, queued, or running jobs\n\nconst result = await client.deleteJobFiles(jobId);\nconsole.log(result.objectsDeleted, result.jobRetained);\n```\n\nThe job record and billing history remain available for auditing. Active jobs return the stable\n`job_active` error until they are canceled.\n\n## Saved Encode Presets\n\nSave reusable encode settings without storing source URLs, storage credentials, or output secrets:\n\n```ts\nconst preset = await client.createEncodePreset({\n  name: \"Default 1080p web MP4\",\n  settings: {\n    codec: \"h264\",\n    resolution: \"1080p\",\n    fps: 30,\n    preset: \"standard\",\n    bitrateTier: \"medium\",\n    passes: 1,\n    policy: \"fastest\",\n    container: \"mp4\",\n    quality: \"better\",\n    optimize: \"none\",\n    vmafTarget: 93,\n    audioPolicy: \"transcode-aac\",\n    frameRatePolicy: \"cap\",\n    scalePolicy: \"no-upscale\",\n  },\n});\n\nconst { presets } = await client.getEncodePresets();\n```\n\n## Saved Output Destinations\n\nSave reusable delivery targets without storing raw storage secrets in the destination itself. S3 destinations reference an encrypted saved S3 credential:\n\n```ts\nconst destination = await client.createOutputDestination({\n  name: \"Customer uploads bucket\",\n  config: {\n    type: \"s3\",\n    credentialId: \"0f5a7f2b-4ff2-45d4-b76f-1f7b6e98d4d1\",\n    keyPrefix: \"processed/\",\n  },\n});\n\nconst { destinations } = await client.getOutputDestinations();\n```\n\n## URL Source To CDN Output\n\n```ts\nconst job = await client.onDemandEncode({\n  sourceUrl: \"https://example.com/input.mp4\",\n  externalId: \"customer-video-123\",\n  metadata: {\n    customerId: \"cus_123\",\n    workflow: \"daily-compression\",\n  },\n  codec: \"h264\",\n  resolution: \"1080p\",\n  quality: \"better\",\n  audioPolicy: \"transcode-aac\",\n  frameRatePolicy: \"cap\",\n  scalePolicy: \"no-upscale\",\n}, {\n  idempotencyKey: \"encode-customer-video-123\",\n});\n\nlet finalStatus;\nwhile (true) {\n  finalStatus = await client.onDemandStatus(job.jobId);\n\n  if (finalStatus.status === \"success\") break;\n  if (finalStatus.status === \"failed\") {\n    throw new Error(finalStatus.failureMessage || \"Encoding failed\");\n  }\n\n  await new Promise((resolve) => setTimeout(resolve, 5000));\n}\n\nconsole.log(finalStatus.downloadUrl);\nconsole.log(finalStatus.requestedExecution);\nconsole.log(finalStatus.effectiveExecution);\nconsole.log(finalStatus.sourceProbe?.color);\nconsole.log(finalStatus.outputProbe);\n```\n\nTerminal users can inspect the same report with:\n\n```bash\nconvertrilo status JOB_ID --json\nconvertrilo wait JOB_ID --json\n```\n\n## URL Source To S3 Output\n\n```ts\nconst job = await client.onDemandEncode({\n  sourceUrl: \"https://example.com/input.mp4\",\n  codec: \"h264\",\n  resolution: \"1080p\",\n  outputS3: {\n    bucket: \"customer-output-bucket\",\n    key: \"encoded/input-1080p.mp4\",\n    region: \"us-east-1\",\n    accessKeyId: process.env.CUSTOMER_S3_ACCESS_KEY_ID,\n    secretAccessKey: process.env.CUSTOMER_S3_SECRET_ACCESS_KEY,\n  },\n});\n\nconsole.log(job.jobId);\n```\n\nFor S3-compatible services, pass `endpoint` and usually `forcePathStyle: true`.\n\n## URL Source To Google Drive Output\n\nFor headless API integrations, save a customer-owned Google service account once.\nUse a Google Shared Drive for output and add the returned service-account email as a member\nwith permission to create files.\n\n```ts\nconst credential = await client.createGoogleDriveCredential({\n  name: \"Production Drive\",\n  serviceAccount: JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_JSON!),\n});\n\nconst job = await client.onDemandEncode({\n  sourceUrl: \"https://example.com/input.mp4\",\n  codec: \"h264\",\n  resolution: \"1080p\",\n  outputGoogleDrive: {\n    folderId: \"GOOGLE_DRIVE_FOLDER_ID\",\n    fileName: \"input-1080p.mp4\",\n    credentialId: credential.id,\n  },\n});\n\nconsole.log(job.jobId);\n```\n\nDashboard Google Picker authorization is separate from SDK automation.\n\n## Folder Ingest\n\nQueue one job per video in an S3 prefix:\n\n```ts\nconst batch = await client.onDemandIngestFolder({\n  externalIdPrefix: \"batch-2026-06-09\",\n  metadata: {\n    customerId: \"cus_123\",\n    workflow: \"folder-compression\",\n  },\n  sourceS3: {\n    bucket: \"customer-source-bucket\",\n    prefix: \"incoming/\",\n    region: \"us-east-1\",\n    accessKeyId: process.env.CUSTOMER_S3_ACCESS_KEY_ID,\n    secretAccessKey: process.env.CUSTOMER_S3_SECRET_ACCESS_KEY,\n  },\n  outputDestination: \"s3\",\n  outputS3: {\n    bucket: \"customer-output-bucket\",\n    prefix: \"encoded/\",\n    region: \"us-east-1\",\n    accessKeyId: process.env.CUSTOMER_S3_ACCESS_KEY_ID,\n    secretAccessKey: process.env.CUSTOMER_S3_SECRET_ACCESS_KEY,\n  },\n  codec: \"h264\",\n  maxFiles: 25,\n  resolution: \"1080p\",\n}, {\n  idempotencyKey: \"folder-batch-2026-06-09\",\n});\n\nfor (const job of batch.jobs || []) {\n  console.log(job.jobId, job.externalId, job.fileName);\n}\n```\n\nUse `maxFiles` to cap how many discovered videos are queued from a folder.\n\nQueue one job per video in a Google Drive folder:\n\n```ts\nconst batch = await client.onDemandIngestFolder({\n  sourceGoogleDrive: {\n    folderId: \"SOURCE_FOLDER_ID\",\n    credentialId: credential.id,\n  },\n  outputDestination: \"google-drive\",\n  outputGoogleDrive: {\n    folderId: \"OUTPUT_FOLDER_ID\",\n    credentialId: credential.id,\n  },\n  codec: \"h264\",\n  maxFiles: 25,\n  resolution: \"1080p\",\n});\n\nfor (const job of batch.jobs || []) {\n  console.log(job.jobId, job.fileName);\n}\n```\n\nConvertrilo mints short-lived Google tokens from the encrypted service-account credential when each worker starts.\n\nPoll each returned `jobId` with `client.onDemandStatus(jobId)`.\n\n## Webhook Delivery History\n\nManaged webhooks are HMAC signed. You can test a webhook and inspect recent delivery attempts:\n\n```ts\nawait client.testWebhook(webhookId);\n\nconst history = await client.getWebhookDeliveries(webhookId);\nfor (const delivery of history.deliveries || []) {\n  console.log(delivery.status, delivery.statusCode, delivery.event);\n}\n```\n\n## Regenerate Types\n\nThe SDK types are generated from `openapi.yaml`.\n\n```bash\npnpm run generate\npnpm run build\n```\n\nThe generate script uses `--default-non-nullable false` so OpenAPI defaults remain optional\nin TypeScript request bodies.\n",
  "bytes": 10198,
  "sha": "be62c92bab5df217145c79e45dae31c4bcd09057507948c952f405a69e18bf78",
  "repo_slug": "serkandrgn/convertrilo-js",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_serkandrgn_convertrilo_b4c521bd/readme"
}