Automatically convert web content to audio
Transform any article or webpage into high-quality, professionally narrated audio with a single API call.
Overview
The Auto Audio API enables automatic conversion of web articles into high-quality audio content. Simply provide a URL, and the system will extract content, generate professional narration, apply audio mixing, and return an embeddable audio player.
Universal Compatibility
Works with any public URL. Automatic content extraction from any website.
Multi-Language
Automatic language detection with support for 11+ languages.
Audio Mixing
Professional background music, intro/outro support, and audio polish.
Smart Caching
Instant retrieval of previously converted articles with automatic update detection.
Conversation Mode
Multi-voice narration for engaging audio experiences.
CMS Integration
Native support for Fusion, NEXT_DATA, and Brightspot structured data.
https://creator.everlit.audio
Usage
The simplest way to add Everlit Auto Audio to your website is by including a widget container and the JavaScript SDK. The widget will automatically detect content on your page and convert it to audio.
Basic Implementation
Add the following HTML to your page where you want the audio player to appear:
<!-- 1. The widget container -->
<div
id="everlit-auto-audio-widget"
data-publication-id="YOUR-PUBLICATION-ID"
style="height: 136px; width: 100%;"
hidden
><a href="https://everlit.audio/" rel="nofollow">Listen to this article — audio by Everlit</a></div>
<!-- 2. The script -->
<script
defer
src="https://cdn.everlit.audio/libs/everlitAutoAudio.js"
type="text/javascript"
></script>
everlitAutoAudio.js moves everything it exposes under the shared
window.Everlit namespace and adds a status field to every response.
See Widget v2.
Widget Container
| Attribute | Required | Description |
|---|---|---|
id |
Yes | Must be everlit-auto-audio-widget |
data-publication-id |
Yes | Your unique Everlit publication ID (e.g., pblc_abc123xyz) |
hidden |
Recommended | Hides the container until audio is ready |
style |
Optional | Set dimensions; recommended height is 136px |
| Fallback link | Recommended | The link inside the container is never shown to visitors — it is replaced by the player (or stays hidden). It gives non-JavaScript crawlers a text fallback and is automatically excluded from audio conversion. |
Script Tag
| Attribute | Description |
|---|---|
defer |
Loads the script without blocking page rendering |
src |
Always use https://cdn.everlit.audio/libs/everlitAutoAudio.js |
data-everlit-host |
Optional, advanced. Overrides the API origin the widget talks to.
Defaults to https://creator.everlit.audio — leave it off unless
Everlit support has given you a different host. See
API host. |
API Host (advanced)
By default the widget calls https://creator.everlit.audio. If Everlit support has
given you a dedicated or regional host, set data-everlit-host on either the script
tag or the widget container — the script tag wins if both are present.
<script
defer
src="https://cdn.everlit.audio/libs/everlitAutoAudio.js"
data-everlit-host="https://creator.everlit.audio"
type="text/javascript"
></script>
<!-- or on the container -->
<div
id="everlit-auto-audio-widget"
data-publication-id="YOUR-PUBLICATION-ID"
data-everlit-host="https://creator.everlit.audio"
></div>
| Rule | Behaviour |
|---|---|
| Default | https://creator.everlit.audio when the attribute is absent, empty, or invalid |
| Scheme | Only http: and https: origins are honoured; anything else falls back to the default |
| Trailing slash | Ignored — https://host/ and https://host are equivalent |
| Resolved value | Readable at runtime as window.Everlit.autoAudio.host |
hidden attribute
is automatically removed when the player is ready.
How It Works
When the script loads, it will:
- Detect the canonical URL of your page
- Check if audio already exists for this content
- If audio exists, display the embedded player
- If not, display a click-to-create widget (if enabled for your publication)
- Dispatch JavaScript events for each state change
Widget v2
Version 3.0.0 of everlitAutoAudio.js shipped on 2026-09-02. The install
snippet, the widget container attributes, and the event names are unchanged — existing
integrations keep working. Three things are new.
What Changed
| Change | Details |
|---|---|
| Namespaced globals | The widget no longer defines page globals. Everything it exposes lives under
window.Everlit.autoAudio. |
| Configurable host | Optional data-everlit-host attribute on the script tag or the widget
container — see API host. |
| Status on every response | Widget responses carry a status field, plus article_id and
player_url when the article is known — see
Response status. |
The window.Everlit Namespace
window.Everlit is shared with the Everlit playlist embed; the Auto Audio widget only
ever writes under Everlit.autoAudio.
| Property | Type | Description |
|---|---|---|
Everlit.autoAudio.version |
string | The loaded widget version, e.g. "3.0.0" |
Everlit.autoAudio.host |
string | The resolved API origin, e.g. "https://creator.everlit.audio" |
Everlit.autoAudio.initialized |
boolean | true once the widget has run its initialization for this page view |
document.addEventListener('everlit:ready', function () {
console.log(window.Everlit.autoAudio.version); // "3.0.0"
console.log(window.Everlit.autoAudio.host); // "https://creator.everlit.audio"
console.log(window.Everlit.autoAudio.initialized); // true
});
everlit* variables
on window. Those were never documented and are gone in v2. If you read one, switch to
the matching property on window.Everlit.autoAudio. Nothing else needs to change —
the DOM events remain the supported integration point.
Version Reporting
The widget identifies itself on every request so support can tell which build a page is running:
POST /audio/auto—client_versionin the JSON bodyGET /audio/auto/check—client_versioninside the encodedevpparameter
GET /audio/auto/check and one POST /audio/auto per page view. It does not
poll.
Response Status
Every response the widget receives now includes a status string. All previously
documented fields (successful, embed, waiting,
create, metadata, reason, …) are still present, so
existing field checks keep working — status is the simpler way to branch.
| Status | Meaning |
|---|---|
ready |
A playable embed is returned. Also used when regenerating: true —
the existing audio plays while a newer version is being produced. |
paywalled |
The publication paywalls audio and this visitor is blocked. embed is the
blurred placeholder with the subscribe call-to-action, not a player;
paywalled: true is also set. |
processing |
A worker is converting this article right now; audio is not playable yet. |
queued |
Nothing is running yet: the request was accepted and is waiting on the touch-count threshold or on conversion capacity. |
click_to_create |
No audio yet; the click-to-create widget is offered to the reader. |
unavailable |
There is no audio and none will be created for this request — for example a crawler hitting a cache miss. |
error |
The check or conversion failed. See reason. |
Article Identifiers
| Field | Type | Description |
|---|---|---|
article_id |
string | Present whenever the article is known, e.g. "artl_abc123" |
player_url |
string | The public player page for the article, e.g.
https://everlit.audio/embeds/artl_abc123. Useful for share links and
"listen elsewhere" affordances. |
Authentication
All API requests require authentication using a bearer token:
Authorization: Bearer YOUR_API_TOKEN
Endpoints
POST /audio/auto POST
Initiates audio conversion for a given URL. This endpoint is asynchronous and will either return a completed embed immediately (if cached), indicate processing is in progress, or start a new conversion job.
Request Body
{
"url": "https://example.com/article",
"publication_id": "pblc_abc123xyz",
"conversation_mode": true,
"sonic_optimizer": true,
"client_version": "3.0.0"
}
everlitAutoAudio.js sends
client_version (currently "3.0.0") with every POST. It is optional for
your own integrations, but helpful to include so support can identify your client.
Required Parameters
| Parameter | Type | Description |
|---|---|---|
url |
string | The URL of the article to convert |
publication_id |
string | Your publication ID (format: pblc_xxxxx) |
Optional Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
primary_voice_id |
string | pub default | Voice ID for primary narrator |
guest_voice_id |
string | pub default | Voice ID for secondary narrator |
conversation_mode |
boolean | false | Enable multi-voice conversation mode |
sonic_optimizer |
boolean | false | Enable Audio Polish for enhanced quality |
optimizer_level |
string | "2" | Audio Polish level: "0" (symbols/numbers only), "1" (light), "2" (default), "3" (heaviest rewrite). Any other value silently uses a conservative built-in prompt |
language |
string | null | Language tag matching the keys of the publication's snrf_voices (en-US, es-MX, es). Skips detection: narrator voice, disclaimer text, disclaimer voice and the on-page disclaimer all resolve through this language, and it is passed to TTS as a language hint. A value that isn't a language tag is ignored |
disable_language_detection |
boolean | false | Legacy alias for language: <publication default language> (snrf_default_language, or en when unset) |
mix |
boolean | true | Run Creator’s mixer for this article. false (or "") means no intro/outro music. Music is always mixed by Creator, so the delivered audio is always a finished master — the player never adds music of its own |
read_urls |
boolean | false | Read URLs found in content aloud |
read_alt_text |
boolean | false | Read image alt text |
intro_mixable_id |
string | null | Custom intro audio mixable ID. "shuffle" plays a random intro; "" turns the intro music off; null keeps the publication default (which shuffles a random intro when none is configured) |
skip_intro_mixable |
boolean | pub setting | Play no intro music — the explicit form of intro_mixable_id: "". Defaults to the publication's snrf_skip_intro_mixable; naming an intro_mixable_id overrides it |
shuffle_intro_mixable |
boolean | pub setting | Play a random intro from the publication's intro/outro music — the explicit form of intro_mixable_id: "shuffle", and what an unset intro already does |
outro_mixable_id |
string | null | Custom outro audio mixable ID. "shuffle" plays a random outro; "" turns the outro music off; null keeps the publication default |
skip_outro_mixable |
boolean | pub setting | Play no outro music — the explicit form of outro_mixable_id: "". Defaults to the publication's snrf_skip_outro_mixable; naming an outro_mixable_id overrides it |
shuffle_outro_mixable |
boolean | pub setting | Play a random outro from the publication's intro/outro music — the explicit form of outro_mixable_id: "shuffle". Defaults to the publication's snrf_shuffle_outro_mixable; naming an outro_mixable_id overrides it. A track is picked per narration, and is not recorded |
disclaimer |
string | null | Disclaimer text to read before content. "" silences it (the same flag suppress_ai_disclaimer: true sets); null uses the publication default. Never send the literal string "false" — it would be narrated aloud |
suppress_ai_disclaimer |
boolean | false | Silence the spoken AI disclaimer for this article, overriding the publication's configured disclaimer |
read_author_in_audio_enabled |
boolean | true | Read the author byline aloud in the audio |
disclaimer, intro_mixable_id, outro_mixable_id and mix, an explicitly sent empty string ("") means turn this off for this article. The music beds take one more value: on intro_mixable_id, outro_mixable_id, intro_music and outro_music, an id plays that track, "shuffle" plays a random track, "" plays none, and null uses the publication default. null is identical to omitting the key entirely — for every parameter on this endpoint, aliases included — and means use the publication default. This applies to POST /audio/auto only.
language tag to skip detection. It is matched against the publication's per-language maps (snrf_voices, snrf_disclaimers, snrf_disclaimer_voices, snrf_disclaimer_ui_text) in one order: the exact regional tag, then the language family (es-MX/es-ES/es for Spanish, hi/in for Devanagari, in for other Indian languages), then the publication's snrf_default_language and its bare family code — or en-US then en when that setting is unset — then the literal default key, and finally the flat legacy settings. snrf_default_language also replaces the hardcoded en when detection is unreliable; unset, everything behaves exactly as before.
These friendly aliases are also accepted, each mapping to the canonical parameter (if both are sent, the canonical key wins): music → mix, intro_music → intro_mixable_id, outro_music → outro_mixable_id, audio_polish → sonic_optimizer, audio_polish_level → optimizer_level, podcast_mode → conversation_mode.
Response Examples
{
"successful": true,
"status": "ready",
"embed": "<iframe src=\"https://everlit.audio/embeds/artl_abc123?eut=xyz\" frameborder=\"0\"></iframe>",
"article_id": "artl_abc123",
"player_url": "https://everlit.audio/embeds/artl_abc123",
"metadata": {
"title": "Article Title",
"summary": "Article summary text",
"duration": 245.5
},
"dom_query_params": null,
"published_at": "2025-09-29T10:30:00Z",
"duration": 245.5,
"title": "Article Title",
"summary": "Article summary text",
"authors": ["Author Name"],
"tags": ["category:News", "author:Author Name", "language:English"],
"disclaimer_ui_text": "This audio was generated using AI"
}
{
"successful": true,
"status": "processing",
"waiting": true,
"article_id": "artl_abc123",
"player_url": "https://everlit.audio/embeds/artl_abc123",
"message": "Everlit audio conversion is still in progress."
}
{
"successful": false,
"status": "error",
"reason": "Unable to fetch the article content."
}
status —
ready, paywalled, processing, queued,
click_to_create, unavailable, or error. See
Response status for the full table.
GET /audio/auto/check GET
Checks the status of audio conversion for a given URL. This is the recommended endpoint for polling conversion status, as it's lightweight and optimized for frequent checks.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
evp |
string | Base64-encoded JSON containing url, publication_id, and optional parameters |
EVP Encoding
The evp parameter contains URL-safe base64-encoded JSON:
// Original object
const params = {
url: "https://example.com/article",
publication_id: "pblc_abc123xyz",
eut: "optional_analytics_token",
client_version: "3.0.0" // sent by everlitAutoAudio.js v2; optional for your own code
};
// Encode to URL-safe base64
const evp = btoa(JSON.stringify(params))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
Code Examples
Complete JavaScript Implementation
async function convertToAudio(url, publicationId) {
// Check current status
const params = {
url: url,
publication_id: publicationId
};
const evp = btoa(JSON.stringify(params))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
const checkResponse = await fetch(
`https://creator.everlit.audio/audio/auto/check?evp=${evp}`
);
const checkData = await checkResponse.json();
if (checkData.embed) {
return checkData.embed; // Already ready!
}
if (checkData.create || checkData.waiting) {
// Initiate if needed
if (checkData.create) {
await fetch('https://creator.everlit.audio/audio/auto', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
});
}
// Poll for completion
return await pollForCompletion(url, publicationId);
}
}
async function pollForCompletion(url, publicationId, maxAttempts = 60) {
for (let i = 0; i < maxAttempts; i++) {
await new Promise(r => setTimeout(r, 5000)); // Wait 5s
const params = { url, publication_id: publicationId };
const evp = btoa(JSON.stringify(params))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
const response = await fetch(
`https://creator.everlit.audio/audio/auto/check?evp=${evp}`
);
const data = await response.json();
if (data.embed) return data.embed;
if (!data.waiting) throw new Error(data.reason);
}
throw new Error('Conversion timeout');
}
// Usage
const embed = await convertToAudio(
'https://example.com/article',
'pblc_abc123xyz'
);
document.getElementById('player').innerHTML = embed;
cURL Commands
# Initiate conversion
curl -X POST https://creator.everlit.audio/audio/auto \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"url": "https://example.com/article",
"publication_id": "pblc_abc123xyz",
"conversation_mode": true,
"sonic_optimizer": true
}'
# Check status (encode the parameters first)
EVP=$(echo -n '{"url":"https://example.com/article","publication_id":"pblc_abc123xyz"}' | \
base64 | tr '+/' '-_' | tr -d '=')
curl "https://creator.everlit.audio/audio/auto/check?evp=$EVP"
Python Implementation
import requests
import json
import base64
import time
class EverlitAutoAudio:
BASE_URL = 'https://creator.everlit.audio'
def __init__(self, publication_id, api_token=None):
self.publication_id = publication_id
self.api_token = api_token
def convert_article(self, url, **options):
# Check status
status = self.check_status(url)
if status.get('embed'):
return status['embed']
# Initiate if needed
if status.get('create'):
self.initiate_conversion(url, **options)
# Poll for completion
return self.poll_for_completion(url)
def check_status(self, url):
params = {'url': url, 'publication_id': self.publication_id}
evp = base64.urlsafe_b64encode(
json.dumps(params).encode()
).decode().rstrip('=')
response = requests.get(
f'{self.BASE_URL}/audio/auto/check',
params={'evp': evp}
)
return response.json()
def initiate_conversion(self, url, **options):
headers = {'Content-Type': 'application/json'}
if self.api_token:
headers['Authorization'] = f'Bearer {self.api_token}'
payload = {
'url': url,
'publication_id': self.publication_id,
**options
}
response = requests.post(
f'{self.BASE_URL}/audio/auto',
headers=headers,
json=payload
)
return response.json()
def poll_for_completion(self, url, max_attempts=60):
for _ in range(max_attempts):
time.sleep(5)
status = self.check_status(url)
if status.get('embed'):
return status['embed']
raise Exception('Conversion timeout')
# Usage
client = EverlitAutoAudio('pblc_abc123xyz', 'your_token')
embed = client.convert_article(
'https://example.com/article',
conversation_mode=True,
sonic_optimizer=True
)
print(embed)
Ruby Implementation
require 'net/http'
require 'json'
require 'base64'
class EverlitAutoAudio
BASE_URL = 'https://creator.everlit.audio'
def initialize(publication_id, api_token = nil)
@publication_id = publication_id
@api_token = api_token
end
def convert_article(url, options = {})
status = check_status(url)
return status[:embed] if status[:embed]
initiate_conversion(url, options) if status[:create]
poll_for_completion(url)
end
def check_status(url)
params = { url: url, publication_id: @publication_id }
evp = Base64.urlsafe_encode64(params.to_json, padding: false)
uri = URI("#{BASE_URL}/audio/auto/check?evp=#{evp}")
response = Net::HTTP.get_response(uri)
JSON.parse(response.body, symbolize_names: true)
end
def initiate_conversion(url, options = {})
uri = URI("#{BASE_URL}/audio/auto")
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = "Bearer #{@api_token}" if @api_token
body = { url: url, publication_id: @publication_id }.merge(options)
request.body = body.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
JSON.parse(response.body, symbolize_names: true)
end
def poll_for_completion(url, max_attempts = 60)
max_attempts.times do
sleep(5)
status = check_status(url)
return status[:embed] if status[:embed]
end
raise "Conversion timeout"
end
end
# Usage
client = EverlitAutoAudio.new('pblc_abc123xyz', 'your_token')
embed = client.convert_article(
'https://example.com/article',
conversation_mode: true,
sonic_optimizer: true
)
puts embed
Error Handling
Common Error Scenarios
| Scenario | Response | Action |
|---|---|---|
| Missing URL | {"successful": false, "message": "No URL was sent..."} |
Provide valid URL parameter |
| Rate Limited | {"successful": true, "reason": "Everlit is already hard at work..."} |
Wait 30+ seconds and retry |
| URL Blocked | {"successful": false, "reason": "This URL is not available..."} |
Check whitelist settings |
| Monthly Limit | 403: {"error": "Monthly article limit reached"} |
Upgrade plan or wait for reset |
| Invalid Voice | {"successful": false, "reason": "Invalid parameters: voice_id..."} |
Use valid voice ID for your publisher |
| Extraction Failed | {"successful": false, "reason": "Unable to fetch article..."} |
Check URL accessibility and format |
Retry Strategy
async function withRetry(fn, maxRetries = 5) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise(r => setTimeout(r, delay));
}
}
}
Complete Parameter Reference
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
url |
string | Yes | - | Article URL to convert |
publication_id |
string | Yes | - | Your publication ID |
eut |
string | No | auto | Analytics tracking token |
primary_voice_id |
string | No | pub setting | Primary narrator voice ID |
guest_voice_id |
string | No | pub setting | Secondary narrator voice ID |
conversation_mode |
boolean | No | false | Enable multi-voice mode |
read_urls |
boolean | No | false | Read URLs aloud |
read_alt_text |
boolean | No | false | Read image alt text |
sonic_optimizer |
boolean | No | false | Enable Audio Polish |
optimizer_level |
string | No | "2" | "0", "1", "2", "3" — anything else uses a conservative built-in prompt |
mix |
boolean | No | true | Run Creator’s mixer for this article. false = no intro/outro music. The delivered audio is always a finished master |
intro_mixable_id |
string | No | null | Custom intro audio ID. "shuffle" = a random intro, "" = no intro music, null = publication default |
skip_intro_mixable |
boolean | No | pub setting | Play no intro music (explicit form of intro_mixable_id: "") |
shuffle_intro_mixable |
boolean | No | pub setting | Play a random intro (explicit form of intro_mixable_id: "shuffle") |
outro_mixable_id |
string | No | null | Custom outro audio ID. "shuffle" = a random outro, "" = no outro music, null = publication default |
skip_outro_mixable |
boolean | No | pub setting | Play no outro music (explicit form of outro_mixable_id: "") |
shuffle_outro_mixable |
boolean | No | pub setting | Play a random outro (explicit form of outro_mixable_id: "shuffle") |
intro_duration |
integer | No | 0 | Intro length (seconds) |
outro_duration |
integer | No | 0 | Outro length (seconds) |
intro_pad |
integer | No | 0 | Intro padding (seconds) |
outro_pad |
integer | No | 0 | Outro padding (seconds) |
disclaimer |
string | No | null | Disclaimer text. "" silences it (same as suppress_ai_disclaimer: true), null = publication default; never send the literal string "false" — it would be narrated aloud |
disclaimer_voice_id |
string | No | null | Disclaimer voice ID |
suppress_ai_disclaimer |
boolean | No | false | Silence the spoken AI disclaimer for this article, overriding the publication's configured disclaimer |
read_author_in_audio_enabled |
boolean | No | true | Read author byline aloud |
language |
string | No | null | Language tag (en-US, es-MX, es) — skips detection for voice, disclaimer and TTS |
disable_language_detection |
boolean | No | false | Legacy alias for language: <publication default language> |
click_to_create |
boolean | No | false | From click widget |
content_last_published_timestamp |
integer | No | null | Unix timestamp (ms) |
dom_content |
string | No | null | Raw page HTML for content extraction |
rendered_html |
string | No | null | Rendered HTML for media extraction |
page_url |
string | No | null | Page URL for syndicated content |
client_version |
string | No | null | Calling client version. The widget sends "3.0.0" in the POST body and
inside evp. |
Server-Side Content Ingestion
Building a server-to-server or agent integration? Use the
Articles API instead: POST /v1/articles takes the same
URL-or-text input with an api_ key, proper status codes, a pollable job resource,
webhooks, and MCP tools. The endpoint below is the widget protocol and stays supported for
existing integrations.
If your system already has the article content (for example, a headless CMS or a feed
ingestion pipeline), you can supply it directly in the POST /audio/auto body
instead of relying on automatic content extraction. This produces deterministic audio that
does not depend on the quality of server-side scraping.
Modes of Operation
- Full override (scraping skipped): when the body contains both
textandtitle, the system uses your content verbatim and does not fetch or scrape the page. Theurlis still required, but only as the canonical identifier and cache key. - Per-field override (scraping fills the gaps): if you provide some content
fields but omit
textortitle(and send no structured data), the page is scraped and your supplied fields take precedence — extraction fills only the fields you leave blank. For example, send onlytextto replace the article body while letting extraction supply the title, image, and byline.
Ingestion Parameters
| Parameter | Type | Description |
|---|---|---|
text |
string | Article body to narrate. With title, fully replaces content extraction. |
title |
string | Article headline. With text, fully replaces content extraction. |
authors |
array of strings | Author bylines. Normalized, read aloud in the narration, and added as author: tags. |
custom_byline |
string | Exact byline text to read aloud, overriding the auto-generated author string. |
summary |
string | Article summary / description. |
category |
string | Single section/category. Added as a category: tag. |
art_url |
string | URL of the article's artwork/featured image. |
published_at |
string | Publication timestamp (ISO 8601). Used for deduplication and update detection. |
tags |
array of strings | Additional pass-through tags (e.g., stock tickers). |
Example (Full Override)
curl -X POST https://creator.everlit.audio/audio/auto \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{
"url": "https://example.com/article",
"publication_id": "pblc_abc123xyz",
"title": "City Council Approves New Transportation Plan",
"text": "The city council voted 7-2 on Tuesday to approve a comprehensive transportation plan...",
"authors": ["Jane Doe", "John Smith"],
"summary": "A comprehensive look at the new transportation plan.",
"category": "Local News",
"art_url": "https://example.com/images/council.jpg",
"published_at": "2026-05-29T10:30:00Z",
"tags": ["transportation", "city-council"]
}'
JavaScript Events
The Auto Audio widget emits custom DOM events that allow you to react to various states of the audio player lifecycle. These events are useful for showing/hiding related UI elements, tracking analytics, or integrating with your page's behavior.
Available Events
| Event Name | When Fired | Use Case |
|---|---|---|
everlit:ready |
Audio player iframe is successfully embedded | Show related UI elements, display duration |
everlit:clickToCreate |
Click-to-create widget is displayed | Show hints or call-to-action elements |
everlit:processing |
Audio conversion has started | Show loading indicators |
everlit:error |
An error occurred during check or conversion | Display error messages, hide player area |
Event Details
everlit:ready
Fired when the audio player is successfully embedded on the page. This is the primary event for most integrations.
Event Detail Properties
| Property | Type | Description |
|---|---|---|
embed |
boolean | Always true for this event |
element |
HTMLElement | The container element holding the audio player iframe |
articleId |
string | null | The Everlit article ID (e.g., "artl_abc123") |
playerUrl |
string | null | Public player page for the article (e.g.
"https://everlit.audio/embeds/artl_abc123"). Added in
widget v2. |
status |
string | null | "ready" for this event (null if the response carried no
status). Added in widget v2. |
metadata |
object | null | Article metadata object (see below) |
Metadata Object
| Property | Type | Example | Description |
|---|---|---|---|
title |
string | "Breaking: Major Event Unfolds" |
The article title |
summary |
string | "A brief overview of the article content..." |
AI-generated summary of the article |
duration |
number | 245.5 |
Audio duration in seconds |
Example Response
// Example e.detail value:
{
embed: true,
element: HTMLDivElement,
articleId: "artl_7kX9mPqR2Nw",
playerUrl: "https://everlit.audio/embeds/artl_7kX9mPqR2Nw",
status: "ready",
metadata: {
title: "City Council Approves New Transportation Plan",
summary: "The city council voted 7-2 to approve a comprehensive transportation plan that includes new bike lanes, expanded bus routes, and infrastructure improvements.",
duration: 187.3
}
}
Usage Example
document.addEventListener('everlit:ready', function(e) {
console.log('Audio player ready!', e.detail);
// Show a related element
document.querySelector('.audio-controls').style.display = 'block';
// Display formatted duration
if (e.detail.metadata?.duration) {
const minutes = Math.round(e.detail.metadata.duration / 60);
document.querySelector('.listen-time').textContent = minutes + ' min listen';
}
// Display title
if (e.detail.metadata?.title) {
document.querySelector('.audio-title').textContent = e.detail.metadata.title;
}
});
everlit:clickToCreate
Fired when a click-to-create widget is displayed (for publications with on-demand audio generation).
document.addEventListener('everlit:clickToCreate', function(e) {
// e.detail contains:
// {
// clickToCreate: true,
// processing: false, // true if already being created
// element: HTMLElement, // The click widget element
// status: "click_to_create" // added in widget v2; "processing" while it is being created
// }
if (!e.detail.processing) {
// Show a hint to encourage users to click
document.querySelector('.create-audio-hint').style.display = 'block';
}
});
everlit:processing
Fired when audio conversion begins (after user clicks to create or auto-conversion starts).
document.addEventListener('everlit:processing', function(e) {
// e.detail contains:
// {
// processing: true,
// url: "https://example.com/article"
// }
// Show a loading state
document.querySelector('.audio-loading').style.display = 'block';
});
everlit:error
Fired when an error occurs during the audio check or conversion process.
document.addEventListener('everlit:error', function(e) {
// e.detail contains:
// {
// error: true,
// reason: "Error description"
// }
console.error('Everlit error:', e.detail.reason);
// Optionally hide the player area on error
document.querySelector('.audio-player-container').style.display = 'none';
});
Complete Integration Example
Here's a full example showing how to use events to control page elements based on audio availability:
<!-- Your page HTML -->
<div id="everlit-auto-audio-widget" data-publication-id="pblc_xxx"></div>
<div class="audio-info" style="display: none;">
<span class="listen-time"></span>
<button class="share-audio-btn">Share Audio</button>
</div>
<script defer src="https://cdn.everlit.audio/libs/everlitAutoAudio.js"></script>
<script>
// Show audio info when player is ready
document.addEventListener('everlit:ready', function(e) {
const audioInfo = document.querySelector('.audio-info');
audioInfo.style.display = 'flex';
// Display duration
if (e.detail.metadata?.duration) {
const minutes = Math.round(e.detail.metadata.duration / 60);
document.querySelector('.listen-time').textContent = minutes + ' min listen';
}
// Log for analytics
console.log('Audio loaded for article:', e.detail.articleId);
});
// Hide audio info on error
document.addEventListener('everlit:error', function(e) {
document.querySelector('.audio-info').style.display = 'none';
});
</script>
document object and bubble up, so you can listen for them anywhere in your page.
detail keys are unchanged.
everlit:ready gained playerUrl and status, and
everlit:clickToCreate gained status. Events remain the only supported
integration point — the widget defines no page globals, only
window.Everlit.autoAudio. See Widget v2.
Best Practices
Widget Placement
- Place the widget container early in your article template for best visibility
- Use the
hiddenattribute to prevent layout shifts before audio loads - Set a fixed height (136px recommended) to reserve space for the player
Performance Tips
- Use the
deferattribute on the script tag to avoid blocking page load - The widget automatically caches results - repeated page loads are instant
- Consider using the JavaScript events to lazy-load related UI elements
Content Optimization
- Ensure your pages have a valid canonical URL for consistent audio matching
- Use semantic HTML to help content extraction identify article body text
- Include proper meta tags (title, description, author) for better audio metadata
Error Handling
- Listen for the
everlit:errorevent to gracefully handle failures - Implement retry logic with exponential backoff for transient errors
- Use the check endpoint for status polling rather than repeatedly calling the POST endpoint
everlit:ready event with your analytics to track audio engagement alongside article pageviews.