Manifest
Define the install manifest for a paperlesspaper Open Integration provider.
The Open Integration manifest is the install contract between your hosted provider and the paperlesspaper app.
The manifest requires three fields:
nameversionrenderPage
You may also define:
descriptioniconlanguagenativeSettingsformSchemasettingsPageconfigVariantsrequiredPermissionscapabilities
formSchema is useful when simple host-rendered fields are enough and you do not need a custom iframe UI.
nativeSettings is treated by the host as defaults, not forced overrides.
renderPage should usually be relative, such as ./render.html, so the same integration works locally and after deployment.
language is an array of supported language codes. If you declare it, add matching JSON object files at languages/<code>.json.
configVariants can describe known-good preview states and screenshots for catalogs or demo runners.
Example manifest:
{
"name": "Example Weather Board",
"version": "1.0.0",
"description": "Displays a compact daily weather view",
"renderPage": "./render.html",
"icon": "./assets/icon.png",
"language": ["de", "en"],
"nativeSettings": {
"color": "light",
"location": "Berlin",
"refreshSeconds": 900
},
"formSchema": {
"type": "object",
"required": ["location"],
"properties": {
"location": {
"type": "string",
"description": "Location name or postal code"
},
"refreshSeconds": {
"type": "integer",
"description": "Refresh interval in seconds",
"minimum": 300,
"maximum": 3600,
"default": 900
}
}
},
"configVariants": [
{
"color": "light",
"location": "Berlin",
"screenshots": {
"800x480": "./screenshots/weather-berlin-800x480-landscape.png",
"480x800": "./screenshots/weather-berlin-480x800-portrait.png"
}
}
]
}Settings fields
Put every normal user-editable setting in both nativeSettings and formSchema.properties. The host uses nativeSettings as defaults and uses formSchema to build controls.
Use the global color setting in nativeSettings when the render page supports the toolkit color themes. Do not add color to formSchema.properties; the host and CLI preview provide the global color control.
If a setting is edited by a custom settingsPage, keep it in nativeSettings but mark its schema property with "inStettingsPage": true. The spelling matches the current toolkit compatibility flag. The CLI preview uses it to avoid rendering a duplicate generated control.
Avoid duplicating the host language as a normal setting when possible. Render pages can read the host language from payload.meta.language and load localized copy with loadLanguageJson(payload).
Content push and status webhooks
An event-driven integration can declare capabilities.contentPush so its backend can send text or images to a saved paper and receive delivery status callbacks:
{
"settingsPage": "./settings.html",
"renderPage": "./render.html",
"requiredPermissions": ["paper:content:write", "paper:delivery:read"],
"capabilities": {
"contentPush": { "callbackPath": "/api/paper-status" }
}
}This is a manifest fragment; keep the required name and version fields in your full manifest. requiredPermissions describes the intended access; authorization comes from a user-approved connection to the saved paper.
For content push, the manifest, settings page, renderer, and resolved callback URL must share one HTTPS origin on port 443. Configured URLs must not contain credentials, query strings, or fragments. The host derives the callback from the saved manifest; changes to the resolved configuration require reconnecting.
Follow Webhooks & content push for the connection-grant handshake, backend examples, and render payload. See Webhook & API reference for endpoint and delivery semantics.
Local validation
The toolkit can validate the manifest and nearby files:
paperlesspaper-openintegration check ./config.jsonUse --json for machine-readable output in CI. The check verifies required fields, local renderPage and settingsPage files, declared languages/*.json files, formSchema shape, and default-exported api/*.js handlers.
CORS and hosting
Because the host fetches the manifest from the browser, your provider should:
- respond to
GET - ideally respond to
OPTIONS - send
Access-Control-Allow-Origin - send
Access-Control-Allow-Methods: GET, OPTIONS
If you echo the request origin instead of using *, also send Vary: Origin.
{
"headers": [
{
"source": "/config.json",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS" }
]
}
]
}