Tools & connections
Think of a key and the machine it opens. The key on its own does nothing; the machine on its own
won't start. A connection (credentials) is the key — the login that proves you're allowed into
Stripe, HubSpot, your own API. A tool (tools) is the machine — one concrete action that turns
the key and does something: create a contact, fetch an invoice, post an update.
That split is the whole page. Your assistant runs machines. It never holds keys.
The verbs
Two areas, and they always work in the same order: find (or set up) the connection, then build or run the tool that uses it.
tools — the action
| Verb | Scope | What it does |
|---|---|---|
tools.use | run | Runs one saved tool in Nirvai's cloud. Needs a session. |
tools.<alias> | run | The same run, addressed directly — every saved tool is callable by its own alias. |
tools.describe_schema | read | The grammar for authoring a tool. Pure documentation, no side effects. |
tools.propose | create | Drafts one or many tools onto a review page. Creates nothing. |
tools.create | create | Creates the tools for real, each bound to a connection that already exists. |
tools.edit | write | Changes a tool you already have — its address, its parameters, or which connection it uses. |
Where you finish this — the tool page
tools.propose drafts; you create. The link opens /external/tools/… inside Nirvai, where you can
run the tool once to see what it returns before it exists at all.
- Test before you commit. A tool that returns the wrong thing is easier to spot here than after an agent has been using it for a week.
- You have to be signed in, and only you can open it. A proposal link is not shareable.
- It can't be clicked twice — re-opening shows Created ✓, and your assistant reads the new tool back from the feed rather than proposing again.
credentials — the login
| Verb | Scope | What it does |
|---|---|---|
credentials.list | read | Every connection you have, each with a stable id and whether it's authorized. |
credentials.search | read | The same list, narrowed by a query. |
credentials.describe | read | One connection's live state — kind, label, and whether it's authorized yet. |
credentials.describe_schema | read | Which fields each kind of connection needs, and which of them are secret. |
credentials.propose | create | Writes a step-by-step setup guide onto a page you open and complete. |
credentials.create | create | Creates a connection directly — only when you handed it the value yourself. |
credentials.edit | write | Changes a connection's non-secret settings, like its display name. |
Listing and inspecting your tools goes through the master tools instead —
nirvai_list_resources(kind="tools") for the catalogue, nirvai_describe(kind="tools", ref=…) for
one tool's live parameters. Always describe before you run: the parameters your assistant may fill
come from that answer, never from memory. Connections are addressed by their id, tools by their
alias or id — never by display name, because names collide.
Where you finish this — the connection page
credentials.propose sends field names only, never a value. The link opens
/external/credentials/… inside Nirvai, and you type the secret there, on Nirvai's own page.
- The secret is yours alone. It never enters your assistant's context, its result, or the feed.
- A provider login finishes in your browser. The action can come back as
needs_authwith a link; you complete the provider's own sign-in, then your assistant lists your connections again to check it worked. There is no signal it can wait on. - You have to be signed in, and only you can open it. A proposal link is not shareable, and re-opening a completed one shows Connected ✓ rather than making a second connection.
- Something wrong on the page? Hand back its copy-paste block and your assistant re-proposes — see the review page.
The secret rule
Your assistant cannot complete a connection. It proposes the field names — "a secret key goes here", "this one is the header name" — and hands you a link. You type the secret on Nirvai's own page. The value never enters your assistant's context, never appears in the result it gets back, and never lands in the Activity Feed. A secret value placed inside a proposal is refused outright. (The single exception is a value you deliberately hand over yourself to pass through once — a proposal never carries one.)
Connections that use a provider login can't be finished headlessly at all: the provider's own approval window has to open on your screen. Once it does, your assistant learns it worked the same way you would — by reading the list back and checking whether the connection is authorized. There is no completion signal it can wait on, so it verifies rather than polls.
When a tool runs, the key is turned on Nirvai's side: tools.use resolves and decrypts the bound
connection in the cloud, runs the tool there, and returns a bounded preview. The secret does not
cross back with the result.
Channels are not connections
WhatsApp · Instagram · Messenger · Meta Ads · Telegram · Slack · Discord · Microsoft Teams · web
chat are not connections and not tools. They're set up in the Nirvai app and then attached to
an agent. credentials.propose actively refuses them and replies with where to go instead —
because a connection named after one of them could never exist, and you'd only find out after
approving it. If your assistant offers to "connect WhatsApp for you", it is wrong; ask it to point
you at the page.
Worked examples
Set up a new connection, then use it. The whole loop, in the order it actually happens:
# 1. Look first — the connection may already be there.
nirvai_execute(alias="credentials.list", args={},
description="Check whether Stripe is already connected")
# 2. Nothing matched. Propose one — field NAMES only, no value.
nirvai_execute(alias="credentials.propose",
args={ items: [{ type: "apikey", label: "Stripe (live)", provider: "stripe",
steps: [ …a guide: where to get the key, then a field to type it into… ] }] },
session="stripe-setup",
description="Propose a Stripe connection for the user to complete")
# → a link to a review page. You open it, you type the key, you click Connect.
# 3. Verify it landed and pick up its id.
nirvai_execute(alias="credentials.list", args={},
description="Read back the new connection's id")
Run a saved tool. Once a tool exists, it is callable by its own alias:
nirvai_execute(
alias = "tools.hubspot_create_contact",
args = { body: { properties: { email: "ada@acme.com", firstname: "Ada" } } },
description = "Add Ada to the CRM after today's call",
session = "crm-cleanup"
)
Only the parameters marked fillable get sent. Fixed settings are applied in the cloud, and the login is resolved there — your assistant never passes either.
Limits & guarantees
- Nothing is created silently.
proposestages a draft and returns a review page; the real create happens when you click. Once you have, the draft is marked done and cannot be created twice. - No delete. There is no verb to remove a tool or a connection over this connection, at any permission level.
- Runs are capped.
tools.useis blocking and time-limited, with no background worker to finish an overrun. Long jobs belong in an automation or a skill. - Results are previewed, not pasted. Large results come back as a summary plus a handle your assistant can fetch — see What comes back.
- An expired login is honest about it. Mid-run expiry returns a
needs_authstatus with a link to reconnect, instead of failing quietly. Your assistant should stop and hand you the link. - Edits are complete, not partial. Re-sending a tool's parameters replaces that whole set, so your assistant sends the full shape and re-reads the tool afterwards to confirm.