Connect a service
// Printify, Etsy, Shopify, Stripe, your own internal API — the whole decision in one page
Which route do I need?
There are three, and the right one depends on what the platform publishes. Check in this order:
- Does the platform publish a remote MCP server? Then add it in TOOLSETS & CONNECTORS → MCP CONNECTORS, or find it already sitting in CATALOG. You get ready-made tools, and it is the least work. Notion, Linear, Stripe, GitHub and Sentry are here.
- Does it publish only a local stdio MCP server? Put an agent on the SAFE CELL execution profile, choose STDIO (Safe Cell) in MCP CONNECTORS, and enter the command and exact arguments. StarNet runs it inside that agent's persistent Docker environment, never as an interactive host child.
- Otherwise it has a plain REST API. Paste its key in KEYS and your agent calls the API
directly with
web_request. This is the route for most of the long tail — including every print-on-demand and marketplace service we checked.
npx server. StarNet can run that server in a Safe Cell; the REST-key route remains
the lighter option when you do not want to maintain a container command.Route 1 — a remote MCP server
Open TOOLSETS & CONNECTORS (dock: ⇄ TOOLSETS).
- CATALOG — browse vetted servers. ▸ no setup connects instantly, API key takes a pasted token, OAUTH opens a browser sign-in. One click and its tools are live.
- MCP CONNECTORS — for anything not in the catalog. Give it a short id, paste the server's
Streamable-HTTP endpoint (usually ending
/mcp), and a bearer token if it needs one.
A local stdio command (npx …, uvx …) requires a named agent on the
SAFE CELL profile and a working Docker runtime. Its command uses exact argv with no shell. If Docker is
unavailable, the connector stays honestly disconnected; StarNet never falls back to spawning it on the
interactive host.
Route 2 — an API key + web_request
1. Paste the key
TOOLSETS & CONNECTORS → KEYS → PICK A PLATFORM. Choose your platform (or type any name), paste your key, and save. StarNet stores it locally and shows only the last four characters, ever.
Each key becomes a named environment variable — Printify becomes
PRINTIFY_API_KEY. Adding the platform's API docs URL is worth the ten seconds: it rides
into the agent's prompt, so it can look up the right endpoint instead of guessing.
2. Give the agent a dish
A key on its own does nothing. Tools come from placed props: the
dish grants the web toolset, which is what contains web_request. Place one in the agent's
bay from the build menu. (If you want the agent to use the key from a terminal instead, that is the
workbench prop — but for calling an API you do not need it.)
3. Ask for the thing you want
“List my Printify shops and tell me which products are unpublished.” The agent calls the API, you approve the call, and it reports back.
Your key is never shown to the model
This is the part worth understanding, because it is unusual. The agent never receives your key. It writes a placeholder naming the key, and StarNet substitutes the real value at the moment the request leaves your machine:
web_request
url: https://api.printify.com/v1/shops.json
headers: { "Authorization": "Bearer ${PRINTIFY_API_KEY}" }
The model sees only the name. A leaked transcript, a log, or a model provider never sees the value, and
the response comes back with any echoed secret scrubbed. Placeholders work in headers only — StarNet
refuses to send a URL the model wrote containing a secret, because URLs leak into browser history, proxy logs
and Referer headers.
Some APIs will not take a header and want the key as a query parameter instead. That still works, and the rule is unchanged: the agent names the key and the slot, and StarNet attaches the value itself, after the agent has finished composing the request.
web_request
url: https://api.example.com/v1/items
auth: { "key": "SOME_API_KEY", "in": "query", "name": "api_key" }
The agent still never handles the value, and it is attached only to the first request — if the server redirects somewhere else, the credential is not carried along.
Uploading files — mockups, images, print files
Upload endpoints take file bytes, and the same substitution idea covers them. The agent never pastes file contents into a request; it writes a reference to a file in its workspace, and StarNet reads and encodes the bytes at the moment the request is sent. Printify's image upload, for example:
web_request
url: https://api.printify.com/v1/uploads/images.json
method: POST
headers: { "Authorization": "Bearer ${PRINTIFY_API_KEY}" }
body: {"file_name":"mockup.png","contents":"${file:mockups/mockup.png}"}
For APIs that want a classic form upload (multipart/form-data) there is a
multipart parameter that attaches workspace files as form fields the same way. Either route:
the file must live in the agent's own workspace (it can never reach outside it), the bytes never pass
through the model, and the limit is 20MB per file / 25MB per request.
Letting it run while you are away
By default a key is usable only in watched sessions — you are at the station and approve the call. Scheduled routines, Night Shift, and agents messaged over Telegram cannot spend it.
To change that for one platform, tick “usable in scheduled & messaged runs” on that key's row in KEYS. It is per key, off by default, and revocable at any time — the next tool call sees the change immediately. Nothing else about unattended runs widens: they still cannot run shell commands, and they still cannot spend any key you have not ticked.
When it does not work
| What you see | What it means |
|---|---|
| “no enabled service key provides X_API_KEY” | The placeholder name does not match a key you saved, or that key is switched off. Check the exact env-var name shown on the key's row in KEYS. |
| “this run is unattended and ‘X’ is not approved for unattended use” | A scheduled or messaged run tried to spend a watched-only key. Tick the unattended box on that key, or run the task while you are at the station. |
The agent has no web_request |
No dish is placed in that agent's bay, or the web toolset is switched off in TOOLSETS. |
| HTTP 401 / 403 from the platform | The key reached the platform and the platform rejected it — wrong key, wrong scope, or the wrong header name. Check the platform's docs for the header it expects. |
| A connector sits red on “stdio” | The selected owner is no longer on SAFE CELL, Docker is unavailable, or the command failed inside the container. Restore that profile/runtime and reload; StarNet will not run it on the host. |
| “refusing private/loopback/intranet host” | Working as intended: agents cannot reach your local network or cloud metadata endpoints. |
| “the page did not finish loading within 30s” (browser) | The site stalled the agent's browser — often a slow origin or a bot challenge. The load is
cancelled so the browser stays usable; try browser.get_text to see what did load. For a
platform you have a key for, its API via web_request is the more reliable route than
reading its web pages. |
| A public page returns 403 to the agent | Some sites (Etsy among them) refuse automated browsers and direct fetches outright. StarNet does not disguise its requests to get around that. Use the platform's API instead. |
What StarNet will not do
- Show your key to the model, write it to a file, or send it anywhere except the platform you named.
- Forward your credential across a redirect to a different host.
- Spawn a local MCP child directly in your interactive host session; stdio is Safe-Cell-only.
- Spend a key in an unattended run you did not explicitly approve for that.