TaskNotes exposes an in-process runtime API on the loaded Obsidian plugin instance:
```javascript
const tasknotes = app.plugins.plugins.tasknotes;
const api = tasknotes?.api;
```
This API is for Obsidian companion plugins and in-vault scripting tools such as Templater, QuickAdd, and MetaBind. It runs inside Obsidian and uses the same TaskNotes services, settings, cache, and vault permissions as the plugin UI.
This is separate from the [HTTP API](HTTP_API.md). The runtime API does not start a server, does not use bearer-token authentication, and is only available inside the running Obsidian app.
For user-facing extensions built on this API, see [Companion Plugins](companion-plugins.md). For a concrete automation companion plugin, see [TaskNotes Workflows](companion-plugins/tasknotes-workflows.md).
The TypeScript contract lives in `src/api/runtime-api.ts`. It is intentionally small and type-focused so it can later become a standalone `@tasknotes/runtime-api` package for companion plugins.
## Version and Capabilities
Check the API version and capabilities before using methods that may not exist in older TaskNotes versions:
```javascript
const tasknotes = app.plugins.plugins.tasknotes;
const api = tasknotes?.api;
if (!api || api.apiVersion !== 1 || !api.hasCapability("tasks.write")) {
The older flat methods, such as `api.getTask(path)` and `api.parseNaturalLanguage(text)`, remain available as compatibility aliases.
## Extension Registry
Companion plugins can publish their own runtime API namespace through `api.extensions`. This is the preferred way to extend TaskNotes without adding arbitrary properties to the core API object.
Extension namespaces are normalized to lowercase and may use letters, numbers, dots, underscores, or dashes. Capability names are also normalized to lowercase. Prefix extension capabilities with the extension namespace to avoid collisions.
## Companion Plugin Access
Companion plugins can read the TaskNotes plugin instance from Obsidian's plugin registry:
If TaskNotes is disabled or has not loaded yet, `api` will be unavailable. Companion plugins should handle that case and retry after plugin load if needed.
## TypeScript
Use the exported runtime contract for type safety:
```typescript
import type { TaskNotesRuntimeApiV1 } from "tasknotes/src/api/runtime-api";
| `api.tasks.get(path)` | Returns a task by path, or `null` when no task is cached at that path. |
| `api.tasks.list(query?)` | Returns all tasks, or tasks matching a runtime task query. Prefer `api.query.tasks()` when count and grouping metadata matters. |
| `api.tasks.create(taskData, context?)` | Creates a task using the normal TaskNotes creation service. |
| `api.tasks.update(path, patch, context?)` | Updates one or more task fields using the normal TaskNotes update service. |
| `api.tasks.delete(path, context?)` | Deletes the task file through TaskNotes' delete service. |
| `api.tasks.complete(path, options?, context?)` | Marks a task complete. |
| `api.tasks.uncomplete(path, options?, context?)` | Moves a completed task back to a non-completed status. |
The query, stats, and system namespaces expose HTTP/MCP-style support data without requiring companion plugins to reach into TaskNotes internals. Runtime queries use a stable DTO rather than TaskNotes' internal view state.
Runtime query fields are canonical IDs such as `task.status`, `task.priority`, `task.due`, `task.projects`, `task.isBlocked`, `file.path`, and `user.<id-or-key>`. `api.catalog.filterProperties()` returns the complete queryable field catalog, including aliases such as `status` and `user:<id>`.
Canonical operators are `eq`, `ne`, `contains`, `notContains`, `in`, `notIn`, `exists`, `missing`, `lt`, `lte`, `gt`, `gte`, `isTrue`, and `isFalse`. Legacy operator aliases such as `is`, `is-not`, `is-on-or-before`, and `is-not-empty` are accepted and normalized.
| `api.system.health()` | Returns runtime status, API version, capabilities, vault identity, and task count. |
TaskNotes compiles this DTO into its internal filter engine. Companion plugins should treat the runtime DTO and the catalog metadata as the public query contract.
`api.parseNaturalLanguage(text)` remains as a compatibility alias. Parsing does not create or modify task files.
See [NLP API](nlp-api.md) for HTTP endpoint details and parser behavior.
## Settings Snapshot
```javascript
const settings = api.settings.snapshot();
console.log(settings.defaultTaskStatus);
```
The returned object is a snapshot. Mutating it does not change TaskNotes settings. `api.getSettingsSnapshot()` remains available as a compatibility alias.
## Mutation Context
Mutating methods accept an optional context object:
```javascript
{
source: "tasknotes-workflows",
correlationId: "run-2026-06-01T09-00-00",
reason: "status changed to active"
}
```
TaskNotes attaches this context to normalized events emitted during the mutation. Use it to make workflow runs debuggable, connect multiple API calls to one run, and prevent companion plugins from responding to their own writes.
## Events
Subscribe with `api.events.on(eventName, handler)` and unsubscribe with `api.events.off(ref)`. In an Obsidian plugin, pass the returned `EventRef` to `this.registerEvent(ref)`.
Specific events are emitted in addition to `task.updated`. For example, changing a task from `open` to `done` emits `task.updated`, `task.status.changed`, and `task.completed`.
Event payloads have this shape:
```javascript
{
event: "task.status.changed",
timestamp: "2026-06-01T09:00:00.000Z",
taskPath: "Tasks/Review automation design.md",
task: { /* current task */ },
before: { /* task before change */ },
after: { /* task after change */ },
deletedTask: undefined,
changes: {
status: {
before: "open",
after: "done"
}
},
data: undefined,
context: {
source: "tasknotes-workflows",
correlationId: "run-2026-06-01T09-00-00",
reason: "status changed to done"
},
source: "tasknotes-workflows",
correlationId: "run-2026-06-01T09-00-00",
reason: "status changed to done",
rawEvent: "task-updated"
}
```
Events caused outside the runtime API may not include `context`, `source`, `correlationId`, or `reason`.
`api.errors.normalize(error)` converts unknown thrown values into a `TaskNotesApiError` payload. Unknown JavaScript errors use `operation_failed` with status `500`.