Connection

Connections: named links to your users' accounts on other services, and what those services can do.

gmail = noclick.Connection.from_name("gmail", integration="gmail", create_if_missing=True)
if not gmail.connected:
    send_to_user(gmail.connect_url)
gmail.wait()
gmail.run("send_email_message", {...})

A connection is a name in one scope (a user, or your account's own). Code, toolkits and agents name it, so they always act in exactly that account; two accounts on one service are two connections. Reconnecting keeps the name.

An operation that needs no account (exa's web search) needs no connection either: noclick.run("exa.search", {...}) and tools=["exa.search"] run it on NoClick's key, for every user. A connection named exa holding your own key takes over the same refs. Connection.integrations() finds a service, and operations() says what a connection's service can do.

class Connection

A named connection to an account on another service. Secrets never come back; token() mints an OAuth access token (credentials:token scope). status is pending until someone connects an account at connectUrl, connected after, and disconnected once it's revoked (connectLink() mints a link to reconnect it).

The accounts connected in the NoClick app are connections too, in your account's own scope: origin is "app" and the id is credential:<id> (their name is the app's, so refer to them by id). They run, look up, verify and take requireApproval like any connection, and an account agent takes one as a tool (the Connection, or "credential:<id>", "credential:<id>.operation"; a user's agent can't); the app connects, reconnects and removes them. One shared with your account has access "view" or "edit": it runs, but its token and approval rules stay its owner's.

Attributes
idstring
namestring
integrationstring
credentialTypestring | null
userIdstring | null
statusConnectionStatus
originConnectionOrigin

api for a named connection, app for an account connected in the app.

accessConnectionAccess

Your account's access: owner, or edit/view on one shared with it.

approvalOperationsstring[]

Operations its owner approves call by call wherever it runs (*: all); requireApproval adds to them.

accountstring | null

The connected account as the provider named it, when proven.

testAccountboolean

Whether it's a test account, which an app's preview writes through as well as reads.

usedByConnectionHolder[]

What holds it: agents (in their tools, as their modelConnection, or given it by their user), hosted MCP servers with it in their tools, and apps that bind it (with the binding name), each with whose it is.

connectedAtDate | null
createdAtDate | null
updatedAtDate | null
connectedboolean
connectUrlstring | null

The open link where someone connects (or reconnects) the account, if one is open.

verificationVerification | null
consoleUrlstring | null

This connection's kind in the developer console (null off the hosted API).

fromNamestatic

static fromName(
  name: string,
  options?: ConnectionCreateOptions & { createIfMissing?: boolean },
): Connection

A lazy reference to the connection called name in the current (or given) user. With createIfMissing it's created on first use, for integration: holding data when given (a key you hold), pending with a connect link otherwise. An existing connection is used as it is. Given integration, operations(), operation() and events() read the catalog without loading or creating the connection.

Parameters
namestringrequired
options.integrationstring | { name: string }

Its integration: a name ("gmail") or an IntegrationInfo.

options.createIfMissingboolean
options.credentialTypestring
options.dataJsonObject

A credential you hold: the connection holds it at once (validated and probed first).

options.redirectUrlstring
options.reasonstring
options.sendToSendTo

Also deliver the link: "user", "owner" (your account's owner), an email, or an object.

options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

A connection that loads, or is created, the first time it's used. Nothing is sent yet.

fromIdstatic

static fromId(connectionId: string, options?: CallOptions): Connection

A lazy reference by NoClick id; credential:<id> for an account connected in the app.

Parameters
connectionIdstringrequired
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
Returns

A connection that loads the first time you read a field. Nothing is sent yet.

createstatic

static create(
  name: string,
  options?: ConnectionCreateOptions,
): Promise<Connection>

Create a connection called name (unique per user; a duplicate throws Conflict with existingId). With data it holds that credential at once; without, it's pending and connectUrl is where someone connects it.

Parameters
namestringrequired
options.integrationstring | { name: string }

Its integration: a name ("gmail") or an IntegrationInfo.

options.credentialTypestring
options.dataJsonObject

A credential you hold: the connection holds it at once (validated and probed first).

options.redirectUrlstring
options.reasonstring
options.sendToSendTo

Also deliver the link: "user", "owner" (your account's owner), an email, or an object.

options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

The new connection: pending with its connect_url, or connected (with its verification) when you passed data.

getstatic

static get(ref: string, options?: CallOptions): Promise<Connection>

The connection with this id or name.

Parameters
refstringrequired
options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

The connection, loaded.

liststatic

static list(
  options?: ReadOptions & ConnectionFilters & { limit?: number },
): Promise<Connection[]>

The current user's connections (the account's own outside asUser), or those userFilter covers: named ones newest first, then, in the account's own scope, the accounts connected in the app. origin keeps only "api" or "app" ones.

const fromTheApp = await Connection.list({ origin: "app" });
Parameters
options.integrationstring | { name: string }
options.originConnectionOrigin
options.limitnumber

Return at most this many.

options.userFilterUserFilter

Which users the read covers (user_id query), instead of acting as one (user).

options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

Every matching connection (or the first limit), fetched page by page.

listPagestatic

static listPage(
  options?: ReadOptions & PageOptions & ConnectionFilters,
): Promise<Page<Connection>>

One page of connections; pass nextCursor back as cursor.

Parameters
options.cursorstring | null

The next_cursor of the page before; omit it for the first page.

options.limitnumber

Return at most this many.

options.integrationstring | { name: string }
options.originConnectionOrigin
options.userFilterUserFilter

Which users the read covers (user_id query), instead of acting as one (user).

options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

A page whose data holds the connections and whose next_cursor is None on the last page.

integrationsstatic

static integrations(
  query?: string,
  options?: { kind?: IntegrationKind; client?: NoClick },
): Promise<IntegrationInfo[]>

The services a connection can be made to and the model providers agents think with: every integration, or those whose name or label contains query (case-insensitive), each with how a person connects it (connectWith) and how many of its operations run with no account, by its name (accountFreeOperationCount). kind: "model" keeps the model providers an agent's modelConnection names; kind: "app" the services whose operations are tools.

const [crm] = await Connection.integrations("hubspot");
const hubspot = Connection.fromName("crm", { integration: crm, createIfMissing: true });
const providers = await Connection.integrations(undefined, { kind: "model" });
Parameters
querystring
options.kindIntegrationKind
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

Each one's name (what integration= takes), label, kind, operation_count, account_free_operation_count (how many run with no account, by the service's name), connect_with (how a person connects an account) and subscription (whether its link also offers signing in with a Claude or ChatGPT plan).

only

only(...operations: string[]): ConnectionOperations

A tool ref to some of its operations, for tools (gmail.only("send_email_message")); a lazy createIfMissing connection is still created first.

Parameters
operationsstring[]required
Returns

A ref to just those operations of this connection, to list in tools=; bind(...) on it fixes arguments the model mustn't choose.

checkout

checkout(repository: string, options?: CheckoutOptions): RepoCheckout

One of its repositories (owner/name on its git host; a github connection) checked out in an agent's workspace, for Agent repos: cloned into /workspace/<path> (the repository's name when omitted) at a conversation's first turn. The agent's git reaches that repository only, through NoClick, with a key made for its sandbox; the connection's own token never enters it. push is the branches it may push to, as globs: NoClick refuses a push to any other ref, and without it the checkout is read-only. gh only reads it; give the agent the connection's operations for pull requests and issues. Sends nothing.

