Integrations#

The same API, reachable from the tools you already use. An MCP server drops EnConvert into a coding agent, an n8n node into a workflow, a CLI into your terminal, ten SDKs into your application code, and a web widget onto your own website.


Pick your surface#

Every surface below calls the same public REST endpoints with the same API key, against the same project and the same monthly operations allowance.

Surface Package Use it when
MCP Setup @enconvert/mcp A coding agent such as Claude Code, Cursor, Windsurf, or Claude Desktop should call the API itself, in chat, without you writing HTTP.
n8n @enconvert/n8n-nodes-enconvert You are building an n8n workflow and want conversions, scraping and crawling as a node that emits real binary data.
CLI @enconvert/cli You want to convert files or pull web data from a terminal or a shell script, with --json output and stable exit codes.
SDKs ten language clients You are writing application code and want typed methods with editor autocomplete instead of hand-rolled HTTP.

There is a fifth surface with no page of its own: web widgets, covered below, which is the only one your end users touch directly.

If you are still deciding, REST, MCP and CLI compares the access surfaces side by side, and Authentication explains which key type each one needs.


Web widgets#

Web widgets embed URL and file conversion into any website with a single script tag, so your visitors convert a URL to a PDF, capture a screenshot, or convert an uploaded file without leaving your page. The embed code carries only a widget ID. Authentication happens inside the iframe through a Cloudflare Turnstile challenge and a short-lived JWT from POST /v1/widget/{widget_id}/token, so no API key is ever exposed in your frontend code.

Each widget is tied to one conversion endpoint and one list of allowed domains, both set in the dashboard. Any conversion endpoint can back a widget:

How Widgets Work#

Setup#

  1. Go to your EnConvert Dashboard > Widgets and click Create Widget.
  2. Select the conversion endpoint (e.g., /v1/convert/url-to-pdf) and specify the domains where the widget will be embedded. Wildcard subdomains are supported (e.g., *.example.com).
  3. An internal public API key is automatically created for the widget, restricted to the selected endpoint and allowed domains. This key is never exposed.

Two preconditions catch people out: your account email must be verified, and the allowed-domains list cannot be empty. Widget creation is rejected if either is missing.

Embedding#

Add the embed script to your website:

<script src="https://enconvert.com/embed.js" data-widget-id="your-widget-id"></script>

The script creates a sandboxed iframe that loads the EnConvert widget. No API key appears in the embed code.

Runtime Flow#

  1. Widget loads in the iframe and fetches its configuration from GET /v1/widget/{widget_id}/config.
  2. Domain validation: the widget verifies the parent page's origin matches the allowed domains list. Supports exact domains and wildcard subdomain patterns (*.example.com).
  3. User submits a URL or file: the widget requests an invisible Turnstile challenge token.
  4. Token exchange: the widget sends the Turnstile token to POST /v1/widget/{widget_id}/token and receives a JWT (1-hour expiry) plus a refresh token cookie (7-day expiry).
  5. Conversion: the widget calls the conversion endpoint with the JWT.
  6. Result: the API returns a JSON response with a presigned_url. The widget displays a download link.
  7. Timeout recovery: if the conversion exceeds reverse-proxy timeout limits, the widget polls GET /v1/convert/status/{job_id} using the pre-generated job ID.

Automatic Token Refresh#

The widget never stops working due to expired authentication:

  • On the initial conversion, the API issues both a JWT (1-hour expiry) and a refresh token (7-day expiry, httpOnly cookie).
  • On subsequent conversions, the widget first attempts to refresh the JWT via POST /v1/widget/{widget_id}/refresh using the refresh token cookie, with no Turnstile challenge required.
  • If the refresh token itself has expired (after 7 days of inactivity), the widget falls back to a new Turnstile challenge.
  • The refresh token is rotated on every refresh: each refresh issues a new 7-day cookie.

This means a widget visitor who converts every few days will never see a Turnstile challenge after the first one.

Widget Endpoints#

Configuration#

Retrieves the configuration for a specific widget. No authentication required.

GET /v1/widget/{widget_id}/config

