This page lists every route that DXPR Builder registers in
dxpr_builder.routing.yml, with the HTTP methods, access
requirements, request parameters and response shapes as
implemented in the controllers. The editor's JavaScript is the
intended client for the AJAX endpoints; nothing on this page is a
stable public API, and parameter names can change between
releases.
Access requirements
Routes use three kinds of requirement. A route can combine them.
Requirement
Meaning
_permission
The user needs the named Drupal permission. DXPR Builder defines administer dxpr builder configuration, edit with dxpr builder and administer dxpr_builder_profile in dxpr_builder.permissions.yml; view media and administer site configuration come from core.
_csrf_token
The request must carry a valid CSRF token in the token query parameter. For the main AJAX handler, the editor fetches a URL with the token from /dxpr_builder/csrf (or from the dxpr_builder_csrf operation). For the other routes, the field formatter puts URLs with a token in drupalSettings.dxprBuilder, for example fileUploadUrl, entityLinkSuggestionsUrl, userSettingsUrl and aiAgent.endpointUrl.
_dxpr_builder_billable_user
The user must be a billable user, as defined below.
What "billable user" means
DxprBuilderBillableUserAccessCheck allows access when
DxprBuilderLicenseService::isBillableUser() returns true for the
current account. That method returns false for blocked accounts
and for accounts whose dxpr_user_is_disavowed field is set (an
administrator excludes a user from editing on the User Licenses
page). Otherwise it returns true for user 1 and for any account
that holds the edit with dxpr builder permission. In short: a
billable user is an active, non-excluded account that is allowed
to edit with DXPR Builder, and the licence service counts these
accounts against your product key.
HTTP methods
Where a route declares methods, only those methods are routed.
Where it does not, the route accepts any method (Drupal's
MethodFilter only filters routes that declare methods). The tables below state the method the editor's
JavaScript actually uses when that is visible in the source.
Editor AJAX endpoints
CSRF token URL
Path
/dxpr_builder/csrf
Route
dxpr_builder.csrf_refresh
Methods
Not declared (any method). The editor sends GET (backend-connect.js, fetchCsrfTokenUrl()).
Requirements
Billable user
Controller
AjaxController::ajaxRefresh()
The request must carry the header X-Requested-With:
XMLHttpRequest; without it the controller returns HTTP 403 with
the JSON body {"error": "Invalid request"}.
Parameter
Source
Required
Notes
enterprise
Query string
Optional
When set to true and the dxpr_builder_e module is installed, the token is generated for the dxpr_builder_e.ajax_callback route instead. When set to true without that module the controller throws an exception.
Response: a JSON string with the URL of the main AJAX handler and
a token query parameter. The URL is a path from the site root
(base path included, no host). The JavaScript posts every builder
operation to this URL.
Main AJAX handler
Path
/dxpr_builder/ajax
Route
dxpr_builder.ajax_callback
Methods
Not declared (any method). The controller reads every parameter from the POST body, and the editor sends POST.
Requirements
Billable user, CSRF token
Controller
AjaxController::ajaxCallback()
The POST body field action selects the operation. Every
operation and its parameters are listed under
Main AJAX handler operations.
When action is missing or unknown, the controller returns an
empty Drupal AjaxResponse with no commands. Most operations that
re-check the billable user status return the same empty response
when that check fails; the tables note the two exceptions.
AI chat
Path
/dxpr_builder/ajax/ai/chat
Route
dxpr_builder.ai_chat
Methods
POST
Requirements
edit with dxpr builder, billable user, CSRF token
Controller
AiChatController::chat()
The request body is JSON. The controller proxies the request to
the dxpr provider of the Drupal ai module, so no API key
reaches the browser. Before the call it merges the domains from
the ai_output_allowed_domains setting into the ai module's
hostname filter, when that service exists. A value of * sets the
filter to full trust instead. The filter is restored afterwards.
Parameter
Source
Required
Notes
messages
JSON body
Required
Array of objects with role and content.
stream
JSON body
Optional
Boolean, default true.
model
JSON body
Optional
Default kavya-m1.
prediction
JSON body
Optional
Passed to the provider configuration as an array.
providers
JSON body
Optional
Passed through to the provider configuration.
allowed_html_tags
JSON body
Optional
Passed through to the provider configuration.
allowed_html_classes
JSON body
Optional
Passed through to the provider configuration.
web_search
JSON body
Optional
Only the value false is forwarded; any other value is ignored.
response_format
JSON body
Optional
Passed to the provider configuration as an array.
Responses:
Case
Status
Body
Streamed (stream true and the provider streams)
200, Content-Type: text/event-stream
Server-sent events. Each event is data: <JSON> where the JSON has choices[0].delta.content and, when the provider supplies them, id, object, created, model, original_model and usage. The stream ends with data: [DONE].
POST, DELETE (declared in routing; the controller has a single upload handler and no separate delete branch)
Requirements
Billable user, CSRF token
Controller
UploadFileController::fileUpload()
Parameter
Source
Required
Notes
upload
Multipart form files, sent as upload[]
Required
One or more uploaded files.
The controller sorts each file by the first part of its client MIME
type into <default scheme>://dxpr_builder_images,
dxpr_builder_videos or dxpr_builder_files. An image needs an
extension of the MIME types image/gif, jpg, jpeg, png,
webp, avif or svg. A video needs one of video/webm, ogv,
ogg, mp4 or quicktime (for example .mov). The size limit
is PHP's upload maximum. A file with the same name and identical
SHA-256 content as an existing managed file that the user can view
is reused instead of saved again. When the
media module is installed and a media type named image,
media_image, video or media_video exists, a published media
entity is created (or reused) for the file. File usage is
recorded under the dxpr_builder module.
Responses:
Case
Status
Body
Success
201
JSON array, one object per file: url (absolute file URL), uuid and id (of the media entity when one exists, otherwise of the file), fid (file ID), entity_type (media or file).
No upload files
400
{"message": "No files were uploaded."}
Invalid upload, unwritable destination or save failure
500
{"message": "<reason>"}
Validation failure (extension or size)
422
{"message": "<violation messages>"}
Filename already locked by another request
503
{"message": "File \"<name>\" is already locked for writing."}
Media library dialog
Path
/dxpr_builder/media-library
Route
dxpr_builder.media_library
Methods
Not declared (any method). Parameters are read from the query string.
Requirements
view media; admin route
Controller
AjaxController::openImageMediaLibrary()
Parameter
Source
Required
Notes
media_type
Query string
Optional
image (default) or video.
remaining_slots
Query string
Optional
Number of items the opener accepts; defaults to 1.
uuid
Query string
Optional
Passed to the opener as opener_parameters.uuid.
The media type offered depends on the media_browser setting:
media_library uses the image or video media type directly;
media_library_acquia_dam uses acquia_dam_image_asset or
acquia_dam_video_asset when that media type exists.
Responses:
Case
Result
Success
A render array built by MediaLibraryUiBuilder::buildUi() with the opener media_library.opener.dxpr_builder. Drupal renders it as an HTML page intended for an iframe.
media_library module not installed
403, plain text Media Library is not enabled. Please enable it to access this feature.
Unsupported media_type
400, plain text Unsupported media type "<value>".
media_browser setting has no usable media type
400, plain text The "<setting>" settings is not supported.
Help link validator
Path
/dxpr_builder/ajax/help_link_validator
Route
dxpr_builder.ajax_help_link_validator
Methods
Not declared (any method). The dev script dxpr_builder_dev.js sends POST with a form-encoded body.
Requirements
Billable user
Controller
AjaxController::validateHelpLink()
Parameter
Source
Required
Notes
help_link
POST body
Required for a meaningful answer
Only http or https URLs on host dxpr.com with a path starting /documentation/ are checked.
Response: a JSON array. [true] when a HEAD request to the link
(without following redirects) returns 200; [false] when the HTTP
client throws; [] in every other case, including a missing
parameter, a non-matching URL or a non-200 status.
Entity link suggestions
Path
/dxpr_builder/entity-link-suggestions
Route
dxpr_builder.entity_link_suggestions
Methods
GET
Requirements
edit with dxpr builder, CSRF token; no_cache route option
Controller
EntityLinkSuggestionsController::suggestions()
Parameter
Source
Required
Notes
q
Query string
Required for results
Search text, lower-cased. A value like /node/123 loads that entity directly.
langcode
Query string
Optional
Language of the host entity; defaults to the current language. Only entities available in this language are returned.
Bundles are those with ckeditor5_link_suggestions enabled in
their bundle info; when none are configured, all node bundles are
searched. Up to 100 entities per entity type are matched with a
CONTAINS search through core's entity reference selection
plugins.
Response: {"suggestions": [...]} with private cacheability and a
max age of 300 seconds. Each suggestion has description,
entity_type_id, entity_uuid, group, label and path. When
q matches nothing, the array holds a single manual-link
suggestion with description, label (the lower-cased input) and
path (the input when it is a valid URL, otherwise an empty
string). When q is empty, suggestions is an empty array.
User editor settings
Path
/dxpr_builder/user-settings
Route
dxpr_builder.user_settings
Methods
POST
Requirements
edit with dxpr builder, CSRF token; no_cache route option
Controller
UserSettingsController::save()
The editor sends this request when a user changes their own editor
settings, for example under Editor settings. The values are
stored with the user account in user.data, module
dxpr_builder.
Parameter
Source
Required
Notes
controls_layout
JSON body
Required (the only setting)
element, breadcrumb or default. default removes the user's choice, so the profile or site setting applies.
Responses:
Case
Status
Body
Saved
200
{"settings": {"controls_layout": "<value>"}}: every setting after saving, default where the user has no choice stored.
Body is not a non-empty JSON object, or a name or value is invalid
400
{"error": "Invalid setting or value."}. Nothing is stored.
Main AJAX handler operations
All operations go to /dxpr_builder/ajax with the token query
parameter obtained from /dxpr_builder/csrf. The operation name
is sent in the POST body field action; all other parameters are
POST body fields unless stated otherwise. The "Re-checks billable"
column marks operations that call isBillableUser() again inside
the switch. When that check fails they return the empty
AjaxResponse described above, unless the row says otherwise.
Session and access
Operation
Parameters
Re-checks billable
Returns
dxpr_login
None
No
JSON boolean: whether the current user is billable.
dxpr_builder_csrf
None
Yes
JSON string: the handler URL with a token query parameter (same as /dxpr_builder/csrf without the enterprise option).
Containers (fields rendered with the DXPR Builder formatter)
Operation
Parameters
Re-checks billable
Returns
dxpr_get_container_types
None
Yes
JSON object keyed entity_type\|bundle with value entity_type - bundle, one entry per bundle whose default view display uses the dxpr_builder_text formatter on at least one field.
dxpr_get_container_names
container_type (required, entity_type\|bundle)
Yes (returns an empty JSON string instead of the empty response)
JSON object keyed entity_id\|field_name (or entity_id\|field_name\|langcode for translatable entities) with value entity label\|field label. When container_type is missing, or the billable check fails, the response is an empty JSON string.
dxpr_save_container
type (required, entity_type\|bundle); name (required, entity_id\|field_name or entity_id\|revision_id\|field_name); htmlContent (required, encoded markup); lang (optional, defaults to the site default language)
Yes
JSON empty string on success. HTTP 400 with a JSON message when htmlContent is not a string or the language does not apply to a non-translatable entity; 404 when the requested translation does not exist; 500 when the entity cannot be loaded.
dxpr_save_container does the following:
Decodes htmlContent: XOR every byte with 7, then URL-decode.
Writes the result to the first delta of the named field.
Creates a new default revision with the log message Saved with
DXPR builder when the entity type is revisionable.
Removes any content lock for the entity.
Updates block_revision_id in Layout Builder sections that
reference the saved inline block.
Increments the dxpr_builder.saves_count state value.
The save only happens when the user has update access to the
entity. Without it, the operation still returns the empty-string
success response.
CMS elements (blocks and views)
Operation
Parameters
Re-checks billable
Returns
dxpr_builder_get_cms_element_names
None
Yes
JSON object from DxprBuilderService::getCmsElementNames() with keys list, disallowed and blockCategories.
dxpr_get_cms_element_settings
name (required): view-<view_id>-<display_id> or block-<plugin_id>; ajax_page_state[libraries] (optional, comma-separated libraries already loaded)
Yes
JSON object with css, js, settings and data. For a view, data is the rendered exposed filter widgets; for a block, data is the block's configuration form wrapped in <form> tags, with the provider, admin_label, label, label_display and context_mapping elements removed and all details elements opened.
dxpr_builder_load_cms_element
name (required, parsed by parseStringForCmsElementInfo()); settings (required); data (required, array); ajax_page_state[libraries] (optional)
No
JSON object with css, js, settings and data, where data is the rendered block or view display.
In both settings-returning operations, css and js are
rendered asset tags not already present in the page, and
settings is the decoded drupalSettings object, or an empty
string when there are none.
Page templates
Operation
Parameters
Re-checks billable
Returns
dxpr_get_page_templates
None
Yes
JSON array of enabled page templates, each with title, uuid, module (always null: the entity does not store it), category, date, weight (integer) and image (a data: URL, or the module's not-found.png URL).
dxpr_load_page_template
uuid (required)
Yes
HTML response containing the template markup with base tokens replaced; empty when no enabled template matches.
User templates
Operation
Parameters
Re-checks billable
Returns
dxpr_get_templates
None
Yes
JSON array of templates owned by the current user or marked global; templates whose author account no longer exists are left out. Each entry has id, uuid, name, global, current_user_is_author, author_id, author_name, type and, when stored, image (a data: URL).
dxpr_load_template
uuid (required)
Yes
HTML response containing the template markup with base tokens replaced; empty when no enabled template matches.
dxpr_save_template
name (required); template (required, markup); type (optional, defaults to custom); global (optional, cast to boolean); image (optional: a base64 string or data:image URL in the POST body, or a multipart file field named image)
Yes
JSON object with message and code. code 200 on success; code 409 (HTTP status 200) when a template with the same machine name exists; code 400 with HTTP 400 when the image is not valid image data.
dxpr_delete_template
name (required)
Yes
JSON empty string.
The machine name is derived from name by lower-casing it and
replacing every run of characters outside a-z0-9_ with an
underscore. type falls back to the data-azb attribute in the
markup when a stored template has no type.
Images
Operation
Parameters
Re-checks billable
Returns
dxpr_builder_get_image_urls
entityIDs (required, array); imageStyle (required; original for the unstyled file); entityType (required; media resolves media items, any other value is used as the entity type of the IDs)
Yes
Text body: comma-separated image URLs. Files give a root-relative URL with a fid query parameter. Media items of the image or video type resolve to their source file. acquia_dam_image_asset items give the DAM URL with mid, acquiaDamAsset and imageStyle added. Other media items are skipped. HTTP 204 with no body when entityIDs is empty, or when no media item can be resolved.
dxpr_builder_get_image_style_url
imageStyle (required); entityId (required); entityTypeId (required, file or media)
Yes
Text body. For a file: its root-relative URL through the image style (SVG files and original skip the style), with a fid query parameter. For an acquia_dam_image_asset media item: the DAM URL. HTTP 204 with no body for any other entity, or when the entity is not found.
dxpr_builder_get_image_metadata
entityId (required); entityType (required, file or media)
Yes (returns 403 JSON instead of the empty response)
{"data": {"alt": "...", "title": "..."}} from the media item's image source field (an Acquia DAM asset gives its DAM alt text and an empty title). For a file, the values come from the first media item whose field_media_image references it. data is an empty array when the entity is neither a file nor a media item, or when no media references the file. Errors: 400 {"error": "Missing required parameters"}, 404 {"error": "Entity not found"}, 500 {"error": "Failed to load entity"}, 403 {"error": "Access denied"}.
JSON object with status (boolean) and label (entity label); when locked, also entity_lock_author_id and entity_lock_author_name.
dxpr_toggle_content_lock
entity_id, revision_id, entity_type, langcode, toggle_action (all required; lock creates the lock, any other value removes it)
No
JSON object with label and status (true after locking, false after unlocking). An empty JSON array ([]) when the entity cannot be loaded or the user lacks update access.
Admin pages
All of these routes accept any method (no methods key). Paths
containing {...} take the config entity ID, or the migration ID
for {migration_id}.
Path
Route
Permission
Purpose
/admin/dxpr_studio
dxpr_builder.admin.studio
administer dxpr builder configuration
DXPR Studio overview; core's admin menu block page listing child pages.
/admin/dxpr_studio/dxpr_builder
dxpr_builder.admin.studio.builder
administer dxpr builder configuration
DXPR Builder overview; core's admin menu block page.
/admin/dxpr_studio/dxpr_builder/settings
dxpr_builder.settings
administer dxpr builder configuration
DxprBuilderSettingsForm: the module settings form.
/admin/dxpr_studio/dxpr_builder/ai_settings
dxpr_builder.ai_settings
administer dxpr builder configuration
DxprBuilderAiSettingsForm: the AI settings form.
/admin/dxpr_studio/dxpr_builder/user_templates
entity.dxpr_builder_user_template.collection
administer site configuration
List of dxpr_builder_user_template config entities.
PageController::createPageTemplate(): copies the enabled user template identified by the uuid query parameter into a new page template with the same ID and category DXPR. It sets a status or error message and redirects to the user template list.
PageController::userLicensesPage(): uncached table of licensed users (columns User, Roles, Sites, Assigned and Operations). Above it, the page renders its own licence summary card (dxpr-license-info) when licence information is available, and, while a freed licence waits out its 30 days, a "Next license available on" notice. On this route the License info block renders nothing, so the summary never shows twice.
PageController::userLicensesSitesPage(): item list of the domains recorded for the user given in the mail request parameter; opened in a modal from the licences table.
/admin/dxpr_studio/dxpr_builder/content
dxpr_builder.licensed_content
administer dxpr builder configuration
PageController::licensedContentPage(): paged table (20 per page) of content items edited with DXPR Builder, with the content items licence summary. The query parameter needs_migration=1 shows only items that need a content migration. Empty when no licence information is available.
/admin/dxpr_studio/dxpr_builder/migration-status
dxpr_builder.migration_status
administer dxpr builder configuration
MigrationStatusController::statusPage(): uncached table of registered content migrations with their pending items. Each pending migration has a Run now link, and a Run all pending migrations button appears when any are pending.