AI

Premium plugins required. The ai options configure backend transport and UI behavior for AI features. They have no effect until you load and enable the matching premium plugin.

ai.url#

Type: false | string

Default: false

Plugin: AI Assistant, AI Commands

Sync HTTP endpoint for text AI requests. When false and no jobs transport is configured, AI plugins does not start.

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai'
  }
});

ai.model#

Type: false | string

Default: false

Plugin: AI Assistant, AI Commands

Optional model id sent to the backend as body.model (for example gpt-4o-mini, claude-3-5-sonnet-20241022).

When omitted, the field is not included in the request — your backend can use its own default model.

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    model: 'gpt-4o-mini'
  }
});

ai.transport#

Type: 'sync' | 'jobs' | 'custom'

Default: 'sync'

Plugin: AI Assistant

How text/image requests reach your backend:

Value Behavior
sync Single HTTP request to ai.url (or ai.image.url for images)
jobs Create a job via ai.jobs.createUrl, then poll until complete
custom Your handler in ai.request / ai.image.request; no default fetch to ai.url
const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs'
    }
  }
});

ai.request#

Type: false | (ctx, app) => Promise<{ raw } | { images }>

Default: false

Plugin: AI Assistant

Custom transport for text requests. The handler receives a context object with kind, action, prompt, context, body, headers, signal, and app. Return { raw } for text/review/report responses, or { images } for image generation.

Required when transport is custom and no handler returns a result.

const app = Redactor('#entry', {
  ai: {
    transport: 'custom',
    request: async (ctx) => {
      const res = await fetch('/api/ai', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(ctx.body),
        signal: ctx.signal
      });
      return { raw: await res.json() };
    }
  }
});

ai.headers#

Type: object

Default: {}

Plugin: AI Assistant, AI Commands

Extra headers for text requests.

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    headers: {
      'X-Api-Key': 'your-key'
    }
  }
});

ai.batchBlocks#

Type: number

Default: 10

Plugin: AI Assistant

Maximum blocks per request when a review operation is split into batches. Used only in review mode when batching is enabled.

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    batchBlocks: 5
  }
});

ai.batchStrategy#

Type: 'fallback' | 'always' | 'never'

Default: 'fallback'

Plugin: AI Assistant

Controls batching for review requests in sync transport:

Value Behavior
fallback Try one request first; split into batches on server/network errors (5xx, timeout, fetch failure)
always Always send review requests in batches of ai.batchBlocks
never Never batch
const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    batchStrategy: 'always',
    batchBlocks: 8
  }
});

ai.jobs#

Plugin: AI Assistant

Async job transport settings. Used when ai.transport is 'jobs'.

const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs'
    }
  }
});

ai.jobs.createUrl

Type: false | string

Default: false

Endpoint that creates a job and returns { jobId } (alias: ai.jobs.url).

const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs'
    }
  }
});

ai.jobs.pollInterval

Type: number

Default: 1500

Polling interval in milliseconds between status checks.

const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs',
      pollInterval: 2000
    }
  }
});

ai.jobs.pollMaxWait

Type: number

Default: 120000

Maximum time in milliseconds to wait for job completion.

const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs',
      pollMaxWait: 300000
    }
  }
});

ai.jobs.headers

Type: object

Default: {}

Extra headers for job create and poll requests.

const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs',
      headers: {
        'X-Api-Key': 'your-key'
      }
    }
  }
});

ai.jobs.getAuth

Type: false | () => Promise<object> | object

Default: false

Optional async provider for auth headers (for example { Authorization: 'Bearer …' }). Merged with ai.jobs.headers on each jobs request.

const app = Redactor('#entry', {
  ai: {
    transport: 'jobs',
    jobs: {
      createUrl: '/api/ai/jobs',
      getAuth: async () => ({
        Authorization: `Bearer ${await getToken()}`
      })
    }
  }
});

ai.actions#

Type: object

Default: built-in action map from the loaded AI plugin (merged with your overrides)

Plugin: AI Assistant, AI Commands

Defines the AI dropdown menu: top-level commands, nested submenus, labels, icons, and prompts sent to the backend.

Your ai.actions object is deep-merged into the plugin defaults. Matching keys override built-in entries; new keys add menu items.

Pass { __replace: true, ... } as the root actions value to drop all built-in items and use only yours.

Action fields

Field Description
title Menu label. Supports ## lang.key ## tokens.
icon Optional SVG/HTML icon for the menu row.
prompt Instruction sent to the backend (AI Commands and custom actions).
items Nested submenu. Keys become submenu entries; paths use dots (translate.en).
group AI Assistant toolbar group: edit, review, insights, generate.
mode AI Assistant pipeline: review (default), report, report-review, apply.
diff AI Assistant — open AI Review with a token diff (true).
language AI Assistant — target language for translate sub-items.
tone AI Assistant — tone instruction for tone sub-items.
documentContext AI Assistant — run on the whole document; hidden from the context toolbar.

Leaf vs submenu

  • Leaf action — has prompt (AI Commands) or mode / language / tone (Assistant). Click runs the command immediately.
  • Submenu — has items with child actions. The parent usually has only title and icon; children hold prompts or nested items.

Override a built-in action

Change the label, icon, or prompt without rebuilding the menu:

// AI Commands
const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    actions: {
      improve: {
        title: 'Polish text',
        icon: '<svg>...</svg>',
        prompt: 'Polish the text. Keep HTML structure. Return HTML only.'
      }
    }
  }
});

// AI Assistant
const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    actions: {
      improve: {
        title: 'Polish text',
        icon: '<svg>...</svg>'
      }
    }
  }
});

Nested actions

