API
Nocarta provides an API that lets you build your own integrations, scripts, or tools that work with your documents, forms, notebooks, and reference lists programmatically.
The same API powers the AI Integration feature (both REST and MCP). Most of what your AI assistant can do, your own code can do too — though a few write operations (value sets, text extraction scans, form submissions, and editing notebooks) are REST-only and have no MCP equivalent yet.
Getting Started
Authentication
Every API request requires an access token. You get one from Settings → Profile and Connections (the same token works for both AI assistants and direct API calls).
Include your token in every request as a header:
Authorization: Bearer nc_token_your_token_here
Alternatively, you can pass it via the X-API-Key header instead:
X-API-Key: nc_token_your_token_here
Base URL
- Production:
https://app.nocarta.ai/api/v1 - Local development:
http://localhost:3000/api/v1
Format
All requests and responses use JSON. When sending data, set the header:
Content-Type: application/json
Quick test: Try this in your terminal to verify your token works:
curl -H "Authorization: Bearer nc_token_your_token" \
https://app.nocarta.ai/api/v1/form_templates
Scopes
Tokens are scoped — each one grants only the specific capabilities you select when creating it. Requests using a scope the token doesn't hold get a 403 Forbidden response with a required_scope field.
| Scope | What it grants |
|---|---|
user:read | Read your account profile, capabilities, and usage stats. |
form_templates:read | List and inspect form templates, notebooks, and fields. |
form_templates:write | Create, edit, scan, and delete form templates. |
form_instances:read | List and view filled form instances and field values. |
form_instances:write | Create, edit, scan, submit, and bulk-operate on form instances. |
notebooks:write | Create, edit, scan, and save values on notebooks (reads use form_templates:read). |
media_notes:read | List and view your recordings, their transcripts, and processing status. |
media_notes:write | Upload, delete, and restore recordings. |
value_sets:read | List and view value sets, entries, and export. |
value_sets:read_one | View a single value set and its entries/export (no listing). |
value_sets:write | Create, edit, delete value sets, and import entries. |
automations:read | List and inspect automations, including their NAL scripts; check a script; and read what a run of one did. |
automations:write | Create, edit and delete automations and their NAL scripts. |
automations:run | Start runs of your automations on your desktop. Separate from automations:write: editing a script and running one are different permissions. |
system_scripts:read | List and inspect scripts and their source; check a script; read runs and their outputs. |
system_scripts:write | Create, edit (each edit is a new version) and archive scripts; delete finished runs. |
system_scripts:run | Start and stop script runs on your desktop or a Nocarta Runner. Separate from system_scripts:write, for the same reason. |
remote_execution:read | See where a remote run would land, where your account sends runs, your machines, and whether a Nocarta Runner is up for your account. |
remote_execution:write | Choose where your account's remote runs go, rename or forget your machines, and shut down your Nocarta Runner. |
search:read | Search everything you own by meaning — documents and value-set entries. |
indexing:read | Read which of your content is searchable by meaning. |
indexing:write | Turn meaning-search indexing on or off. |
partners:read | List and view partner-enabled templates and their configuration. |
partners:write | Enable and configure partner mode on templates you publish. |
partner_onboarding:read | View partner onboarding/compliance status and guidance. |
partner_onboarding:write | Complete partner onboarding/compliance setup. |
partner_instances:read | Read a partner template's field map. |
partner_instances:write | Create instances via the partner API. |
partner_service:read | View the account-level partner service status. |
partner_service:write | Activate and update partner service settings. |
You can also restrict a token's reach to specific records (e.g. "only this template") using resource claims when creating it in Settings → Profile and Connections. See AI Integration for the full walkthrough, including the super_user wildcard scope.
Pagination
List endpoints return paginated results. Use these parameters:
| Parameter | Default | Description |
|---|---|---|
page |
1 | Which page of results to return |
per_page |
25 | Results per page (max 100; value set entries max 250) |
Every paginated response includes a meta object:
{
"meta": {
"total_count": 142,
"page": 1,
"per_page": 25,
"total_pages": 6
}
}
Form Templates
Templates define the structure of your forms — what fields exist, their names, and their order. You create a template once, then fill it many times by creating instances.
Required scope: form_templates:read for reads, form_templates:write for writes.
List your templates
GET /api/v1/form_templates
Filters:
| Parameter | Description |
|---|---|
name |
Search by name (partial match, case-insensitive) |
published |
true or false |
archived |
true to include archived templates (hidden by default) |
shared |
true to show only templates shared with you |
Example:
GET /api/v1/form_templates?name=invoice&published=true&per_page=10
Get a template's details
GET /api/v1/form_templates/{id}
Returns the template's metadata and its blocks. Field-level detail lives at the /fields endpoint below.
Get a template's fields
GET /api/v1/form_templates/{id}/fields
Returns fields grouped by block. Field IDs are the keys you use when submitting instance values.
Create a template
POST /api/v1/form_templates
| Field | Required | Description |
|---|---|---|
form_template[name] |
Yes | Template name |
form_template[description] |
No | Description |
form_template[tag] |
No | Tag for organization |
form_template[published] |
No | true or false |
form_template[original_uploaded_image_file] |
No | PDF or image file. Use multipart/form-data |
Update a template
PATCH /api/v1/form_templates/{id}
Partial update. Accepts name, description, tag, published, notify_on_submission.
Update a template's fields
PATCH /api/v1/form_templates/{id}/fields
Sync field creation/updates/deletion in one transaction. Send a fields array of field operation objects.
Delete a template
DELETE /api/v1/form_templates/{id}
Archives the template (soft delete). Owner only. Returns 204 No Content on success.
Scan a template (text extraction)
POST /api/v1/form_templates/{id}/scan
Triggers field detection on the template's uploaded file. The scan runs in the background — check the template details to see when it finishes.
The template must have an uploaded file and must not have been scanned before (returns 409 Conflict if it has).
Scan to markdown
POST /api/v1/form_templates/{id}/scan_markdown
Extracts the document's full text content as structured markdown, page by page. Runs in the background.
Form Instances
Instances are filled-in copies of a template. When someone fills out a form, they create an instance. Each instance holds the actual values for that template's fields.
Required scope: form_instances:read for reads, form_instances:write for writes.
List instances
GET /api/v1/form_instances
Filters:
| Parameter | Description |
|---|---|
template_id |
Show only instances of a specific template |
status |
active (in progress), closed (submitted), or canceled |
instance_type |
IMAGE-FILL, MANUAL, CHAT, or DIGITAL-TWIN |
name |
Search by name (partial match) |
Example:
GET /api/v1/form_instances?template_id=abc-123&status=active
Get an instance's details
GET /api/v1/form_instances/{id}
Returns the instance's metadata and full field values.
Create an instance
POST /api/v1/form_instances
| Field | Required | Description |
|---|---|---|
form_instance[form_template_id] |
Yes | The template to create an instance from |
form_instance[name] |
No | A name for this instance |
form_instance[values] |
No | Initial field values (a JSON object keyed by field ID) |
form_instance[original_uploaded_image_file] |
No | Image to scan for auto-fill later (use multipart/form-data) |
Fill in an instance
PATCH /api/v1/form_instances/{id}
Send the fields you want to update. Values are merged with existing values — you don't need to send all fields every time, only the ones you're changing.
{
"form_instance": {
"values": {
"field-id-1": "Jane Smith",
"field-id-2": "2026-02-17"
}
}
}
Create many instances at once
POST /api/v1/form_instances/bulk_create
Create or upsert many instances under one template in a single all-or-nothing transaction:
{
"form_template_id": "abc-123",
"idempotency_key_fields": ["field-id-1"],
"instances": [
{ "values": { "field-id-1": "INV-001", "field-id-2": "100.00" } },
{ "name": "Invoice 2", "values": { "field-id-1": "INV-002" } }
]
}
Response: { created, updated, skipped, total, instance_ids, unmatched_keys, errors }.
Use field names instead of IDs
Values can be keyed by field name, so you don't have to look up a single UUID first. Names and IDs can be mixed freely:
{
"form_template_id": "abc-123",
"idempotency_key_fields": ["Invoice number"],
"instances": [
{ "values": { "Invoice number": "INV-001", "Amount": "100.00" } }
]
}
If two fields on the template share a name, that name is rejected rather than guessed at — use those fields' IDs. Any key matching no field at all is stored as given and listed back in unmatched_keys, so a typo shows up immediately instead of as a mysteriously empty field.
Send a CSV instead
For many rows — or a template with a lot of fields — send a CSV document instead of JSON. The column names are stated once in the header rather than beside every value, which is dramatically smaller. A column headed name sets the instance label:
{
"form_template_id": "abc-123",
"csv": "name,Invoice number,Amount\nInvoice 1,INV-001,100.00\nInvoice 2,INV-002,250.00"
}
Blank cells are left alone rather than written as empty, so a blank column won't erase a stored value when the row updates an existing instance. Send either csv or instances — not both.
Note on idempotency_key_fields: matching is scoped to the template, not to you specifically. If a template is shared with other people who also have write access, avoid reusing the same key values across different callers of that shared template — a match could update a record someone else created.
Submit an instance
POST /api/v1/form_instances/{id}/submit
Closes the instance — status becomes closed and it becomes read-only. You can optionally include final field values to save before closing: { "fields": { "field-id-1": "..." } }.
Scan an instance (auto-fill via text extraction)
POST /api/v1/form_instances/{id}/scan
Triggers text extraction on the instance's attached image (or its template's image) and automatically fills in matching fields. Runs in the background.
Bulk scan instances
POST /api/v1/form_instances/bulk_scan
Scan up to 20 of your own instances at once. Send an array of instance IDs:
{ "instance_ids": ["id-1", "id-2", "id-3"] }
Delete an instance
DELETE /api/v1/form_instances/{id}
Archives the instance (soft delete). Returns 204 No Content.
Notebooks
Notebooks are the quickest way to capture data from a single image — take a photo, and Nocarta reads the text automatically. Each notebook holds one image and its extracted data.
Required scope: form_templates:read for reads, notebooks:write for writes.
List notebooks
GET /api/v1/notebooks
Filters:
| Parameter | Description |
|---|---|
name |
Search by name (partial match) |
archived |
true to include archived notebooks |
tag |
Filter by tag |
image_name |
Search by original file name |
Get a notebook
GET /api/v1/notebooks/{id}
Returns the notebook's details. Use /fields below for field values.
Create a notebook
POST /api/v1/notebooks
Upload an image and Nocarta will automatically scan it for text. Use multipart/form-data:
| Field | Required | Description |
|---|---|---|
notebook[name] |
Yes | Notebook name |
notebook[original_uploaded_image_file] |
No | Image file |
scan |
No | false to skip automatic text extraction (default: true, scans automatically) |
Update a notebook
PATCH /api/v1/notebooks/{id}
Accepts name, description, tag, settings.
Get fields and values
GET /api/v1/notebooks/{id}/fields
Returns all extracted fields, their current values, and formatting settings.
Save field values
PATCH /api/v1/notebooks/{id}/save_values
Values are merged into the notebook's primary instance, same as form instances.
{
"values": {
"field-id-1": "John Doe",
"field-id-2": "2026-02-17"
}
}
Get extracted text
GET /api/v1/notebooks/{id}/text_content
Returns the full extracted text content from the notebook's fields.
Re-scan a notebook
POST /api/v1/notebooks/{id}/scan
Triggers a new text extraction scan on the notebook's image. Requires an attached image. Runs in the background.
Delete a notebook
DELETE /api/v1/notebooks/{id}
Archives the notebook (soft delete). Owner only.
Value Sets
Value sets are reference lists — think country codes, product categories, department names, or any structured list you want to reuse across forms. They support columns, search, CSV import/export, and optional hierarchy.
Required scope: value_sets:read or value_sets:read_one for reads, value_sets:write for writes.
List value sets
GET /api/v1/value_sets
Filters:
| Parameter | Description |
|---|---|
name |
Search by name (partial match) |
archived |
true to include archived sets |
Get a value set
GET /api/v1/value_sets/{id}
Returns the full schema (column names, display column, value column) and settings.
Create a value set
POST /api/v1/value_sets
| Field | Required | Description |
|---|---|---|
value_set[name] |
Yes | Set name |
value_set[description] |
No | Description |
value_set[columns] |
No | Comma-separated column names (e.g., name,code,region) |
value_set[display_column] |
No | Which column to show as the label |
value_set[value_column] |
No | Which column to store as the value |
value_set[key_columns] |
No | Comma-separated columns used to detect duplicate entries on import |
value_set[hierarchical] |
No | true to enable parent-child hierarchy |
value_set[hierarchy_column] |
No | Column that identifies each entry's parent (hierarchical sets only) |
Update a value set
PATCH /api/v1/value_sets/{id}
List entries
GET /api/v1/value_sets/{id}/entries
| Parameter | Description |
|---|---|
q |
Search within entries |
parent_id |
Filter by parent entry (hierarchical sets only) |
per_page |
Up to 250 entries per page |
Import entries from CSV
POST /api/v1/value_sets/{id}/import_entries
Upload a CSV file (use multipart/form-data, field name file). The CSV columns should match the value set's column names. Response: { imported, skipped, linked, errors }.
Export entries as CSV
GET /api/v1/value_sets/{id}/export_entries
Downloads all entries as a CSV file. You can filter with q to export a subset.
Delete a value set
DELETE /api/v1/value_sets/{id}
Archives the value set (soft delete).
Automations
An automation is a script — written in NAL — that does work for you on a website, against an API, or in a Windows application. Over the API you can list automations, read and edit their scripts, check a script without saving it, start a run, and read what a run did.
Required scopes: automations:read for reads, automations:write for writes, and automations:run to start a run. Running is a separate permission from writing, because editing a script and making one act are different powers: a token that may rewrite an automation cannot also fire it unless you grant that too.
List your automations
GET /api/v1/automations
Summary rows, most recently updated first: id, name, description, mode and enabled.
Get an automation
GET /api/v1/automations/{id}
Everything in the summary plus the script itself, its allowed_hosts, and the inputs and outputs it declares. Read inputs before you start a run — those are the values the run expects.
Create an automation
POST /api/v1/automations
| Field | Required | Description |
|---|---|---|
automation[name] |
Yes | Automation name |
automation[mode] |
No | desktop (the default), browser or windows. Decides which commands the script may use |
automation[script] |
No | The NAL source |
automation[description] |
No | Description |
automation[allowed_hosts] |
No | Array of hosts a browser automation may contact — exact (api.example.com) or wildcard (*.example.com). An empty list reaches nothing at all |
automation[inputs] |
No | Array of {name, label, type, required, default, help}. Names are lower_snake_case; the script reads them as input["name"] |
automation[outputs] |
No | Array of {name, type, description} — what the script promises to emit with output |
automation[enabled] |
No | true or false |
The response carries a check alongside the automation: every write parses the script, so a syntax error, or a command the chosen mode does not offer, comes back in the same reply with line numbers.
Update an automation
PATCH /api/v1/automations/{id}
Partial update, same fields. The response carries check too.
Delete an automation
DELETE /api/v1/automations/{id}
Permanent, and it deletes the automation's runs with it. Returns 204 No Content.
Check a script without saving it
POST /api/v1/automations/check
Body: { "script": "…", "mode": "desktop" }. Saves nothing. Returns { "check": { "ok": …, "errors": [], "violations": [] } } — an error is a parse problem with a line, a message and often a hint; a violation names a command the chosen mode does not offer. Write, check, fix the lines it names, then save.
Start a run
POST /api/v1/automations/{automation_id}/runs
Requires automations:run. Body: { "input": { … } }, with a value for each input the automation declares.
The run does not happen inside this request. Automations drive real applications and real websites, so they execute on a desktop, never on Nocarta's servers. The reply is 202 Accepted with the run — created and queued, not finished:
{
"run": { "id": "…", "status": "dispatched", "trigger": "api", … },
"delivered": true,
"target": "attended"
}
delivered: false is not a failure — it means a desktop is still coming up and the run is waiting for it. target says where it went: attended is your own connected desktop app, runner a Nocarta Runner, machine the one machine your account sends runs to. Where an automation run goes follows your account's choice — see Remote execution.
Either way, poll GET /api/v1/automations/{automation_id}/runs/{run_id} until status is completed, failed or cancelled. A run usually begins within seconds when your desktop app is already connected, and within a couple of minutes when one has to be started for you. A run nothing ever picks up is failed after 20 minutes, so polling always ends.
Plan for one difference from running an automation yourself: nobody is at the screen, so ask fails the run instead of waiting for an answer.
If the run could never have executed, you are told now rather than left polling:
| Status | error |
What to do |
|---|---|---|
403 |
signing_blocked |
The automation is signed and its signature no longer verifies. Sign it again in the app |
403 |
remote_dispatch_not_authorized |
Turn on desktop execution and browser dispatch under Settings → Remote Dispatch |
422 |
vault_unlock_required |
The automation uses vault credentials, which only you can unlock, at a desktop. Run it from the desktop app instead |
422 |
input_invalid |
An input is missing or the wrong type — errors names which |
409 |
already_running |
One automation at a time per account. Wait for the current run or batch to finish |
409 |
target_offline |
Your account sends runs to one machine and it is not connected; the message names it. Open the desktop app there, or choose another place to run |
List an automation's runs
GET /api/v1/automations/{automation_id}/runs
Newest first. limit defaults to 20, maximum 100.
Read one run
GET /api/v1/automations/{automation_id}/runs/{run_id}
{
"run": {
"id": "…", "automation_id": "…", "status": "completed",
"trigger": "api", "started_at": "…", "finished_at": "…", "duration_ms": 1500,
"inputs": { "customer_ref": "AB-1024" },
"output": { "invoice_number": "2026-0041" },
"error": null,
"log": ["[10:00:00] Opening the portal", "…"],
"log_truncated": false,
"steps": { "total": 9, "failed": 0 },
"screenshots_uploaded": 2
}
}
output— what the script emitted withoutput. It isnullwhen the script emitted nothing, and it is recorded even for a run that stopped part-way, so you can see what it produced before it stopped.error—nullunless the run failed.source_lineis the line of the script it stopped on, the same line number the check endpoint reports, andstuck_contextcarries the detail when the script stopped itself withstuck.log— the last 200 lines the script wrote withlog;log_truncatedsays whether earlier lines were dropped. Lines arrive while the run is still going, so polling shows progress, not just the ending.screenshots_uploaded— how many screenshots the run saved. The images themselves are viewed in the app.
Scripts
A script is a small Node or Shell program that works on your data — converting files, generating reports, calling other APIs (see the Scripts Guide). Over the API you can list scripts, read and edit their source, check a script without saving it, start a run, read what a run did, stop it, and delete it once finished.
Required scopes: system_scripts:read for reads (scripts, their source, runs and their outputs), system_scripts:write for writes (create, edit — every edit is a new version — archive, and delete finished runs), and system_scripts:run to start and stop runs. As with automations, running is a separate permission from writing.
List your scripts
GET /api/v1/system_scripts
Get a script
GET /api/v1/system_scripts/{id}
Everything about the script, including its source, its kind and the parameters it declares. Read the parameters before you start a run — those are the values the run expects.
Create a script
POST /api/v1/system_scripts
The same information as the script form: name, description, kind (Node or Shell), run command and source filename for Shell, source code, parameters, the time limits for remote runs, and remote_target — where the script runs when started remotely: account (the default — follow your account's choice), nocarta_runner, or machine:<id> for one of your machines (GET /api/v1/remote_execution lists them). The response carries a check: every write parses the source with the kind's own checker, so a syntax error comes back in the same reply.
Update a script
PATCH /api/v1/system_scripts/{id}
Partial update, same fields. A change to the source creates a new version; past runs keep pointing at the version they used.
Delete a script
DELETE /api/v1/system_scripts/{id}
Check a script without saving it
POST /api/v1/system_scripts/check
Parses the source with the kind's own checker and saves nothing. Write, check, fix the lines it names, then save.
Start a run
POST /api/v1/system_scripts/{script_id}/runs
Requires system_scripts:run. Send a value for each parameter the script declares. A file parameter is sent as a file upload with the request — this endpoint is the only way to supply file inputs; the MCP tool run_script takes text parameters only.
Optionally send target to choose where this one run goes: nocarta_runner, or machine:<id> for one of your machines. Without it, the script's remote_target decides, and when that is account, your account's choice does.
The run does not happen inside this request. Like an automation, a script executes on a desktop, never on Nocarta's servers: on your own desktop app or one of your machines, or on a Nocarta Runner that Nocarta starts for you. The reply is 202 Accepted with the run, delivered and target, exactly as for an automation run — attended is your own connected desktop app, runner a Nocarta Runner, machine the machine the run was aimed at. Poll GET /api/v1/system_scripts/{script_id}/runs/{run_id} until status is completed, failed or cancelled.
Two things to plan for. Runs started this way are remote runs, so the script's time limits apply (5 minutes by default; up to 30 minutes on a Nocarta Runner, any value or no limit on your own machine). And secrets decide where a script can run: Tier 1 secrets work everywhere; Tier 2 (master-password) secrets mean your own desktop app, open and with the vault unlocked. A Nocarta Runner has only bash, python3 and curl for Shell scripts; Node scripts and npm packages are fully supported.
If the run could never have executed, you are told now rather than left polling:
| Status | error |
What to do |
|---|---|---|
403 |
remote_dispatch_not_authorized |
Turn on desktop execution and browser dispatch under Settings → Remote Dispatch |
422 |
vault_unlock_required |
The script uses master-password (Tier 2) secrets and would land on a Nocarta Runner, which cannot unlock them. Open the desktop app on your machine with the vault unlocked, or bind Tier 1 secrets instead |
422 |
input_invalid |
A parameter is missing or the wrong type — errors names which |
409 |
already_running |
One run at a time per account, scripts and automations alike. Wait for the current one to finish |
409 |
target_offline |
The run is aimed at one of your machines and it is not connected; the message names it. Nocarta cannot start your machine, so it does not wait. Open the desktop app there, or send the run elsewhere |
422 |
target_invalid |
target is not account, nocarta_runner or one of your machines |
List a script's runs
GET /api/v1/system_scripts/{script_id}/runs
Read one run
GET /api/v1/system_scripts/{script_id}/runs/{run_id}
Status, where it ran, how it was started, exit code, the time limit that applied, the logs when the script keeps them, and its output files. Each output file carries a download_url.
Download an output file
GET /api/v1/system_scripts/{script_id}/runs/{run_id}/outputs/{output_id}
Requires system_scripts:read. Returns the file itself, with the same token you used to read the run — this is the address in the output's download_url.
Stop a run
POST /api/v1/system_scripts/{script_id}/runs/{run_id}/stop
Requires system_scripts:run. The machine running the script sends it a stop signal and force-kills it a few seconds later if needed; the run is marked cancelled.
Delete a finished run
DELETE /api/v1/system_scripts/{script_id}/runs/{run_id}
Requires system_scripts:write. Removes the run with its logs and output files. Stop a run before deleting it.
Remote execution
Where your remote runs go, your machines, and the one Nocarta Runner your account may have. All of it is account-level: there is no record id to restrict a token to. It is the same setting as Settings → Profile and Connections → Where my automations and scripts run.
Where would a run land?
GET /api/v1/remote_execution
Requires remote_execution:read. Returns:
situation—desktop(your desktop app will take the run),runner(a Nocarta Runner is up and will take it),none(a run would start a Nocarta Runner and wait for it),machine_offline(your account sends runs to one machine and it is not connected — runs are refused) oroff(desktop execution and browser dispatch are not both on in Settings).target— where your account sends runs:automatic,nocarta_runner, or one machine, with its name and whether it is connected.holder— what is taking runs right now, ornull.machines— the computers you run the desktop app on:id,name,online,service_mode,last_seen_at, andtarget_value— the value to send to choose it.- The two remote-execution settings, and the Nocarta Runner that is up for your account, if any.
Choose where your runs go
PATCH /api/v1/remote_execution
Requires remote_execution:write. Body: { "execution_target": "…" } with one of:
automatic— your connected desktop app takes runs; when none is connected, Nocarta starts a Nocarta Runner.nocarta_runner— always a Nocarta Runner, even while the desktop app is open.machine:<id>— only that machine (itstarget_value). While it is not connected, runs are refused withtarget_offlineinstead of waiting.
Returns the same body as the GET. Any other value is 422 target_invalid. This applies to automations and scripts; a script can still have its own remote_target, and a single script run its own target. The two remote-execution settings themselves can only be changed in Settings.
Rename a machine
PATCH /api/v1/remote_execution/machines/{id}
Requires remote_execution:write. Body: { "name": "build-vm" } (up to 80 characters). Returns the machine.
Forget a machine
DELETE /api/v1/remote_execution/machines/{id}
Requires remote_execution:write. The machine is no longer offered as a place to run. If your account sent runs there, it goes back to automatic; scripts set to run there go back to account. If the desktop app connects from that machine again, it is listed again. Returns the same body as the GET.
Shut down the Nocarta Runner
POST /api/v1/remote_execution/shut_down_runner
Requires remote_execution:write. Shuts down your Nocarta Runner, running or idle. The next remote run starts a new one.
Search
Search everything you own
GET /api/v1/search?q=rental+contract
Requires search:read. Searches by meaning as well as by words, so a query finds a document about the topic even when it shares no words with it — and it works across languages.
| Parameter | Meaning |
|---|---|
q | The query. Two characters minimum. |
scopes | documents, value_sets, or both (comma-separated). Defaults to both. |
limit | Maximum hits. Defaults to 10. |
Every hit carries method: "semantic" when it was matched by meaning, "lexical" when matched by words. score is that hit's native score and is not comparable between the two methods — use the returned order, not the number.
Search never fails to an error. When meaning-search is unavailable — switched off, nothing indexed yet, or the backend is temporarily down — results fall back to word matching and every hit comes back as "lexical". You always get an answer; check method if you need to know which kind.
Why a search might return nothing
Meaning-search is opt-in per record type. Notebooks and automations are indexed by default; form templates are off until switched on per template, and value sets are off until switched on per set. Read the current state with GET /api/v1/indexing (indexing:read) before concluding the content does not exist.
Content warnings on write
Endpoints that store user content may return a warnings array alongside the normal response:
{
"instance": { "id": "…", "name": "Invoice 4471" },
"warnings": ["Saved. Heads up: this content contains instructions that look aimed at an AI assistant. …"]
}
It means the text you stored reads like an instruction to an AI rather than ordinary data. The write succeeded — nothing is rejected or altered. Show it to the user rather than dropping it: content written through the API is exactly the case where no human read it on the way in.
The key is absent when there is nothing to report. Returned by POST/PATCH /api/v1/form_instances and POST/PATCH /desktop/automations.
Error Handling
The API uses standard HTTP status codes:
| Code | Meaning | What to do |
|---|---|---|
| 200 | Success | Request completed |
| 201 | Created | Resource was created successfully |
| 202 | Accepted | Background job started (e.g., text extraction scan). Check back later for results |
| 204 | No Content | Delete succeeded (no response body) |
| 400 | Bad Request | Missing or malformed parameters |
| 401 | Unauthorized | Token missing, invalid, revoked, or expired |
| 403 | Forbidden | Your token doesn't have the required scope, or its resource claims exclude this record |
| 404 | Not Found | Resource doesn't exist or you don't have access |
| 409 | Conflict | Action conflicts with current state (e.g., scanning an already-scanned template) |
| 422 | Validation Error | Check the validation_errors array in the response for details |
| 429 | Rate Limited | Too many requests. Wait and try again |
Error responses include a message explaining what went wrong:
{ "error": "Not Found", "message": "Resource not found" }
{ "error": "Unprocessable Entity", "message": "Name can't be blank", "validation_errors": ["Name can't be blank"] }
Rate Limits
The API allows 60 requests per minute, keyed by your token (or by IP if unauthenticated). When you hit the limit, you'll receive a 429 response — wait a minute and retry.
Background Jobs
Some operations (text extraction scans, markdown scans) run in the background because they take a few seconds to complete. These endpoints return 202 Accepted immediately.
To check if a background job has finished:
- Request the resource again (e.g.,
GET /api/v1/form_templates/{id}) - Check for new fields, updated values, or changed status
Permissions
- You can only access your own documents and documents that have been shared with you
- Shared documents respect the permissions set by the owner (read-only, read-write, etc.)
- The API never exposes other users' data
- The API never exposes secrets or credentials from your vault
Quick Reference
| Action | Method | Endpoint |
|---|---|---|
| Templates | ||
| List templates | GET |
/api/v1/form_templates |
| Get template | GET |
/api/v1/form_templates/{id} |
| Get fields | GET |
/api/v1/form_templates/{id}/fields |
| Create template | POST |
/api/v1/form_templates |
| Update template | PATCH |
/api/v1/form_templates/{id} |
| Update fields | PATCH |
/api/v1/form_templates/{id}/fields |
| Delete template | DELETE |
/api/v1/form_templates/{id} |
| Scan (text extraction) | POST |
/api/v1/form_templates/{id}/scan |
| Scan to markdown | POST |
/api/v1/form_templates/{id}/scan_markdown |
| Instances | ||
| List instances | GET |
/api/v1/form_instances |
| Get instance | GET |
/api/v1/form_instances/{id} |
| Create instance | POST |
/api/v1/form_instances |
| Bulk create | POST |
/api/v1/form_instances/bulk_create |
| Fill in fields | PATCH |
/api/v1/form_instances/{id} |
| Submit | POST |
/api/v1/form_instances/{id}/submit |
| Scan instance | POST |
/api/v1/form_instances/{id}/scan |
| Bulk scan | POST |
/api/v1/form_instances/bulk_scan |
| Delete instance | DELETE |
/api/v1/form_instances/{id} |
| Notebooks | ||
| List notebooks | GET |
/api/v1/notebooks |
| Get notebook | GET |
/api/v1/notebooks/{id} |
| Create notebook | POST |
/api/v1/notebooks |
| Get fields | GET |
/api/v1/notebooks/{id}/fields |
| Save values | PATCH |
/api/v1/notebooks/{id}/save_values |
| Get text | GET |
/api/v1/notebooks/{id}/text_content |
| Re-scan | POST |
/api/v1/notebooks/{id}/scan |
| Delete notebook | DELETE |
/api/v1/notebooks/{id} |
| Value Sets | ||
| List value sets | GET |
/api/v1/value_sets |
| Get value set | GET |
/api/v1/value_sets/{id} |
| Create value set | POST |
/api/v1/value_sets |
| List entries | GET |
/api/v1/value_sets/{id}/entries |
| Import CSV | POST |
/api/v1/value_sets/{id}/import_entries |
| Export CSV | GET |
/api/v1/value_sets/{id}/export_entries |
| Delete value set | DELETE |
/api/v1/value_sets/{id} |
Next Steps
- AI Integration — Connect an AI assistant using the same API (REST or MCP), including scopes and resource claims
- Forms Guide — Learn about templates and instances
- Notebook Guide — Quick document capture
- Value Sets Guide — Reference lists for forms
Need More Help?