---
title: "doo.ai"
url: "https://help.tabidoo.cloud/en/article/dooai"
language: "en"
last_updated: "2026-09-16T10:17:05.703Z"
---

# doo.ai

`doo.ai` lets a script ask a Tabidoo AI agent a question and use the answer like any other value — summarize a long note, classify an incoming request, translate a text, extract data from a document, draft a reply. You write the prompt, the agent answers, your script decides what to do with the answer.

It is the same agent technology the AI assistant in the application uses; `doo.ai` is the way to reach it from your own scripts.

* * *

## Where you can use it

| Place | Works |
| --- | --- |
| Workflow —<br>**Run Script**<br>step (server) | yes |
| Button field, Free HTML field, form scripts (browser) | yes |
| Calculated field | no — a calculated field is a single synchronous expression,<br>`callAgent`<br>is asynchronous |

Always `await` the call:

```
const res = await doo.ai.callAgent('Summarize the text in three sentences.');
```

* * *

## callAgent

### The short form

Pass a string and you get the answer from the default agent:

```
const res = await doo.ai.callAgent(`Classify this request as Complaint, Question or Order:\n${doo.model.text?.value}`);
console.log(res.message);
```

### The full form

Pass an object when you need more control:

```
const res = await doo.ai.callAgent({
    prompt: `Summarize the following note in two sentences:\n${doo.model.note?.value}`,
    context: 'You write in Czech, in a neutral business tone.',
    agentShortName: 'my-agent',
    useWebSearchTool: false
});
```

| Property | Meaning |
| --- | --- |
| `prompt` | The question or instruction. Required. |
| `context` | Extra instructions for the agent — role, tone, output format. |
| `model` | Code of the LLM model to use. Leave out to use the agent's own model. |
| `agentShortName` | Short name of the agent to call (from the AI Agents table). |
| `agentId` | Id of the agent record — an alternative to<br>`agentShortName`<br>. |
| `agentApplicationId` | The application the agent is defined in, when it is not the current one. |
| `useSystemAgents` | Allows Tabidoo's built-in agents to be used. |
| `useTabidooDocumentationTool` | The agent may search the Tabidoo documentation. |
| `useTabidooGetStructureTool` | The agent may read the structure of the application (tables, fields). |
| `useTabidooGetTsDefinitionTool` | The agent may read the TypeScript definitions of the application. |
| `useWebSearchTool` | The agent may search the web. |
| `attachments` | Files for the agent:<br>`[{ fileName, fileData, mimetype }]`<br>,<br>`fileData`<br>in base64. |
| `chatState` | The conversation so far — see<br>*Asking follow-up questions*<br>below. |

Every tool you switch on makes the call larger and more expensive, so switch on only what the prompt really needs.

### What you get back

```
{
    message,        // the agent's answer as text
    inputTokens,    // tokens sent to the model
    outputTokens,   // tokens the model produced
    credits,        // the cost of this call, counted against your AI limit
    chatState,      // the conversation, to pass into the next call
    fullAgentResult,// the raw result of the run
    debugLogs       // which tools the agent loaded and used
}
```

* * *

## Examples

### Write the answer into a record

```
const res = await doo.ai.callAgent({
    prompt: `Summarize this customer note in one sentence:\n${doo.model.note?.value}`
});
if (!res?.message) {
    doo.workflow.stop();
}
await doo.table.updateFields('tickets', doo.model.id, { summary: res.message });
```

### Ask for a result you can process

The answer is plain text, so ask for the shape you want and check it before you use it:

```
const res = await doo.ai.callAgent({
    prompt: `Return only JSON in the form {"category":"...","priority":1-5} for this request:\n${doo.model.text?.value}`
});
let data = null;
try {
    data = JSON.parse(res?.message || '');
} catch (e) {
    console.log('The agent did not return valid JSON: ' + res?.message);
}
```

### Asking follow-up questions

`chatState` carries the conversation. Send back what the previous call returned and the agent remembers what was said:

```
const first = await doo.ai.callAgent('List the three main risks of this project.');
const second = await doo.ai.callAgent({
    prompt: 'Now write one mitigation measure for each of them.',
    chatState: first.chatState
});
```

Store `chatState` in a field (as text) when the conversation should continue in a later run.

### Sending a file to the agent

```
const file = doo.model.attachment?.value?.[0];
const f = await doo.table.getFileBase64('tickets', file.fileId);
const res = await doo.ai.callAgent({
    prompt: 'Read the invoice and return the invoice number and the total amount.',
    attachments: [{ fileName: f.fileName, fileData: f.content, mimetype: f.mimeType }]
});
```

* * *

## Your script as a tool of an agent

The other direction is possible as well: a script can *be* a tool that an agent calls (AI Tools, tool type **Simple Script**). Inside such a script:

-   `doo.ai.aiToolParam` holds the parameters the agent passed in,
-   `doo.ai.writeToAiToolResult(value)` returns the result to the agent.

```
const id = doo.ai.aiToolParam?.orderNumber;
const rows = (await doo.table.getData('orders', { filter: `number(eq)${id}`, limit: 1 })).data;
doo.ai.writeToAiToolResult(rows[0]?.fields ?? 'Order not found.');
```

* * *

## Cost and limits

**Every call is paid for in AI tokens.** The `credits` value of the response is what is counted against the AI token limit of your plan. In a browser script you can read the current consumption with `doo.environment.getCurrentUserTechnicalLimits()` — item `aiTokensCount`, with the limit of your plan next to it.

**Even a short prompt is not cheap.** An agent carries its instructions and its tools into every call, so a one-word answer can still mean thousands of input tokens. In a measured example a single trivial call cost roughly 14,000 input tokens. The more tools an agent has switched on, the higher the number.

**A workflow run is stopped after 90 seconds.** One AI call takes seconds, so plan for one or two per run. Never call the agent in a loop over hundreds of records — process them in batches or in one prompt.

**The answer is not deterministic.** The same prompt can return a differently worded answer next time. Anything you write into a field or compare against should be checked in the script first.

* * *

## Troubleshooting

**`message`** **comes back empty.** The call did not fail and no exception was raised — check that `agentShortName`, `agentId`and `agentApplicationId` point to an agent that really exists and that you have access to. Always test `res?.message`before you use it.

**The answer is in the wrong language or format.** Put the requirement in `context` ("Answer in Czech, return only the number"), not only in the prompt.

**Nothing happens in the workflow.** Open the run log of the workflow. An AI call that was not written with `await` ends as an unhandled rejection, and the rest of the step runs without an answer.

**Too many requests / the AI limit is reached.** `aiTokensCount` is a limit of your plan, not a bug. Reduce the number of calls, shorten the prompts, or switch off the tools the agent does not need.

* * *

## Related

-   [doo.environment](https://help.tabidoo.cloud/en/article/dooenvironment) — `getCurrentUserTechnicalLimits()` for the current AI consumption
-   [Getting Started with Workflow Automation](https://help.tabidoo.cloud/en/article/workflow-automation)
-   [Scripting — Getting Started](https://help.tabidoo.cloud/en/article/introduction-to-scripting)
