
Use vendor API definitions without another integration runtime
A provider module describes a vendor’s API operations as data: request methods, paths, inputs and authentication. You install it from Wildo’s module registry into your application, where it becomes your own code, and Wildo’s executor calls the vendor from those definitions.
The registry offers three modules. Asana creates a task and lists the workspaces and projects a person’s connected account can see. Microsoft Teams lists teams, channels and chats, and reads, replies to, updates, reacts to and deletes messages. Azure Document Intelligence reads scanned documents for the application’s search and extraction. Each module also brings a skill that teaches the coding agent working in your repository how to use it.
Teams adds its operations to the Microsoft connection the engine already owns, so a person connects their Microsoft account once and every Microsoft operation the application opts into spends that one grant. For a vendor the registry does not offer, author a module in your application with the same shape.
Example — Create a task in a person’s own Asana account
The Asana module declares a task-creation operation and the listings its inputs are picked from. The application calls that operation through the provider executor, spending the account the person connected.
For engineers
wildo registry add wildo/providers/asana copies the module into providers/asana/ and renames its package to your application’s scope, @<your-scope>/provider-asana. It adds the package to your workspace and to every backend service, and writes a scope entry for the ref with empty capability and protocol lists: the install cannot know what the vendor does for you. Wonder Todos completes that entry with engineCapabilities: [], providerCapabilities: [] and protocols: ['REST_API']. That is intentional: the application calls this REST provider directly rather than selecting it for an engine service slot. Supply its credentials and synchronize its runtime artifacts.
The copy is yours. Edit it where it is installed; wildo registry update keeps a file you changed and says so, rather than overwriting it. The module’s skill is installed beside the framework’s own skills when the skills are synchronized, and removed with the module.
Opt in a module that extends an engine provider
Teams is an augmentation: it declares no provider reference of its own, so the install writes no scope entry and tells you the one line it still needs. Wonder Todos declares the engine’s microsoft provider for its REST interface and lists the installed package under it:
Selected from wildo.saas.config.ts; the explanatory comments and the rest of the providers block are omitted.
microsoft: { engineCapabilities: [], providerCapabilities: [], protocols: ['REST_API'] },
// …
augmentations: {
microsoft: ['@wonder-todos/provider-microsoft-teams'],
},
Until it is listed there, the installed package contributes nothing. Once listed, its operations run on the engine provider’s Microsoft Graph connection and registration; the connection’s client ID and secret are the operator’s to supply, and each operation asks for its own Graph permission the first time it is used.
Azure Document Intelligence is the third shape: an extension that fills the engine’s document text extraction slot. An application selects it as that capability’s primary provider rather than calling it by operation. See scanned document extraction.
Call with an account and inspect the vendor result
This illustrative application-service body uses an injected ProviderOperationExecutorBackendService as executor and the current operation’s executionContext. userId, workspaceGid and taskName are application inputs. Asana must already be declared in this backend, and the person must have connected their Asana account. Import z from zod and the connection enums from @wildo-ai/external-connectors-models.
const response = await executor.execute(
{
providerRef: 'asana',
operationId: 'create_task',
connection: {
ownerScope: ExternalProvider_Connection_OwnerScope.USER,
ownerId: userId,
delegation: ExternalProvider_Connection_DelegationMode.SERVICE,
},
input: { name: taskName, workspace: workspaceGid },
},
executionContext,
);
if (response.status < 200 || response.status >= 300) {
throw new Error(`Asana did not create the task: HTTP ${response.status}`);
}
const task = z.looseObject({ gid: z.string().min(1), name: z.string().optional() }).parse(response.output);
const createdTask = { gid: task.gid };
The target names the person’s own account, which only that person may spend. Omitting connection would spend the application’s own account instead; to spend an organization’s, supply its authorized connection target. The execution context carries the caller’s authority; a provider reference alone does not choose an account.
An HTTP failure comes back as a status rather than an exception, so the caller checks it. The operation reads Asana’s answer at its data field, so output is the created task; this operation declares no output schema, so the example validates the task before recording its reference. A timeout is not proof that no task was created: do not blindly repeat a write that may already have succeeded.
For a typed convenience client, the module exports createAsanaClient: createAsanaClient(executor.forExecution(executionContext)) binds the same context, and each method takes the connection as its last argument. Its createTask method returns the vendor payload, without the status envelope; apply the same checks. Use executor.execute when the application needs both status and output.
Correct the definitions you run
Asana’s module was seeded from an open integration catalogue and then corrected by hand, because the imported task creation could not create a task:
Selected from operations.ts; the entry’s inputOptions, surrounding declarations and imports are omitted.
create_task: {
operationId: 'create_task',
domain: 'default',
method: API_HttpMethod.POST,
pathTemplate: '/api/1.0/tasks',
classification: ExternalOperation_Classification.WRITE,
summary: 'Creates a task in a workspace, or in one or more projects.',
requestBody: { encoding: ProviderRestRequestBodyEncoding.JSON, envelope: 'data' },
inputSchema: CreateTaskInputSchema,
// Asana answers `{ data: <task> }` as well; the payload a caller relies on is the task itself.
resultPath: 'data',
},
The imported operation sent its fields without the data envelope Asana reads, named the wrong field for projects, and lost the rule that a task must be placed in a workspace, in projects or under a parent task. The adopted module declares the envelope on the request body and refuses a call that places the task nowhere before anything is sent. The same entry names the listings its workspace and projects are picked from, which is what lets a person choose them from their own account.
A module enters the registry by being adopted and corrected by hand; nothing regenerates the registry from other catalogues. After installation it is maintained as your code. A module is software to review like any other dependency.