> ## Documentation Index
> Fetch the complete documentation index at: https://docs.beyondwords.io/llms.txt
> Use this file to discover all available pages before exploring further.

# WordPress

The BeyondWords for WordPress plugin allows you to automatically generate and embed audio and video versions of your posts.

<Tip>
  Per-post settings default to your BeyondWords project settings. Configure project defaults in the dashboard—[script templates](/docs-and-guides/tools/generate-scripts), [video templates](/docs-and-guides/content/video), [voices](/docs-and-guides/voices/overview), and [player settings](/docs-and-guides/distribution/player/player-settings).
</Tip>

## Requirements

* **WordPress**: 5.8 or higher

* **PHP**: 8.0 or higher (including PHP 8.4)

* **WordPress**: 5.8 or higher

* **PHP**: 8.0 or higher (including PHP 8.4)

For the latest WordPress version the plugin has been tested against, see the [plugin directory listing](https://wordpress.org/plugins/beyondwords-text-to-speech/).

## Install the plugin

<Steps>
  <Step title="Create a BeyondWords project">
    If you haven't already, create a project in BeyondWords to store your content and settings.

    Go to **Settings → Integrations → WordPress** in your project dashboard and note your **API key** and **Project ID** for step 3.
  </Step>

  <Step title="Install the plugin">
    Install and activate **BeyondWords – Text to Speech** from the [WordPress plugin directory](https://wordpress.org/plugins/beyondwords-text-to-speech/).
  </Step>

  <Step title="Add your credentials">
    In WordPress, open the BeyondWords plugin settings and paste your API key and project ID on the **Authentication** tab.

    Click **Save changes**.
  </Step>

  <Step title="Configure the plugin">
    Set your **Integration** and **Preferences** (see below), then configure per-post **Settings** in the post editor before publishing.
  </Step>

  <Step title="Publish as normal">
    Publish posts in WordPress as normal. BeyondWords generates the assets you configured and embeds the player if **Embed** and player visibility are enabled.
  </Step>
</Steps>

## Generate content for existing posts

To generate content for posts published before the plugin was installed:

1. Go to **All Posts** in the WordPress dashboard
2. Select the posts you want to process
3. From the **Bulk actions** dropdown, select **Generate audio** and click **Apply**

Configure generation and embed settings per post in the BeyondWords panel before running bulk actions, or update individual posts afterwards.

## Plugin settings

The plugin settings page has three tabs:

### Authentication

Add your **API key** and **Project ID** to connect the plugin to your BeyondWords project. Find these in **Settings → Integrations → WordPress** in your project dashboard.

### Integration

Choose how the plugin creates and updates content items in BeyondWords:

| Method                        | Use when                                                                                                                                                                                                                                                                            |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **REST API** (default)        | Most WordPress sites—the plugin uses the BeyondWords API directly                                                                                                                                                                                                                   |
| **Magic Embed (Client-side)** | Page builders such as Elementor cause issues with the REST API—the plugin uses [Magic Embed](/docs-and-guides/integrations/magic-embed) instead. You must also [configure Magic Embed](/docs-and-guides/integrations/magic-embed#install-magic-embed) in your BeyondWords dashboard |

### Preferences

Site-wide defaults for new posts in the editor:

* **Posts or Pages**—which post types are preselected for generation. Defaults to **Posts**.
* **Include excerpt**—prepend the WordPress **Excerpt** to the start of generated content. Applies to both audio and video. Disabled by default.
* **Player UI**—whether the BeyondWords player appears on published posts:
  * **Enabled** (default)—embed the player on posts
  * **Headless**—generate content without embedding the player. Use when [building your own UI](/docs-and-guides/distribution/player/developer-guides/build-your-own-ui).
  * **Disabled**—do not show the player on posts

## Post editor

The BeyondWords panel in the **block editor** and **classic editor** exposes the same sections: **Preview**, **Settings**, **Data**, **Help**, and **Inspect**.

Per-post fields default to your BeyondWords project settings unless overridden.

### Generate audio

In the block editor, the **Generate audio** checkbox is always visible in the BeyondWords panel.

* **Checked**—saving or updating the post sends the content to BeyondWords for generation (or regeneration when the post body changes).
* **Unchecked**—saving the post does **not** regenerate audio from the updated content. Use this when you have edited the post but want to keep the existing audio.

### Preview

Hidden until audio or video has been generated for the post. Once content exists, shows a preview of the BeyondWords player for the asset currently selected in **Settings → Player → Embed**.

Toggle whether the player is **visible on the published post**, directly below the preview.

### Settings

#### Content

* **Source**—what to generate from: **Post**, **Script**, or **Post + Script**. Defaults to project settings.
* **Script template**—shown when Source includes Script. Choose which [script template](/docs-and-guides/tools/generate-scripts) to use. Defaults to project settings.

#### Format

* **Output**—what to generate: **Audio**, **Video**, or **Audio + Video**. Defaults to project settings.
* **Video template**—shown when Output includes Video. Defaults to project settings. See [video generation](/docs-and-guides/content/video).
* **Video size**—shown when Output includes Video. Defaults to project settings.

#### Voice

* **Language**—defaults to project settings
* **Voice**—filtered by the selected language; defaults to project settings
* **Model**—shown when the selected voice has more than one model; defaults to project settings

#### Player

* **Embed**—which generated asset to show on this post. Options are derived from your **Source** and **Output** selections. Only one asset can be embedded per post—all other generated assets remain available in BeyondWords.

| Source        | Output        | Embed options                                                    |
| ------------- | ------------- | ---------------------------------------------------------------- |
| Post          | Audio         | None, Audio (post)                                               |
| Post          | Video         | None, Video (post)                                               |
| Script        | Audio         | None, Audio (script)                                             |
| Post + Script | Audio + Video | None, Audio (post), Audio (script), Video (post), Video (script) |

### Data

Enter a **Content ID** to link an existing BeyondWords content item to this WordPress post when you know the ID.

### Help

Links and guidance for using the plugin. No configuration options.

### Inspect

View, copy, or remove the BeyondWords data stored in WordPress for this post. Used for debugging and support—see [Support and troubleshooting](#support-and-troubleshooting) below.

In the block editor, **Fetch** retrieves audio that already exists in BeyondWords and restores the link to this WordPress post—without regenerating audio. Use this when the connection between the post and its BeyondWords content is missing or broken but the audio still exists in your project.

## WordPress filters

The plugin provides WordPress filters to customize content sent to the API, player rendering, SDK behavior, and plugin settings.

| Filter                                                                      | What it does                                                                                                                     |
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| [`beyondwords_content_params`](#beyondwords_content_params)                 | Control what the plugin sends to the BeyondWords API (`body`, metadata, and more)                                                |
| [`beyondwords_player_html`](#beyondwords_player_html)                       | Modify the player HTML embedded in posts                                                                                         |
| [`beyondwords_player_script_onload`](#beyondwords_player_script_onload)     | Append JavaScript to the player script's `onload` handler                                                                        |
| [`beyondwords_player_sdk_params`](#beyondwords_player_sdk_params)           | Modify [player SDK](/docs-and-guides/distribution/player/developer-guides/player-properties) parameters passed at initialization |
| [`beyondwords_settings_player_styles`](#beyondwords_settings_player_styles) | Modify player style options in plugin settings and the post editor                                                               |
| [`beyondwords_settings_post_statuses`](#beyondwords_settings_post_statuses) | Control which post statuses trigger audio processing                                                                             |
| [`beyondwords_settings_post_types`](#beyondwords_settings_post_types)       | Control which post types the plugin supports                                                                                     |

### How to add a filter

Every example below uses `add_filter()`. You can add that code to your WordPress site in one of two ways:

| Approach                                                                 | Best for                                                                          |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| **[Code Snippets](https://wordpress.org/plugins/code-snippets/) plugin** | Most publishers—no theme edits, easy to enable or disable, survives theme updates |
| **Child theme `functions.php`**                                          | Developers comfortable editing theme files                                        |

<Steps>
  <Step title="Install Code Snippets (recommended)">
    In WordPress admin, go to **Plugins → Add New**, search for **Code Snippets**, then install and activate it.
  </Step>

  <Step title="Create a new snippet">
    Go to **Snippets → Add New**, give it a name (for example, "BeyondWords player customisation"), and paste your `add_filter()` code.
  </Step>

  <Step title="Set scope and activate">
    Set the snippet to run on the **front end only** unless the filter only needs the admin area. Save and activate.
  </Step>
</Steps>

Alternatively, add the same code to your **child theme's** `functions.php` file. Use a child theme so parent theme updates do not overwrite your customisations.

<Note>
  You do not need to change the filter hook names or signatures—copy any example below into either approach.
</Note>

### beyondwords\_content\_params

Filters the content parameters sent to the BeyondWords API during generation when using the **REST API** integration method. Use it to control what gets sent—for example, modify `$params['body']` (the post HTML), adjust `title` or `metadata`, or pass custom fields.

As with other integrations, you can also configure [content filters](/docs-and-guides/integrations/content-extraction#filters) in **Settings → Extraction → Filters** to include or exclude parts of the HTML during processing. The plugin sends content with `type` set to `auto_segment`, so dashboard filters apply to `$params['body']` on the BeyondWords side. Use content filters for recurring page elements (newsletter sign-ups, related-article blocks); use this PHP filter for WordPress-specific changes before content is sent.

When using **Magic Embed (Client-side)**, content is extracted from the live page instead. Use [content filters](/docs-and-guides/integrations/content-extraction#filters) and [data attributes](/docs-and-guides/integrations/data-attributes) to control what gets ingested.

| Argument   | Type    | Description                                                                                    |
| ---------- | ------- | ---------------------------------------------------------------------------------------------- |
| `$params`  | `array` | Parameters sent to the BeyondWords API, including `body`, `title`, `source_id`, and `metadata` |
| `$post_id` | `int`   | WordPress post ID                                                                              |

**Prepend the author name and publish date**

```php theme={null}
function my_beyondwords_content_params( $params, $post_id ) {
    $name = get_the_author_meta( 'display_name', $post_id );
    $date = get_the_date( '', $post_id );

    $prepend = '';

    if ( $name ) {
        $prepend .= '<p>By ' . esc_html( $name ) . '</p>';
    }

    if ( $date ) {
        $prepend .= '<p>' . esc_html( $date ) . '</p>';
    }

    $params['body'] = $prepend . $params['body'];

    return $params;
}

add_filter( 'beyondwords_content_params', 'my_beyondwords_content_params', 10, 2 );
```

**Send a custom field as metadata**

```php theme={null}
function my_beyondwords_content_params( $params, $post_id ) {
    if ( is_object( $params['metadata'] ) ) {
        $params['metadata']->my_custom_key = get_post_meta( $post_id, 'my_custom_field', true );
    }

    return $params;
}
add_filter( 'beyondwords_content_params', 'my_beyondwords_content_params', 10, 2 );
```

**Forward a custom field to `ads_enabled`**

```php theme={null}
function my_beyondwords_content_params( $params, $post_id ) {
    $params['ads_enabled'] = (bool) get_post_meta( $post_id, 'my_ads_enabled', true );

    return $params;
}
add_filter( 'beyondwords_content_params', 'my_beyondwords_content_params', 10, 2 );
```

### beyondwords\_player\_html

Filters the HTML of the BeyondWords player.

| Argument      | Type     | Description                                                                          |
| ------------- | -------- | ------------------------------------------------------------------------------------ |
| `$html`       | `string` | Player HTML—the SDK may fail to locate the target if you remove the default contents |
| `$post_id`    | `int`    | WordPress post ID                                                                    |
| `$project_id` | `string` | BeyondWords project ID                                                               |
| `$content_id` | `string` | BeyondWords content ID                                                               |
| `$context`    | `string` | `auto` if prepended to `the_content`, or `shortcode` if inserted via shortcode       |

**Wrap the player in a container**

```php theme={null}
function my_beyondwords_player_html( $html, $post_id, $project_id, $content_id, $context ) {
    return '<div class="my-container">' . $html . '</div>';
}
add_filter( 'beyondwords_player_html', 'my_beyondwords_player_html', 10, 5 );
```

**Hide the player for non-signed-in users**

```php theme={null}
function my_beyondwords_player_html( $html, $post_id, $project_id, $content_id, $context ) {
    $current_user = wp_get_current_user();

    if ( $current_user->exists() ) {
        return $html;
    }

    return '';
}
add_filter( 'beyondwords_player_html', 'my_beyondwords_player_html', 10, 5 );
```

**Hide the auto player but keep shortcode/block players**

```php theme={null}
function beyondwords_remove_auto_player( $html, $post_id, $project_id, $content_id, $context ) {
    if ( $context === 'auto' ) {
        return '';
    }

    return $html;
}
add_filter( 'beyondwords_player_html', 'beyondwords_remove_auto_player', 10, 5 );
```

### beyondwords\_player\_script\_onload

Filters the `onload` attribute of the BeyondWords player script. Strings should use double quotes—the output is run through `esc_js()` before being added to the DOM.

| Argument  | Type     | Description                                                            |
| --------- | -------- | ---------------------------------------------------------------------- |
| `$onload` | `string` | The onload script string                                               |
| `$params` | `array`  | SDK params for the current post, including `projectId` and `contentId` |

**Append a custom command**

```php theme={null}
function my_beyondwords_player_script_onload( $onload, $params ) {
    return $onload . 'initializeCustomUserInterface();';
}
add_filter( 'beyondwords_player_script_onload', 'my_beyondwords_player_script_onload', 10, 2 );
```

**Log player params to the console**

```php theme={null}
function my_beyondwords_player_script_onload( $onload, $params ) {
    $my_command = 'console.log("🔊", ' . wp_json_encode( $params, JSON_FORCE_OBJECT | JSON_UNESCAPED_SLASHES ) . ');';

    return $my_command . $onload;
}
add_filter( 'beyondwords_player_script_onload', 'my_beyondwords_player_script_onload', 10, 2 );
```

### beyondwords\_player\_sdk\_params

Filters the BeyondWords player SDK parameters. See [player properties](/docs-and-guides/distribution/player/developer-guides/player-properties) for available options.

| Argument   | Type    | Description       |
| ---------- | ------- | ----------------- |
| `$params`  | `array` | SDK parameters    |
| `$post_id` | `int`   | WordPress post ID |

**Set a custom text color**

```php theme={null}
function my_beyondwords_player_sdk_params( $params, $post_id ) {
    $params['textColor'] = 'rgba(255, 0, 0, 0.8)';

    return $params;
}
add_filter( 'beyondwords_player_sdk_params', 'my_beyondwords_player_sdk_params', 10, 2 );
```

**Set advert consent for all users**

```php theme={null}
function my_beyondwords_player_sdk_params( $params, $post_id ) {
    $params['advertConsent'] = 'non-personalized';

    return $params;
}
add_filter( 'beyondwords_player_sdk_params', 'my_beyondwords_player_sdk_params', 10, 2 );
```

**Use a different icon color for posts tagged "News"**

```php theme={null}
function my_beyondwords_player_sdk_params( $params, $post_id ) {
    $tags = get_the_tags( $post_id );

    foreach ( $tags as $tag ) {
        if ( isset( $tag->name ) && $tag->name === 'News' ) {
            $params['iconColor'] = '#000080';
        }
    }
    return $params;
}
add_filter( 'beyondwords_player_sdk_params', 'my_beyondwords_player_sdk_params', 10, 2 );
```

**Skip ads for signed-in users**

```php theme={null}
function my_beyondwords_player_sdk_params( $params, $post_id ) {
    $current_user = wp_get_current_user();

    if ( $current_user->exists() ) {
        $params['adverts'] = [];
    }

    return $params;
}
add_filter( 'beyondwords_player_sdk_params', 'my_beyondwords_player_sdk_params', 10, 2 );
```

For a complete example that sets `accessTier` based on [MemberPress](https://memberpress.com/) subscription status, see [MemberPress](/docs-and-guides/distribution/player/access-tiers#memberpress) under Paywall providers in the access tiers guide.

### beyondwords\_settings\_player\_styles

Filters the player style options shown in the plugin settings and post edit screens.

| Argument  | Type    | Description                        |
| --------- | ------- | ---------------------------------- |
| `$styles` | `array` | Associative array of player styles |

**Remove "Small" from the player style dropdown**

```php theme={null}
function my_beyondwords_settings_player_styles( $styles ) {
    if ( array_key_exists( 'small', $styles ) ) {
        unset( $styles['small'] );
    }

    return $styles;
}
add_filter( 'beyondwords_settings_player_styles', 'my_beyondwords_settings_player_styles' );
```

### beyondwords\_settings\_post\_statuses

Filters the post statuses BeyondWords considers for audio processing.

| Argument    | Type       | Description              |
| ----------- | ---------- | ------------------------ |
| `$statuses` | `string[]` | Post statuses to process |

**Add a custom post status**

```php theme={null}
function my_beyondwords_settings_post_statuses( $statuses ) {
    $statuses[] = 'your_custom_status';

    return $statuses;
}
add_filter( 'beyondwords_settings_post_statuses', 'my_beyondwords_settings_post_statuses' );
```

### beyondwords\_settings\_post\_types

Filters the post types supported by BeyondWords. Defaults to all post types with `custom-fields` support—content and project IDs are stored in custom fields, so unsupported post types will not work as expected.

| Argument      | Type       | Description               |
| ------------- | ---------- | ------------------------- |
| `$post_types` | `string[]` | Supported post type names |

**Limit support to posts only**

```php theme={null}
function my_beyondwords_settings_post_types( $post_types ) {
    return [ 'post' ];
}
add_filter( 'beyondwords_settings_post_types', 'my_beyondwords_settings_post_types' );
```

## Support and troubleshooting

When contacting [support](/docs-and-guides/support/get-support), include:

* **Site health**—go to **Tools → Site Health → Info**, click **Copy site info to clipboard**, and paste the result
* **Inspect panel data**—copy the BeyondWords data for the affected post from the **Inspect** section of the BeyondWords panel (block editor and classic editor)

### 404 not found

If the BeyondWords sidebar shows a `#404: Not Found` error in `beyondwords_error_message`, the WordPress post has become disconnected from its BeyondWords content.

<Steps>
  <Step title="Open the Inspect panel">
    Access the **Inspect** section in the block or classic editor (see above).
  </Step>

  <Step title="Try Fetch (block editor)">
    In the block editor, click **Fetch** in the Inspect panel to retrieve the existing audio from BeyondWords and restore the link—without regenerating audio.

    If Fetch succeeds, save the post and confirm the player works on the published page.
  </Step>

  <Step title="Link by Content ID (optional)">
    If you know the BeyondWords content ID, enter it in the **Data** section instead.
  </Step>

  <Step title="Remove and regenerate (last resort)">
    If the content no longer exists in BeyondWords, click **Remove** in Inspect to delete the stored data, then save your post. Recent plugin versions also attempt to delete the corresponding content from your BeyondWords dashboard.

    Ensure **Generate audio** is checked, configure generation in **Settings**, then republish to trigger new content generation.
  </Step>
</Steps>

If this error occurs regularly, [contact support](/docs-and-guides/support/get-support).
