Skip to main content

Blocks and extension targets

A block is how an app shows something inside ARC without sending markup. The app (or ARC, from a layout in the app's package) produces a tree of fixed components. ARC checks the tree, resolves every record link in the viewer's own scope, and each ARC screen draws it with its own components. Your app never sends HTML, styles or scripts, and never formats money or dates.

Where a block can appear​

Declare each place in your manifest's extensions list, one entry per place. The place is the target.

TargetSurfaceHow it is drawnOpened on
admin.order.details.blockAdmin consoleNative slot: an iframe url, or a layout / blockEndpointThe order
admin.product.details.blockAdmin consoleNative slot: an iframe url, or a layout / blockEndpointThe product
admin.app.homeAdmin consoleNative slot: an iframe url, or a layout / blockEndpoint—
admin.order.details.actionAdmin consoleIframe urlThe order
admin.print.actionAdmin consoleIframe url—
admin.order.index.selection-actionAdmin consoleIframe url, with resourcesThe selected rows
buyer.account.blockBuyer app, portalBlock—
buyer.cart.blockBuyer app, portalBlock—
buyer.header.badgeBuyer app, portalBlock (one line)—
buyer.order.details.blockBuyer app (Track order), portal (order page) — not drawn yetBlockThe order
product.details.app_blockBuyer app, portalBlockThe product
storefront.footer.blockPortal, signed-out visitors tooBlock—
sales.order.blockSales appBlockThe order
sales.customer.blockSales appBlockThe customer
sales.visit.actionSales appNative button through the app proxyThe visit

A block entry carries exactly one of:

  • blockEndpoint: a path on your verified origin (appUrl). ARC POSTs a signed request to it and you answer with the component tree.
  • layout: a file blocks/<name>.json in your app's package. ARC renders it itself; you need no server.

A native admin slot (admin.order.details.block, admin.product.details.block, admin.app.home) carries exactly one of url (your own page in a sandboxed iframe), layout or blockEndpoint. The other admin targets take only a url.

Not drawn yet. The console does not draw a layout or blockEndpoint on a native admin slot yet. A manifest may declare one, but the slot shows nothing for it until native drawing ships. A url entry draws as before.

A buyer block opened on a record checks that record against the viewer: a buyer only ever gets blocks for their own orders.

Components​

A block answer is { "title"?: string, "components": [...] }. Each component has a type.

TypeFieldsNotes
texttext, variant? (body, heading, caption), tone?
numbervalue, label?, unit?Never money
moneyamount, currencyCode, label?amount is a decimal string such as "1250.50"; ARC formats it
ratingvalue, max? (1–10, default 5), label?0 ≤ value ≤ max
actionlabel, path, tone?path goes through the app proxy
entityRefref: { type, id }, label?customer, order or visit; dropped if the viewer cannot see it
listitems, label?Holds the components above; lists do not nest
tablevariant, columns, rows, label?Top level only
formformKey, submitLabel, fields, label?Admin console only, from a layout only

Tables​

Not drawn yet. ARC accepts tables today, but no screen draws them yet. A block made only of tables shows nothing until table drawing ships.

{
"type": "table",
"variant": "steps",
"columns": [
{ "key": "step", "label": "Step", "kind": "text" },
{ "key": "at", "label": "When", "kind": "date" }
],
"rows": [
{ "state": "done", "cells": [{ "key": "step", "text": "Order placed" }, { "key": "at", "date": "2026-11-10" }] },
{ "state": "current", "cells": [{ "key": "step", "text": "Dispatched" }, { "key": "at", "date": "2026-11-11T11:05:00.000Z" }] },
{ "state": "upcoming", "cells": [{ "key": "step", "text": "Delivered" }, { "key": "at" }] }
]
}
  • variant is grid (plain rows) or steps (a journey, one row per step).
  • Each column has a key, a label and a kind: text or date. At most 6 columns.
  • Each row has cells. A cell's key names a column, and its value matches that column's kind: text in a text column, date in a date column. A cell may be empty. At most 20 rows.
  • In steps, every row has a state: done, current or upcoming. The first text column is the step's label and the first date column its time. A grid row has no state.
  • Keys are 1–63 lowercase letters, digits or underscores, starting with a letter.
  • A table counts as 1 + rows components toward the 50-component limit.

Dates​

A date cell holds one of two things:

  • A day: YYYY-MM-DD, a real calendar date. Drawn as a date only, never shifted by a time zone. Use it for things that happen on a day, such as a packing date.
  • A moment: a full ISO date-time with Z or an offset, such as 2026-11-11T11:05:00.000Z (what Date.toISOString() gives). Drawn as date and time in the viewer's time zone.

Anything else is refused. Never send a formatted date; each ARC screen formats it.

Forms​

A form lets an admin edit some of your app's own custom fields on the record the block is open on.

{
"type": "form",
"formKey": "driver",
"submitLabel": "Save driver",
"fields": [
{ "key": "driver_name", "label": "Driver name", "kind": "text", "value": "Sam Lee" },
{ "key": "driver_phone", "label": "Driver phone", "kind": "phone" }
]
}
  • Fields are text or phone, at most 6, each value at most 200 characters. A form with no fields is a single button.
  • Forms appear only in the admin console, and only from an ARC-hosted layout. A blockEndpoint answer that holds a form is refused, and the whole block is hidden.
  • A form writes only your app's own custom fields. It never sets a price, a total, a date or a status on an order.

Limits​

LimitValue
Components in one block (list items, table rows and form fields count)50
Answer size32 KB
Time ARC waits for your blockEndpoint3 s
Text500 characters
Label80 characters

A block that breaks a rule, times out or fails is hidden. ARC never shows half a block, and an older app build skips a component type it does not know.

Apps with no server​

A manifest with no appUrl, no extension url or blockEndpoint, no proxyPaths and no webhooks is server-less: everything it shows comes from layouts in its package. Such a manifest may leave out redirectUrls. Every other manifest needs 1 to 10. Which apps may publish a server-less manifest is decided when the version is published.