const github = Connection.fromName("github", { integration: "github", createIfMissing: true });
const repos = [github.checkout("acme/docs", { push: "noclick/*" })];
Parameters
repositorystringrequired
options.branchstring

The branch it checks out (the repository's default when omitted).

options.pathstring

The folder under /workspace it's cloned into (the repository's name when omitted).

options.pushstring | string[]

Branches it may push to, exact or a prefix ending in /* ("noclick/*"); without it the checkout is read-only.

Returns

A RepoCheckout to list in an agent's repos.

operations

operations(options?: { search?: string }): Promise<IntegrationOperation[]>

What its integration can do: every operation, or those whose name or description contains search (case-insensitive), with parameter schemas and whether each needsAccount.

Parameters
options.searchstring
Returns

Each operation's name, description, the JSON schema of its parameters, needs_account (False when it runs on NoClick's key, with no connection or on a pending one) and read_only, a hint from its name rather than a guarantee.

operation

operation(name: string): Promise<IntegrationOperation>

One of its integration's operations; throws NotFound for a name it doesn't have.

Parameters
namestringrequired
Returns

The operation, with the JSON schema of the parameters that run takes for it.

events

events(): Promise<IntegrationEvent[]>

The app events its integration can wake an agent with, for OnEvent (what it pushes as it happens, never a poll).

Returns

Every event, with its description and the filters it takes (channel for a Slack message, repository for a GitHub issue).

request

request(options?: LinkOptions): Promise<ConnectLink>

Ask the user (or sendTo) to connect it; await link.wait() resolves to the Connection. A connection of your account's own has no user: send it to "owner" (your account's owner) or an email.

Parameters
options.reasonstring
options.sendToSendTo

Also deliver the link: "user", "owner" (your account's owner), an email, or an object.

(default "user")
options.redirectUrlstring
Returns

The link, already sent: delivered_via says where it went, and .wait() on it returns the connected Connection.

wait

wait(options?: { timeoutS?: number }): Promise<this>

Resolve once an account is connected (at once when it is). It follows the connection, not one link: an account connected through any of its links ends the wait, and a link that lapses after a fresh one was made (connectLink() or request(), from anywhere) hands the wait to the fresh one. Throws OperationExpired when the open link lapses with no fresh one, or OperationTimeout after timeoutS seconds.

Parameters
options.timeoutSnumber
Returns

This connection, connected. After a wait it's reloaded, so account names the account when the provider says who it is.

import

import(data: JsonObject, options?: { credentialType?: string }): Promise<this>

Replace the connection's credential with one you hold (a rotated key), validated first.

Parameters
dataJsonObjectrequired
options.credentialTypestring
Returns

This connection, holding the new key, with the probe's verification. The key it replaced is deleted.

run

run(
  operation: string,
  args?: JsonObject,
  options?: { approvalId?: string },
): Promise<R>

Run one of the integration's operations in this account. Throws NeedsConnection while pending (an operation whose needsAccount is false runs anyway, on NoClick's key, or on your own key once connected, and needs no connection at all through run) and CredentialDisconnected once revoked (both carry connectUrl); a policy may throw PolicyDenied or ApprovalRequired. Resolves to the operation's data (what the service answered); a service that refuses the call throws UpstreamError in its own words, its HTTP status in details.upstream_status.

Parameters
operationstringrequired
argsJsonObject
options.approvalIdstring
Returns

The operation's data, as plain data (what the service answered).

lookup

lookup(
  field: string,
  options?: { arguments?: JsonObject; search?: string; pageToken?: string },
): Promise<LookupOptions>

The allowed values of an ID-like field (channels, repos, sheets) in this account.

Parameters
fieldstringrequired
options.searchstring
options.pageTokenstring
options.argumentsJsonObject
Returns

One page of options, each with a value to pass and a label to show; next_page_token is set when there are more.

verify

verify(): Promise<Verification | null>

Re-check the account against the provider.

Returns

The provider's verdict, also kept as verification: reachable is True when it accepted the account, False when it refused it, and None when NoClick can't tell; message says what was found.

requireApproval

requireApproval(...operations: string[]): Promise<this>

From now on its owner approves each call of these operations wherever it runs (your code, agents, the app's workflows); "*" means every operation. It only adds to approvalOperations; lifting one is done in the app. On one shared with your account it throws PermissionDenied.

await Connection.fromName("gmail").requireApproval("send_email_message", "delete_message");
Parameters
operationsstring[]required
Returns

The connection, with its approval_operations.

markTestAccount

markTestAccount(testAccount?): Promise<this>

Mark it as a test account (or, with false, not one). An app's preview only reads through a connection; through a test account it writes as well, as production does. Only the account that connected it marks it (PermissionDenied on one shared with yours), and it must hold an account (InvalidRequest while pending).

await Connection.fromName("slack-sandbox").markTestAccount();
Parameters
testAccountbooleandefault true
Returns

The connection, with test_account set.

token

token(): Promise<Token>

A fresh OAuth access token for calling the provider yourself (credentials:token scope). A model provider's connection never hands out its key or plan token (PermissionDenied).

Returns

The access token, its token_type and when it expires_at; never a refresh token or an API key.

delete

delete(): Promise<void>

Delete the connection, revoking its account at the provider where possible.

run

function run(
  ref: string,
  args?: JsonObject,
  options?: CallOptions & { approvalId?: string },
): Promise<R>

Run one operation by its ref, "name.operation", as tools names it. name is a connection in the scope the call acts for (the user's, else the account's), else an integration whose operation needs no account, which then runs on NoClick's key with no connection at all. A connection named like the integration (exa, holding your own key) wins, so the same ref then runs on your key. An account connected in the app is "credential:<id>.operation".

Throws like connection.run: NeedsConnection (or CredentialDisconnected) with connectUrl when the connection has no working account, InvalidRequest when the operation needs an account and no connection has that name, NotFound when name is neither a connection nor an integration, PolicyDenied or ApprovalRequired (retry with its approvalId once err.operation.wait() resolves to true), and UpstreamError when the service refuses the call (its own words, its HTTP status in details.upstream_status). Resolves to the operation's data.

const results = await run("exa.search", { query: "invoice automation for small businesses" });
await run("gmail.send_email_message", { to: ["ana@example.com"], subject: "Hi", body: "..." }, { user: "user_42" });
Parameters
refstringrequired
argsJsonObject
options.approvalIdstring
options.userUserOption

undefined = the ambient user (asUser), null = the account itself.

(defaults to the current user)
options.clientNoClick

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

Returns

The operation's data, as plain data (what the service answered).

ConnectionOperations

class ConnectionOperations

Some of a connection's operations as tools (connection.only(...)), with the arguments bind(...) fixed.

Attributes
connectionConnection
operationsstring[]
bindingsJsonObject

The arguments NoClick fills on every call, out of the model's reach.

bind

bind(args: JsonObject): ConnectionOperations

Fix arguments of these operations: a bound parameter leaves the tool's schema, so the model never sees or sets it, and NoClick fills it on every call. A value may name the user the call acts for: {{user.id}}, {{user.email}}, {{user.name}} or {{user.metadata.<key>}}. A bound value always wins, over what a model passes through in an operation's free-form parameters too; a tool whose binding can't be filled isn't offered that turn. Policy judges the final arguments. Works in agents, toolkits and hosted MCP servers.

gmail.only("send_email_message").bind({ to: "{{user.email}}" })
Parameters
argsJsonObjectrequired
Returns

A new ref with these arguments bound on top of any bound before.

Verification

interface Verification

Whether the provider accepted the credential (reachable null = cannot judge).

Attributes
reachableboolean | null
samplesunknown[]
messagestring | null
hintstring | null
rawJsonObject

The response JSON exactly as the API sent it.

Token

interface Token

A short-lived OAuth access token (never a refresh token or API key).

Attributes
accessTokenstring
tokenTypestring | null
expiresAtDate | null
rawJsonObject

The response JSON exactly as the API sent it.

LookupOption

interface LookupOption
Attributes
valueunknown
labelstring | null
rawJsonObject

The response JSON exactly as the API sent it.

LookupOptions

interface LookupOptions

A page of options; pass nextPageToken back as pageToken.

Attributes
nextPageTokenstring | null

optionsTypeScript only

options: LookupOption[]

IntegrationInfo

interface IntegrationInfo

A service in the catalog (Connection.integrations()), such as gmail, or a model provider such as anthropic.

Attributes
namestring
labelstring | null
kindIntegrationKind

"app", a service whose operations are tools, or "model", a model provider whose connection an agent names as its model_connection (never in tools=).

subscriptionboolean

Whether its connect link also offers signing in with the provider's plan (a Claude or ChatGPT subscription) instead of pasting a key. Only a user's own connection takes a plan.

connectWithstring[]

How a person connects an account: "oauth" (signing in), "key" (pasting a key or token), "qr" (scanning a code) or "purchase" (a phone number, bought rather than connected).

operationCountnumber | null
accountFreeOperationCountnumber

How many of its operations need no account (0 for most): run and tools run them on NoClick's key by the integration's name ("exa.search"), with no connection.

credentialTypesstring[]

The exact credential types: the credentialType that Connection.create and connection.import take.

rawJsonObject

The response JSON exactly as the API sent it.

IntegrationOperation

interface IntegrationOperation

One operation of an integration, with its JSON-schema parameters.

Attributes
namestring
descriptionstring | null
parametersJsonObject
readOnlyboolean | null

A hint from the operation's name (reads start with list_, get_ and the like), not a guarantee.

scopesstring[]

The OAuth scopes it needs, where NoClick knows them.

needsAccountboolean

False when it runs on NoClick's key with no account at all (web search, public data), so it runs with no connection ("exa.search") and on a pending one; true otherwise, including when the need depends on its arguments.

rawJsonObject

The response JSON exactly as the API sent it.

IntegrationEvent

interface IntegrationEvent

An app event an integration can deliver to an agent's OnEvent trigger or a channel's subscription: its name (channel_message, contact_created), what it means, and the filters that narrow it, each with what it matches (one marked Required. must be given).

Attributes
namestring
descriptionstring | null
filtersRecord<string, string>
rawJsonObject

The response JSON exactly as the API sent it.