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:
- URL-based: url-to-pdf, url-to-screenshot
- File-based: All data format, document to PDF, and image conversion endpoints
How Widgets Work#
Setup#
- Go to your Enconvert Dashboard > Widgets and click Create Widget.
- 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). - 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#
- Widget loads in the iframe and fetches its configuration from
GET /v1/widget/{widget_id}/config. - Domain validation -- the widget verifies the parent page's origin matches the allowed domains list. Supports exact domains and wildcard subdomain patterns (
*.example.com). - User submits a URL or file -- the widget requests an invisible Turnstile challenge token.
- Token exchange -- the widget sends the Turnstile token to
POST /v1/widget/{widget_id}/tokenand receives a JWT (1-hour expiry) plus a refresh token cookie (7-day expiry). - Conversion -- the widget calls the conversion endpoint with the JWT.
- Result -- the API returns a JSON response with a
presigned_url. The widget displays a download link. - 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}/refreshusing 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.