Built-in translate and tone entries are submenus. Override the parent title/icon, add a language, or replace the whole submenu:

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    actions: {
      translate: {
        title: 'Translate to…',
        items: {
          pl: {
            title: 'Polish',
            prompt: 'Translate to Polish. Preserve HTML. Return HTML only.'
          },
          en: {
            title: 'English',
            language: 'English'
          }
        }
      },
      tone: {
        items: {
          friendly: {
            title: 'Friendlier',
            tone: 'Rewrite in a warm, friendly tone. Preserve HTML. Return HTML only.'
          }
        }
      }
    }
  }
});

Use prompt in nested AI Commands items. In AI Assistant translate children use language; tone children use tone.

Add a custom action

Top-level keys become new menu rows:

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    actions: {
      seo: {
        title: 'SEO rewrite',
        icon: '<svg>...</svg>',
        prompt: 'Rewrite for SEO. Keep meaning and HTML structure. Return HTML only.'
      }
    }
  }
});

AI Assistant — assign a toolbar group:

actions: {
  seo: {
    title: 'SEO rewrite',
    group: 'edit',
    prompt: 'Rewrite for SEO. Return HTML only.'
  }
}

Replace the whole menu

const app = Redactor('#entry', {
  ai: {
    url: '/api/ai',
    actions: {
      __replace: true,
      rewrite: {
        title: 'Rewrite',
        prompt: 'Rewrite the selection. Return HTML only.'
      },
      shorten: {
        title: 'Shorten',
        prompt: 'Make it shorter. Return HTML only.'
      }
    }
  }
});

Hide a built-in action

const app = Redactor('#entry', {
  ai: {
    actions: {
      tone: false
    }
  }
});

ai.image#

Type: object

Plugin: AI Assistant, AI Commands

Image generation settings shared by both plugins. When ai.image.url is set, the image button appears in the addbar (if the plugin is enabled).

const app = Redactor('#entry', {
  ai: {
    image: {
      url: '/api/ai/image'
    }
  }
});

ai.image.url

Type: false | string

Default: false

Dedicated image generation endpoint. When false, the image UI is disabled.

const app = Redactor('#entry', {
  ai: {
    image: {
      url: '/api/ai/image'
    }
  }
});

ai.image.model

Type: false | string

Default: false

Optional image model id (for example dall-e-3, gpt-image-1), sent as body.model.

When omitted, the field is not included in the request — your backend should use its own default image model.

const app = Redactor('#entry', {
  ai: {
    image: {
      url: '/api/ai/image',
      model: 'dall-e-3'
    }
  }
});

ai.image.headers

Type: object

Default: {}

Extra headers for image generation requests.

const app = Redactor('#entry', {
  ai: {
    image: {
      url: '/api/ai/image',
      headers: {
        'X-Api-Key': 'your-key'
      }
    }
  }
});

ai.image.size

Type: string

Default: '1024x1024'

Default image size sent as body.size.

const app = Redactor('#entry', {
  ai: {
    image: {
      url: '/api/ai/image',
      size: '1536x1024'
    }
  }
});

ai.image.n

Type: number

Default: 1

Number of images to request per generation (body.n).

const app = Redactor('#entry', {
  ai: {
    image: {
      url: '/api/ai/image',
      n: 2
    }
  }
});

ai.image.request

Type: false | (ctx, app) => Promise<{ images }>

Default: false

Optional image-only custom handler. When set, it takes priority over ai.request for kind: 'image'. Falls back to ai.request if not set.

const app = Redactor('#entry', {
  ai: {
    transport: 'custom',
    image: {
      request: async (ctx) => {
        const res = await fetch('/api/ai/image', {
          method: 'POST',
          body: JSON.stringify(ctx.body),
          signal: ctx.signal
        });
        const data = await res.json();
        return { images: data.images };
      }
    }
  }
});

ai.report.handler#

Type: false | (report) => void

Default: false

Plugin: AI Assistant

Called when a report-mode AI action returns HTML. Use it to render results in your own panel instead of the default report UI (host tabs).

The handler receives a report object:

Field Type Description
key string Tab/section id (raw.tab, raw.key, raw.section, or action name).
title string Report title from the backend or the default lang string.
html string Report HTML to display.
action string Action id that produced the report (for example summarize).
raw object Full backend response.
app object Redactor editor instance.
const app = Redactor('#entry', {
  ai: {
    report: {
      handler: (report) => {
        document.querySelector('#ai-report').innerHTML = report.html;
      }
    }
  }
});

ai.review.diffClasses#

Type: { original: string, replacement: string }

Default:

{
  original: 'rx-ai-diff-original',
  replacement: 'rx-ai-diff-replacement'
}

Plugin: AI Assistant

CSS class names used to wrap changed tokens in the Original / Suggestion diff panels inside AI Review.

const app = Redactor('#entry', {
  ai: {
    review: {
      diffClasses: {
        original: 'my-diff-old',
        replacement: 'my-diff-new'
      }
    }
  }
});

ai.review.refine#

Type: Array<object>

Default: built-in list (rephrase, refine-prompt, …)

Plugin: AI Assistant

Preset entries for the Refine dropdown in AI Review. Each item:

Field Description
id Unique entry id
title Label (supports ## lang.key ##)
icon Optional SVG icon HTML
action AI action name (for preset actions)
type: 'prompt' Inline prompt field instead of a fixed action
hideOnDiff Hide when diff view is active
const app = Redactor('#entry', {
  ai: {
    review: {
      refine: [
        {
          id: 'shorter',
          action: 'shorter',
          title: '## ai.shorter ##',
          reviewOnly: true
        },
        {
          id: 'custom-prompt',
          type: 'prompt',
          title: '## ai.refine-prompt ##',
          hideOnDiff: true
        }
      ]
    }
  }
});