Advanced HTML Settings
Build a custom iframe settings UI for a paperlesspaper Open Integration.
The settings page is the optional iframe-based part of an Open Integration. Use it when formSchema is not enough and you need a richer configuration UI, previews, or an external auth flow.
When to use a settings page
Use a settings page when your integration needs:
- custom form layout or validation
- live previews or contextual help
- multi-step configuration
- OAuth or other redirect-based account linking
- connecting an integration backend for content push and delivery webhooks
If plain fields are enough, prefer formSchema alone because it keeps the provider simpler.
When a custom settings page owns a field, keep the value in nativeSettings and add the field to formSchema.properties with "inStettingsPage": true. The spelling matches the current toolkit compatibility flag. The CLI preview uses it to avoid showing the same setting twice in the generated form.
Runtime model
The settings UI runs inside an iframe embedded by the paperlesspaper host app.
Communication happens through window.postMessage, so your page should:
- listen for host messages after load
- initialize its UI from the incoming payload
- send settings patches back to the host
- report iframe height when the layout changes
Host to plugin messages
The preferred protocol uses a structured message envelope:
type OpenIntegrationAppToPluginMessage =
| {
source: "paperlesspaper-app";
type: "INIT";
payload: {
settings: Record<string, unknown>;
nativeSettings: Record<string, unknown>;
paper?: { id?: string; kind?: string; organization?: string };
device: { deviceId?: string; kind?: string };
app: { language?: string };
};
}
| {
source: "paperlesspaper-app";
type: "CONNECTION_GRANT";
payload: { requestId: string; grant?: string; error?: string };
}
| {
source: "paperlesspaper-app";
type: "REDIRECT";
payload: {
redirectUrl: string;
tempToken: string;
};
};INIT is sent after the iframe loads and contains the current settings plus some host context.
paper.id identifies the saved paper. CONNECTION_GRANT answers a content push connection request with a short-lived, single-use grant or an error. Forward the grant to your own backend for exchange; keep permanent credentials server-side.
REDIRECT is optional and is used for OAuth-style flows where the host needs your provider to send the user through an external authorization step.
Plugin to host messages
Your settings page replies to the host with messages like these:
type OpenIntegrationPluginToAppMessage =
| {
source: "paperlesspaper-plugin";
type: "REQUEST_CONNECTION";
payload: { requestId: string };
}
| {
source: "paperlesspaper-plugin";
type: "UPDATE_SETTINGS";
payload: Record<string, unknown>;
}
| {
source: "paperlesspaper-plugin";
type: "SET_HEIGHT";
payload: { height: number };
}
| {
source: "paperlesspaper-plugin";
type: "INFO";
payload: Record<string, unknown>;
};UPDATE_SETTINGS sends a patch that the host merges into the stored integration settings.
REQUEST_CONNECTION asks the host to authorize a backend connection for a saved paper with the contentPush capability. Use a unique request ID of at most 100 characters and match it in the CONNECTION_GRANT reply. The host validates the iframe window and origin.
SET_HEIGHT lets the host resize the iframe to match your content.
INFO is optional and can be used for non-critical hints such as opening a connect flow.
Height and iframe sizing
The host sizes the iframe based on messages from your page. The example provider uses a simple pattern:
- measure
document.documentElement.scrollHeight - clamp it to a reasonable range
- send
SET_HEIGHT - repeat after state or layout changes
This keeps the settings UI compact while avoiding clipped controls.
Security recommendations
Because the page is cross-window and can be cross-origin, keep the message bridge strict:
- validate the parent window and an explicitly allowed host origin before trusting a message
- store
event.sourceand reply to that window only - use a strict
targetOrigininstead of*when replying - treat incoming settings and redirect data as untrusted input
- keep permanent connection and callback tokens out of settings, URLs, render payloads, and browser responses
Content push connection flow
For webhook-driven content, wait for INIT with a saved paper.id, send REQUEST_CONNECTION, then forward the matching CONNECTION_GRANT to your backend. This uses its own grant exchange and does not require the REDIRECT flow below.
The grant expires after five minutes and can be used once. An error is returned as payload.error; if the paper or provider configuration has not been saved, save it and reconnect. Your backend receives a paper-scoped content token and a separate callback token after exchange.
Follow Webhooks & content push for the complete setup and Webhook & API reference for status callbacks and revocation.
Redirect and OAuth-style flows
If the host sends REDIRECT, your settings page can start an external auth flow.
The example provider builds a URL to its own helper page, passes along the host-provided redirectUrl and tempToken, and then redirects the user back to the host after approval.
In practice the host looks for query parameters such as:
tempTokensettings
The returned settings value is usually a JSON string that the host merges into stored integration settings.
Practical constraints:
- keep the returned payload small
- URL-encode it correctly
- do not rely on query parameters for sensitive long-term secrets
Implementation pattern
A good baseline flow for a React settings page is:
- wait for the first host message
- capture host origin and host window
- parse
INITinto local component state - render your UI
- send
UPDATE_SETTINGSwhen state changes - send
SET_HEIGHTafter render and after layout changes - optionally react to
REDIRECTby exposing a connect button
Checklist
Before shipping a settings page, verify all of these are true:
- it handles
INIT - it can optionally handle
REDIRECT - it sends
UPDATE_SETTINGS - it sends
SET_HEIGHT - it replies to the correct host origin and window
- it still works if the host sends existing stored settings on first load
paperlesspaper-openintegration dev ./config.jsonembeds the page and receives settings updates
Related guide
For the full provider model, manifest, and render-page contract, go back to Open Integrations.