The plugin api
The capability api a tool receives — api.fetch (host-allowlisted) and api.secret (declared, encrypted), per method with examples.
Every AI-plugin tool receives two arguments: (args, api). args is the JSON the
assistant filled in against your parameters schema; api is the capability object —
the entire surface a tool can reach. There is no require, no fetch, no filesystem,
no session — only what api exposes, and only what your manifest declared and the user
approved.
- Members:
api.fetch,api.secret - Enforced in: the main process (not on the plugin's honor)
New to plugins? Start with Create an AI plugin for the concepts and
manifest; this page is the runtime api reference.
Walkthrough
Call an authenticated API from a tool.
- Declare the host and the secret in your manifest's
permissions. - Read the secret with
api.secretand send it in a header. - Fetch the host with
api.fetchand return the parsed result.
// plugin.json
"permissions": {
"network": ["api.example.com"],
"secrets": ["API_TOKEN"]
}// index.js — one exported async function per tool
export async function lookup(args, api) {
const token = api.secret('API_TOKEN'); // declared → allowed
const res = await api.fetch(`https://api.example.com/things/${args.id}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (res.status !== 200) throw new Error(`upstream ${res.status}`);
return res.json(); // returned to the assistant
}Members
api.fetch(url, init?)
Make an HTTP request to a host your manifest declared in permissions.network. The call
runs in the main process, bound to the profile's session (its cookies, proxy, and
user-agent) — not in the worker.
How to use it
- Make sure the URL's host is listed in
permissions.network(exact hostname). - Pass
init(method / headers / body) if you need more than a GET. awaitthe result; read the body withres.text()orres.json().
Signature
await api.fetch(url, init?)Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | yes | http/https only. Its host must be an exact match in permissions.network. |
init.method | string | no | HTTP method (default GET). |
init.headers | Record<string, string> | no | Request headers. |
init.body | string | no | Request body (already serialised). |
Returns — Promise<{ status, headers, text(), json() }>:
| Member | Type | Description |
|---|---|---|
status | number | HTTP status code. |
headers | Record<string, string> | Response headers. |
text() | () => string | The body as text. |
json() | () => unknown | The body parsed as JSON (throws on invalid JSON). |
Errors & edge cases
- Throws if the URL's host isn't in
permissions.network, or the scheme isn'thttp/https— enforced before the request is ever made. - Host matching is an exact hostname compare (case-insensitive);
api.example.comdoes not coverwww.api.example.com. List each host you need. res.json()throws if the body isn't valid JSON — guard it or usetext().
Examples
// GET + JSON
const res = await api.fetch('https://catfact.ninja/fact');
return res.json().fact;// POST with a body and a header
const res = await api.fetch('https://api.example.com/notes', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ text: args.text }),
});
return res.status === 201 ? 'saved' : `error ${res.status}`;api.secret(name)
Read a secret the user entered for your plugin. Secrets are stored encrypted on the device, never written to logs, and never shown to the assistant — only your tool code sees the value.
How to use it
- Declare the name in
permissions.secrets(UPPER_SNAKE). - Call
api.secret('NAME')— it's synchronous. - Handle
null(declared, but the user hasn't filled it in yet).
Signature
const value = api.secret(name) // synchronousParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | Must be declared in permissions.secrets. |
Returns — string | null — the user's value, or null if they haven't set it.
Errors & edge cases
- Throws if
nameisn't declared inpermissions.secrets(no reaching for undeclared secrets). - Returns
nullwhen the secret is declared but empty — check for it and fail clearly.
Example
const token = api.secret('API_TOKEN');
if (!token) throw new Error('Set API_TOKEN in the plugin settings first.');What a tool cannot do
The api object is the whole surface. A tool runs in an isolated worker with no
access to Node, the filesystem, the browser session, other plugins, or any host you didn't
declare. api.page.* (read/act on the current tab) is coming and will require the page
permission plus a per-site grant.
See also: Create an AI plugin · SDK & aip CLI reference.