-
Notifications
You must be signed in to change notification settings - Fork 32
Integrations
How user-configured webhooks, other extensions, scripts, and AI agents connect to Save In. External integrations are opt-in: webhooks are off by default, and extension-initiated downloads are denied unless the user allows the caller's extension ID. Save In listens on browser.runtime.onMessageExternal for other extensions and onMessage for same-extension callers, and — experimentally — registers WebMCP tools.
Extension IDs are platform-specific:
- Chrome Web Store:
jpblofcpgfjikaapfedldfeilmpgkedf - Firefox:
{72d92df5-2aa0-4b06-b807-aa21767545cd}
The options page shows the ID of the installed build. Other extensions must send to the ID for the browser they are running in.
User-configurable recipes are collected on this page. If you maintain another extension, use the separate extension integration guide.
Save In can send a versioned JSON notification to a user-configured HTTPS endpoint after a direct, non-private Save In download starts. Webhooks are off by default and do not run for automatic Page Sources saves, ordinary browser downloads, or external Download API requests. See the dedicated Webhooks guide for setup, payload fields, delivery guarantees, privacy limits, and troubleshooting.
Push a URL into the routing/rename pipeline. PING first to negotiate the version.
Before sending DOWNLOAD, paste the calling extension's exact runtime ID under Save In → Advanced → External integrations → Approved extensions and select Allow. This is not Save In's destination ID shown above. Approved IDs appear as removable rows; a bulk line editor remains available under Advanced: edit IDs as text. The allowlist is empty by default; an unlisted caller receives UNAUTHORIZED before Save In resolves the active tab or starts a download. An integration should display its own browser.runtime.id so the user can copy it. Firefox users can also find IDs in about:debugging#/runtime/this-firefox; Chrome users can enable Developer mode on chrome://extensions.
// Choose the ID for the browser running the calling extension.
const ID = "jpblofcpgfjikaapfedldfeilmpgkedf"; // Chrome
// const ID = "{72d92df5-2aa0-4b06-b807-aa21767545cd}"; // Firefox
// Discover the version and capabilities
await browser.runtime.sendMessage(ID, { type: "PING" });
// -> { type: "PONG", body: { version: 1, capabilities: [...] } }
// Save a URL
await browser.runtime.sendMessage(ID, {
type: "DOWNLOAD",
body: {
url: "https://example.com/pic.jpg",
comment: "foo", // optional; matched by `comment:` rules
info: {
// all optional
pageUrl: "https://example.com/",
srcUrl: "https://example.com/pic.jpg",
selectionText: "…",
},
},
});
// -> { type: "DOWNLOAD", body: { status: "OK", version, url } }
// | { type: "DOWNLOAD", body: { status: "ERROR", error, message, version } }-
errorisUNAUTHORIZED,BAD_REQUEST, orINVALID_URL; unknown message types returnUNKNOWN_TYPE. - Treat
UNAUTHORIZEDas a request for user configuration, not as a transient failure to retry repeatedly. - Only
http(s),ftp,data, andblobURLs are accepted. - The download runs through the same routing rules,
:variables:, Referer feature, auto-retry, history, and notifications as a context-menu save.status: "OK"means accepted, not finished.
For a non-private rejected request, Save In shows an External download blocked notification. Clicking it opens Options, where Advanced → External integrations → Pending approval lists the caller ID, attempt count, request kind, and last-seen time. Select Approve to append that exact caller ID to the approved list and clear the rejection. The review list keeps at most 20 callers and never stores the rejected URL. Private-window rejections are neither recorded nor notified.
Callers that cannot construct a dynamic URL can explicitly target the active tab. Check that PING advertises active_tab, then send:
await browser.runtime.sendMessage(ID, {
type: "DOWNLOAD",
body: {
version: 1,
target: "activeTab",
comment: "my-extension",
},
});Save In prefers the originating tab when the message came from a tab. Messages sent by another extension's background context use the active tab in the last-focused browser window. An explicit url takes precedence when both url and target are present.
Firefox only.

