
1. ACF InDesign action return contract
An ACF function invoked from an InDesign package menu or floating palette controls the next host action through its return value. Simple strings become messages. Native ACF JSON objects can request messages, package reloads, document operations, or no further action.
This page documents version 1 of the published return contract, including the currently implemented document operations and their JSON fields.
- ACF InDesign action return contract
- Return types at a glance
- Action function signatures
- The action information object
- General JSON response rules
indesign.noopindesign.messageindesign.reload_packagesindesign.documentindesign.operations- Command execution and identity
- Geometry and placement
- Implemented operation names
define_paragraph_stylesdefine_character_stylesset_markdown_stylescreate_text_frameset_textplace_imageset_imagegroup_items- Complete placement example
- Validation and error handling
- Compatibility rules
- Related documentation
1.1. Return types at a glance ↑
| ACF function result | InDesign behavior |
|---|---|
Empty return or empty STRING |
Finish without further host action. |
Non-empty STRING |
Show an InDesign information dialog containing the string. |
JSON with type: "indesign.noop" |
Finish without further host action while preserving a JSON return type. |
JSON with type: "indesign.message" |
Show a native information, warning, or error dialog. |
JSON with type: "indesign.reload_packages" |
Ask the user whether packages should be rescanned and menus rebuilt. |
JSON with type: "indesign.document" |
Open an existing InDesign document or create and save it when missing. |
JSON with type: "indesign.operations" |
Validate and execute ordered document operations. |
| Unsupported ACF type | Show and log an unsupported return-type error. |
| Runtime exception | Catch it at the Adobe SDK boundary, show an error dialog, and append it to the plugin log. |
Package actions should return native JSON, not JSON serialized into a string. String-encoded JSON exists only for transitional compatibility.
An ACF function must also keep one consistent return type. If a JSON-returning function has a cancelled or empty branch, return indesign.noop from that branch instead of returning an empty string.
1.2. Action function signatures ↑
A function referenced by a package menu or dispatched palette action can use either signature:
function Package_action ()
function Package_action (JSON info)
The zero-parameter form remains valid. The one-parameter form must have exactly one JSON parameter. InDesign supplies a fresh versioned context snapshot when the user invokes the action.
The package discovery function <package_name>_menu() is separate and always has zero parameters.
1.3. The action information object ↑
The one-parameter action receives an object like this:
{
"type": "indesign.action_info",
"version": 1,
"units": "pt",
"document": {
"name": "Tomorrow's Newspaper.indd",
"path": "/Volumes/News/Tomorrow's Newspaper.indd",
"saved": true,
"modified": false,
"readOnly": false
},
"page": {
"index": 4,
"name": "4",
"width": 595.276,
"height": 841.89,
"margins": {
"left": 36,
"top": 36,
"right": 36,
"bottom": 42
},
"placementArea": {
"origin": "top_left_margin",
"width": 523.276,
"height": 763.89
}
}
}
| Field | Type | Meaning |
|---|---|---|
type |
string | Always indesign.action_info. |
version |
integer | Action-information schema version. Version 1 is 1. |
units |
string | Always pt in version 1. |
document |
object or null | Active document information, or null when no document is active. |
page |
object or null | Active layout page information, or null when no page is active. |
palette |
object, optional | Current palette values when invoked through returnAction: "dispatch". |
document.path is an absolute path only after the document has been saved. It is null for a new unsaved document or when InDesign cannot provide a filesystem path.
page.index is the one-based document page index. page.name is the displayed InDesign page name and can differ from the index.
The placement area is the usable page rectangle inside the margins:
placement width = page width - left margin - right margin
placement height = page height - top margin - bottom margin
Version 1 document-operation coordinates use the top-left margin as origin. This means [0, 0] is the upper-left corner inside the page margins.
1.4. General JSON response rules ↑
Every JSON action response must be an object containing:
| Field | Required | Type | Meaning |
|---|---|---|---|
type |
Yes | string | Host action type. |
version |
Yes | integer | Must be 1 for this contract. |
extensions |
No | object | Package-defined data not interpreted by version 1. |
Unknown response types and unsupported versions are errors. Unknown fields are also rejected unless the specification explicitly allows them. Put package-private additions under extensions to avoid hiding misspelled contract fields.
1.5. indesign.noop ↑
Use indesign.noop when a JSON-returning action has completed but does not need another host action. It is particularly useful after a cancelled dialog or after open_palette() has opened a modeless palette.
return JSON(
"type", "indesign.noop",
"version", 1
);
JSON form:
{
"type": "indesign.noop",
"version": 1
}
| Field | Required | Type | Meaning |
|---|---|---|---|
type |
Yes | string | Must be indesign.noop. |
version |
Yes | integer | Must be 1. |
extensions |
No | object | Package-defined metadata. |
No other fields are accepted.
1.6. indesign.message ↑
Use indesign.message to request a native InDesign message dialog.
return JSON(
"type", "indesign.message",
"version", 1,
"severity", "information",
"title", _("Article status"),
"message", _("No articles are scheduled for this page.")
);
JSON form:
{
"type": "indesign.message",
"version": 1,
"severity": "information",
"title": "Article status",
"message": "No articles are scheduled for this page."
}
| Field | Required | Type | Meaning |
|---|---|---|---|
type |
Yes | string | Must be indesign.message. |
version |
Yes | integer | Must be 1. |
message |
Yes | string | Dialog text. |
severity |
No | string | information, warning, or error. Defaults to information. |
title |
No | string | Optional title when supported by the native alert. |
extensions |
No | object | Package-defined metadata. |
An ordinary non-empty ACF STRING also produces an information dialog. Use indesign.message when severity or a consistent JSON return type matters.
1.7. indesign.reload_packages ↑
Use indesign.reload_packages when an action has changed package-related state and should offer to rescan compiled packages. Language installation is a typical example because package menu functions must run again before translated menu labels appear.
return JSON(
"type", "indesign.reload_packages",
"version", 1,
"message", _("The language was installed. Reload ACF packages now?")
);
JSON form:
{
"type": "indesign.reload_packages",
"version": 1,
"message": "The language was installed. Reload ACF packages now?"
}
| Field | Required | Type | Meaning |
|---|---|---|---|
type |
Yes | string | Must be indesign.reload_packages. |
version |
Yes | integer | Must be 1. |
message |
Yes | string | Confirmation shown before reloading. |
extensions |
No | object | Package-defined metadata. |
When the user accepts, the plugin rescans compiled packages, executes package startup functions, and rebuilds dynamic menus. Choosing Later leaves the current installed packages and menus active.
1.8. indesign.document ↑
indesign.document establishes a document from an absolute .indd path. By default, an existing document is opened and a missing document is created, saved at that path, and shown in a layout window. Creation settings never modify a document that already exists.
1.8.1. Advertisement example ↑
return JSON(
"type", "indesign.document",
"version", 1,
"path", "/Users/editor/Advertisements/AD-18425.indd",
"units", "cm",
"document", JSON(
"pageSize", JSON("width", 15, "height", 10),
"pages", 1,
"facingPages", false,
"margins", JSON(
"top", 0, "bottom", 0,
"inside", 0, "outside", 0
),
"bleed", JSON(
"top", 0.3, "bottom", 0.3,
"inside", 0.3, "outside", 0.3
)
)
);
1.8.2. Newspaper-edition example ↑
{
"type": "indesign.document",
"version": 1,
"path": "/Volumes/Production/2026-07-25/Edition.indd",
"ifExists": "open",
"ifMissing": "create",
"showWindow": true,
"units": "mm",
"document": {
"pageSize": {
"width": 280,
"height": 400
},
"orientation": "portrait",
"pages": 32,
"facingPages": true,
"binding": "left_to_right",
"margins": {
"top": 12,
"bottom": 15,
"inside": 12,
"outside": 10
},
"columns": {
"count": 6,
"gutter": 4
},
"bleed": {
"top": 3,
"bottom": 3,
"inside": 3,
"outside": 3
}
},
"completion": {
"message": "The newspaper edition is ready."
}
}
1.8.3. Root fields ↑
| Field | Required | Type | Meaning |
|---|---|---|---|
type |
Yes | string | Must be indesign.document. |
version |
Yes | integer | Must be 1. |
path |
Yes | string | Absolute path ending in .indd. |
ifExists |
No | string | open or error; defaults to open. |
ifMissing |
No | string | create or error; defaults to create. |
showWindow |
No | boolean | Open a layout window for a newly opened or created document; defaults to true. |
units |
No | string | pt, mm, cm, or in; defaults to pt. |
document |
Conditional | object | Required when a missing document may be created. Ignored when the file already exists. |
completion |
No | object | Optional { "message": "..." } success notification. |
extensions |
No | object | Package-defined extension data. |
1.8.4. Creation fields ↑
| Field | Required | Type | Meaning |
|---|---|---|---|
pageSize |
Yes | object | Positive width and height in the root units. |
orientation |
No | string | as_specified, portrait, or landscape; defaults to as_specified. Portrait/landscape swaps the supplied dimensions when necessary. |
pages |
No | integer | Initial page count from 1 through 10000; defaults to 1. |
facingPages |
No | boolean | Use two-page spreads; defaults to false. |
binding |
No | string | left_to_right or right_to_left; defaults to left_to_right. |
margins |
No | object | Non-negative top, bottom, inside, and outside; omitted values are zero. |
columns |
No | object | count from 1 through 100 and non-negative gutter. Gutter must be zero or omitted for one column. |
bleed |
No | object | Non-negative top, bottom, inside, and outside; omitted values are zero. |
The destination directory must already exist. If the file is already open, the plugin reuses that document rather than opening a duplicate. Use a later menu or palette action returning indesign.operations to place content in the newly active layout.
1.9. indesign.operations ↑
indesign.operations requests one or more native document changes. The complete response is validated before execution begins.
{
"type": "indesign.operations",
"version": 1,
"units": "pt",
"coordinateSpace": "page",
"transaction": {
"name": "Place scheduled article",
"cancelPolicy": "keep"
},
"commands": [],
"completion": {
"message": "The article was placed successfully."
}
}
1.8.3. Root fields ↑
| Field | Required | Type | Meaning |
|---|---|---|---|
type |
Yes | string | Must be indesign.operations. |
version |
Yes | integer | Must be 1. |
commands |
Yes | array | Ordered operation objects. |
units |
No | string | Default units: pt, mm, cm, or in. Defaults to pt. |
coordinateSpace |
No | string | Must be page in version 1. Defaults to page. |
transaction |
No | object | Operation name and cancellation policy. |
metadata |
No | object | Response-level package metadata. |
completion |
No | object | Optional success notification. |
extensions |
No | object | Package-defined extension data. |
Each command can override the root units value.
1.9.2. Transaction object ↑
{
"name": "Place article: New harbour project",
"cancelPolicy": "keep"
}
| Field | Required | Type | Meaning |
|---|---|---|---|
name |
No | string | Human-readable transaction label. Version 1 accepts it as descriptive metadata. |
cancelPolicy |
No | string | keep or rollback. Defaults to keep. |
keep preserves commands completed before the user presses Escape during interactive placement.
rollback is reserved for fully reversible execution. The current implementation rejects rollback when the command list contains interactive user placement; use keep for interactive workflows.
1.9.3. Completion object ↑
{
"message": "The article was placed successfully."
}
The optional message string is shown after successful completion. Omit completion when no confirmation dialog is wanted.
1.10. Command execution and identity ↑
Commands execute in array order. Commands that create page objects can define temporary and persistent identifiers.
1.10.1. Common creation fields ↑
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Operation name. |
id |
No | string | Batch-local identifier, unique in this response. |
persistentId |
No | string | Package-owned identity stored on the InDesign object. |
page |
No | string | Currently only active is implemented. |
units |
No | string | Command-level unit override. |
metadata |
No | object | Metadata stored with the created object when supported. |
extensions |
No | object | Package-defined extension data. |
id exists only while one indesign.operations response is executing. Later commands can refer to an earlier id.
persistentId survives separate action invocations and document save/reopen. The plugin stores it together with the calling package name and namespace, preventing another package from finding the object merely by guessing the same ID.
Batch-local IDs must be unique within the response. Persistent IDs attached to create commands must also be unique within that response.
Use stable logical identifiers:
article:18425:headline
article:18425:body
article:18425:main_image
product:SKU-4821:price
Moving an object or changing its text must not change its persistent ID.
1.10.2. Target selectors ↑
A batch-local target is a string:
{
"op": "set_text",
"target": "headline",
"text": "Updated during the current operation"
}
A persistent target is an object:
{
"op": "set_text",
"target": {
"persistentId": "article:18425:headline"
},
"text": "Updated in a later action"
}
Persistent lookup must find exactly one compatible page item in the active document. No match and duplicate matches are both errors.
1.11. Geometry and placement ↑
Commands that create a page item use either bounds, or size together with place. These forms cannot be mixed.
1.11.1. Explicit bounds ↑
{
"bounds": {
"left": 0,
"top": 0,
"right": 320,
"bottom": 210
}
}
All values use the command or root units. right must exceed left, and bottom must exceed top.
create_text_frame can omit bottom to request automatic height. place_image always requires all four bounds.
1.11.2. Coordinate placement ↑
{
"size": [320, 210],
"place": [0, 0]
}
size is [width, height]. place is [left, top], relative to the top-left margin of the active page.
1.11.3. Interactive placement ↑
{
"size": [320, 210],
"place": "user"
}
The plugin pauses the command sequence, activates the ACF placement tool, and waits for a valid page click. The click becomes the top-left anchor. After placement, the next command runs. Several commands can request user placement and are handled in command-array order.
Escape cancels the uncompleted commands according to transaction.cancelPolicy.
The expanded object form for user placement is reserved but is not implemented in version 1. Use the exact string "user".
1.11.4. Relative placement ↑
{
"size": [320, 80],
"place": {
"mode": "relative",
"relativeTo": "headline",
"edge": "bottom",
"offset": [0, 8],
"referenceBounds": "frame",
"matchWidth": false
}
}
| Field | Required | Type | Meaning |
|---|---|---|---|
mode |
Yes | string | Must be relative. |
relativeTo |
Yes | string | Batch-local id of an earlier create command. |
edge |
Yes | string | top, right, bottom, or left. |
offset |
No | array | [x, y]; defaults to [0, 0]. |
referenceBounds |
No | string | frame (default), or content for the actual content inside a referenced graphic frame. |
matchWidth |
No | boolean | For create_text_frame, replace the requested width with the selected reference bounds' width. |
Relative placement uses the referenced object's final bounds after text composition, column setup, and automatic height. The new item inherits the referenced item's page.
Using referenceBounds: "content" together with matchWidth: true makes an
image caption align with the photograph inside a wider image canvas.
bottom: new top touches referenced bottom; left edges align.top: new bottom touches referenced top; left edges align.right: new left touches referenced right; top edges align.left: new right touches referenced left; top edges align.
The offset is applied after edge alignment.
1.12. Implemented operation names ↑
The current native executor implements:
define_paragraph_stylesdefine_character_stylesset_markdown_stylescreate_text_frameset_textplace_imageset_imagegroup_items
Other operation names reserved by design documents are not yet implemented. They produce a clear unsupported op error rather than being silently ignored.
1.13. define_paragraph_styles ↑
Creates missing paragraph styles or updates the supplied properties of existing styles.
{
"op": "define_paragraph_styles",
"mode": "add_missing",
"styles": [
{
"name": "News Headline",
"properties": {
"fontFamily": "Times New Roman",
"fontStyle": "Bold",
"pointSize": 28,
"leading": 30,
"spaceAfter": 6,
"alignment": "center"
}
}
]
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be define_paragraph_styles. |
mode |
No | string | add_missing or force_update; defaults to add_missing. |
styles |
Yes | array | One or more style definitions. |
extensions |
No | object | Package-defined extension data. |
Each entry in styles has a required non-empty name and a required properties object.
1.13.1. Paragraph-style properties ↑
| Property | Type | Meaning |
|---|---|---|
fontFamily |
string | Installed font family. Supply together with fontStyle. |
fontStyle |
string | Installed style within fontFamily. Supply together with fontFamily. |
pointSize |
number | Font size, greater than zero. |
leading |
number | Line leading, greater than zero. |
spaceBefore |
number | Paragraph space before. |
spaceAfter |
number | Paragraph space after. |
leftIndent |
number | Left paragraph indent. |
rightIndent |
number | Right paragraph indent. |
firstLineIndent |
number | First-line indent; a negative value creates a hanging indent. |
alignment |
string | Paragraph alignment value listed below. |
shading |
object | Paragraph background color and offsets. |
border |
object | Paragraph border color, weight, and offsets. |
Supported alignment values are:
leftcenterrightjustify_fulljustify_leftjustify_centerjustify_righttoward_spineaway_from_spine
Shading example:
{
"shading": {
"color": "Paper",
"tint": 100,
"offset": [3, 4, 3, 4]
}
}
Border example:
{
"border": {
"color": "Black",
"tint": 25,
"weight": 0.25,
"offset": [0, 0, 0, 0]
}
}
color can be Black, Paper, or the exact name of a document swatch. tint is from 0 through 100. weight must be greater than zero. An offset is [left, top, right, bottom]. Paragraph-style typographic measurements, border weights, and decoration offsets are always points; they do not inherit the operation envelope's units.
With add_missing, an existing style is left untouched. This lets a designer customize document styles while later package revisions add only newly introduced styles. With force_update, the supplied properties are reapplied to existing styles, and text already using those styles updates immediately.
Properties omitted during force_update are not reset; only supplied properties are changed.
spaceBefore, spaceAfter, leftIndent, and rightIndent cannot be negative. firstLineIndent may be negative to create a hanging indent. Style names must be unique within the command.
1.14. define_character_styles ↑
Creates missing character styles or updates the supplied properties of existing styles.
{
"op": "define_character_styles",
"mode": "add_missing",
"styles": [
{
"name": "News Strong Emphasis",
"properties": {
"fontStyle": "Bold Italic"
}
},
{
"name": "News Inline Code",
"properties": {
"fontFamily": "Menlo",
"fontStyle": "Regular",
"pointSize": 9
}
}
]
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be define_character_styles. |
mode |
No | string | add_missing or force_update; defaults to add_missing. |
styles |
Yes | array | One or more character-style definitions. |
extensions |
No | object | Package-defined extension data. |
1.14.1. Character-style properties ↑
| Property | Type | Meaning |
|---|---|---|
fontFamily |
string | Optional font-family override. If supplied, fontStyle is also required. |
fontStyle |
string | Face such as Bold or Italic. It can be supplied alone to inherit the paragraph style's family. |
pointSize |
number | Optional font-size override, greater than zero. |
strikethrough |
boolean | Enables or disables strikethrough. |
The modes behave like define_paragraph_styles. Inheriting the paragraph font while applying only fontStyle is normally the best choice for Markdown strong and emphasis decorations.
Each properties object must contain at least one supported property, and style names must be unique within the command.
1.15. set_markdown_styles ↑
Sets the document style-name mapping used by subsequent Markdown text operations. The mapping remains active for later Markdown blocks until another set_markdown_styles command replaces it.
{
"op": "set_markdown_styles",
"paragraphStyles": {
"paragraph": "News Body",
"paragraphIndented": "News Body Indented",
"heading1": "News Heading 1",
"heading2": "News Heading 2",
"bullet": "News Bullet",
"codeBlock": "News Code Block",
"codeBlockIndented": "News Code Block Indented",
"tableLeft": "News Table Left",
"tableCenter": "News Table Center",
"tableRight": "News Table Right"
},
"characterStyles": {
"strong": "News Strong",
"emphasis": "News Emphasis",
"strongEmphasis": "News Strong Emphasis",
"strike": "News Strike",
"inlineCode": "News Inline Code",
"tableHeader": "News Table Header"
}
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be set_markdown_styles. |
paragraphStyles |
Yes | object | Paragraph-role to style-name mapping. |
characterStyles |
Yes | object | Character-role to style-name mapping; may be empty. |
extensions |
No | object | Package-defined extension data. |
1.15.1. Paragraph roles ↑
| Key | Required | Meaning |
|---|---|---|
paragraph |
Yes | Ordinary paragraph style and fallback for unspecified paragraph roles. |
paragraphIndented |
No | Paragraph nested inside a Markdown list. |
heading1 … heading6 |
No | ATX heading levels # through ######. |
bullet |
No | Unordered list item. |
codeBlock |
No | Top-level fenced code block. |
codeBlockIndented |
No | Fenced code block nested inside a list. |
tableLeft |
No | Left-aligned table cell. |
tableCenter |
No | Center-aligned table cell. |
tableRight |
No | Right-aligned table cell. |
1.15.2. Character roles ↑
| Key | Meaning |
|---|---|
strong |
**bold** text. |
emphasis |
*italic* text. |
strongEmphasis |
Combined bold and italic text. |
strike |
~~strikethrough~~ text. |
inlineCode |
Inline backtick code. |
tableHeader |
Header-row text in a Markdown table. |
Referenced styles must already exist in the active document. Define them earlier in the same command list when necessary.
The current native Markdown subset supports paragraphs, ATX headings, unordered lists, strong/emphasis/strong-emphasis/strikethrough, inline code, fenced code blocks, links, standalone images, and pipe tables. Ordered lists and embedded HTML are not part of version 1.
1.16. create_text_frame ↑
Creates a text frame, inserts plain text or Markdown, optionally applies a paragraph style, and can calculate its height from the composed text.
{
"op": "create_text_frame",
"id": "headline",
"persistentId": "article:18425:headline",
"page": "active",
"size": {
"width": 523.276
},
"place": [0, 220],
"text": "A headline that can wrap",
"style": {
"paragraphStyle": "News Headline"
}
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be create_text_frame. |
id |
No | string | Batch-local identifier. |
persistentId |
No | string | Persistent document identity. |
page |
No | string | Currently only active; defaults to the active page. |
units |
No | string | pt, mm, cm, or in. |
bounds |
Conditional | object | Explicit rectangle; omit bottom for automatic height. |
size |
Conditional | array or object | Frame size. Use together with place. |
place |
Conditional | array, string, or object | Coordinate, user, or relative placement. |
text |
No | string or object | Plain Unicode text or Markdown definition. |
style |
No | object | Plain-text paragraph style. Not valid together with Markdown text. |
columns |
No | integer | Positive column count; defaults to 1. |
gutter |
Conditional | number | Non-negative inter-column distance. Required only when columns is greater than 1. |
overflow |
No | object | Automatic-page continuation settings. |
metadata |
No | object | Metadata stored with the frame when supported. |
extensions |
No | object | Package-defined extension data. |
Supply either bounds, or both size and place. Do not combine these geometry forms.
1.16.1. Plain text ↑
{
"text": "Unicode text: blåbær, français, 日本語",
"style": {
"paragraphStyle": "News Body"
}
}
All text-placement operations use Unicode. A style object accepts only paragraphStyle.
1.16.2. Markdown text ↑
{
"text": {
"format": "markdown",
"content": "## Native Markdown\n\nText with **strong emphasis**.",
"sourcePath": "/Volumes/Editorial/articles/article-18425.md"
}
}
format must be markdown; content is required. Optional sourcePath establishes the base directory for relative Markdown image paths. Do not combine Markdown text with the plain-text style field; Markdown uses the active mapping from set_markdown_styles.
1.16.3. Fixed and automatic height ↑
An array size is always fixed:
{
"size": [320, 90],
"place": [0, 0]
}
An object size can omit height:
{
"size": {
"width": 320
},
"place": [0, 0]
}
With no height, InDesign composes the text and adjusts the frame bottom. Explicit bounds request the same behavior when bottom is omitted:
{
"bounds": {
"left": 0,
"top": 0,
"right": 320
}
}
Automatic height is useful for headlines, ingress blocks, interactive placement, and relative layouts because the next relative item uses the resulting final bounds. It is not valid with auto_add_pages.
1.16.4. Columns and gutter ↑
{
"columns": 3,
"gutter": 12
}
For a multi-column text frame, the supplied width means the complete article width. The plugin calculates the individual column width as:
column width = (total width - gutter × (columns - 1)) / columns
Do not supply gutter when columns is 1. When columns is greater than 1, gutter is required.
1.16.5. Automatic page continuation ↑
{
"op": "create_text_frame",
"id": "article_body",
"persistentId": "article:18425:body",
"page": "active",
"bounds": {
"left": 0,
"top": 0,
"right": 523.276,
"bottom": 763.89
},
"text": {
"format": "markdown",
"content": "A long Markdown article..."
},
"overflow": {
"mode": "auto_add_pages",
"maxAddedPages": 50
}
}
| Overflow field | Required | Type | Meaning |
|---|---|---|---|
mode |
Yes | string | Must be auto_add_pages. |
maxAddedPages |
No | integer | Safety limit from 1 through 9998; defaults to 100. |
The first frame must have a fixed height on the active page. It can use explicit bounds or a fixed size with coordinate placement. If text is overset, the executor adds pages, creates frames inside each new page's margins, and threads them together until the story fits or the safety limit is reached.
Continuation frames derived from persistentId: "article:18425:body" receive identities such as:
article:18425:body:continuation:1
article:18425:body:continuation:2
Automatic page continuation cannot be combined with relative placement, interactive placement, or automatic height. Coordinate placement with a fixed width and height is supported.
1.17. set_text ↑
Replaces the contents of an existing text frame.
{
"op": "set_text",
"target": {
"persistentId": "article:18425:headline"
},
"text": "Updated headline",
"fitHeight": true
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be set_text. |
target |
Yes | string or object | Earlier batch-local id, or { "persistentId": "..." }. |
text |
Yes | string or object | Plain Unicode text or Markdown definition. |
fitHeight |
No | boolean | Recompose and adjust the frame height after replacement. Defaults to false. |
extensions |
No | object | Package-defined extension data. |
Markdown replacement uses:
{
"format": "markdown",
"content": "Updated **Markdown** content"
}
Unlike create_text_frame, the current set_text Markdown object accepts only format and content; it does not accept sourcePath.
fitHeight: true changes the height but does not move the frame. This supports refreshing placed editorial content while respecting a designer's manual position changes.
1.18. place_image ↑
Creates an image frame and places a local image file. The source path must exist when the command is validated.
SVG compatibility: InDesign supports SVG placement, but its SVG renderer has a more limited feature set than modern browsers and dedicated SVG editors. Complex SVG files may appear incomplete whether they are placed manually or through the ACF plugin. This is an InDesign limitation, not a plugin limitation. Convert problematic SVG artwork to PDF for vector output, or to PNG or TIFF for raster placement.
1.18.1. Fixed frame ↑
{
"op": "place_image",
"id": "main_image",
"persistentId": "article:18425:main_image",
"page": "active",
"size": [320, 210],
"place": [0, 0],
"source": "/Volumes/Images/18425-main.jpg",
"fit": "fill_proportionally"
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be place_image. |
id |
No | string | Batch-local identifier. |
persistentId |
No | string | Persistent document identity. |
page |
No | string | Currently only active. |
units |
No | string | pt, mm, cm, or in. |
bounds |
Conditional | object | Fixed frame rectangle; all four sides are required. |
size |
Conditional | array or object | Fixed frame or content sizing. Use with place. |
place |
Conditional | array, string, or object | Coordinate, user, or relative placement. |
source |
Yes | string | Existing local file path. |
fit |
No | string | Fitting policy described below; defaults to none. |
canvas |
No | object | Wider layout canvas for an object-sized image. |
metadata |
No | object | Metadata stored with the frame when supported. |
extensions |
No | object | Package-defined extension data. |
Supported fit values are:
| Value | Behavior |
|---|---|
none |
Keep intrinsic placed size; used by content sizing. |
fit_proportionally |
Fit the entire image inside the frame without distortion. |
fill_proportionally |
Fill the frame proportionally; excess image area can be cropped. |
center |
Center the placed content without proportional fitting. |
Explicit bounds always require left, top, right, and bottom.
1.18.2. Image-sized frame ↑
An object size provides content-oriented sizing:
{
"size": {
"height": 210
},
"place": [0, 0],
"source": "/Volumes/Images/18425-main.jpg"
}
height is required. If width is omitted, the plugin derives it from the image's aspect ratio. If both are supplied, they define the displayed image size. fit must be omitted or none for object sizing.
1.18.3. Image in a layout canvas ↑
The optional canvas reserves a wider placement area while keeping the image itself smaller:
{
"op": "place_image",
"id": "main_image",
"size": {
"height": 210
},
"canvas": {
"width": 523.276,
"alignment": "center"
},
"place": [0, 0],
"source": "/Volumes/Images/18425-main.jpg"
}
| Canvas field | Required | Type | Meaning |
|---|---|---|---|
width |
Yes | number | Total layout width, at least as wide as the displayed image. |
alignment |
No | string | left, center, or right; defaults to center. |
canvas is valid only with object size. The canvas controls horizontal placement but is not an additional visible InDesign frame. Relative placement uses the canvas footprint by default. A relative text frame can use referenceBounds: "content" and matchWidth: true when it must align with the photograph rather than the canvas.
1.19. set_image ↑
Replaces the linked image in an existing persistent image frame while preserving the frame geometry.
{
"op": "set_image",
"target": {
"persistentId": "article:18425:main_image"
},
"source": "/Volumes/Images/18425-main-revised.jpg"
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be set_image. |
target |
Yes | object | Must be { "persistentId": "..." }. Batch-local strings are not accepted by the current implementation. |
source |
Yes | string | Existing local image path. |
extensions |
No | object | Package-defined extension data. |
The command updates the placed content, not the frame position. This is useful when refreshing an article from an editorial system after the designer has adjusted the layout.
1.20. group_items ↑
Creates a native InDesign group from page items created earlier in the same operation list.
{
"op": "group_items",
"id": "article_image_1_group",
"persistentId": "article:18425:image_group:1",
"items": [
"article_image_1",
"article_image_1_caption"
]
}
| Field | Required | Type | Meaning |
|---|---|---|---|
op |
Yes | string | Must be group_items. |
items |
Yes | array | At least two unique batch-local ids created by earlier create, image, or group commands. |
id |
No | string | Batch-local id assigned to the new group. Later relative placements can use it as relativeTo. |
persistentId |
No | string | Stable document identity for the group. |
metadata |
No | object | Package-defined metadata stored on the group. |
extensions |
No | object | Package-defined extension data. |
The child objects keep their own persistent ids. This allows an image and its caption to move together while later set_image and set_text commands still update the individual children.
1.21. Complete placement example ↑
This response defines document styles, establishes the Markdown mapping, places a centered image, and positions automatically sized text blocks relative to the object before them.
{
"type": "indesign.operations",
"version": 1,
"units": "pt",
"coordinateSpace": "page",
"transaction": {
"name": "Place article 18425",
"cancelPolicy": "keep"
},
"metadata": {
"articleId": "18425",
"source": "Editorial API"
},
"commands": [
{
"op": "define_paragraph_styles",
"mode": "add_missing",
"styles": [
{
"name": "News Headline",
"properties": {
"fontFamily": "Times New Roman",
"fontStyle": "Bold",
"pointSize": 28,
"leading": 30,
"spaceAfter": 4,
"alignment": "center"
}
},
{
"name": "News Body",
"properties": {
"fontFamily": "Times New Roman",
"fontStyle": "Regular",
"pointSize": 10,
"leading": 12
}
}
]
},
{
"op": "define_character_styles",
"mode": "add_missing",
"styles": [
{
"name": "News Strong",
"properties": {
"fontStyle": "Bold"
}
},
{
"name": "News Emphasis",
"properties": {
"fontStyle": "Italic"
}
}
]
},
{
"op": "set_markdown_styles",
"paragraphStyles": {
"paragraph": "News Body",
"heading1": "News Headline"
},
"characterStyles": {
"strong": "News Strong",
"emphasis": "News Emphasis"
}
},
{
"op": "place_image",
"id": "main_image",
"persistentId": "article:18425:main_image",
"page": "active",
"size": {
"height": 220
},
"canvas": {
"width": 523.276,
"alignment": "center"
},
"place": [0, 0],
"source": "/Volumes/Images/18425-main.jpg"
},
{
"op": "create_text_frame",
"id": "headline",
"persistentId": "article:18425:headline",
"size": {
"width": 523.276
},
"place": {
"mode": "relative",
"relativeTo": "main_image",
"edge": "bottom",
"offset": [0, 8]
},
"text": "Harbour expansion approved",
"style": {
"paragraphStyle": "News Headline"
}
},
{
"op": "create_text_frame",
"id": "body",
"persistentId": "article:18425:body",
"size": {
"width": 523.276
},
"place": {
"mode": "relative",
"relativeTo": "headline",
"edge": "bottom",
"offset": [0, 6]
},
"columns": 2,
"gutter": 12,
"text": {
"format": "markdown",
"content": "The council approved the **revised plan** today."
}
}
],
"completion": {
"message": "Article 18425 was placed."
}
}
1.22. Validation and error handling ↑
The executor validates the complete response before it starts changing the document. Validation covers:
- supported response type and version;
- required fields and exact field types;
- unknown or misspelled fields;
- supported operation names;
- valid units, enum values, and geometry;
- mutually exclusive geometry forms;
- unique batch-local IDs and valid relative references;
- available fonts and styles where required;
- existing source files for image placement;
- compatible transaction and interactive-placement options.
If validation fails, no command is executed. The error identifies the command index and field path, for example:
news_package.place_article: commands[3] (create_text_frame).gutter:
gutter cannot be specified when columns is 1
An error during execution is caught at the Adobe SDK boundary, written to the ACF InDesign log, and shown in a native dialog. Package code should still validate source data before returning operations—for example, resolve missing image paths before creating place_image commands.
1.23. Compatibility rules ↑
- Version 1 requires
version: 1; later contracts must use a new version number when semantics change incompatibly. - Consumers reject unknown fields, so package-specific additions belong under
extensions. - Commands execute in order and can refer only to earlier batch-local IDs.
- The exact implemented syntax in this article takes precedence over reserved forms mentioned in development notes.
- Preserve persistent IDs across package revisions so update actions can continue to locate existing document objects.
- Return native ACF JSON objects. Do not stringify the response unless interoperating with transitional code that explicitly requires it.
1.24. Related documentation ↑
- ACF Plugin for Adobe InDesign explains package discovery, menus, palettes, and the overall execution model.
- Ad hoc dialogs with open_dialog documents the JSON definition shared by modal dialogs and floating palettes.
- SQLite databases in ACF documents persistent package-side state such as article and image mappings.
