Outside APIs: declared origins, OAuth and published adapters
Call outside APIs from a module Block with each person's own key or OAuth sign-in, through declared origins and reviewed OpenAPI adapters, with keys kept on StillMade's servers.
Module Blocks can call any outside API with the account of the person running them. Three pieces work together:
- Declared origins.
permissions.networklists the exact https origins the
Block calls (up to 8), each with the key it needs. Code calls stillmade.net.fetch(url, {method, headers, body}) and gets a standard Response.
- OAuth. A key can be an OAuth sign-in through a reviewed StillMade OAuth
app: {scheme: "oauth2", app: "notion", label: "Notion workspace"}.
- Published adapters.
permissions.connectionslists approved OpenAPI
adapters by app ID (app:openapi:notion). Code calls stillmade.connections.call(appId, operationId, input) and gets the checked JSON result.
- Catalog apps.
permissions.connectionscan also list apps from
StillMade's connections catalog (Composio) by app ID, such as app:notion, app:gmail or app:google-sheets. Code calls stillmade.connections.call(appId, actionName, input) with the service's action name, and the action runs with the account the person connected. See catalog apps.
"permissions": {
"project": [],
"connections": ["app:openapi:notion"],
"network": [
"https://api.example.com",
{"origin": "https://api.airtable.com", "auth": {"scheme": "bearer", "label": "Airtable personal access token", "help": "https://airtable.com/create/tokens"}}
],
"filesystem": [],
"secrets": []
}const page = await stillmade.connections.call('app:openapi:notion', 'retrievePage', { path_page_id: input.pageId });
const response = await stillmade.net.fetch(`https://api.airtable.com/v0/${input.baseId}/Pages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ records: [{ fields: { Name: 'New row' } }] }),
});
if (!response.ok) throw new Error(`Airtable answered ${response.status}`);Catalog apps
const page = await stillmade.connections.call('app:notion', 'NOTION_CREATE_NOTION_PAGE', {
parent_id: input.parentPage,
title: input.title,
});- The first time a person runs the Block, StillMade asks them to connect their
account for the app (they sign in to the app in a new window) and to allow this Block to use it. Then, once per session for each action, it shows what the action does (reads, makes changes, sends, publishes or deletes, or that StillMade has not reviewed it) and its price.
- The action runs on StillMade's servers with that account. The Block gets the
checked result; the account and its tokens never reach it. People remove a Block's access in its network access panel.
- Credits are charged to the person running the Block: the action's reviewed
price, or 1 credit per action when StillMade has not reviewed it. If the app clearly fails, the credits are returned. If it may have carried out the action (for example a timeout), the credits are kept and the person is told to check the app before running it again.
- Actions reviewed as spending money on the person's account, or priced by
usage, run only as reviewed workflow Steps. Actions that look like moving money (payments, charges, transfers, orders, refunds) and payment apps such as Stripe or PayPal need a reviewed price first. For any other action StillMade has not reviewed, the approval tells the person it may change, send or pay for things in their account.
- Offline, answer calls in
tests/mocks.jsonunder
connections["app:notion/NOTION_CREATE_NOTION_PAGE"].
Keys belong to the people running the Block
Key schemes are bearer (the Authorization header), header (a named header, for example X-Api-Key), query (a named URL parameter) and oauth2. The first time a person runs the Block, StillMade asks for their key in its own dialog, or opens the service's sign-in in a new window. The key is stored encrypted for that person and that Block only (another Block declaring the same service gets nothing), added to requests on StillMade's servers, and never reaches the Block's code. People can replace or remove keys from the Block's Network access panel. Listing pages and previews show the declared services before anyone runs the Block.
What the host enforces
- Requests go only to declared origins of a saved Block the person can open, over
https on the standard port, to public internet addresses. Each connection is pinned to the address that was checked, and redirects are followed (up to 3) only to declared origins; a key is never sent across a redirect.
- Block code cannot set the key's header or parameter, host-owned headers
(Host, Cookie, Origin, Content-Length and similar) or a body on GET.
- Limits: 4 MiB per request, 8 MiB per response (compressed responses are
measured after decompression), 30 seconds per request including redirects, and 120 requests a minute per person and Block.
- A response that contains the key (raw, URL-encoded or base64) is withheld.
OAuth apps
A creator submits the provider's authorization and token endpoints, scopes, client ID and secret with POST /api/blocks/network/oauth-apps. StillMade reviews the app before any Block can use it. Sign-in uses the authorization code flow with PKCE; StillMade keeps the tokens and refreshes them.
Published OpenAPI adapters
Import an API's OpenAPI document as an API connection, then submit it with POST /api/blocks/network/adapters: {connectionId, slug, name, auth, headers}, where auth is the key it needs and headers are constant headers the API requires (such as a version header). The definition is copied when submitted. After StillMade approves it, the adapter has a public app ID, appears in GET /api/blocks/network/adapters, and any module Block can declare it. An approved adapter's origin also counts as reviewed for API connection read tests.
examples/module-notion-to-airtable reads a Notion page through a published adapter (with the person's Notion sign-in) and adds an Airtable row through a declared origin (with their own token).