-
Find Foxy Gestures' own Extension ID in
about:debugging#/runtime/this-firefox, paste it under Save In's Approved extensions, and select Allow. -
Foxy Gestures → options →
User Scripts→ new gesture. -
Use this script (the destination ID is Save in's Mozilla Addons ID):
const source = data.element.mediaInfo && data.element.mediaInfo.source; if (source) { browser.runtime.sendMessage("{72d92df5-2aa0-4b06-b807-aa21767545cd}", { type: "DOWNLOAD", body: { url: source, info: { pageUrl: `${window.location}`, srcUrl: source }, comment: "foo", }, }); }
-
Assign a gesture to the script.
comment is targetable in routing rules:
comment: foo
into: ./from-foxy/:filename:
Firefox only. Gesturefy has a built-in cross-add-on command, so no user script is required.
-
Find Gesturefy's own Extension ID in
about:debugging#/runtime/this-firefox, paste it under Save In's Approved extensions, and select Allow. -
Open Gesturefy's settings and create or edit a gesture.
-
Select Send message to other add-on as its command.
-
Set Add-on ID to
{72d92df5-2aa0-4b06-b807-aa21767545cd}. -
Set Message to:
{ "type": "DOWNLOAD", "body": { "version": 1, "target": "activeTab", "comment": "gesturefy" } } -
Turn Parse message on, save the gesture, and try it on an ordinary web page.
Route these saves independently with:
comment: gesturefy
into: ./from-gesturefy/:filename:
The command cannot run on Firefox-restricted pages such as about: pages or Mozilla's add-on site.
Firefox only. Tridactyl can bind an Ex command that messages Save In from its background context. First find Tridactyl's own Extension ID in about:debugging#/runtime/this-firefox, paste it under Save In's Approved extensions, and select Allow. Then run these two commands in Tridactyl's command line:
:command savein jsb browser.runtime.sendMessage("{72d92df5-2aa0-4b06-b807-aa21767545cd}", {type: "DOWNLOAD", body: {version: 1, target: "activeTab", comment: "tridactyl"}})
:bind ,s savein
Press ,s to route and save the active page. Choose another key sequence if that binding is already in use. For persistent dotfile configuration, put the same commands without their leading : characters in tridactylrc.
Route these saves independently with:
comment: tridactyl
into: ./from-tridactyl/:filename:
Like other WebExtensions, Tridactyl cannot invoke this binding from browser-restricted pages.
Read the schema, validate paths/rules, and apply config — the basis for scripted and AI-assisted setup.
-
GET_SCHEMA→{ version, options: [{ name, type, default, description }] }. Read-only. -
VALIDATE{ paths?, filenamePatterns? }→{ pathErrors, ruleErrors, menuPreview }. Dry-run, read-only. Since Save In 4.1, traces additionally report each rule'soutcomeand the selected rule'sselectedOutcomeandselectedTabActionforexclude: trueandafter: close-tabrules. -
GET_KEYWORDS→ path variables, routing matchers, since Save In 4.1 the routing action clauses (exclude: true,after: close-tab), automatic-source matchers, and supported source kinds. -
GET_GRAMMARS→ the EBNF, semantic constraints, option name, and examples for the directory and unified routing languages. Read-only. -
GET_CONFIG→ the complete saved configuration in apply-ready form. Same-extension only — not reachable viaonMessageExternal. -
APPLY_CONFIG{ config: { name: value } }→{ applied, rejected }. Validated against the schema (unknown keys and type mismatches rejected). Same-extension only — not reachable viaonMessageExternal.
In a WebMCP-capable browser, the options page registers seven tools an in-browser agent can call.
| Tool | Purpose | Input |
|---|---|---|
save_in_get_schema |
List every option with its type, default, and description | {} |
save_in_get_config |
Read the complete saved configuration in apply-ready form | {} |
save_in_list_vocabulary |
List variables, matchers, automatic matchers, and source kinds | {} |
save_in_get_grammars |
Read both editable configuration grammars | {} |
save_in_validate_config |
Dry-run paths/rules and optionally trace representative input | { paths?, filenamePatterns?, info?, automaticCandidate? } |
save_in_apply_config |
Validate and store a partial configuration | { config: { optionName: value } } |
save_in_download |
Save a URL through the normal routing/rename pipeline | { url, pageUrl?, comment? } |
- In Chrome, enable
chrome://flags/#enable-webmcp-testingand relaunch. Origin-trial builds can use their trial enrollment instead. - Open Save In's options page and keep the tab open.
- Under Advanced → External integrations → WebMCP, confirm
Active — 7 tools registered. - Point your in-browser agent at the tab.
Ask in plain language — e.g. "sort my downloads into folders by website and date, number each file, and notify me only on failures." The recommended flow is:
save_in_get_schemasave_in_get_configsave_in_list_vocabularysave_in_get_grammarssave_in_validate_config- Fix any
pathErrorsorruleErrors save_in_apply_config
Example validation input for a positive fileext trace:
{
"filenamePatterns": "fileext: jpg\ninto: gallery/:sourcedomain:/:filename:",
"info": {
"srcUrl": "https://example.test/photo.jpg",
"filename": "photo.jpg",
"pageUrl": "https://example.test/gallery"
}
}Use srcUrl for fileext matching. If the sample only has url, use the urlfileext matcher. A successful trace reports the selected rule and the expanded, sanitized final path.
save_in_apply_config is partial: omitted options stay unchanged; omission does not delete a stored key. To restore an option, apply the default returned by save_in_get_schema. Unknown options, malformed tool arguments, and type mismatches are rejected.
For manual debugging, document.modelContext.executeTool(tool, args) expects args as a JSON string. Chrome may also return structured results as JSON text, so debugging clients should decode either form. Normal agents handle this transport detail themselves.
The tools are annotated so agents can distinguish read-only operations from config/download mutations and inputs that may contain untrusted page data. save_in_get_config returns the complete saved configuration, including destinations, routing rules, approved extension IDs, and webhook details. Save In adds no separate consent step; the browser or agent applies its normal access and confirmation policy. The API is still subject to change and the tools remain available only while the options tab is open.
- The browser can deliver
onMessageExternaldiscovery requests from installed extensions, but Save In checks the browser-authenticatedsender.idagainst the user's exact allowlist before resolving an active tab or triggering a download. - The allowlist is empty by default. Only add extensions you trust to choose URLs and initiate downloads through Save In's configured routing and fetch behavior.
- Rejected non-private callers appear in a bounded local review list; Save In stores only caller ID, request kind, count, and time—not the rejected URL. Private-window rejections leave no review item or notification.
-
APPLY_CONFIGis deliberately internal-only. - WebMCP tools can read the complete configuration, apply settings, and start downloads while Options is open. Their annotations are browser hints, not a Save In authorization prompt.
- No
externally_connectableblock, so web pages can't message Save in directly — only extensions can.