Embed a File Converter Widget on Your Website#

Enconvert web widgets let you embed URL and file conversion directly into any website with a single script tag — visitors convert URLs to PDFs, capture screenshots, or convert uploaded files without leaving your page. Each widget authenticates through Cloudflare Turnstile challenges and short-lived JWTs issued by POST /v1/widget/{widget_id}/token, so no API key is ever exposed in your frontend code. Every conversion runs synchronously and returns a JSON response with a presigned download URL.


Overview#

Widgets are configured through the Enconvert dashboard and embedded as iframes via a lightweight script tag. Each widget is tied to a single conversion endpoint and a list of allowed domains. Authentication is handled automatically using Cloudflare Turnstile challenges, short-lived JWT tokens, and automatic token refresh.

Supported conversion endpoints:

Any Enconvert conversion endpoint can be used with widgets, including:


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.

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 -- 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.

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
Free / Starter Displayed
Pro / Enterprise Hidden

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

To remove branding, upgrade to a Pro or Enterprise plan.


Widget Management#

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

Operation Endpoint Description
Create POST /api/widgets Creates a widget and auto-generates an internal public API key.
List GET /api/widgets?project_id={id} Lists all active widgets for a project.
Get GET /api/widgets/{id} Retrieves a single widget's details.
Update PATCH /api/widgets/{id} Updates widget name, endpoint, or API key.
Delete DELETE /api/widgets/{id} Soft-deletes the widget (sets active=false).

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 -- 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.

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.

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: Free and Starter plans display it, while Pro and Enterprise plans hide it. Upgrade to a Pro or Enterprise plan to remove the branding.