Response:

{
    "endpoint": "/v1/convert/url-to-pdf",
    "input_type": "url",
    "allowed_domains": ["https://example.com", "*.example.com"],
    "turnstile_site_key": "1x00000000000000000000AA",
    "widget_branding": true
}
Field Description
endpoint The conversion endpoint this widget is configured to use.
input_type "url" for URL-based endpoints, "file" for file upload endpoints.
allowed_domains Domains authorized to embed this widget. Supports wildcards.
turnstile_site_key Cloudflare Turnstile site key for bot verification.
widget_branding Whether the "Powered by EnConvert" badge is displayed. Determined by the subscription plan.

Token Exchange#

Exchanges a Turnstile challenge token for a JWT. Sets a refresh token cookie.

POST /v1/widget/{widget_id}/token
X-Parent-Origin: https://your-website.com
Content-Type: application/json

{
    "turnstile_token": "cloudflare-challenge-response-token"
}

Response:

{
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 3600
}

Also sets an httpOnly refresh_token cookie (7-day expiry, Secure, SameSite=none).

Token Refresh#

Refreshes an expired JWT using the httpOnly refresh token cookie. No Turnstile challenge required.

POST /v1/widget/{widget_id}/refresh
X-Parent-Origin: https://your-website.com

No request body needed. The refresh token is read from the cookie automatically.

Response:

{
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 3600
}

The refresh token cookie is rotated on each refresh (new 7-day cookie issued).

Error responses: - 401: no refresh token cookie or refresh token expired - 403: refresh token does not match the widget's project, or domain not authorized - 404: widget not found or deactivated

Conversion Response#

Both URL-based and file-based widget conversions return a consistent JSON response with a presigned download URL:

{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-pdf/example_20260405_123456789.pdf",
    "filename": "example_20260405_123456789.pdf",
    "file_size": 123456,
    "conversion_time_seconds": 8.5,
    "job_id": "client-generated-uuid"
}

The widget uses the presigned_url to display a download link. Presigned URLs expire after 15 minutes. See Signed URLs for what that window means for your visitors.

Widget conversion restrictions: - Single URL / single file only - Synchronous mode only (no async or batch) - No webhook callbacks or notification emails - Endpoint restricted to the one configured for the widget

Widget Branding#

Plans that include widget branding display a small "Powered by EnConvert" badge at the bottom of the widget. This is controlled by the widget_branding field on the subscription plan:

Plan Branding
Founding (free) Displayed
Indie, Studio, Production, Enterprise Hidden

The branding badge links to https://www.enconvert.com and is styled to be unobtrusive: small text below the widget form with reduced opacity.

Widget Management#

Widgets are managed through the EnConvert dashboard or the backend API:

Operation Endpoint Description
Create POST /widgets Creates a widget and auto-generates an internal public API key.
List GET /widgets?project_id={id} Lists all active widgets for a project.
Get GET /widgets/{id} Retrieves a single widget's details.
Update PATCH /widgets/{id} Updates widget name, endpoint, or API key.
Delete DELETE /widgets/{id} Soft-deletes the widget (sets active=false).
Different host: these management routes live on the EnConvert backend that serves the dashboard, not on api.enconvert.com. The conversion and widget-auth routes under /v1/ are the gateway.

Embed Code Reference#

Standard HTML#

<script src="https://enconvert.com/embed.js" data-widget-id="your-widget-id"></script>

The script: - Creates a sandboxed iframe (allow-scripts allow-same-origin allow-forms allow-popups) - Sets width: 100%, initial height 400px, no border - Enables clipboard-write permission - Uses lazy loading - Listens for Enconvert:resize messages to auto-adjust height

WordPress Shortcode#

If you use the EnConvert WordPress plugin, embed widgets using the shortcode:

[enconvert_widget id="your-widget-id"]

Style Customization#

Customize the widget appearance via query parameters on the embed script URL or the iframe source:

Parameter CSS Variable Description
bg --w-bg Widget background color
text --w-text Text color
btn-bg --w-btn-bg Button background color
btn-text --w-btn-text Button text color
border --w-border Border color
radius --w-radius Border radius
input-bg --w-input-bg Input field background
result-bg --w-result-bg Result area background
error --w-error Error text color
font --w-font Font family
padding --w-padding Widget padding
max-width --w-max-width Maximum widget width

Iframe Communication#

The widget communicates with the parent page via postMessage. Listen for these events on the parent page:

Event Type Data Description
Enconvert:ready none Widget has loaded and is ready.
Enconvert:resize { height: number } Widget content height changed. Use to resize the iframe.
Enconvert:conversion:complete { url: string, filename?: string } Conversion completed. url is the presigned download URL.
Enconvert:conversion:error { error: string } Conversion failed.

These four event names are a frozen wire contract. Match the casing exactly.

Example: Listening for Events#

window.addEventListener("message", function(e) {
    if (!e.data || !e.data.type) return;

    if (e.data.type === "Enconvert:conversion:complete") {
        console.log("Conversion done:", e.data.data.url);
    }

    if (e.data.type === "Enconvert:conversion:error") {
        console.error("Conversion failed:", e.data.data.error);
    }
});

Security#

Layer Protection
Domain whitelisting Widget only functions on listed domains. Supports exact matches and wildcard subdomains. Server-side validation on token issuance.
Turnstile verification Every initial token request requires a valid Cloudflare Turnstile challenge response.
Endpoint restriction Each widget is locked to a single conversion endpoint via allowed_endpoints in the JWT.
Token expiry JWT expires after 1 hour. Refresh token expires after 7 days. Both are rotated on refresh.
Refresh token security httpOnly cookie with Secure and SameSite=none, inaccessible to JavaScript, only sent over HTTPS.
CORS protection API gateway validates the widget iframe origin on every request.
CSP frame-ancestors Widget config and token endpoints set frame-ancestors headers restricting which domains can embed the iframe.
No exposed keys Embed code contains only the widget ID. The internal API key is never visible.
Never put a private key in a page. Widgets exist so that browser traffic runs on a scoped public key and a short-lived JWT. A private sk_ key sent from a browser is rejected with HTTP 403 on sight. See Public keys and JWT.

Frequently asked questions#

How do I embed a file converter widget on my website?#

Create a widget in the EnConvert dashboard (Dashboard > Widgets > Create Widget), then add one script tag to your page: <script src="https://enconvert.com/embed.js" data-widget-id="your-widget-id"></script>. If your site runs WordPress, you can use the [enconvert_widget id="your-widget-id"] shortcode from the EnConvert WordPress plugin instead.

Do I need to expose an API key to embed a conversion widget?#

No. The embed code contains only the widget ID. An internal public API key is auto-generated for each widget, restricted to its configured endpoint and allowed domains, and is never visible in your frontend code.

How does the widget authenticate users without an API key?#

The widget requests an invisible Cloudflare Turnstile challenge and exchanges it at POST /v1/widget/{widget_id}/token for a JWT with 1-hour expiry plus an httpOnly refresh token cookie with 7-day expiry. Subsequent conversions refresh the JWT via POST /v1/widget/{widget_id}/refresh with no new challenge, and the refresh token is rotated on every refresh.

Can I restrict which domains can use my embedded widget?#

Yes. Each widget has an allowed-domains list that supports exact domains and wildcard subdomains like *.example.com, validated server-side on token issuance, with frame-ancestors CSP headers restricting which pages can embed the iframe.

How do I remove the Powered by EnConvert badge from the widget?#

The badge is controlled by the widget_branding field on your subscription plan. Only the free Founding plan shows it. Indie, Studio, Production and Enterprise all hide it, so any paid plan removes the badge.

Which integration should I start with?#

If you are writing code, start with an SDK for your language. If you are automating without code, use n8n. If you want an AI assistant to do the work, install the MCP server. If you just want your own visitors to convert files, use a web widget.

Do the integrations share one API key and one quota?#

Yes. All of them authenticate as the same project, so operations count against a single monthly allowance no matter which surface issued the call. See Rate Limits and Quotas.