# Autessa Docs — full documentation > Documentation for building agentic applications on the Autessa platform. This file concatenates every documentation page as plain markdown. # Guide --- # What is Autessa Source: https://docs.autessa.com/docs/guide > Autessa in one page: tools, agents, and multi-agents, backed by AutessaDB, connectors, hosted apps, and APIs. # What is Autessa Autessa is a platform for building agentic applications. You define tools (the actions), give them to agents (AI workers that decide when to act), and optionally coordinate agents with multi-agent managers — then back the whole thing with a built-in database, external connectors, hosted apps, and production APIs. _(interactive UI demo omitted)_ ## Tools, agents, and multi-agents The core hierarchy has three levels: - **Tools** are the lowest-level abstraction — like functions in programming. Each tool performs one specific, well-defined action: run an AutessaScript, call an API, query a database, send an email. Tools take typed arguments and produce results. - **Agents** are AI workers that use your tools. An agent interprets a request, plans a sequence of tool calls, and handles the logic dynamically — you write instructions and attach tools; Autessa handles the orchestration. Agents work conversationally (chat, even voice) or offline on schedules and triggers. - **Multi-agents (managers)** orchestrate several agents. A manager decides which agent to delegate each subtask to and synthesizes the results, so complex workflows stay composed of small, testable agents. Everything is versioned: you iterate in a draft sandbox, **publish** when it's ready, and roll back instantly if a published version misbehaves. Start with [Tools](/docs/guide/agentic/tools.md), [Agents](/docs/guide/agentic/agents.md), and [Multi-Agents](/docs/guide/agentic/managers.md). ## AutessaDB [AutessaDB](/docs/guide/autessadb/tables.md) is the built-in database. Create tables for structured data or knowledge bases optimized for semantic search, and let agents read and write rows like any other data store. [Triggers](/docs/guide/autessadb/triggers.md) fire automation when rows are created, updated, or deleted — chaining agents and tools directly off your data. ## Connectors [Connectors](/docs/guide/connectors.md) link your workspace to external services — Google Calendar, Gmail, Outlook, QuickBooks, Slack, and more. Once a connector is authorized, tools built on it can act on those services without you touching OAuth or token refresh. ## Apps and platform products Beyond the agentic core, the builder ships full products on top of it: - **[App Builder](/docs/guide/platform/app-builder.md)** — hosted React web apps over your agents and data, generated from chat by the vibecode AutoBot and publishable to a public URL. - **[Document Signing](/docs/guide/platform/document-signing.md)** — a complete e-signature product: envelopes, templates, audit trails, and sealed PDFs. - **[Meeting Recorder](/docs/guide/platform/meeting-recorder.md)** — bots that join your meetings, record, transcribe, and summarize into AutessaDB. - **[Scheduled Jobs](/docs/guide/scheduled-jobs.md)** — run agents on cron schedules for reports, syncs, and maintenance. ## Marketplace The [Marketplace](/docs/guide/marketplace.md) distributes complete solutions as installable bundles — agents, tools, tables, and apps packaged with pinned versions. Install a template to start from something working, or [publish](/docs/guide/marketplace/publishing.md) your own bundles for others. ## AutoBots [AutoBots](/docs/guide/autobots.md) are AI copilots built into the builder itself: one drafts agents and tools with you, one manages AutessaDB in natural language, one writes AutessaScript, and one — [vibecode](/docs/guide/autobots/vibecode.md) — writes entire apps. You describe; they build; you review. ## APIs Everything you publish is callable: - The **[Client API](/docs/api.md)** executes agents, tools, and AutessaDB from your own applications with resource-scoped API keys — REST and streaming WebSockets. - The **[Poweruser API](/docs/api/poweruser.md)** manages every resource in your company programmatically, authenticated as you. - **[Webhook handlers](/docs/guide/api-management/webhook-handlers.md)** and **[OAuth clients](/docs/guide/api-management/oauth-clients.md)** round out inbound integration and third-party auth. ## Where to go next Three steps take you from reading to shipping: 1. **Build something** — the [Quickstart](/docs/guide/quickstart.md) walks you from a fresh account to a published agent you can call over the API. 2. **Understand what you built** — [Core Concepts](/docs/guide/concepts.md) pins down the vocabulary: tools, agents, versions, variables, companies, and access. 3. **Wire it into your product** — [Authentication](/docs/guide/authentication.md) covers API keys and credentials, then the [API Reference](/docs/api.md) takes over. Ready for something bigger? The [Build your first app](/docs/guide/tutorials/first-app.md) tutorial assembles a database, an agent, a nightly schedule, and a generated web app into one working product — in about 40 minutes. Along the way, the [tool type catalog](/docs/tools.md) shows everything tools can do, and the [AutessaScript reference](/docs/autessascript.md) covers custom tool logic. Prefer a terminal to a browser? The [Autessa CLI](/docs/cli.md) exposes the same platform as a discoverable command surface, so you can build [entirely headless](/docs/cli.md) — and everything you create is immediately visible in the builder. [Open the Builder →](https://builder.autessa.com) ## Related - [Quickstart](/docs/guide/quickstart.md) - [Core Concepts](/docs/guide/concepts.md) - [Authentication](/docs/guide/authentication.md) - [Dashboard](/docs/guide/platform/dashboard.md) --- # Quickstart Source: https://docs.autessa.com/docs/guide/quickstart > Build and run your first Autessa agent: create a tool, attach it to an agent, test, publish, and call the API. # Quickstart In this walkthrough you'll go from a fresh account to a published agent you can call over the API: create a tool, test it, attach it to an agent, chat with the agent, publish, and make your first API call. ## Step 1: Sign up Create an account at the builder. Your personal company is created automatically and becomes your active workspace — everything in this quickstart lands there. [Open the Builder →](https://builder.autessa.com) ## Step 2: Create a tool Tools are the building blocks agents work with. We'll make a simple LLM-powered summarizer. 1. In the sidebar, open **Agentic Workflows → Tools** and click **Create a Tool**. 2. Name it `SummarizeText` and describe it clearly — for example, "Summarizes a passage of text into three bullet points." Agents read the name and description to decide when to call a tool, so make both intuitive. 3. Pick the **LLM_CALL** tool type. Choose a model and write the prompt template: ```text Summarize the following text into three concise bullet points: @@{text} ``` 4. Add an argument named `text` with a description like "The passage of text to summarize." The `@@{text}` placeholder in the prompt template is filled with this argument at runtime. 5. Save the tool. _(interactive UI demo omitted)_ > Prefer code over prompts? Choose the **AUTESSA_SCRIPT** tool type instead and write the logic in [AutessaScript](/docs/autessascript.md) — same arguments, same testing, same everything else. ## Step 3: Test the tool 1. On the tool page, click **Execute Tool**. 2. Fill in the `text` argument with any paragraph and run it. You'll see the summarized output — and you can also **Execute with Streaming** to watch longer-running tools report progress. 3. Iterate on the prompt template until you like the output. Nothing here touches production; you're editing the draft. ## Step 4: Create an agent 1. Open **Agentic Workflows → Agents** and click **Create an Agent**. Choose the **Simple** type — you pick the tools. 2. Name it (for example `ResearchAssistant`) and give it instructions — the system prompt that defines its role and rules, such as "You help users digest long documents. When the user provides text, use SummarizeText and present the result." 3. Choose an AI model, and attach your `SummarizeText` tool. 4. Save the agent. ## Step 5: Test conversationally Open the agent's test conversation, paste some text, and ask for a summary. Watch the agent decide to call your tool, pass the argument, and answer with the result. _(interactive UI demo omitted)_ ## Step 6: Publish Everything so far lives in your draft sandbox. To make a version customers and APIs use by default: 1. On the agent page, open **Manage** and choose **Publish**. Do the same for the tool. 2. Open the agent's **Details** panel and copy its ID — a short string like `V1StGXR8Z5`. That's your `resourceId` for API calls. If a published version ever misbehaves, you can roll back to a prior version from the same Details panel — see [Versions](/docs/guide/agentic/versions.md). ## Step 7: Call it from the API 1. In the sidebar, open **API Management → API Keys** and create a key. Under **Resource Access**, grant it access to your agent. Copy the key — it's shown only once. 2. Execute the agent with a single request (no `Bearer` prefix on the key): ```bash curl "https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5" \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": [ { "inputType": "TEXT", "content": "Summarize this: Autessa lets you build tools, agents, and multi-agent workflows..." } ] }' ``` The response contains the agent's output in the `output` array. For multi-turn conversations, streaming over WebSockets, and audio input and output, see the [Agent API reference](/docs/api/agents.md). ## You've shipped your first agent That's the whole loop: you wrote a tool, taught an agent to use it, watched it reason in a live conversation, and put a published version behind a real API endpoint. Every production system on Autessa — voice assistants, document pipelines, multi-agent workflows — is built from exactly these moves, repeated and composed. Where to next: - **Build something real** — the [Build your first app](/docs/guide/tutorials/first-app.md) tutorial takes these same moves further: a todo app with an agent that reads your email every night and files it as tasks. About 40 minutes, and it touches nearly the whole platform. - **Solidify the mental model** — [Core Concepts](/docs/guide/concepts.md) pins down the vocabulary you just used in practice: tools, agents, versions, variables, and scopes. - **Give your agent memory** — create an [AutessaDB table](/docs/guide/autessadb/tables.md) so your agent can store, search, and react to data. - **Publish safely** — [Versions](/docs/guide/agentic/versions.md) explains the draft/publish/rollback cycle so you can iterate without breaking production. - **Call it from your product** — read [Authentication](/docs/guide/authentication.md), then the [Agent API reference](/docs/api/agents.md) for streaming, conversations, and audio. ## Related - [Core Concepts](/docs/guide/concepts.md) - [Authentication](/docs/guide/authentication.md) - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [API Keys](/docs/guide/api-management/api-keys.md) --- # Core Concepts Source: https://docs.autessa.com/docs/guide/concepts > The vocabulary of Autessa: tools, agents, managers, versions, variables, resource IDs, companies, and access levels. # Core Concepts This page defines the vocabulary used throughout the docs and the builder. Skim it once now, and come back whenever a term needs pinning down. ## Tools A tool is the smallest building block of automation — a function. Every tool has a **name** and **description** (agents read these to decide when to call it), a **tool type** that determines how it executes (AutessaScript, API call, LLM call, database query, connector actions, and more), and a typed **configuration** specific to that type. Tools declare **arguments** — the inputs an agent fills in when calling the tool, each with its own description. **Private arguments** are collected directly from the user, never passed through the LLM and never stored in any history — use them for sensitive values like card numbers. See [Tools](/docs/guide/agentic/tools.md) and the [tool type catalog](/docs/tools.md). ## Agents An agent is an LLM worker: **instructions** (its system prompt), an **AI model**, and a set of attached **tools** it may call. The agent decides which tools to invoke and in what order based on the request. Agents come in four types: - **Simple** — you pick the tools. - **MCP** — automatically uses the tools of an [MCP server](/docs/guide/agentic/mcp-servers.md). - **Critic** — self-evaluates its output against your criteria and refines it over bounded iterations. - **Search** — queries an AutessaDB knowledge base with semantic search. See [Agents](/docs/guide/agentic/agents.md). ## Managers (multi-agents) A manager orchestrates a pool of agents. Its instructions describe delegation rules — which agent handles what, in what order, and how to synthesize results — and its model makes those calls at runtime. Managers appear in the builder as **Multi-Agents**. See [Multi-Agents](/docs/guide/agentic/managers.md). ## Versions Every publishable resource (tools, agents, managers, apps) has a **draft** and any number of released versions. Version states are `DRAFT`, `PUBLISHED`, and `ARCHIVED`: - Work in the draft sandbox never affects customers — API calls default to the **published** version. - **Publishing** promotes the draft to the published version; it requires PUBLISH or ADMIN access on the resource. - **Rollback** repoints the published version to any prior archived version, taking effect immediately, while you keep iterating in the draft. See [Versions](/docs/guide/agentic/versions.md). ## Variables — `@@{variable}` The placeholder syntax `@@{name}` inserts values at runtime, in tool configurations and in agent/manager instructions. It resolves three kinds of values: - **Environment variables** — company-stored secrets and settings (below). - **Prompt template variables** — placeholders you declare on an agent or manager (for example `customer_name`) and supply per invocation, in the test panel or as `promptTemplateVariables` in API calls. - **Built-in variables** — always available without any setup: `@@{currentDate}` resolves to the current UTC date ("January 8, 2026") and `@@{currentTime}` to the current UTC time ("2:30 PM"). Handy for prompts like `Today is @@{currentDate}.` Your own variables win on a name clash — a built-in never overrides a value you supplied. See the full list in [Environment Variables](/docs/guide/agentic/environment-variables.md). ## Environment variables Environment variables store sensitive values — API keys, secrets, configuration — securely at the company level. Tools and agents reference them with `@@{MY_API_KEY}` instead of hardcoding credentials; values are never exposed in agent conversations. See [Environment Variables](/docs/guide/agentic/environment-variables.md). ## Resource IDs Every resource has an opaque public ID — a short string like `V1StGXR8Z5` — shown in the resource's Details panel in the builder. These IDs are what you use in API calls (the `resourceId` query parameter) and what appears in any user-visible JSON. Treat them as stable identifiers — never construct or increment them. They are IDs only, not secrets. ## Companies and scopes Every resource belongs to exactly one **company**, and you always work inside one company at a time — your active **scope**. Switching scope re-scopes the entire builder and APIs to that company's resources, members, and limits. You can belong to many companies, including your personal one. See [Companies](/docs/guide/platform/companies.md). ## Sharing and access levels Within a company, each resource is **private** to its owner, **shared** with specific teammates, or **company-wide**. Grants carry one of four access levels: - **READ** — view and execute the resource. - **WRITE** — edit the draft. - **PUBLISH** — everything in WRITE, plus publish and roll back versions. - **ADMIN** — everything, plus sharing, transfer, and deletion. The owner always holds ADMIN implicitly, and for most resource types transferring ownership is reserved for the owner alone — ADMIN is not enough (custom apps and voice clones are the exceptions, where any ADMIN can transfer). The levels also mean slightly different things per resource: on an AutessaDB table, WRITE means writing rows; on a custom app, READ means launching it. Each resource's guide page has a **Sharing and access levels** section with the exact mapping. > Access levels control what teammates can do in the builder. What *API callers* can do is scoped separately, by the credential — see [Authentication](/docs/guide/authentication.md). ## Where to go from here With the vocabulary in place, pick the path that matches where you are: - **Haven't built anything yet?** The [Quickstart](/docs/guide/quickstart.md) takes you from a fresh account to a published agent in seven steps. - **Ready to integrate?** [Authentication](/docs/guide/authentication.md) explains API keys, poweruser credentials, and OAuth — everything your code needs to call what you've built. - **Wondering what tools can do?** Browse the [tool type catalog](/docs/tools.md) — scripts, API calls, LLM calls, database queries, connectors, and more. ## Related - [Quickstart](/docs/guide/quickstart.md) - [Authentication](/docs/guide/authentication.md) - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Versions](/docs/guide/agentic/versions.md) --- # Authentication Source: https://docs.autessa.com/docs/guide/authentication > Autessa's three credential types — client API keys, poweruser keys, and OAuth clients — and when to use each. # Authentication Autessa has three credential types for programmatic access: client API keys for executing the resources you've published, poweruser keys for managing everything in your company as yourself, and OAuth clients for letting third-party applications authenticate Autessa users. ## What it's for Each credential answers a different question: - **Client API keys** — "let my application *run* these specific agents, tools, and tables." Resource-scoped and safe to hand to a production backend. - **Poweruser keys** — "let this script act *as me*" — create, edit, and manage every resource my account can touch, via the [Poweruser API](/docs/api/poweruser.md). - **OAuth clients** — "let a third-party app sign in Autessa users" via Autessa's hosted sign-in page, which redirects back to your app with a token pair. ## Navigating here All three are managed under **API Management** in the sidebar: **API Keys**, **Poweruser Keys**, and **OAuth Clients**. [Open API Management →](https://builder.autessa.com/keys) ## Key concepts ### Client API keys Client keys authenticate the [Client API](/docs/api.md) — executing agents, tools, and AutessaDB. Requests pass the raw key in the `Authorization` header (no `Bearer` prefix) and select the resource with a `resourceId` query parameter; WebSocket endpoints take the key as an `?authorization=` query parameter instead. ```bash curl "https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5" \ -H "Authorization: $API_KEY" ``` When creating a key you set its name, per-minute and per-day rate limits, an optional allowed-origins list, and — most importantly — its **resource access**: all resources, or a hand-picked set of agents, tools, and AutessaDB tables (tables with per-table READ/WRITE/DELETE granularity). The key value is shown once at creation; **Reset token** rotates it later. See [API Keys](/docs/guide/api-management/api-keys.md). ### Poweruser keys A poweruser key is a personal, full-management credential with the shape `pwrusr:{id}:{secret}`, passed in the `Authorization` header. Every call acts as your user — same resources, same permissions — against the [Poweruser API](/docs/api/poweruser.md), which covers management of agents, tools, tables, envelopes, connectors, and more. ```bash curl https://api.autessa.com/poweruser/... \ -H "Authorization: $POWERUSER_KEY" ``` Each user has at most one active permanent key, plus on-demand **temporary keys** that expire after 15 minutes. Keys can be rotated (invalidates the old one immediately) or revoked. Because poweruser access is broad, delegated uses of it are endpoint-scoped — for example, a hosted [custom app](/docs/guide/platform/app-builder.md) must declare exactly which poweruser endpoints it may call on the owner's behalf. See [Poweruser Keys](/docs/guide/api-management/poweruser-keys.md). ### OAuth clients OAuth clients make Autessa an OAuth 2.0 provider for your own applications: register a client, send users to Autessa's hosted `/oauth/login` page with your `client_id` and `redirect_uri`, and on success Autessa redirects back to your app with `access_token`, `refresh_token`, `expires_in`, and `token_type` in the URL fragment — there is no authorization-code exchange step. Refresh and revoke endpoints round out the token lifecycle. You configure a client name, a type — **Public** (SPA / browser extension, no secret) or **Confidential** (server-side, secret shown once and required on refresh) — and redirect URIs. New clients may require Autessa admin approval before use. See [OAuth Clients](/docs/guide/api-management/oauth-clients.md). > Never expose a credential in client-side code. Client keys support an allowed-origins list, but the key itself still belongs on a server; poweruser keys and OAuth client secrets are strictly server-side. ## How to choose 1. Embedding a published agent, tool, or table in your product → create a **client API key** scoped to exactly those resources. 2. Automating your workspace — provisioning resources, CI, migrations, back-office scripts → generate a **poweruser key** (or a 15-minute temporary key for one-off jobs). 3. Building a third-party app that signs in Autessa users → register an **OAuth client**. 4. Reacting to inbound events from other systems → you may not need a credential at all; see [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md). ## Related - [API Keys](/docs/guide/api-management/api-keys.md) - [Poweruser Keys](/docs/guide/api-management/poweruser-keys.md) - [OAuth Clients](/docs/guide/api-management/oauth-clients.md) - [Client API Reference](/docs/api.md) - [Poweruser API Reference](/docs/api/poweruser.md) --- # Tutorials Source: https://docs.autessa.com/docs/guide/tutorials > Guided end-to-end builds — real, working projects you can finish in under an hour. # Tutorials Guided builds that take you from an empty workspace to something real and running. Each one strings several Autessa features together, the way actual projects do. The [Quickstart](/docs/guide/quickstart.md) gets you from zero to your first agent in about fifteen minutes. Tutorials pick up from there: longer, more ambitious, and closer to what you'll ship for real — a database, an agent, a schedule, and an app, all working together. Every tutorial is self-contained. You don't need to have read the rest of the guide first, though the [Core Concepts](/docs/guide/concepts.md) page is a useful companion when you meet a term you don't recognize. ## Available tutorials - **[Build your first app](/docs/guide/tutorials/first-app.md)** — a todo list app with an agent that reads your unread email every night and files each actionable message as a task. Touches AutessaDB, connectors, tools, agents, scheduled jobs, and the App Builder. About 40 minutes. More tutorials are on the way. If there's a build you'd like to see walked through, tell us at [support@autessa.com](mailto:support@autessa.com). ## Related - [Quickstart](/docs/guide/quickstart.md) — your first tool and agent in 15 minutes - [Core Concepts](/docs/guide/concepts.md) — the mental model behind everything here - [App Builder](/docs/guide/platform/app-builder.md) — the feature the first tutorial builds on --- # Build your first app Source: https://docs.autessa.com/docs/guide/tutorials/first-app > A todo list app with an agent that reads your email every night and files each message as a task — built end to end in about 40 minutes. # Build your first app By the end of this tutorial you'll have a real product: a todo app you can open in your browser, fed by an agent that reads your unread email every night and quietly turns it into tasks. You describe the app in plain language — Autessa's AutoBot writes it. Here's the shape of what you're building: - An **AutessaDB table** called `Tasks` — the single source of truth. - An **agent** with three tools: read unread Gmail, insert a task, mark the email as read. - A **scheduled job** that runs the agent every night at 2 AM. - A **web app**, generated from one prompt, that shows and manages the tasks. The magic is that these four pieces don't know about each other directly — they all just meet at the table. The agent writes rows; the app reads them; you check things off. That's the Autessa pattern in miniature, and once you've built it you'll recognize it everywhere. > You'll need an Autessa account at [builder.autessa.com](https://builder.autessa.com) and a Gmail account you're willing to connect. Total time: about 40 minutes. If you've never created a tool or agent before, the [Quickstart](/docs/guide/quickstart.md) is a gentler on-ramp — but you can also just start here. ## Step 1 — Create the Tasks table Everything meets at the table, so the table comes first. (This order matters for a second reason too: in Step 8 the AutoBot will *discover* this table by name — it has to exist before the app is built.) Go to **AutessaDB → Tables → Create Table**, name it `Tasks`, and add these columns: | Column | Type | Why | | --- | --- | --- | | `title` | Short Text | The task, in a few words | | `notes` | Long Text | Detail pulled from the email body | | `done` | Boolean | What the app's checkbox toggles | | `source` | Short Text | `"gmail"` for agent-created tasks, `"manual"` for yours | | `email_from` | Short Text | Who sent it | | `email_subject` | Short Text | The original subject line | | `email_message_id` | Short Text | Dedupe key — one task per email, ever | You don't need a "created" column: every AutessaDB table automatically carries `id`, `created_at`, and `last_updated`, managed by the system (timestamps are UTC). If you ever want a *custom* date column — say, a due date — store it as Short Text holding an ISO-8601 UTC string; the built-in timestamps are the only date-typed columns a table has. Prefer to delegate? Open the **DB Copilot** (the Autobot button on the Tables page) and paste: ```text title="DB Copilot prompt" Create a table called Tasks with columns: title (short text), notes (long text), done (boolean), source (short text), email_from (short text), email_subject (short text), email_message_id (short text). ``` Either way, you should end up looking at an empty Tasks table. Empty is correct — the agent fills it. ## Step 2 — Create the agent The agent is the heart of this build, so create it next — the tools it will use come right after. Go to **Agentic Workflows → Agents → Create Agent**, name it `EmailTaskAgent`, and give it a description a future teammate (or a manager agent) would understand: *"Reads unread email and files actionable messages as tasks in the Tasks table."* Then the instructions — the agent's standing orders, so be specific about the sequence and the judgment calls: ```text title="EmailTaskAgent instructions" Today is @@{currentDate}. Your job is to turn unread email into tasks. 1. Call Read Unread Emails. 2. For each message, decide whether it is actionable — something the user needs to do, reply to, review, or pay. Skip newsletters, promotions, receipts, and automated notifications. 3. For each actionable message, call AddTask exactly once: - title: a crisp task phrase (max ~10 words), not the raw subject line - notes: one or two sentences of context from the subject and body - source: "gmail" - email_from, email_subject: from the message headers - email_message_id: the message's id 4. After filing a task, call Mark Email as Read with that message's id. Also mark skipped messages as read so they are not reconsidered tomorrow. 5. Finish with a one-line summary: how many emails you read, how many tasks you created. ``` That first line uses `@@{currentDate}` — a [built-in variable](/docs/guide/agentic/environment-variables.md#built-in-variables) that resolves to today's date at run time, so the agent never has to guess what day it is. Notice the instructions name three tools — **Read Unread Emails**, **AddTask**, **Mark Email as Read** — that don't exist yet. That's deliberate: you've defined *what* the agent does; the next three steps give it its hands. ## Step 3 — Connect Gmail Tools that touch your email need a live connection to your Gmail account, so the connection comes first. Go to **Connectors → Gmail**, name the connection (something like `MyGmail`), and click through the Google sign-in. When you're back, the connector card shows your Gmail address — you're connected. A connector is just the authorized pipe; it does nothing by itself. Tools built on top of it are what agents actually call — and that's the next step. ## Step 4 — Mint the email tools Now the shortcut most people miss: on the Gmail connector, open **Create Gmail Tools**. This modal creates ready-made, pre-wired tools against this connection — no configuration screens. Select: - **Read Unread Emails** — lists your unread messages with sender, subject, and full body (up to 10 by default). - **Mark Email as Read** — takes a message id, marks that email read. Marking emails read is how the nightly agent avoids processing the same message twice: what's been filed is no longer unread. Then hand them to your agent: open `EmailTaskAgent`, click **Import Existing Tool**, and attach both. ## Step 5 — Create the AddTask tool The agent needs one more ability: writing a row into your Tasks table. Go to **Agentic Workflows → Tools → Create New Tool**, name it `AddTask`, and choose the type **AutessaDB Insert**. In the configuration, pick the `Tasks` table. For each column you now choose between a fixed value and a variable — this is the important part: - `title`, `notes`, `source`, `email_from`, `email_subject`, `email_message_id` → toggle **Use Variable** on each, which binds them to `@@{title}`, `@@{notes}`, and so on. - `done` → leave static, set to `false`. New tasks start open. Those `@@{...}` placeholders become the tool's arguments. When the agent calls `AddTask`, it supplies a value for each one, and the row is written with exactly those values. One tool, reusable for any task from any caller. Attach it the same way as before — **Import Existing Tool** on `EmailTaskAgent` — and the agent now has all three tools its instructions were written for. ## Step 6 — Test it while you watch Send yourself two test emails with something actionable in each ("Can you review the Q3 pricing doc by Friday?", "Don't forget to send Dana the onboarding checklist"). Then, on the agent page, hit **Test** to open the streaming test window and type: *"Read my unread emails and file the actionable ones as tasks."* _(interactive UI demo omitted)_ Watch the tool-call cards appear in order: **Read Unread Emails** first, then an **AddTask** per actionable message — you should see it fire once for each of your two test emails, with a title the agent wrote itself — then **Mark Email as Read**. When the summary arrives, open **AutessaDB → Tables → Tasks**: two new rows, ready for the app you're about to build. If the agent filed something you'd have skipped (or skipped something you wanted), edit the instructions — the judgment lives in instruction 2's wording — and test again. This tighten-the-prompt loop is most of what "building an agent" means in practice. ## Step 7 — Put it on a schedule Go to **Scheduled Jobs → Create**. Name it `NightlyEmailSweep`, then: 1. **Schedule**: choose **Cron → Daily**, set **2:00 AM**, and pick your timezone from the selector — the schedule runs in the timezone you choose, and the form shows the next three run times so you can sanity-check it. (The Advanced tab accepts a raw AWS six-field Quartz expression like `0 2 * * ? *`, if you ever need something the friendly builder can't say.) 2. **Action**: choose **Invoke Agent**, select `EmailTaskAgent`, and set the agent input to: *"Read all unread emails and add each actionable one as a task."* Save, and the loop is closed: every night at 2 AM, your inbox gets triaged into the Tasks table while you sleep. The job's **History** page shows each run and what the agent did. ## Step 8 — Describe the app, watch it get built Now for the part that feels like magic. Go to **App Builder → New App**, name it `TaskBoard`, add a short description, and create it. The editor opens on a working app scaffold — it already runs; it just doesn't do anything yet. Open the **AutoBot** panel, click **Start New Conversation**, and paste: ```text title="AutoBot prompt" Build a clean, minimal todo app backed by my existing AutessaDB table called "Tasks". - Show open tasks (done = false) newest first, each with a checkbox that toggles done. - When a task's source is "gmail", show the email_subject and email_from underneath the title in smaller text. - Let me add a manual task (title + optional notes, source "manual"). - Let me click a task to view and edit its notes. - A small toggle reveals completed tasks. ``` Here's what happens next, and why each part exists: 1. The AutoBot replies with an **implementation plan** — a summary, numbered steps, and the files it intends to touch. This is your chance to redirect before any code is written. 2. Then comes a **changes card**: every file creation and edit as a reviewable diff. Click through them if you're curious — it's real React code, and it's yours. 3. Click **Apply Changes**, and the **live preview** re-renders. Your test emails from Step 6 are already sitting there as open tasks, subject lines and senders underneath, exactly as you asked. Notice what you *didn't* do: you never told the app your table's ID, and you never configured data access. The AutoBot looked up your `Tasks` table by name and registered it in the app's `autessa.config.json` — the file that maps every Autessa resource an app uses. And the app reads and writes AutessaDB *as you*, through a short-lived credential Autessa hands the app at runtime. Your data never becomes anonymous — it's your session doing the reading. Want to iterate? Just keep talking: *"Add a count of open tasks in the header"* — plan, diff, apply, preview. This conversational build loop is the whole App Builder workflow; there is no other editor you're secretly supposed to learn. (Full detail: [Vibecode AutoBot](/docs/guide/autobots/vibecode.md).) ## Step 9 — Publish and launch The editor preview is private to you. To get a real, launchable app, click **Publish**. Publishing kicks off a build — the status shows **Building** for a minute or so, then the version flips to **Published**, and **Launch** opens your app at its own URL. One architectural note worth knowing as your apps grow: your app reads the table as the signed-in user, while the nightly agent writes rows under your company's system identity. Two identities, one table — which is exactly why the table, not the app or the agent, is the source of truth. ## What you just built Take stock, because this little todo app quietly used most of the platform: - **AutessaDB** as the shared state everything meets at - A **connector** turning your Gmail into agent-usable tools - A **tool** with variable bindings (`@@{title}`, …) as a reusable write path - An **agent** exercising judgment on your behalf, on a **schedule** - An **app**, described in a paragraph and generated in a conversation Every bigger thing you build in Autessa is this same pattern with more pieces. ## Where to next - **Make it event-driven** — add a [trigger](/docs/guide/autessadb/triggers.md) on the Tasks table so that inserting a task fires a follow-up (a Slack-style notification tool, a daily digest, anything). - **Ship it to others** — bundle the table, tools, agent, job, and app into a [marketplace listing](/docs/guide/marketplace/publishing.md) that another company can install in one click. - **Embed the agent elsewhere** — call `EmailTaskAgent` from your own product via the [Agent API](/docs/api/agents.md), with keys from [Authentication](/docs/guide/authentication.md). ## Related - [Quickstart](/docs/guide/quickstart.md) — the 15-minute version of this journey - [App Builder](/docs/guide/platform/app-builder.md) — everything the editor can do - [AutessaDB Tables](/docs/guide/autessadb/tables.md) — why the table is the center of gravity - [Scheduled Jobs](/docs/guide/scheduled-jobs.md) — schedules beyond "nightly" - [Tool Types](/docs/tools.md) — the full catalog of what agents can do --- # Dashboard Source: https://docs.autessa.com/docs/guide/platform/dashboard > The builder home screen: workspace overview, activity stats, and quick starts. # Dashboard The dashboard is the home screen of the Autessa builder. It shows which workspace you're in, this month's agent and API activity, and quick starts for building your first app or agent. ## What it's for The dashboard answers two questions at a glance: *where am I?* (which company scope is active, and what else you belong to) and *what's happening?* (which agents and API keys are actually being used). It's also the fastest jumping-off point into the rest of the platform via the sidebar. ## Navigating here The dashboard is the first page after signing in, and always one click away via **Dashboard** at the top of the sidebar. [Open Dashboard →](https://builder.autessa.com/dashboard) ## Key concepts - **Workspace strip** — shows your **Current Workspace** (the active company and whether you're Owner or Member) and all companies available to you, with a **Change** button that takes you to [Companies](/docs/guide/platform/companies.md) to switch scope. Everything on the dashboard is scoped to the active company. - **Latest activity stats** — four cards for the current month: **Active Agents** (with conversations), **Conversations**, **Active API Keys** (with usage), and **API Requests**. - **Agent activity** — a table of agents active in the last three months, with month-by-month activity badges, conversation counts, and a color-coded success rate computed across the evaluated conversations in that three-month window. **View** jumps into that agent's [history](/docs/guide/agentic/agent-history.md). - **API key usage** — the same three-month view for API keys: active months and total requests, with a link into each key's request history. - **Featured templates** — up to six curated marketplace templates you can install, shown on both the first-run and regular dashboards, with **Browse all** leading to the [marketplace catalog](/docs/guide/marketplace/catalog.md). - **First-run quick starts** — a brand-new workspace shows two cards instead of stats: **Build an App** (describe what you want in plain English and deploy it — the [App Builder](/docs/guide/platform/app-builder.md)) and **Create an Agent** ([Agents](/docs/guide/agentic/agents.md)). - **Guided tour** — the help icon next to the page title starts an interactive tour of the dashboard, walking through each section in place. Handy for onboarding teammates. > Dashboard analytics are cached and refresh once per day; use the **Refresh** button to force a reload after a burst of activity. ## How to get oriented from the dashboard 1. Check the **Current Workspace** cell — everything you create lands in this company. Click **Change** if you need a different scope. 2. Scan **Latest activity** to confirm your agents and API keys are behaving; a dropping success rate is your cue to open **Agent activity → View** and read the conversation history. 3. Use the sidebar to move on: **Agentic Workflows** for tools/agents/multi-agents, **AutessaDB** for data, **App Builder** for hosted apps, **Connectors**, **Scheduled Jobs**, **API Management**, and **Company** settings. ## Related - [Quickstart](/docs/guide/quickstart.md) - [Companies](/docs/guide/platform/companies.md) - [Agent History](/docs/guide/agentic/agent-history.md) - [API Keys](/docs/guide/api-management/api-keys.md) --- # Companies Source: https://docs.autessa.com/docs/guide/platform/companies > How companies scope everything you build in Autessa, and how to create and switch between them. # Companies Every resource in Autessa — tools, agents, tables, apps, keys — belongs to a company. The Companies page is where you see every company you're a member of, create new ones, and switch which one you're working in. ## What it's for Companies are Autessa's workspaces. The company you're switched into (your *scope*) determines which resources you see and where everything you create lands. Keeping work in separate companies keeps clients, teams, or environments cleanly isolated, with their own members, usage, and billing. ## Navigating here From the dashboard, select **Companies** in the sidebar. The dashboard's **Change** button on the Current Workspace card leads to the same place. [Open Companies →](https://builder.autessa.com/companies) ## Key concepts - **Scope** — exactly one company is active at a time. Switching scope re-scopes the entire builder: dashboard stats, resource lists, permissions, everything. - **Membership** — you can belong to any number of companies. Each card on the page is badged **Personal**, **Owner**, or **Member**; your personal company is the one created with your account. - **Creating companies** — any confirmed user can create a company; a new company needs only a name and becomes your active scope immediately. - **Company administration** — the sidebar's **Company** section manages the *active* company: [Team](/docs/guide/company/team.md) for members and invitations, [Usage & Limits](/docs/guide/company/usage-limits.md), and [Billing](/docs/guide/company/billing.md). Company settings there also let the owner rename the company or transfer ownership. > Sharing happens inside a company, not across companies. Resources can be private to you, shared with specific teammates, or company-wide — but they never leak into another company's scope. ## How to switch companies 1. Open **Companies** from the sidebar. Your active company is highlighted with a **Current** badge. 2. Click **Switch to this company** on the company you want. 3. The builder refreshes in place — no reload — and every page now shows that company's resources. ## How to create a company 1. On the Companies page, click **Create company**. 2. Enter a company name and click **Create Company**. 3. The new company becomes your active scope immediately. Invite teammates from the **Company → Team** page. ## Related - [Dashboard](/docs/guide/platform/dashboard.md) - [Team](/docs/guide/company/team.md) - [Usage & Limits](/docs/guide/company/usage-limits.md) - [Consultant Access](/docs/guide/partners/consultant.md) --- # App Builder Source: https://docs.autessa.com/docs/guide/platform/app-builder > Build and publish hosted web apps on top of your agents, tools, and AutessaDB — generated from a chat. # App Builder The App Builder turns your agents, tools, and AutessaDB tables into real web apps. Describe what you want in chat and the AutoBot writes a full React app for you — or open the built-in IDE and edit every file yourself — then publish and share it, up to and including a public URL. ## What it's for Agents are great, but your users often need an interface: an intake form, a client portal, an internal dashboard, a chat frontend. The App Builder hosts that interface on Autessa. Apps run in a sandbox with scoped credentials, so a hosted app can invoke exactly the agents, tools, and tables you allow — nothing else. ## Navigating here From the dashboard, select **App Builder** in the sidebar. Apps are listed under the usual ownership tabs — **Owned By Me**, **Shared With Me**, and **Company Wide**. Users with viewer-only access see the same section as **Apps**, listing published apps they can launch. [Open App Builder →](https://builder.autessa.com/app-builder) ## Key concepts - **Apps are React apps** — each app is a set of JSX/TSX/CSS files with an `App.jsx` entry point, bundled live in the editor with an instant preview. npm packages are available, and internal navigation uses standard React routing. - **Starter templates** — every new app begins from a template you pick at creation: **Landing Page** (a polished marketing site with hero, features, pricing, and FAQ sections built from reusable blocks), **SaaS Application** (a modern product-app shell with light/dark themes and a full built-in component library — buttons, tables, modals, toasts, and more), or **Dashboard** (an analytics starter with KPI tiles, charts, and data tables). The SaaS and Dashboard templates include a live **Components** page showing every built-in component, and AutoBot reuses the same components when it writes new pages, so the app stays visually consistent as it grows. - **AutoBot (vibecode)** — the in-editor AI assistant. It reads your files, your published tools and agents, and your AutessaDB schemas, proposes a plan, and writes the code as reviewable diffs you **Apply** or **Reject** (or turn on auto-apply). A "mock only" mode builds frontend-first against mock data. See [Vibecode](/docs/guide/autobots/vibecode.md). - **The editor** — a three-pane IDE: file tree, Monaco code editor with auto-save, and a live preview with console. You can export the whole app as a ZIP or import files back in. Prefer chat-only building? **Hide Code** collapses to just AutoBot plus the preview. - **Versions** — file edits always target the draft. **Publish** builds and promotes a published version; Version History shows published and archived versions with one-click rollback to any successful build. Apps can also be managed programmatically through the [Custom Apps Poweruser API](/docs/api/poweruser/custom-apps.md). - **Sharing vs hosting** — two separate dials. *Sharing* controls who can open the code (private, specific teammates, or company-wide, with Read/Write/Publish/Admin levels). *Hosting mode* controls how end users run the published app: default (Autessa login required), **Public** (anyone with the link), **Access Code**, **Custom Auth** (app-specific accounts backed by an auto-created AutessaDB table, with a themeable sign-in page and optional Google and Microsoft sign-in), or **Personal access links** (each person gets their own private link, with no password). See [End-user sign-in](#end-user-sign-in). - **Public slugs** — non-default hosting modes get a public URL like `https://public.autessa.com/app/your-slug`. - **Runtime permissions** — in App Settings, hosted apps declare exactly which resources they may touch at runtime: AutessaDB tables (read/write/delete per table), agents, tools, managers, e-signature envelopes, and any admin API endpoints. The app receives scoped credentials for just that list. ## How to build an app with AutoBot 1. In **App Builder**, click **New App**, give it a name and optional description, and pick a starter template — Landing Page, SaaS Application, or Dashboard. The editor opens with the template's scaffold ready to customize. 2. Click **AutoBot** and describe the app — "a support portal where customers chat with my SupportAgent and past tickets load from my Tickets table." 3. AutoBot inspects your published agents, tools, and table schemas, proposes a plan, and streams file changes. Review the diff and click **Apply Changes** (or enable auto-apply). 4. Watch the live preview update; keep iterating in chat, or hand-edit any file directly in the editor. > Want this as a guided walkthrough? [Build your first app](/docs/guide/tutorials/first-app.md) takes you from an empty workspace to a published todo app — complete with an agent that files your email as tasks overnight. ## How to publish and share 1. Click **Publish**. Autessa builds the app server-side and promotes the draft to the published version; the build status is shown as it runs. 2. Open **Share** to set who can access the app's code and pick a hosting mode for end users. For Public, Access Code, Custom Auth, or Personal access links hosting, choose a slug to get your `.../app/your-slug` URL. 3. For hosted modes, open **Settings → Permissions** and grant the app exactly the tables, agents, tools, and endpoints it needs at runtime. 4. If something breaks after a publish, open **Versions** and roll back to a previous successful build. > Publish access follows the same [access levels](/docs/guide/concepts.md) as every other resource — you need PUBLISH or ADMIN on the app, and end users only ever see the published version. ## End-user sign-in Two hosting modes give every end user their own identity inside the app. ### Custom Auth: accounts with email, Google, or Microsoft Custom Auth apps keep their end users in an AutessaDB table that Autessa creates for the app. Under **Sign-in methods** in the Share settings, choose any combination of: - **Email and password.** Visitors sign up with an email address and password. You can require them to confirm an emailed code first. - **Google.** Visitors choose **Continue with Google** and sign in with their Google account. - **Microsoft.** Visitors choose **Continue with Microsoft** and sign in with a work, school, or personal Microsoft account, including Outlook.com. At least one method must stay on. There is nothing to set up with Google or Microsoft: Autessa handles the connection. The first time someone signs in with Google or Microsoft, a row for them is added to the app's user table. If that email already has a password account and Google or Microsoft confirms that the person owns the address, both sign-in methods open the same account. ### Personal access links With **Personal access links**, nobody creates an account or remembers a password. You add the people who should have access, and each one gets a private link. Opening the link signs them in, and the app knows exactly who they are. 1. In **Share**, pick **Personal access links** and choose a slug. 2. Click **Manage access links**, then **Add people**. Add one person at a time, or paste a list with one `Name, email` per line. Email and an expiry date are optional. 3. Copy each person's link, or use **Copy all**, and send the links however you like. Every link also contains a 16-character code that people can type on the app's sign-in page. From **Manage access links** you can copy a link again at any time, **Rotate** it to issue a new link and stop the old one, or **Revoke** it to shut that person out. People are stored in an AutessaDB table created for the app. Add your own columns to that table, such as team, role, or region, and the app can read them for whoever is signed in. ### Knowing who is signed in In both modes the app can read the signed-in person through the Autessa SDK built into every app. The person comes back with their ID, name, email, and sign-in method. For personal access links, the result also includes every custom column from their row. Tools and agents that the app calls receive the same person as `@@{appUserId}` and `@@{appUserEmail}`, so a tool can scope its work to the user. In row security rules, use `$CURRENT_CUSTOMAPP_USER_ID` for Custom Auth accounts and `$CURRENT_CUSTOMAPP_ACCESS_LINK_ID` for personal access links. The two never match each other, so switching an app between modes can't expose one person's rows to someone else. ### Custom domains Both modes work on a custom domain. Visitors sign in on the Autessa sign-in page and land back on your domain at the page they started from. Personal access links point at your custom domain automatically once it is active. ### How long visitors stay signed in A visitor stays signed in until they go a set time without opening the app. Each visit starts the clock again. By default that's 8 hours for access code apps and 24 hours for Custom Auth and personal access links. To change it, open **Share**, find **Keep visitors signed in for**, and pick anything from 1 hour to 30 days. The new length applies to people who are already signed in the next time they open the app. Team-only apps (the default hosting mode) use the company's Autessa sign-in instead. See [Team](/docs/guide/company/team.md#how-to-change-how-long-members-stay-signed-in). ### Signing out Apps let people sign out through the sign-out call in the Autessa SDK. Signing out ends the person's session, forgets it in their browser, and returns them to the app's sign-in page with a note that they've been signed out, so the next person on a shared computer starts fresh. It works in every signed-in mode (access code, Custom Auth, and personal access links), both on Autessa and on a custom domain. The newer starter templates include a **Sign out** button that appears only when someone is signed in. Public apps and apps opened inside Autessa have no app sign-in, so the button stays hidden there. When you ask AutoBot to build a signed-in app, it adds a sign-out action for you. ## Sharing and access levels Every app starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. The levels carry app-specific meanings: | Level | What it lets a teammate do | | --- | --- | | READ | Launch the published app and clone it — viewer access, no source code | | WRITE | Open the editor, read and edit files, and see builds and versions — builder access | | PUBLISH | Publish the app | | ADMIN | Share, delete, and transfer the app | The owner always has ADMIN implicitly; unlike most resource types, an app's ownership can be transferred by any ADMIN, not only the owner. Remember this dial governs teammates in the builder — end-user access is the separate *hosting mode*. ## Related - [Vibecode AutoBot](/docs/guide/autobots/vibecode.md) - [Agents](/docs/guide/agentic/agents.md) - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [Versions](/docs/guide/agentic/versions.md) - [Custom Apps Poweruser API](/docs/api/poweruser/custom-apps.md) --- # Document Signing Source: https://docs.autessa.com/docs/guide/platform/document-signing > Send, sign, and track e-signature envelopes with signing order, CC viewers, reusable templates, and sealed PDFs. # Document Signing Document Signing is Autessa's built-in e-signature product. Upload a document, place signature fields, choose the order people sign in, CC anyone who needs a copy, and send. Every view, signature, and download lands on a cryptographically chained audit trail that ends in a sealed PDF. ## What it's for Contracts, engagement letters, consent forms — anything that needs a legally signed PDF can be run end to end inside Autessa. Signers don't need an Autessa account: they get an emailed link and sign in the browser. Reusable templates make repeat documents a two-minute job, and the Poweruser API and AutessaScript SDK let your agents and apps drive the whole flow programmatically. ## Navigating here From the dashboard, select **Document Signing** in the sidebar. It's permission-gated, so it only appears for companies with e-signature enabled. [Open Document Signing →](https://builder.autessa.com/e-signature) The list page is your workspace: a folder tree down the left, and a table of envelopes filtered by status (**Drafts**, **Out for signature**, **Completed**, **Rejected**, **Voided**, **Expired**), by recipient, by title search, and sorted however you like. It opens on Drafts + Out for signature — the day-to-day working set. Expired envelopes stay hidden until you ask for them. ## The three things most people want Before the full walkthroughs, the three questions that come up first. ### 1. You choose the order people sign in Every envelope picks one of two orders on the **Recipients** step: | Order | What happens | | --- | --- | | **Everyone at once** (parallel) | All signers get their link the moment you send. They can sign in any order, simultaneously. | | **One at a time** (sequential) | Only the first signer is invited. When they finish, the next is invited automatically, and so on down the list. | Signers sit in numbered **slots**, and under *One at a time* the slot number *is* the turn order — put a signer in slot 1 and they go first. A slot can hold more than one person as a **client group**, in which case any one of them signing satisfies the slot. ### 2. You can CC people who never sign Add anyone as a **Viewer** and they're CC'd, read-only. Viewers: - get a read-only copy the moment you send, - get an updated copy each time someone signs, - get the final sealed document once everyone has signed, - never sign, hold no slot, and never hold up completion. This is the right home for a records inbox, a paralegal, or the counterparty's accountant. See [Send a document](/docs/guide/platform/document-signing/sending.md#step-1--recipients). ### 3. Repeat documents become templates Set a document up once as a **template** — its field layout plus named roles like "Client" and "Provider" — then create an envelope from it by assigning a real person to each role. Signing order and CC roles are baked into the template too, so instantiation is just binding people to roles. > Templates are built by uploading a document under **Templates**, not by > converting an existing envelope — there is no "save this envelope as a > template" action. If you have already sent something you want to reuse, upload > the same file under Templates and lay the fields out there once. From then on > every send is a two-minute job. ## Walkthroughs | Guide | What it covers | | --- | --- | | [Send a document for signature](/docs/guide/platform/document-signing/sending.md) | Upload → recipients and order → fields → review → send, with page editing, prefilled content, and custom invite messages. | | [What the signer sees](/docs/guide/platform/document-signing/signing-experience.md) | The recipient's side: the emailed link, consent, filling fields, adopting a signature, delegating, and declining. | | [Track and close out an envelope](/docs/guide/platform/document-signing/tracking.md) | Reminders, direct links, voiding, sealing early, downloads, and reading the audit trail. | | [Reusable templates](/docs/guide/platform/document-signing/templates.md) | Roles, field layout, multi-document templates, and creating envelopes from them. | | [Organize and share](/docs/guide/platform/document-signing/organizing.md) | Folders, contacts, sharing levels, and transferring ownership. | | [Automate it](/docs/guide/platform/document-signing/automating.md) | Driving signing from AutessaScript, the Poweruser API, and Custom Apps. | ## Key concepts - **Envelope** — one document (or document set) sent for signing. It moves through Draft → Sent → Completed, or ends in Rejected, Voided, or Expired. - **Signer slot** — each required signature is a numbered slot. A slot holds one signer, or several as a client group where *any one of them* can sign. Under *One at a time*, the slot number is also the turn order. - **Viewer** — a CC recipient. Read-only, never signs, never blocks completion. - **Field** — dropped onto the document. *Recipient fields* are filled by the signer you assign (Signature, Initials, Name, Email, Date signed, Text, Checkbox); *prefilled content* is filled by you before sending (static text, static date, image) and signers can't change it. - **Template** — a document plus named roles and a field layout, reusable for every new envelope. - **Folder** — a nestable, shareable container for filing envelopes. One envelope can sit in several folders. - **Completion policy** — `ALL` (every slot must sign) or `ANY_ONE` (the first signature completes the envelope). `ANY_ONE` does not combine with *One at a time*. - **Audit trail and sealing** — every event (created, sent, viewed, signed, reminded, delegated, downloaded, voided…) is recorded on a cryptographically chained audit trail with a chain-verification badge. Each signature is embedded as a PAdES advanced electronic signature, and the final PDF gets a Certificate of Completion appended. ## What your signers need Nothing. No Autessa account, no app, no password. Possession of the emailed link is the credential — a single-use, high-entropy token that only ever exists in that email; Autessa stores a hash of it and never the token itself. Links carry an expiry you set per send. > If invitations land in spam, don't fight the mail server — open the envelope > and use **Direct link** to get a signing URL you can paste into a chat, a text > message, or a calendar invite. See > [Track and close out](/docs/guide/platform/document-signing/tracking.md#how-to-share-a-direct-signing-link). ## Under the hood — tools, agents, tables & jobs Unlike Meeting Recorder or Meeting Reminders, Document Signing is a **conventional, self-contained service** — not an agentic feature. This is worth knowing as an admin because it means there's very little to inspect or switch off: no AI tools, no agents, and nothing running in the background. > Document Signing provisions **no [tools](/docs/guide/agentic/tools.md), no > [agents](/docs/guide/agentic/agents.md), and no [scheduled > jobs](/docs/guide/scheduled-jobs.md)**. Sealing happens the moment the last > required signer submits (event-driven, not a background job), and reminders are > **manual only** — sent when you click **Send reminder**. Envelopes carry an > expiry date, but nothing sweeps expired ones automatically. ### Tables The feature's data lives in a set of plain database tables. You won't edit these directly, but they map cleanly onto what you see in the UI: | Table | Holds | | --- | --- | | `signing_envelope` | Each document sent for signing — its status, signing order, completion policy, sharing level, expiry, and the stored PDF versions (original, prefilled, sealed). | | `signing_recipient` | The signers and viewers on an envelope, and each one's status. | | `signing_field` | The fields placed on the document (signature, initials, date, text, etc.) and where they sit. | | `signing_audit_event` | The immutable, cryptographically chained audit trail of every event on an envelope. | | `signing_envelope_permission` | Per-user share grants on an envelope. | | `signing_folder` / `signing_folder_permission` / `signing_envelope_folder` | Folders, their share grants, and which envelopes are filed in them. | | `signing_contact` | Each sender's personal address book of people they have sent to. | | `signing_template` / `signing_template_document` / `signing_template_role` / `signing_template_field` / `signing_template_permission` | Reusable templates — their source files, roles, field layouts, and share grants. | ### Disabling the feature Document Signing is gated by the **`E_SIGNATURE`** company permission — turn that off to remove it for a company. Because there are no background jobs or provisioned tools, there's nothing else to disable; per-envelope behavior is controlled by the signing order, completion policy, sharing level, and link expiry you choose when you send. ## Related - [Send a document for signature](/docs/guide/platform/document-signing/sending.md) - [Reusable templates](/docs/guide/platform/document-signing/templates.md) - [E-Signature Envelopes API](/docs/api/poweruser/esignature-envelopes.md) - [E-Signature Templates API](/docs/api/poweruser/esignature-templates.md) - [E-Signature Folders API](/docs/api/poweruser/esignature-folders.md) - [autessa.eSignature SDK](/docs/autessascript/sdk/eSignature.md) --- # Send a Document for Signature Source: https://docs.autessa.com/docs/guide/platform/document-signing/sending > End-to-end walkthrough of sending an e-signature envelope: upload, signing order, CC viewers, field placement, prefilled content, and review. # Send a Document for Signature The full path from a file on your desktop to an envelope sitting in someone's inbox. Four steps — upload, recipients, fields, review — and the builder saves your work as you move between them. ## What it's for This is the core Document Signing workflow: take a contract, decide who signs it and in what order, CC anyone who needs a copy, drop signature fields onto the pages, and send. If you send the same document repeatedly, do this once and then switch to [templates](/docs/guide/platform/document-signing/templates.md). ## Navigating here **Dashboard → Document Signing → New envelope.** [Start a new envelope →](https://builder.autessa.com/e-signature/new) ## Step 0 — Upload 1. Drop a file onto the upload area, or click to browse. 2. Give it a **Document title**. The filename (minus its extension) is filled in for you — "Vendor MSA — ACME Corp." reads better in the envelope list than "scan_final_v3". 3. Click **Continue**. **What you can upload.** Not just PDFs. Autessa accepts PDF, images (PNG, JPG, GIF, BMP, TIFF, HEIC), Word, Excel, PowerPoint, OpenDocument, RTF, plain text, HTML, Pages, and `.msg` files — anything that isn't already a PDF is converted to one on upload. The button reads **Converting…** while that happens, which takes a few seconds on large files. The limit is **25 MB** per file. > Conversion is one-way and happens at upload. What every signer sees, and what > gets sealed at the end, is the converted PDF — so check the preview on the next > step before you place fields, especially for Word documents with unusual > layouts. ## Step 1 — Recipients This is the step that decides the shape of the whole envelope. ### Pick a signing order At the top of the step, choose one: | Option | Behavior | | --- | --- | | **Everyone at once** | Every signer is emailed the moment you send. They can sign in any order, at the same time. Use this when nobody's signature depends on anyone else's. | | **One at a time** | Only slot 1 is invited on send. The moment they finish, slot 2 is invited automatically, and so on. Use this when an internal approver must sign before the document goes to a customer, or when a countersignature has to come last. | Under **One at a time** the slot numbers *are* the running order. Slot 1 signs first, slot 2 second. Order the slots the way you want the document to travel. ### Add your signers Each numbered slot is one required signature. 1. Click **Add signer** for each person who must sign. Email is required; a full name is optional but makes the invitation email and the audit trail far easier to read. 2. As you type, Autessa suggests people you have sent to before — your personal contact list, ranked by how often you have used them. Pick one to fill in both name and email. 3. To let *any one of several people* satisfy a slot, add them to the **same slot** as a client group — for example either of two contacts at a customer, or whichever partner is around to countersign. The review screen labels these "any one of them signs". > Fields belong to the slot, not to the person. When a client group is used, > whoever signs first fills every field assigned to that slot — you don't place > duplicate fields for the other members of the group. ### Add viewers (CC) Below the signers is a **Viewers** section. Anyone you add here is CC'd, read-only, and never signs. A viewer receives: - a read-only copy when you send the envelope, - an updated copy each time a signature lands (clearly marked *not yet fully executed*, so nobody files a mid-flight copy as the final contract), - the final sealed document once everyone has signed. Viewers hold no slot, take no turn under *One at a time*, and never hold up completion. Use them for a records inbox, a colleague who needs visibility, or the other side's accountant. Click **Continue** when your recipients are set. Autessa saves them before moving on. ## Step 2 — Fields The field editor is a toolbox on the left, the document in the middle, and a properties panel for whatever you select. ### Drag fields onto the page **Fields** (filled by the signer you assign them to): | Field | What the signer does | | --- | --- | | Signature | Draws, types, or uploads their signature | | Initials | Same, sized for an initials box | | Name | Types their full name | | Email | Types their email address | | Date signed | Stamped automatically when they sign | | Text | Free-text entry | | Checkbox | Ticks or leaves blank | **Prefilled content** (filled by *you*, before sending — signers see it baked into the document and can't change it): | Field | Use it for | | --- | --- | | Static text | A price, a term, a party name | | Static date | An effective date | | Image | A logo, a stamp, a pre-applied signature | To place one, drag it from the toolbox onto the page. Then: - **Assign it to a signer.** Every recipient field belongs to exactly one slot, colour-coded to match the signer. - **Mark it required** if the signer must not skip it. - **Give it a label** ("Pricing", "Start date"). Labels name the field in validation errors, so a blocked send tells you *which* box is empty instead of just "a field on page 3". - Adjust font, size, colour, and rotation in the properties panel. ### Editor shortcuts The editor is built for laying out long documents quickly: - **Ctrl+Z / Ctrl+Shift+Z** — undo and redo. - **Ctrl+C / Ctrl+V** — copy and paste fields, including across pages. Ideal for an initials box that repeats on every page. - **Zoom** control for precise placement on dense pages. - **Page thumbnails** panel for jumping around a long document. ### Edit the document itself While the envelope is still a draft you can change the document without starting over: - **Delete pages** you don't need. - **Append a PDF** to the end — a schedule or an addendum. - **Reorder pages.** - **Strike** — toggle strike mode and drag to draw a red line through a clause, the way you would redline on paper. Click the × on a line to erase it. > Page editing and strikes are draft-only. Once an envelope is sent, the document > is frozen — that's the point of the hash recorded on the audit trail. To change > a sent document, void the envelope and send a new one. Click **Review** when your fields are placed. ## Step 3 — Review The review screen shows the document with every field in place, and a summary panel: document title, signing order, signatures required, recipient count, viewer count, and fields placed. Below that, each numbered slot with its signer — and "any one of them signs" where you built a client group. **Before you can send**, every *required prefilled* field must be filled. If any are blank, a warning lists them by label and page; click one to jump straight back to it. Autessa blocks the send rather than let a contract go out with empty clauses. ### Customize the invitation email Use **Edit message** to set a custom subject line and a short note that appears near the top of the invitation. Left blank, the default wording is used. A sentence of context ("This is the renewal we discussed Tuesday — the only change is the term") measurably improves response times. ### Send Click **Send for signing**. Autessa then: 1. Flattens your prefilled content into the PDF — this becomes the canonical document every signature is computed over. 2. Mints a unique signing link for each recipient and emails the invitations (or just slot 1, under *One at a time*). 3. Emails every viewer their read-only copy. 4. Stamps the envelope `SENT` and records it on the audit trail. You land on the envelope's detail page to [track it](/docs/guide/platform/document-signing/tracking.md). ## How to set a link expiry Signing links carry an expiry, set when you send. Shorten it for a time-boxed offer; lengthen it when a counterparty is slow. When a link expires the signer sees an expired-link page with a one-click request for a fresh one, so an expiry never strands anybody. The envelope's own status flips to `EXPIRED` at the same moment, and expired envelopes are hidden from the list until you select the **Expired** filter. ## How to take the guided tour If you'd rather learn by doing, the in-app tour walks this exact flow with a sample agreement pre-loaded — you place three fields and send the envelope to yourself. Open **Help** from the dashboard and choose the Document Signing walkthrough. ## Related - [Document Signing overview](/docs/guide/platform/document-signing.md) - [What the signer sees](/docs/guide/platform/document-signing/signing-experience.md) - [Track and close out an envelope](/docs/guide/platform/document-signing/tracking.md) - [Reusable templates](/docs/guide/platform/document-signing/templates.md) - [E-Signature Envelopes API](/docs/api/poweruser/esignature-envelopes.md) --- # What the Signer Sees Source: https://docs.autessa.com/docs/guide/platform/document-signing/signing-experience > The recipient's side of an Autessa e-signature envelope: the emailed link, consent, filling fields, adopting a signature, delegating, and declining. # What the Signer Sees Your signers never create an Autessa account, install anything, or set a password. They click a link in an email and sign in the browser. This page walks their side of the flow so you know exactly what you're asking people to do. ## What it's for Two reasons to read this: to answer "what will they have to do?" before you send something to an important counterparty, and to talk a stuck signer through the screen they're looking at. ## The invitation email Each signer gets their own email with their own link. It carries the envelope title, who sent it, your custom note if you wrote one, and a button through to the signing page. Under **One at a time**, only the first slot is emailed on send; each subsequent signer is invited automatically the moment the previous one finishes. Under **Everyone at once**, all signers are emailed together. The link itself is the credential — a single-use, high-entropy token that exists only in that email. Autessa stores a hash of it and never the token, so nobody (including Autessa staff) can reconstruct a signing link from the database. There is **no separate one-time code or password step**: clicking the link opens the document. > Because the link is the credential, treat a forwarded invitation email the way > you'd treat a forwarded signed blank cheque. If a signer needs someone else to > sign, they should use **Delegate signer** (below) rather than forwarding the > email — delegation is recorded on the audit trail; forwarding is not. ## The signing flow, screen by screen ### 1. Electronic records & signatures consent The first screen states that an electronic signature carries the same legal effect as a handwritten one, and that the document and signature may be stored and delivered electronically. The signer picks **I agree — continue**, or **I don't agree**, which opens the decline flow. Nothing is submitted at this point; consent can be withdrawn by simply closing the window at any time before the final submit. ### 2. Fill in the fields The document renders in full, with the signer's own fields highlighted and a checklist in the sidebar. Clicking a checklist item scrolls that field into view, and items tick off as they're completed — so on a 40-page agreement nobody has to hunt for the one initials box on page 31. Fields assigned to *other* signers are visible but not editable. Prefilled content is already baked into the page and can't be touched. ### 3. Adopt a signature Tapping a Signature or Initials field opens the signature pad with three tabs: | Tab | How it works | | --- | --- | | **Draw** | Draw with a mouse, trackpad, or finger on a touchscreen. | | **Type** | Type a name and pick from several script fonts. The signer's name is suggested. | | **Upload** | Upload a PNG or JPG of an existing signature. | The signer previews the result before applying it, and can redo it. ### 4. Confirm identity Before submitting, the signer ticks a statement confirming that they are the named person at that email address, and that they're authorized to sign as that individual. This attestation is part of what the signature certificate records. ### 5. Done On **Sign document**, the signature is embedded and the signer gets a confirmation screen: - If others still have to sign: their signature is recorded, the next signer is notified automatically, and they'll receive the final copy when everyone has finished. - If they were the last: the envelope completes immediately and a sealed copy goes out to everyone shortly. A download link for the final document is emailed to each signer at completion. That link points at a hosted download page that mints a fresh, short-lived URL each time, and offers a **request a new link** button if it has gone stale — so signers can retrieve their copy months later without contacting you. ## How a signer delegates to someone else A signer who isn't the right person — the contract should go to their colleague, or they're out of office — uses **Delegate signer** in the header of the signing page. They enter the delegate's email and optional name. The delegate is added to that same slot — the delegate screen calls this an **alternate signer**, which is the same thing as joining the slot's client group: either the original signer or the delegate can now sign, and whichever does satisfies the slot. The original signer is *not* removed. Requesting the delegation is the signer's own authorization for the delegate to sign, and the event is recorded on the audit trail with both parties named. ## How a signer declines Any signer can decline from the signing page. They're asked for a reason, which is shown to you. Declining: - notifies you, the sender, - records the reason on the audit trail, - moves the whole envelope to **Rejected**, - cannot be undone by the signer. To proceed after a decline, fix whatever they objected to and send a new envelope. ## What a viewer (CC) sees Viewers get a different, simpler experience. They receive a read-only view link — no consent screen, no fields, no signing. Their emails arrive: - on send, with a read-only copy, - after each signature, with a progress copy plus a Signing Status Report page, worded to make clear the document is **not yet fully executed**, - at completion, with the final sealed document. Progress links are deliberately short-lived, since a fresh copy goes out on the next signature anyway. ## If the link doesn't work | What they see | What happened | What to do | | --- | --- | --- | | "This signing link has expired" | The envelope's expiry passed | The page offers a one-click request for a fresh link, emailed to the same address | | "This link can't be opened" | The link was voided, already used, or malformed | Check the envelope status — if it was voided, the signer's link is dead by design | | "Check your inbox" | They requested a new link | A fresh invitation is on its way to the address on the envelope | | Invitation never arrived | Spam filtering | Open the envelope and use **Direct link** to send a signing URL through another channel | ## Related - [Send a document for signature](/docs/guide/platform/document-signing/sending.md) - [Track and close out an envelope](/docs/guide/platform/document-signing/tracking.md) - [Document Signing overview](/docs/guide/platform/document-signing.md) --- # Track and Close Out an Envelope Source: https://docs.autessa.com/docs/guide/platform/document-signing/tracking > Follow a sent e-signature envelope: signer progress, reminders, direct links, voiding, sealing early, downloads, and the audit trail. # Track and Close Out an Envelope Once an envelope is out for signature, its detail page is where you watch progress, nudge people, and close the deal — by letting it complete on its own, sealing it early with what you have, or voiding it. ## What it's for Knowing exactly where a contract stands ("she opened it twice and never signed"), getting unstuck when email fails you, and producing a defensible record of what happened when. ## Navigating here **Dashboard → Document Signing →** click any envelope. Drafts open in the preparation editor; everything else opens on the detail page. ## How to read signer progress Each recipient gets a card showing their milestones: | Milestone | Meaning | | --- | --- | | **Invited** | The invitation email was sent | | **Viewed** | They opened the signing link at least once | | **Signed** | Their signature is recorded | | **Rejected** | They declined, with the reason they gave | Under **One at a time**, signers past the current turn show as pending and haven't been emailed yet — that's expected, not a delivery failure. The envelope row also shows a counter of **signatures collected versus required**, which counts *slots*, not people. A client group of three people counts as one required signature. Viewers appear separately, marked *Notified* — they are CC'd only and never factor into the count. ## How to nudge a signer **Send reminder** emails everyone still outstanding. On an envelope with an individual hold-out, open their card and use **Resend link** to email just that person a fresh invitation. > Reminders are manual only. Nothing in Autessa chases signers on a schedule — no > background job sends nightly nudges. If you want automated follow-up, drive it > from a [scheduled job](/docs/guide/scheduled-jobs.md) against the > [envelopes API](/docs/api/poweruser/esignature-envelopes.md). ## How to share a direct signing link When invitation emails land in spam — the single most common reason an envelope stalls — use **Direct link** to bypass email entirely. It gives you two kinds of link: - **Per-recipient links** — one per signer. Opens straight into signing with that person already identified. Send it to the right person and nobody else. - **One envelope-level link** — the same link for everybody. Whoever opens it enters their email address and is matched to their own slot. Paste either into a chat message, a text, or a calendar invite, or open one on a tablet for in-person signing. > A per-recipient direct link is exactly as powerful as the emailed invitation — > it *is* that person's credential. Send it only to that person, and use the > envelope-level link when you're posting into a channel where several > recipients will see it. ## How to close out an envelope There are three endings. ### Let it complete When the last required slot signs, Autessa immediately appends a Certificate of Completion, seals the PDF, and emails every signer and viewer a download link. No action needed from you. ### Seal early — "Mark as executed" Sometimes you have the signatures that matter and the rest are never coming. **Mark as executed** seals the envelope with the signatures collected so far. - Signers who haven't signed are marked *not required* and their links stop working immediately. - Everyone who did sign receives a download link to the final document. - It can't be undone. Available on a sent envelope once at least one signature is in. ### Void it **Void envelope** cancels it. You supply a reason, which is shown to recipients. Every outstanding signing link stops working at once, and the envelope moves to **Voided**. Void when the terms changed, the deal died, or you sent the wrong file. Then send a corrected envelope. ## How to download the document | Button | When it appears | What you get | | --- | --- | --- | | **Download in-progress copy** | Any sent, not-yet-completed envelope | The document with signatures collected so far, plus the audit trail to date | | **Download sealed PDF** | Completed envelopes | The final sealed document with the Certificate of Completion | Downloads are recorded on the audit trail, including who took them. ## How to read the audit trail Every envelope carries a complete, immutable event log: created, fields set, recipients set, sent, viewed, signed, reminded, delegated, rejected, sealed, downloaded, voided, expired, shared, unshared, ownership transferred. Each row records the event, the recipient it concerns, the actor's email, their IP address and browser, and the timestamp. The rows are **cryptographically chained** — each row hashes into the next, so removing or altering an entry breaks the chain. The panel shows a **Chain verified** badge when the chain validates, and **Chain compromised** if it ever doesn't. ## What the sealed PDF actually contains Autessa builds the final document in three stages: 1. **Prefill** — your static content is flattened into the uploaded PDF at send. These bytes are the canonical document that every signature is computed over. 2. **Per-signer signing** — each signature stamps that signer's fields and embeds a PAdES-B-T advanced electronic signature (CMS SignedData plus an RFC 3161 trusted timestamp) keyed to an ephemeral per-signer certificate. Signatures accumulate; each one covers everything before it. 3. **Finalize** — when the last signer is done, a Certificate of Completion is appended and the sealed PDF is stored. The practical consequence: any modification after signing is detectable in any standard PDF reader, not just in Autessa. SHA-256 hashes of the original, prefilled, and sealed versions are all retained on the envelope. ## Who can do what Actions are gated by your access level on the envelope: | Action | Needs | | --- | --- | | View, download in-progress copy, download sealed PDF | READ | | Rename | WRITE (or ownership) | | Direct links, send reminder, resend link, void, mark as executed | PUBLISH | | Share, unshare, transfer ownership, delete | ADMIN (or ownership) | **Deleting** is deliberately restricted: only **drafts** (still being prepared) and **voided** envelopes can be deleted. A sent envelope can't be — deleting it would break signers' live links. A completed envelope never can be — it's a legally signed record. You can select several rows in the envelope list and delete them together; ineligible rows are skipped and the dialog tells you how many. ## Related - [Send a document for signature](/docs/guide/platform/document-signing/sending.md) - [What the signer sees](/docs/guide/platform/document-signing/signing-experience.md) - [Organize and share](/docs/guide/platform/document-signing/organizing.md) - [E-Signature Envelopes API](/docs/api/poweruser/esignature-envelopes.md) --- # Reusable Templates Source: https://docs.autessa.com/docs/guide/platform/document-signing/templates > Set a signing document up once with named roles and a field layout, then stamp out ready-to-send envelopes by assigning real people to each role. # Reusable Templates A template is a document plus a field layout plus named roles — "Client", "Provider" — instead of specific people. Build it once, then every future send is just binding real people to those roles. Signing order and CC roles come along for the ride. ## What it's for Anything you send more than twice: engagement letters, NDAs, onboarding packs, renewal agreements. Templates remove the two slow parts of sending — laying out fields and getting the signing order right — and leave only the part that actually changes, which is who signs. ## Navigating here **Dashboard → Document Signing → Templates.** [Open Templates →](https://builder.autessa.com/e-signature/templates) ## Key concepts - **Role** — a named signing slot ("Client"). Fields bind to roles, not to people. At instantiation you assign a real person to each role. - **Viewer role** — a CC role. Emailed copies, never signs, holds no slot. - **Document set** — a template can hold several source files (an MSA, a terms sheet, a payment addendum) merged into one signing package. - **Instantiation copies, it doesn't link.** Creating an envelope from a template clones the document and field layout into a normal draft. Editing the template later never touches envelopes already created from it. > Templates are created by uploading a document under **Templates**. There's no > "save this envelope as a template" action — an existing envelope can't be > converted. If you've already sent a document you want to reuse, upload the same > file here and lay the fields out once. ## How to build a template 1. From the Templates list, click **New template**. 2. Upload the document and give the template a **title** — plus a short description, which helps when a team shares dozens of them. The same broad file support applies as for envelopes: PDFs, images, and Office documents, converted to PDF on upload. 3. **Roles** — name the signing roles, in the order they should sign. Use the role people will recognise ("Client", "Ops rep"), not a person's name. Mark any role that only needs a copy as a **viewer role**. Set the default signing order here too — *Everyone at once* or *One at a time* — and it is inherited by every envelope made from this template. 4. **Fields** — drag fields onto the document exactly as you would on an envelope, assigning each to a **role** rather than a person. Same toolbox, same undo/redo and copy/paste, same required flags and labels. 5. Click **Save template**. Your template now appears in the Templates list, with its role, field, and document counts. Use **Edit details** at any time to rename the template, change its description, or change its default signing order and completion policy. ## How to create an envelope from a template 1. In the Templates list, click **Use template** on the one you want. 2. Give the new envelope a **title** — typically the template name plus the counterparty, e.g. "Engagement Letter — Northwind". 3. Assign an email address (and optional name) to each role. Contact autocomplete suggests people you've sent to before. 4. For any signer role, add extra people to form a **client group** — any one of them can sign for that role. Viewer roles take a single CC address and no group, since they hold no slot. 5. Create it. You land on a ready-to-send **draft** envelope with the field layout, signing order, and CC roles already in place. From here it's a normal envelope: adjust anything you like, fill any required prefilled content, then **Send for signing**. See [Send a document](/docs/guide/platform/document-signing/sending.md#step-3--review). ## How to manage a multi-document template Real agreements are often several files — the master agreement, a terms sheet, an addendum. A template keeps them as a **document set**: separate source files merged, in order, into one signing package. Why it works this way: fields anchor to a *document* and a page within that document, not to a page number in the merged output. So the layout survives changes to the set. From the template's documents panel you can: | Action | Effect on your field layout | | --- | --- | | **Add** a document | Appended to the end. No existing field moves. | | **Replace** a document | Swaps the file, keeps its id and position. Fields on every *other* document stay exactly where they are; fields on this one that fall past its new last page are dropped. | | **Reorder** documents | A document's fields travel with it. | | **Rename** a document | Cosmetic — names the file in the panel. | | **Remove** a document | Takes its fields with it. A template must keep at least one document. | **Replace** is the one that earns its keep: when Legal sends back an edited MSA, swap the file and every other document in the pack is untouched. ## How to share a template with your team Templates follow Autessa's standard sharing model. Every template starts **private** to its owner. Share it with **specific teammates**, each at their own access level, or **company-wide**, which gives every member view-and-use access. | Level | What it lets a teammate do | | --- | --- | | READ | View the template, its roles, and its fields, and create envelopes from it | | WRITE | Edit the template — fields, roles, documents, and layout | | PUBLISH | Same as WRITE — templates have no publish step | | ADMIN | Share, unshare, and delete the template | The owner always holds ADMIN implicitly. See [access levels](/docs/guide/concepts.md) for the general model. > READ is the level you want for most of a team: it lets people *send* from the > template without being able to change the layout everyone depends on. ## Related - [Send a document for signature](/docs/guide/platform/document-signing/sending.md) - [Organize and share](/docs/guide/platform/document-signing/organizing.md) - [Document Signing overview](/docs/guide/platform/document-signing.md) - [E-Signature Templates API](/docs/api/poweruser/esignature-templates.md) --- # Organize and Share Source: https://docs.autessa.com/docs/guide/platform/document-signing/organizing > Keep signing work tidy with folders, contacts, search and filters, and control who on your team can see each envelope. # Organize and Share A few hundred contracts in, finding things matters more than sending them. Folders, a contact book, and per-envelope sharing keep a busy signing workspace navigable — and keep sensitive agreements away from the whole company. ## What it's for Filing executed contracts by client or by year, giving a colleague access to one deal without opening your whole workspace, and handing an envelope over when someone leaves the team. ## Navigating here **Dashboard → Document Signing.** The folder tree runs down the left of the envelope list; filters and search sit above the table. ## How to find an envelope The list page gives you four filters, which combine: - **Status** — a multi-select of Drafts, Out for signature, Completed, Rejected, Voided, Expired. It opens on Drafts + Out for signature, your day-to-day working set. Clear the selection to see everything except expired; expired envelopes appear only when you select that chip explicitly. - **People** — narrow to a single recipient. - **Sort** — by last activity, creation date, or title. - **Search** — matches envelope titles and recipient emails. ## How to use folders Folders are the filing system for envelopes. 1. Click **+** beside **Folders** in the sidebar and name it. 2. **Nest** folders by creating one inside another — "Clients" → "Northwind" → "2026". 3. **File an envelope** by dragging it from the table onto a folder. Select several rows first to move them together. 4. **Unfile** by dragging onto **Unfiled**. 5. Click a folder to see only what's in it; **All** shows everything. Two things worth knowing: - **An envelope can live in several folders at once.** Filing isn't moving — it links. A renewal can sit under both "Northwind" and "Q3 Renewals". - **Deleting a folder never deletes contracts.** It un-files them (and cascade-deletes any subfolders and their share grants), but every envelope survives and reappears under Unfiled. ### Sharing a folder Folders share exactly like envelopes and templates: **private** by default, shareable with **specific teammates** at per-person levels, or **company-wide**. A folder you can't see doesn't appear in your sidebar at all. Sharing a folder shares the folder — it's a way to give a team a shared filing structure. It does not retroactively grant access to the envelopes inside it; those carry their own sharing. ## How contacts work There's nothing to set up. Every time you send an envelope, the recipients are remembered in **your own** contact list, ranked by how often you've used them. From then on, typing in any name or email box on the recipients step, the viewers section, or the template-instantiation dialog suggests them — pick one and both name and email fill in. The list is personal to you, not shared company-wide, and it only ever contains people you have actually sent to. ## How to share an envelope By default an envelope is **private** to whoever sent it. Recipients interact with it through their signing and viewing links — they never see your workspace. To bring a colleague in, open the envelope and use **Share**: - **Private** — only you (and anyone you grant explicitly). - **Specific teammates** — each at their own access level. - **Company-wide** — every member of the company gets baseline access. Access levels: | Level | What it allows | | --- | --- | | READ | View the envelope and its audit trail; download the in-progress or sealed copy | | WRITE | Also rename it | | PUBLISH | Also direct links, reminders, resend link, void, and mark as executed | | ADMIN | Also share, unshare, transfer ownership, and delete | The owner always holds ADMIN implicitly. Every share and unshare is recorded on the envelope's audit trail. See [access levels](/docs/guide/concepts.md) for the general model. > PUBLISH is the level to give a colleague covering for you: they can chase > signers, void a dead deal, and seal an executed one, but can't change who the > envelope is shared with. ## How to transfer ownership When someone leaves or a deal changes hands, **transfer ownership** moves an envelope to another company member. The new owner gets implicit ADMIN; you keep whatever explicit grant you had, if any. The transfer is recorded on the audit trail. ## Related - [Track and close out an envelope](/docs/guide/platform/document-signing/tracking.md) - [Reusable templates](/docs/guide/platform/document-signing/templates.md) - [Document Signing overview](/docs/guide/platform/document-signing.md) - [E-Signature Folders API](/docs/api/poweruser/esignature-folders.md) --- # Automate Document Signing Source: https://docs.autessa.com/docs/guide/platform/document-signing/automating > Drive e-signature envelopes from AutessaScript, the Poweruser API, and Custom Apps — including embedding the signer UI in your own app. # Automate Document Signing Everything the builder does, your scripts and apps can do. Generate a contract from a template when a deal closes, send it, watch for completion, and file the sealed PDF — without anyone opening the Document Signing UI. ## What it's for The builder is for one-off sends. Automation is for the repeatable ones: onboarding every new client with the same engagement letter, issuing a renewal the week a subscription lapses, or letting customers sign inside your own product. ## Three ways in | Surface | Use it when | | --- | --- | | [AutessaScript SDK](/docs/autessascript/sdk/eSignature.md) — `autessa.eSignature` | Logic runs inside Autessa: a table trigger, a scheduled job, an agent tool, a custom app backend | | [Poweruser API](/docs/api/poweruser/esignature-envelopes.md) | Logic runs in your own system and calls Autessa over HTTP | | [Custom Apps](/docs/guide/platform/app-builder.md) | You want signers to sign inside an app you built, without leaving it | All three drive the same service, so an envelope created by a script is identical to one created in the builder — same statuses, same audit trail, same sealed PDF. ## The lifecycle, in calls Whichever surface you use, the sequence is the same: 1. **Get an upload URL** and PUT your document bytes to it. 2. **Create** a draft envelope referencing the uploaded file. 3. **Set recipients** — this is where signing order, completion policy, and CC viewers are decided. 4. **Set fields**, binding each signer field to a recipient. 5. **Set prefill values** for any static content. 6. **Send.** 7. **Poll or check** the envelope until it completes, then **download** the sealed PDF. ## How to send an envelope from AutessaScript ```javascript // 1. Upload the document const up = autessa.eSignature.generateFileUploadLink({ fileExtension: 'pdf' }).data; // PUT your PDF bytes to up.uploadUrl, then: // 2. Create the draft const created = autessa.eSignature.envelope.create({ title: 'Engagement Letter — Northwind', s3Uri: up.s3Uri, }); const envelopeId = created.data.envelope.id; // 3. Recipients: signing order, completion policy, and a CC viewer autessa.eSignature.envelope.recipients({ envelopeId, signingOrder: 'SEQUENTIAL', // 'PARALLEL' = everyone at once completionPolicy: 'ALL', // 'ANY_ONE' completes on the first signature recipients: [ { id: 'r1', email: 'client@northwind.com', name: 'Dana Fox', recipientKind: 'SIGNER', signingGroup: 1 }, { id: 'r2', email: 'partner@ourfirm.com', name: 'Sam Reid', recipientKind: 'SIGNER', signingGroup: 2 }, { id: 'r3', email: 'records@ourfirm.com', recipientKind: 'VIEWER' }, // CC'd, never signs ], }); // 4. Fields, bound to recipients autessa.eSignature.envelope.fields({ envelopeId, fields: [ { id: 'f1', fillSource: 'SIGNER', recipientId: 'r1', fieldType: 'SIGNATURE', label: 'Client signature', pageIndex: 0, xPct: 0.6, yPct: 0.8, widthPct: 0.25, heightPct: 0.05 }, { id: 'f2', fillSource: 'PREFILL', fieldType: 'DATE_STATIC', label: 'Effective date', pageIndex: 0, xPct: 0.2, yPct: 0.1, widthPct: 0.15, heightPct: 0.035 }, ], }); // 5. Fill the prefilled content autessa.eSignature.envelope.prefill({ envelopeId, values: [{ fieldId: 'f2', value: '2026-07-15' }], }); // 6. Send — optionally overriding the signing-link expiry const sent = autessa.eSignature.envelope.send({ envelopeId, linkTtlDays: 14 }); if (!sent.ok) console.log('send blocked:', sent.errors); ``` Note the shape of steps 3 and 4: the `id` values you pass for recipients (`r1`, `r2`) are what fields reference as `recipientId`. `signingGroup` is the signer slot — under `SEQUENTIAL` it's also the turn order, and two recipients sharing a `signingGroup` form a client group where either can sign. > `completionPolicy: 'ANY_ONE'` doesn't combine with `signingOrder: 'SEQUENTIAL'` > — a one-at-a-time envelope that completes on the first signature is a > contradiction, and the call is rejected. ## How to send from a template Far less code, because the layout already exists: ```javascript const created = autessa.eSignature.template.createEnvelope({ templateId, title: 'Engagement Letter — Northwind', roleAssignments: [ { roleId: 'role_client', recipients: [ { id: 'r1', email: 'client@northwind.com', name: 'Dana Fox', recipientKind: 'SIGNER' }, ]}, { roleId: 'role_partner', recipients: [ { id: 'r2', email: 'partner@ourfirm.com', recipientKind: 'SIGNER' }, ]}, ], }); autessa.eSignature.envelope.send({ envelopeId: created.data.envelope.id }); ``` Every role must get at least one recipient with a non-blank email. Only READ access on the template is required, so a shared team template can be driven by anyone's automation. Signing order, completion policy, field layout, and CC roles all come from the template. ## How to watch for completion There's no completion webhook. Poll the envelope, or list by status: ```javascript const { data } = autessa.eSignature.envelope.list({ statusFilter: ['SENT'], page: 0, pageSize: 25, }); data.envelopes.forEach(e => console.log(e.title, e.collectedSignatureCount + '/' + e.requiredSignatureCount)); ``` For a single envelope, `envelope.get` returns full state including every recipient's status and the whole audit trail: ```javascript const { ok, data } = autessa.eSignature.envelope.get({ envelopeId }); if (ok && data.envelope.status === 'COMPLETED') { const dl = autessa.eSignature.envelope.download({ envelopeId }); // dl.data.url is a presigned URL to the sealed PDF; dl.data.filename names it } ``` A [scheduled job](/docs/guide/scheduled-jobs.md) running this hourly is also the right way to build automated signer chasing — reminders are manual in the UI, but `envelope.remind` is a call like any other. ## How to check your own signing inbox `autessa.eSignature.inbox.list` returns envelopes where **you** are a recipient, rather than ones you own. There's no inbox view in the builder — it is an API- and SDK-only surface, useful for building a "documents awaiting your signature" panel into an internal app. ```javascript const { data } = autessa.eSignature.inbox.list({ statusFilter: ['SENT'], page: 0 }); ``` ## How to embed signing in a Custom App Custom Apps ship a `` component that renders Autessa's hosted signer UI inline, so a user can sign without leaving your app or opening an email. The flow is: create and send the envelope as usual, resolve a signing token for the current user, then render the component with it. ```jsx ``` The App Builder's copilot also has read-only awareness of your envelopes through two tools — `list_esignature_envelopes` and `get_esignature_envelope_details` — so you can ask it to build screens against real signing data. Both are discovery-only: the copilot can read envelopes, not create, send, or void them. ## How to call it over HTTP If your logic lives outside Autessa, the Poweruser API exposes the same operations under `https://api.autessa.com/autessa_poweruser/e-signature`: - [Envelopes](/docs/api/poweruser/esignature-envelopes.md) — create, recipients, fields, prefill, send, remind, void, download, inbox - [Templates](/docs/api/poweruser/esignature-templates.md) — create, roles, fields, instantiate, share - [Folders](/docs/api/poweruser/esignature-folders.md) — create, nest, file envelopes, share See the [Poweruser API overview](/docs/api/poweruser.md) for authentication. ## Related - [autessa.eSignature SDK reference](/docs/autessascript/sdk/eSignature.md) - [E-Signature Envelopes API](/docs/api/poweruser/esignature-envelopes.md) - [E-Signature Templates API](/docs/api/poweruser/esignature-templates.md) - [Reusable templates](/docs/guide/platform/document-signing/templates.md) - [Document Signing overview](/docs/guide/platform/document-signing.md) --- # Meeting Recorder Source: https://docs.autessa.com/docs/guide/platform/meeting-recorder > Meeting bots that join your calls, record, transcribe, summarize, and share recordings. # Meeting Recorder Meeting Recorder gives your company named recording bots that join Zoom, Google Meet, and Microsoft Teams calls — then transcribe the audio, write an AI summary, email participants a recap, and land everything in an AutessaDB table your agents can use. ## What it's for Instead of taking notes, you invite a bot. Each meeting produces a video (or audio) recording, a transcript, and a Markdown summary you can share with anyone via a tokenized link — and because every recording is a row in AutessaDB, you can chat with the transcript, trigger follow-up automation, or query meetings like any other data. ## Navigating here From the dashboard, select **Meeting Recorder** in the sidebar to open the **Meeting bots** list. The feature is permission-gated, so it only appears for companies with Meeting Recorder enabled. [Open Meeting Recorder →](https://builder.autessa.com/meeting-recorder/bots) ## Key concepts - **Bots** — a bot is a named recorder with its own email address. Invite that email to a meeting (or paste a meeting URL) and the bot joins and records. The display name is what meeting participants see. Each bot owns its own AutessaDB recordings table, a summarizer agent, and its automation tools; deleting the bot deletes them too. - **Bot identity** — the default is an Autessa-hosted alias like `yourname-xxxxxx@bot.autessa.com` (zero setup). Advanced options let you connect a Google Workspace / Microsoft 365 mailbox, or run the bot on your own subdomain (`bot.your-company.com`) after verifying DNS records. - **Calendar auto-record** — link your Google Calendar or Outlook [connectors](/docs/guide/connectors.md) to a bot and choose, per calendar: **Manual — only when I pick**, **Auto-record every meeting**, or **Only meetings matching keywords** (matched against meeting titles). You can also have the bot added to the calendar invite so attendees see it coming. - **Recording lifecycle** — recordings move through Scheduled → Joining → Recording → Processing → Done. The pipeline extracts audio, transcribes, summarizes, notifies participants, and applies retention: recordings are automatically purged after about 180 days. - **What lands** — video/audio media, a transcript, an AI summary, and the participant list, all on an AutessaDB row you can open directly. A "Chat with transcript" panel lets you ask questions about any recording. - **Customizing the summary** — each bot's summary is generated by its own editable LLM Call tool, named `meeting_recorder_summarize_llm_` in your [Tools](/docs/guide/agentic/tools.md) list. Open it and edit the prompt template (it receives the transcript, participants, and language as `@@{...}` arguments) to change the structure, tone, or language of every future summary that bot writes — sections, action-item formats, whatever you need. - **Share links** — tokenized public links (optionally protected with an emailed one-time code and an expiry) let anyone view the summary, play the media, and read the transcript without an Autessa account. Links can be revoked at any time. ## How to create a bot 1. Open **Meeting Recorder** in the sidebar and click to create a new bot. 2. Give the bot a **display name** — this is the name participants see when it joins a call. 3. Pick an email identity. The recommended Autessa-hosted address works instantly; **Show advanced options** offers Workspace OAuth and your-own-subdomain setups. 4. Optionally link calendars and pick an auto-record mode per calendar. 5. Review and create. The bot's email address is ready to invite to meetings. ## How to record a meeting 1. **By invite** — add the bot's email address to the meeting invite, exactly like a human attendee. 2. **By URL** — on the bot's page, click **Paste URL**, paste the Zoom / Meet / Teams link, and optionally set a join time. 3. **From your calendar** — on the **Upcoming** tab, click **Add bot** next to any event in the next 24 hours. 4. **Automatically** — set a linked calendar to auto-record or keyword mode and the bot dispatches itself. 5. You can also **Upload recording** to run an existing MP4/MOV/MKV/WEBM/AVI or MP3/M4A/WAV file through the same transcription and summary pipeline. ## How to share a recording 1. Open the recording from the bot's **Recordings** tab. 2. Use **Share with recipients** to email each person a tokenized link, or **Copy link** for a link you can paste anywhere — optionally with an expiry and a one-time-code requirement. 3. Track and revoke links on the **Share links** tab; **Revoke all** kills every outstanding link at once. > Auto-record runs against the bot owner's linked calendars. Teammates using a shared bot should connect their own calendars or invite the bot's email directly. ## Sharing and access levels Bots follow the standard sharing shape — every bot starts **private** to its creator; share it with **specific teammates**, each at their own level, or **company-wide** at a single level — but use their own three levels rather than the standard four: | Level | What it lets a teammate do | | --- | --- | | Viewer | See the bot and its recordings — transcripts, summaries, media | | Editor | Operate it — dispatch and upload recordings, retry processing, share recordings and links, manage calendar auto-record | | Admin | Manage the bot itself — share it and delete it | The owner always has Admin implicitly. This is separate from public **share links**, which give people *outside* Autessa access to a single recording. See [access levels](/docs/guide/concepts.md) for the general model. ## Under the hood — tools, agents, tables & jobs Meeting Recorder is a fully agentic feature: each bot is assembled from Autessa's standard tools, agents, AutessaDB triggers, and scheduled-job primitives, so you can inspect, disable, edit, or build on top of any part of it. Some resources are shared across all your bots; others are created per bot (their names end in the bot's short id). > These resources are auto-provisioned when you create a bot and are meant to be inspected and tweaked, not hand-deleted one by one. **Deleting a bot deletes its own tools, agents, triggers, scheduled jobs, and recordings table** in one cascade. To simply stop a bot, disable it rather than deleting pieces. ### Tools Find these in your [Tools](/docs/guide/agentic/tools.md) list. | Tool | Type | What it does | | --- | --- | --- | | `autessa_extract_audio` | Extract Audio | Transcodes a recording's video/audio into a clean audio file for transcription. Shared. | | `autessa_transcribe_audio` | Transcribe | Speech-to-text on the extracted audio → transcript. Shared. | | `meeting_recorder_search_old_summaries` | AutessaDB Semantic Query | Semantic search across your past meeting summaries (used by the internal chat agent). Shared. | | `meeting_recorder_summarize_llm_` | LLM Call | **The editable summarizer.** Its prompt template (Big Picture, Highlights, Objections, Action Items by person, Goal for next call) is what shapes every summary this bot writes — edit it to change structure, tone, or language. | | `meeting_recorder_retention_delete_` | AutessaDB Delete | **The retention purge.** Preconfigured to delete recordings older than the retention window (unless marked legal hold). Edit its filters to keep recordings longer or shorter. | | `meeting_recorder_notify_participants_` | Notify | Emails participants the recap with a share link. System-managed. | | Three AutessaScript orchestrators | AutessaScript | Drive the pipeline stages — extract audio, transcribe, and summarize-and-notify — each invoked by the matching trigger below. | ### Agents Both live in your [Agents](/docs/guide/agentic/agents.md) list and answer questions grounded in a recording's transcript. Edit their instructions to change how "Chat with transcript" behaves. | Agent | What it does | | --- | --- | | `meeting_recorder_chat_with_transcript` | The **internal** chat agent for the signed-in recording view. Has the semantic-search tool attached for cross-meeting context. | | `meeting_recorder_public_chat_with_transcript` | The **public** chat agent for share-link viewers. Same grounding rules but no tools attached, so it can't reach other meetings' data. | ### Tables The recordings themselves live in a per-bot [AutessaDB table](/docs/guide/autessadb/tables.md) with columns for the video/audio media, transcript, summary, participants, status, start time, and a legal-hold flag — this is the table the tools and triggers read and write. Behind it, a set of control-plane tables track each bot's config (`meeting_recorder_config`, including its `is_enabled` flag, retention window, quotas, and the ids of all its provisioned resources), its linked calendars, share tokens, usage minutes, connector health, and a webhook/event log. ### AutessaDB triggers These per-bot [triggers](/docs/guide/autessadb/triggers.md) fire the pipeline as each stage's data lands on a recording row. Disable one to pause that stage. | Trigger | Fires when | Runs | | --- | --- | --- | | Extract audio | A recording's video arrives | The extract-audio orchestrator | | Transcribe | The audio is ready | The transcribe orchestrator | | Summarize & notify | The transcript is ready | The summarize-and-notify orchestrator (writes the summary and emails participants) | ### Scheduled jobs Find these in [Scheduled Jobs](/docs/guide/scheduled-jobs.md). | Job | Schedule | What it does | | --- | --- | --- | | Retention purge | Weekly (Sun 03:00 UTC) | Runs the retention-delete tool to purge recordings past the retention window. | | Calendar scan | Every 30 minutes | Checks linked calendars and dispatches the bot to auto-record meetings. | | Connector health check | Hourly | Refreshes the bot's calendar/email OAuth connection so it keeps working. | ### Disabling the feature The whole feature is gated by the **`MEETING_RECORDER`** company permission — turn that off to remove it for a company. To stop an individual bot without deleting it, disable it (its scheduled jobs stop with it). To tweak rather than disable, edit the summarizer prompt, retention filters, triggers, or schedules through the standard UIs above. ## Related - [Meeting Recorder API](/docs/api/poweruser/meeting-recorder.md) - [Connectors](/docs/guide/connectors.md) - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [Triggers](/docs/guide/autessadb/triggers.md) --- # Meeting Reminders Source: https://docs.autessa.com/docs/guide/platform/meeting-reminders > Get a phone call a few minutes before every timed meeting on your linked calendars — with quiet hours, a per-call log, and a customizable reminder voice. # Meeting Reminders Meeting Reminders rings your own phone a few minutes before each timed meeting on your linked calendars, speaks a short reminder, and leaves it as a voicemail if you don't pick up — so a back-to-back day never slips past you. ## What it's for Calendar pop-ups are easy to miss when you're heads-down or away from your screen. Meeting Reminders turns each upcoming meeting into an actual phone call: a scan of your Google Calendar and Outlook connectors runs continuously, and a few minutes before a meeting starts your phone rings with a spoken reminder. Miss the call and it's left as a voicemail. Everything it will do is visible in a per-call log you can open, edit, and cancel from — and, because it's built entirely out of Autessa's standard tools, table, agent, and scheduled-job primitives, you can inspect or rewire any part of it. ## Navigating here From the dashboard, select **Meeting Reminders** in the sidebar to open your reminder setup. Each setup is personal to the person who created it. [Open Meeting Reminders →](https://builder.autessa.com/phone-reminders) ## Key concepts - **Setup** — one reminder configuration, tied to the phone number it calls (your own phone) and the calendars it watches. Creating a setup provisions an Autessa phone number and everything listed under [Under the hood](#under-the-hood--tools-agents-tables--jobs) below. - **Lead time** — how many minutes before a meeting starts the call is placed (default **3**, adjustable 1–60). A meeting already inside the lead window when it's discovered is called right away. - **Linked calendars** — connect your Google Calendar and/or Outlook [connectors](/docs/guide/connectors.md), and (for Google) pick which calendars count. Only timed meetings are considered; all-day events are ignored. - **Quiet hours** — an optional daily window (with its own timezone) during which no calls are placed. A meeting whose reminder would fire inside quiet hours is logged as **skipped** instead of called. - **The calls log** — a table with one row per meeting the scanner acted on, showing the meeting, when the reminder will fire, and a status (**Scheduled**, **Called**, **Skipped (quiet hours)**, **Failed**, or **Cancelled**). This is the source of truth for "what will ring my phone and when." - **The reminder voice** — the call is spoken by a friendly voice assistant. It reads a short prewritten reminder and only starts a back-and-forth if you actually talk back to it. ## How to set it up 1. Open **Meeting Reminders** in the sidebar and create a setup. 2. Enter the **phone number to call** — this is your own phone, in international format. 3. Set the **lead time** (how many minutes ahead the call should come). 4. Link your **calendars** and, for Google, choose which calendars to watch. 5. Optionally enable **quiet hours** and pick the window and timezone. 6. Save. The scanner starts immediately; use **Refresh** any time to scan right now instead of waiting for the next cycle. ## Pausing, cancelling, and deleting - **Pause / resume the whole setup** — use the **Pause** button (or the enabled toggle). Pausing stops the recurring scan, so no new calls are booked; resuming turns it back on. This is the switch to reach for when you want to mute reminders for a while. - **Cancel a single upcoming call** — open the calls log and set that meeting's row **status** to **Cancelled**. When its scheduled time arrives the call is skipped. - **Quiet hours** — the least destructive way to suppress a recurring stretch (e.g. evenings) without pausing everything. - **Delete / reset** — deleting the setup tears everything down (see the note below); reset re-creates it with the same settings on a fresh number. ## Under the hood — tools, agents, tables & jobs Meeting Reminders is assembled from the same building blocks you use everywhere else in Autessa, so you can inspect, disable, edit, or extend any part of it. Creating one setup provisions the following. > These resources are auto-provisioned per setup and are meant to be inspected and tweaked, not hand-deleted one by one. **Deleting the setup deletes them all** (the phone number, tools, agent, table, and scheduled job) in one cascade. To simply stop reminders, **pause** the setup rather than deleting pieces. ### Tools Find these in your [Tools](/docs/guide/agentic/tools.md) list. Some are shared across all your setups; others are created per setup (their names end in a short setup id). | Tool | Type | What it does | | --- | --- | --- | | `phone_reminder_scan_orchestrator` | AutessaScript | The scanner. Lists the next 24 hours of meetings, applies quiet-hours and all-day filters, and books a reminder call for each. | | `phone_reminder_fire_orchestrator` | AutessaScript | Runs when a booked reminder fires: re-checks the meeting's row status, then places the call (skips it if the row was cancelled). | | `phone_reminder_query_rows` / `_insert_row` / `_update_row` | AutessaDB | Read, add, and update rows in the calls log table. | | `meeting_reminder_call_` | Make Phone Call | Places the actual call from your Autessa number and speaks the reminder (leaving voicemail if unanswered). | | `meeting_reminder_schedule_call_` | Schedule Event | Books the one-shot reminder at *(meeting start − lead time)*. | | `meeting_reminder_list_google_` / `_outlook_` | Calendar — List Events | Lists upcoming events from each linked calendar connector. One per connector. | To **customize behavior**, edit the orchestrator scripts (e.g. change the 24-hour horizon or filtering) or the call tool (e.g. tweak the spoken message). To **stop a specific action**, you can disable a tool — but pausing the setup is the cleaner switch. ### Agent | Agent | What it does | | --- | --- | | `meeting_reminder_agent_` | The voice that makes the call — a simple reminder assistant. It reads the prewritten reminder and only engages in conversation if you talk back. Open it in [Agents](/docs/guide/agentic/agents.md) to change its instructions or its voice. | ### Table The calls log is an [AutessaDB table](/docs/guide/autessadb/tables.md) named `meeting_call_reminders_`, with one row per meeting the scanner acted on. Key columns: the meeting title / URL / organizer, the start time, the reminder fire time, and a **status** (`SCHEDULED`, `CALLED`, `SKIPPED_QUIET_HOURS`, `FAILED`, `CANCELLED`). Editing a row's status to `CANCELLED` is how you skip an individual call. (Two small config tables behind the scenes hold the setup's settings and its linked calendars.) ### Scheduled job | Job | Schedule | What it does | | --- | --- | --- | | Calendar scan | Every **30 minutes** | Runs the scan orchestrator to discover meetings and book reminder calls. Its enabled state mirrors the setup — **pausing the setup disables this job**. | | Per-meeting reminder | One-shot, at *(start − lead)* | Booked by the scan for each meeting; runs the fire orchestrator to place that call. | Find the recurring scan in [Scheduled Jobs](/docs/guide/scheduled-jobs.md). The per-meeting one-shots appear there too as they're booked. ### Disabling the feature There's no global on/off flag — reminders are controlled per setup. **Pause** the setup to stop all future calls (this disables the scan job), set individual rows to `CANCELLED` to skip single calls, use **quiet hours** for recurring windows, or **delete** the setup to remove everything. ## Related - [Connectors](/docs/guide/connectors.md) - [Scheduled Jobs](/docs/guide/scheduled-jobs.md) - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) --- # Marketplace Source: https://docs.autessa.com/docs/guide/marketplace > Move complete workflows between companies with private bundles and public catalog templates. # Marketplace The Marketplace moves resources between companies, intact. Package a working setup — agents, tools, tables, and everything they depend on — into a bundle, then hand it to another company as a file or publish it as a template in the public catalog. ## What it's for Once you've built a workflow that works, the Marketplace is how you distribute it. It solves the hard part for you: resolving every dependency a resource has, stripping secrets so nothing sensitive leaves your company, and recreating the whole graph on the other side in the right order — with a clean rollback if anything fails mid-install. There are two distribution mechanisms, both built on the same bundle pipeline: - **Private bundles** — export a selection of resources to a downloadable JSON file and share it directly. Anyone in another company uploads the file and installs it. No review, no listing. See [Bundles](/docs/guide/marketplace/bundles.md). - **Public catalog templates** — publish a bundle as a versioned, reviewed listing with a name, screenshots, and release notes. Other companies discover and install it from the catalog. See [Catalog](/docs/guide/marketplace/catalog.md) for installing and [Publishing](/docs/guide/marketplace/publishing.md) for listing your own. > The Marketplace is managed entirely in the app — exporting, publishing, and installing all happen in the builder. There is no public Marketplace API. ## Navigating here Click **Marketplace** in the builder sidebar. The landing page offers three actions — **Browse catalog**, **Export bundle**, and **Import bundle** — plus a **Recent installs** feed showing your company's latest installations with their status. Click any install to open its detail page. ## Key concepts ### What can be packaged A bundle can contain nine resource types: **tools**, **agents**, **managers** (multi-agents), **AutessaDB tables** (including their triggers), **scheduled jobs**, **webhooks**, **custom apps**, **MCP servers**, and **connector requirements**. Custom apps are pinned to their latest published version at export; the other types embed their full configuration inline so an install never depends on the source company's live state. ### Dependencies resolve automatically Select a manager and its agents come along; select an agent and its tools and MCP server come along. The export wizard walks the dependency graph transitively and shows you exactly what was auto-included before anything leaves your company. Bundles are capped at 200 resources with a dependency depth of 8. ### Secrets are stripped, then re-supplied Credentials never travel in a bundle. MCP server auth configuration and connector credentials are removed at export and recorded in the manifest, and the installer asks the receiving company to supply its own: secret fields are re-entered during import, and connectors must be linked to an already-authenticated connector in the target company. ### Installs are a resumable saga Installation creates resources one at a time, dependencies first, recording each step as it commits — so the status page updates live. If any step fails, every resource created so far is rolled back automatically and the company is left exactly as it was. Completed installs can be revisited any time from **Recent installs** or the installations list, where you can also share the installed resources with your team or uninstall the whole set. ### Versions Catalog templates are versioned with dotted version numbers (semver-style, starting at 1.0.0). Each new version is a fresh bundle with its own changelog and goes through review before it is publicly visible. > Installing a template never reads the publisher's live resources — everything comes from the manifest that was captured at export time, so a listing can't change under you. ## Related - [Bundles](/docs/guide/marketplace/bundles.md) - [Catalog](/docs/guide/marketplace/catalog.md) - [Publishing](/docs/guide/marketplace/publishing.md) - [Connectors](/docs/guide/connectors.md) --- # Bundles Source: https://docs.autessa.com/docs/guide/marketplace/bundles > Exporting resources to a bundle file and importing one into another company. # Bundles A bundle is a JSON file containing a manifest of resources — your selection plus everything it depends on — with all secrets stripped. Export one from your company, hand the file to anyone, and they import it into theirs. ## What it's for Bundles are the private, no-review way to move work between companies: cloning a workflow into a client's workspace, migrating between environments, or sharing a setup with a partner without publishing it publicly. The same bundle format also powers catalog templates, so everything on this page applies there too. ## Navigating here From **Marketplace** in the sidebar, choose **Export bundle** or **Import bundle** on the landing page. Past and in-progress installations live under the installation detail pages, reachable from the landing page's **Recent installs** feed. ## Key concepts - **Manifest** — the bundle file (`autessa-bundle-.json`) contains every resource definition, the dependency graph between them, connector requirements, and a record of which secret fields were stripped. - **Dependency auto-resolution** — the export walks references transitively: a manager pulls in its agents, an agent pulls in its tools and MCP server, a tool pulls in the table it queries, and so on. Auto-included resources are shown separately from your explicit selection and can be unchecked to drop them (and their own dependents) from the bundle. - **Secrets stripping** — MCP server auth configuration and connector credentials/OAuth state are removed at export. The receiving company re-supplies them at import; connectors specifically can never be recreated from a bundle and must be linked to an existing authenticated connector on the target side. - **Limits** — a bundle holds at most **200 resources**, and the dependency graph may be at most **8 levels deep**. Exceeding either fails the export with a message to narrow the selection or split the bundle. Custom apps must have a published version; the export pins each app to its latest published version. - **Install saga** — imports create resources dependencies-first, committing each step individually. On failure, everything already created is rolled back and the install is marked failed with an error log. ## How to export a bundle 1. Open **Marketplace → Export bundle**. 2. **Pick resources** — filter by type in the left rail, search by name or description, and select the resources you want. The summary rail tracks your running total and reminds you that dependencies are pulled in automatically. Click **Continue to review**. 3. **Review & export** — the preview splits the bundle into **You selected** and **Auto-included as dependencies**. Uncheck any auto-included resource you don't want to ship. 4. Click **Download bundle** to save the JSON file (or **Publish to marketplace** to turn the same selection into a catalog template instead). ## How to import a bundle 1. Open **Marketplace → Import bundle** and upload the `.json` file (drag and drop or browse). 2. **Review** — the wizard lists everything inside, grouped by type, and how many resources will install into your company. 3. **Dependencies** — for each resource, choose **Create new** (the default) or **Link to existing** if you already have an equivalent; the picker shows match confidence. **Connectors must be linked** to an existing, authenticated connector — if you don't have one, the wizard deep-links to the connector's setup page, and you can't proceed until every connector is linked. 4. **Supply credentials** — enter values for each stripped secret field, or leave them blank to configure later (MCP server auth, for example, can be finished afterwards on the MCP Servers page). 5. **Install** — the install runs automatically with a live per-resource timeline. Linked resources complete instantly; created resources appear as they commit. Newly created tools, agents, managers, and custom apps are published automatically when the install finishes. ## How to track an installation 1. Open the installation from **Recent installs** on the Marketplace landing page (or from the import wizard's final screen). 2. While the install runs, the page polls live: each resource shows **Creating…**, then **Created** with a link to its editor, or **Linked** if you mapped it to an existing resource. 3. If the install fails, the error log is shown and each created resource reads **Rolled back** — your company is left in the state it was before. Fix the cause (most often a missing connector or invalid secret) and re-import. 4. On success, a share card appears — see below. ## How to share installed resources Completed installations offer sharing without leaving the status page: 1. Use the per-resource **Share** button, or the bulk **Share these resources?** card for the whole install. 2. Choose **Share with company (read-only)**, **Share with company (can edit)**, or **Keep private**; the bulk card can also share with specific teammates with read or write access. 3. MCP servers and connectors are excluded — MCP servers are company-wide by construction, and connectors are gated by their own credentials. > To share a single resource with another company rather than your own team, export it as a one-resource bundle: the export wizard accepts a single seed resource and will pull in only what it needs. ## Related - [Marketplace overview](/docs/guide/marketplace.md) - [Catalog](/docs/guide/marketplace/catalog.md) - [Publishing](/docs/guide/marketplace/publishing.md) - [Connectors](/docs/guide/connectors.md) - [MCP Servers](/docs/guide/agentic/mcp-servers.md) --- # Catalog Source: https://docs.autessa.com/docs/guide/marketplace/catalog > Browsing the public template catalog, installing templates, and managing installations. # Catalog The catalog is the public face of the Marketplace: a browsable library of reviewed templates you can install into your company in a few clicks, each with screenshots, a resource breakdown, and release notes. ## What it's for Instead of building a workflow from scratch, install one. Catalog templates are bundles that publishers have listed publicly (after admin review) or shared with their own company. Every template page shows exactly what will be installed — down to a dependency diagram — before you commit. _(interactive UI demo omitted)_ ## Navigating here From **Marketplace** in the sidebar, click **Browse catalog**. Your installed templates live under **My Installations**, linked from the Marketplace pages. ## Key concepts - **Tabs and discovery** — the catalog has three tabs: **All**, **Featured**, and **My templates**. The All tab surfaces a **Featured** row of hand-picked templates. Search matches template names and descriptions, and a **Category** sidebar filters by the catalog's categories (categories are curated by Autessa and evolve over time — there is no fixed list). - **Visibility badges** — cards are badged **Public** (approved for everyone), **Company** (shared within the publisher's company), or, for your own templates, **Draft**, **Pending**, **Rejected**, or **Suspended**. Only public and company templates can be installed. - **Template detail page** — the hero shows the name, author, current version, and resource count with an **Install template** button. Below it: the long description, a **What gets installed** dependency diagram, a full resource breakdown from the manifest, **Release notes** for every version, a screenshots gallery, and a **Security** card confirming all credentials were stripped at export and you'll supply your own. - **Pre-check** — clicking install runs a pre-check against your company before anything is created: it fetches the manifest, finds existing resources you could link instead of duplicating, and lists the secrets and connector requirements you'll need to satisfy. - **Install** — from there, installation is the standard bundle import: review contents, link or create each resource, connect required connectors, supply secrets, and watch the live install timeline. See [Bundles](/docs/guide/marketplace/bundles.md) for the step-by-step. ## How to install a template 1. Find the template in the catalog and open its detail page. 2. Review **What gets installed** and the **Contents** counts — tables, tools, agents, multi-agents, scheduled jobs, webhooks, custom apps, MCP servers, and connectors. 3. Check the **Security** card and release notes, then click **Install template**. 4. The import wizard opens pre-loaded with the template. Work through **Dependencies** (create new or link existing; connectors must be linked to an authenticated connector you already have) and **Supply credentials**. 5. The install runs with a live per-resource timeline. On success, the resources are yours — visible and editable like anything you built by hand — and the install appears in **My Installations**. > If a template requires a connector you haven't set up (say, Google Calendar), authenticate it on the Connectors page first — the wizard links you there, but the OAuth flow happens outside the install. ## How to manage your installations 1. Open **My Installations** from the Marketplace. The page lists every completed install with totals for installations and resources created, plus search. 2. Click an installation to see its detail page: each resource with a link to its editor, and sharing controls. 3. To remove one, use **Uninstall**. Uninstalling deletes every resource the install created (resources you linked to existing ones are left untouched). ## How to move to a newer version Templates version over time — release notes on the detail page show what changed. In-place upgrade is not currently supported: to move to a newer version, uninstall the old installation and install the latest version fresh. If you've customized installed resources, note your changes first, since uninstalling deletes the created resources. ## Related - [Marketplace overview](/docs/guide/marketplace.md) - [Bundles](/docs/guide/marketplace/bundles.md) - [Publishing](/docs/guide/marketplace/publishing.md) - [Connectors](/docs/guide/connectors.md) --- # Publishing Source: https://docs.autessa.com/docs/guide/marketplace/publishing > Turning your resources into a catalog template: the publish wizard, review lifecycle, and versioning. # Publishing Publishing turns a bundle of your resources into a catalog template — a listing with a name, screenshots, and release notes that other companies can discover and install. Public listings go through Autessa review; company-only and private drafts don't. ## What it's for If you've built something worth reusing — a support workflow, a CRM on AutessaDB, an app — publishing is how you distribute it at scale. The publish flow captures your resources into a versioned manifest, collects listing metadata, and manages the review lifecycle from submission to going live. ## Navigating here Two paths lead to the same place: - **From the export wizard** — select resources under **Marketplace → Export bundle** and choose **Publish to marketplace** at the review step. This is the primary flow. - **The create wizard** at `/marketplace/create` — a four-step version of the same thing: select resources, preview the manifest, fill in metadata, review and submit. Your existing templates live under the catalog's **My templates** tab; each template's detail page has a **Manage** menu for editing, versioning, and submitting. ## Key concepts - **Listing metadata** — a template has a **name**, a URL **slug** (auto-derived), a **short description** shown on cards, a markdown **long description**, an optional **category**, **tags**, a 16:9 **cover image**, up to **6 screenshots** (each cropped 16:9, 8 MB max per image), and optional **setup instructions**. - **Visibility** — at publish time you pick one of three: **Save as draft (private)**, **Publish to your company** (visible and installable inside your company only), or **Submit for public review**. Public submissions include submission notes for the reviewer. - **Review lifecycle** — a submitted template is **pending review** until an Autessa admin **approves** (it goes live in the public catalog, optionally featured) or **rejects** it (you'll get the reasoning in the review thread on your template page, fix things, and resubmit). Live templates can later be **suspended** by admins and **reinstated**. Once a template has a published version, review of a new version never takes the existing version away from installers. - **Versions** — the first publish creates version **1.0.0**. New versions default to a patch bump (1.0.0 → 1.0.1) with a **What changed** changelog that becomes the release notes readers see. Each version is a freshly captured bundle and is reviewed independently. Draft and rejected versions can be deleted; a template that was never public and has no installs can be deleted entirely. - **What travels** — the manifest captures your resources as they are at publish time. Secrets are stripped exactly as in a bundle export, and custom apps are pinned to their latest published version — publish the app first. ## How to publish a template 1. Open **Marketplace → Export bundle**, select your resources, and review the dependency preview — everything shown will be part of the listing. 2. Click **Publish to marketplace**. 3. Fill in the listing: name, short and long descriptions, category, tags, cover image, and screenshots. 4. Choose visibility — draft, company, or **Submit for public review** (add submission notes explaining what the template does and how you tested it). 5. Submit. Drafts and company templates are usable immediately; public submissions appear as **Pending** until reviewed, and you'll be emailed on approval or rejection. ## How to release a new version 1. Open your template's detail page and choose **New version** from the **Manage** menu. 2. The export wizard opens pre-seeded with the previous version's resources — adjust the selection if the workflow changed. 3. In the publish dialog, confirm the version number (a patch bump is suggested) and write the **What changed** changelog. Cover image and screenshots carry forward; update them if the UI changed. 4. Submit for review. Your currently published version stays live for installers until the new one is approved. ## Making listings people trust Installing a template means creating real resources in someone's company, so the listing has to earn that trust: - **Screenshots are the showcase** — capture the actual installed experience (the app, the agent conversation, the table) rather than logos or abstract art. Use all six slots if the template has that much surface. - **Write release notes like a changelog, not a commit message** — every version's notes are public on the detail page. Say what changed and why an installer should care. - **Make the long description answer "what will this do to my company?"** — the dependency diagram and resource breakdown render automatically, but your description should explain what each major piece is for and what connectors or credentials the installer must bring. - **Include setup instructions** for anything the install can't do itself: connector authentication, secrets to obtain, configuration to review after install. > Reviewers install your template into a clean company. If it needs undocumented setup to work, that's the most common reason for a rejection. ## Related - [Marketplace overview](/docs/guide/marketplace.md) - [Bundles](/docs/guide/marketplace/bundles.md) - [Catalog](/docs/guide/marketplace/catalog.md) - [App Builder](/docs/guide/platform/app-builder.md) --- # Tools Source: https://docs.autessa.com/docs/guide/agentic/tools > Creating and configuring tools — the atomic actions agents can take. # Tools A tool is the lowest-level abstraction in Autessa: one specific, executable action — call an API, run a script, query AutessaDB, send an email. Agents accomplish work by choosing and invoking tools, so well-named, well-described tools are the foundation of every workflow you build. ## What it's for If you think of a workflow as a flow chart, a tool is a single node — the smallest building block of automation. Tools are reusable: the same `GetWeather` tool can serve a chat agent, an [AutessaDB trigger](/docs/guide/autessadb/triggers.md), a [scheduled job](/docs/guide/scheduled-jobs.md), or a direct API call. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Tools**. The list has ownership tabs (**Owned By Me / Shared With Me / Company Wide**), search, and type/tag filters. [View Tools →](https://builder.autessa.com/agentic/tools) ## Key concepts ### Name and description matter Agents read a tool's name, description, and argument descriptions to decide *when* to call it and *what* to pass. A tool that gets the weather should be named something like `GetWeather` (names allow letters, numbers, and underscores — no spaces), with a description covering what it does and when to use it. ### Tool types Every tool has a **type** that determines how it executes and what its configuration looks like. The type selector organizes roughly 300 types into categories — Core Tools, AutessaDB, Communication, File Management, Social Media, Search & Utilities, CRM, Accounting & Finance, and more. A few foundational ones: - **Autessa Script** — a scripting language built on top of JavaScript, for when you need full custom logic. See the [AutessaScript documentation](/docs/autessascript.md). - **API Call** — make a GET, POST, or PATCH request to any API, whether you host it or someone else does (for example, a weather API). - **LLM Call** — run a prompt template against an LLM, with dynamic fields filled from arguments; great for summarization or generation steps. - **Connector-backed types** — Gmail, Google Calendar, QuickBooks, Notion, and many more, each executing against a [connector](/docs/guide/connectors.md) you've linked. For the full catalog with every configuration field, see the [Tool Types reference](/docs/tools.md). ### Arguments and placeholders Arguments are the values passed to the tool dynamically at call time. In the collapsible **Arguments** panel on the left of the tool editor, each argument gets a name, a type (String, Number, or Boolean), a description, and a required flag. For a weather tool, you might define `latitude` and `longitude`. _(interactive UI demo omitted)_ Write a detailed description for every argument — like the tool description, this is what the model reads when deciding what to pass. Inside the type-specific configuration, reference arguments and [environment variables](/docs/guide/agentic/environment-variables.md) with `@@{variableName}` placeholders. Typing `@@` in any config field opens an autocomplete over your arguments, private arguments, prompt variables, and environment variables; placeholders are substituted at execution time. ### Private arguments The Arguments panel has a second tab: **Private**. Private arguments come into play when an agent uses the tool and needs a value you don't want an LLM involved in collecting — a credit card number, for example. They're collected directly from the user in-memory and are **never stored in any Autessa history**. > Private arguments are never stored in any history. When collecting sensitive data from a user, use private arguments. _(interactive UI demo omitted)_ Because private arguments are collected via a hardcoded question rather than by the model, the **Message to User** is what matters most — make it a clear instruction for what to provide. Each private argument also requires validation: either a **regex pattern** (e.g. `^[A-Za-z0-9-_]+$`) or a comma-separated list of **allowed values**, so you can guarantee the input arrives in the format you expect. Private arguments are always required strings. ## How to create a tool 1. On the Tools page, click **Add New Tool → Create New Tool**. (You can also bulk-create tools from a connected [MCP server](/docs/guide/agentic/mcp-servers.md).) _(interactive UI demo omitted)_ 2. Give it a name that's intuitive to what it does and a detailed description of what it does and when to use it, then click **Create Tool** — you land in the tool editor. 3. Pick the **tool type** from the searchable category tree and fill in its configuration (endpoints, scripts, queries, connector selection — whatever the type requires). 4. Define **arguments** for every dynamic input, using `@@{argName}` placeholders in the config where the values should land. 5. **Save**. From the **Manage** menu you can share the tool, clone it, or publish a version (see [Versions](/docs/guide/agentic/versions.md)). ## How to test a tool 1. After saving, click **Execute Tool** in the top right of the editor. A panel opens with one input per argument (private arguments show their Message to User). 2. Run it either way: - **Execute Tool** — a plain request/response execution. - **Execute with Streaming** — runs over a live connection with a console of timestamped status updates. Streaming is useful for longer-running tools, where you can stream progress updates to the user during execution. 3. Review the result: success/failure badge, elapsed time, the returned data, and — for Autessa Script tools — the execution logs. ## Let the copilot build it You don't have to configure tools by hand. The **Autobot** button on the Tools page opens the [Agentic Copilot](/docs/guide/autobots/agentic-copilot.md) — describe the tool you want in plain language and it proposes a complete tool definition you review and apply. ## Sharing and access levels Every tool starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. What each level allows: | Level | What it lets a teammate do | | --- | --- | | READ | View the tool, its versions, and its sharing details, and test-run it | | WRITE | Edit the draft | | PUBLISH | Publish and roll back versions | | ADMIN | Share and delete the tool | The owner always has ADMIN implicitly; transferring ownership is owner-only. ## Related - [Tool Types reference](/docs/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Environment Variables](/docs/guide/agentic/environment-variables.md) - [MCP Servers](/docs/guide/agentic/mcp-servers.md) - [Versions](/docs/guide/agentic/versions.md) - [AutessaScript](/docs/autessascript.md) - [Tools Poweruser API](/docs/api/poweruser/tools.md) --- # Agents Source: https://docs.autessa.com/docs/guide/agentic/agents > Building conversational and autonomous agents with instructions, models, and tools. # Agents An agent is essentially your own custom LLM with tools attached. Given a request, it intelligently chooses which tools to invoke and in what order — chatting with a user in your custom voice, or running unattended and deciding its next step from each tool's output. ## What it's for Agents are the workers of the platform. In a chatbot setting, an agent asks the user guiding questions to gather what it needs and invokes tools on their behalf. In an offline setting — a [scheduled job](/docs/guide/scheduled-jobs.md) or an [AutessaDB trigger](/docs/guide/autessadb/triggers.md) — it chains tool calls autonomously. > Example: a blog-writing agent on a weekly schedule could scour the internet for trending topics, check your existing blog for what's already published, research its selected topic, and write a draft post — deciding each step from the previous one's output. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Agents**. The list has the usual ownership tabs (**Owned By Me / Shared With Me / Company Wide**) plus search and filters by type, model, and tags. **Add New Agent → Create New Agent** starts a new one. ## Key concepts ### Agent types You pick a type at creation (it's fixed afterwards, except when cloning): | Type | Behavior | | --- | --- | | **Simple** | You manually select and configure the agent's tools — the default choice. | | **MCP** | Powered by an [MCP server](/docs/guide/agentic/mcp-servers.md); automatically uses all of that server's tools, kept in sync. | | **Critic** | Iterative refinement: a critic model evaluates each response against your good/bad criteria and sends it back for another attempt until it passes or the max iterations are reached. | | **Search** | Performs semantic search over an AutessaDB knowledge base table and retrieves matching content. | Type-specific configuration: MCP agents pick their server; Critic agents get a **Critic Model**, **Max Critic Iterations** (1–10), and lists of **Good Criteria** and **Bad Criteria**; Search agents pick the **Knowledge Base Table**, the **Search Column**, **Top K Results** (1–100), and a **Score Threshold** (0–1). ### Name, description, and instructions Name the agent for what it does at a high level — a blog-writing agent might be `BlogWriter` — and give it a detailed description of when it's useful. The description isn't just for humans: [managers](/docs/guide/agentic/managers.md) read it to decide when to delegate to this agent. **Instructions** are the agent's system prompt — the protocol it should always follow. For an autonomous agent, spell out the sequence of steps; for conversational agents this can be lighter since the user steers, but high-level rules still help. Define **Prompt Variables** (e.g. `customer_name`) and reference them — along with environment variables — in the instructions as `@@{customer_name}`; they're filled in at runtime when the agent is invoked. Built-ins like `@@{currentDate}` and `@@{currentTime}` (UTC) work here too with no setup — starting instructions with `Today is @@{currentDate}.` keeps a date-aware agent from guessing the date; see [built-in variables](/docs/guide/agentic/environment-variables.md#built-in-variables). ### Model and voice The **Model Type** determines the AI powering the agent: **Small Instruction Following**, **Big Instruction Following**, and **Big Instruction Following Reasoning** are always available, with additional privately-hosted models available to companies with access. Larger models are more capable but use more credits. For spoken interactions, choose a **Voice Model** and **Voice Name** — including your own [custom voices](/docs/guide/custom-voices.md). ### Linked tools An agent is nothing without its unique capabilities. In the **Associated Tools** section, either create a new tool in place or import existing ones. Remember: the tool name, tool description, and argument descriptions are how the model chooses the appropriate functions to call and when — make them as clear as possible. (MCP agents skip this section; their tools sync from the server automatically.) ## How to create an agent 1. On the Agents page, click **Add New Agent → Create New Agent**. 2. Pick the **Agent Type**, then enter a name (no spaces) and a description of when this agent is useful. 3. In the editor, set the **Model Type**, write the **Instructions**, and define any **Prompt Variables**. 4. Attach tools under **Associated Tools** (or configure the type-specific section for MCP, Critic, or Search agents). 5. **Save** — and iterate. The [Agentic Copilot](/docs/guide/autobots/agentic-copilot.md) can also draft agents for you from a plain-language description. ## How to test your agent 1. Click **Test** in the top right of the agent editor. A streaming chat window opens with a live connection to your draft. 2. Send a prompt or starter sentence and watch it respond — tool calls appear as expandable cards so you can inspect exactly what was invoked with which arguments (and, for Critic agents, each rejected attempt with the critic's reasoning). 3. If the agent defines prompt variables, a form above the input lets you fill them for the session. You can also attach images and files or record audio input from the microphone. _(interactive UI demo omitted)_ ### Testing with audio The test window's output toggle switches between **Text** and **Audio** responses. In Audio mode the agent's replies stream back as speech in the configured voice and auto-play, with a transcription history panel alongside — so you can hear exactly how your agent will sound before wiring it to a [phone number](/docs/guide/agentic/phone-numbers.md). There's also a separate **Real Time Voice** button for a live, full-duplex voice conversation. ## How to publish Editing happens in the draft; when the agent is ready for customers, use **Manage → Publish** (requires PUBLISH or ADMIN access). Publishing makes this version the default served through the API, and you can roll back any time — see [Versions](/docs/guide/agentic/versions.md). **Manage → Embed** generates a chat-widget snippet for your own site, and **Share** controls teammate and company access. ## Sharing and access levels Every agent starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. What each level allows: | Level | What it lets a teammate do | | --- | --- | | READ | View the agent, its versions, and its sharing details, and test-run it | | WRITE | Edit the draft and submit human evaluations | | PUBLISH | Publish and roll back versions | | ADMIN | Share and delete the agent | The owner always has ADMIN implicitly; transferring ownership is owner-only. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Managers](/docs/guide/agentic/managers.md) - [Agent History](/docs/guide/agentic/agent-history.md) - [Versions](/docs/guide/agentic/versions.md) - [Custom Voices](/docs/guide/custom-voices.md) - [Agents API](/docs/api/agents.md) - [Agentic Copilot](/docs/guide/autobots/agentic-copilot.md) --- # Managers Source: https://docs.autessa.com/docs/guide/agentic/managers > Orchestrating multiple agents with a manager: the delegation model, when to use one, testing, and billing. # Managers A manager (shown as a Multi-Agent in the builder) orchestrates a team of agents. Given a request, it breaks the work into subtasks, delegates each one to the best-suited agent from its pool, and synthesizes the results into a single answer — possibly over several rounds of delegation. ## What it's for A single agent works best with a focused job and a modest toolset. When a workflow spans several distinct capabilities — say, researching a customer, drafting a proposal, and updating your CRM — a manager lets you keep each capability in its own specialist agent with tight instructions, and puts one coordinator in front of all of them. Callers (a chat user, a [phone number](/docs/guide/agentic/phone-numbers.md), an embed, or the API) talk to the manager; the manager decides who does what. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Multi-Agents**. The list has the usual ownership tabs (**Owned By Me / Shared With Me / Company Wide**) plus search and filters. **Create Multi-Agent** starts a new one — like agents and tools, the name allows letters, numbers, and underscores (no spaces), and the description should say what workflow this manager orchestrates. [View Multi-Agents →](https://builder.autessa.com/agentic/managers) ## Key concepts ### The delegation model Under the hood, each agent in the manager's pool is exposed to the manager's model as an `invoke_` function whose main argument is the task to assign. A run proceeds as a loop: 1. The manager reads the request (and its own instructions) and delegates a task to one or more sub-agents. 2. Each sub-agent runs its own conversation with its own tools, plus two injected signal tools: **Complete** (report back a summary of what was accomplished) and **Help** (report that it's stuck — what it needs and what it already tried). 3. The manager reads each result. A *Complete* feeds into its synthesis; a *Help* prompts it to supply the missing information or hand the task to a different agent. 4. When all subtasks are resolved, the manager composes the final response. This is why sub-agent **descriptions** matter so much: they're what the manager reads when deciding where to delegate. An agent whose description clearly says when it's useful gets the right tasks. ### The manager definition A manager's editor mirrors an agent's: a **Model Type** (the model that plans delegations and synthesizes results), **Instructions** (delegation rules, ordering, and how to combine outputs), **Prompt Variables** referenced as `@@{variable_name}`, tags, and an optional **Voice Model / Voice Name** for spoken interactions — including [custom voices](/docs/guide/custom-voices.md). ### The agent pool The **Agents** section holds the sub-agents this manager can delegate to. **Import** existing agents or create a new one in place. Each should cover a distinct capability — overlapping agents force the manager to guess. ### When to use a manager vs. one agent with many tools Prefer a single agent when one system prompt can sensibly govern all its tools. Reach for a manager when: - **The toolset is getting large or mixed.** A model choosing among dozens of loosely related tools makes worse calls than a manager choosing among a few well-described specialists. - **Subtasks need different instructions.** A research step and a bookkeeping step want different rules, different models, maybe different knowledge bases — that's per-agent configuration a single prompt can't express cleanly. - **You want to reuse existing agents.** Agents you've already built and tested slot straight into a pool. > Billing: a manager conversation bills as the number of sub-agents configured on the manager — a manager with three agents bills three AI tasks per conversation, and the sub-agent invocations inside the run are not billed separately. Keep the pool to agents the workflow actually needs. ## How to create a manager 1. On the Multi-Agents page, click **Create Multi-Agent**; enter a name (no spaces) and a description of the workflow it orchestrates. 2. In the editor, pick the **Model Type** and write **Instructions**: which agent handles what, in what order, and how to combine results. 3. Under **Agents**, import existing agents or create new ones. Review each agent's description — it's the manager's routing signal. 4. Define any **Prompt Variables**, then **Save**. ## How to test a manager 1. Click **Test** in the top right of the editor. The streaming test window opens against your draft. 2. Send a request and watch the orchestration live: each delegation appears as a collapsible **agent invocation card** showing the sub-agent's name, the exact task the manager assigned, its status, and the tool calls it makes as it works. 3. Iterate: if the wrong agent got a task, sharpen the agents' descriptions or the manager's instructions; if the right agent failed, fix that agent (its own **Test** button is one click away). There's also a **Real Time Voice** option for a live voice conversation with the manager. Past runs — including full nested sub-agent transcripts — live in [Manager History](/docs/guide/agentic/manager-history.md). ## How to publish and share **Manage → Publish** makes the current draft the version served through the API (see [Versions](/docs/guide/agentic/versions.md)), **Manage → Embed** generates a chat-widget snippet, and **Share** grants teammate or company-wide access — the same lifecycle as agents. ## Sharing and access levels Every multi-agent starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. What each level allows: | Level | What it lets a teammate do | | --- | --- | | READ | View the manager, its versions, and its sharing details, and test-run it | | WRITE | Edit the draft | | PUBLISH | Publish and roll back versions | | ADMIN | Share and delete the manager | The owner always has ADMIN implicitly; transferring ownership is owner-only. ## Related - [Agents](/docs/guide/agentic/agents.md) - [Manager History](/docs/guide/agentic/manager-history.md) - [Phone Numbers](/docs/guide/agentic/phone-numbers.md) - [Versions](/docs/guide/agentic/versions.md) - [Managers API](/docs/api/managers.md) - [Managers Poweruser API](/docs/api/poweruser/managers.md) --- # MCP Servers Source: https://docs.autessa.com/docs/guide/agentic/mcp-servers > Connect Model Context Protocol servers and import their tools into your agents. # MCP Servers MCP (Model Context Protocol) servers expose external tool capabilities over a standard protocol. Register a server once and you can instantly import its tools as regular Autessa tools, or point an MCP agent at it to use everything the server offers automatically. ## What it's for If a vendor or your own team already ships an MCP server — GitHub, an internal API gateway, anything speaking the protocol — you don't have to rebuild each capability as a hand-configured tool. Connecting the server gives you two ways to use it: - **Import its tools** — one click creates a regular Autessa tool for each tool the server exposes. Imported tools are first-class: you can edit, share, test, and attach them to any agent. - **Drive it with an MCP agent** — an agent with type **MCP** automatically uses whatever tools its linked server exposes, with no per-tool setup. See [Agents](/docs/guide/agentic/agents.md). ## Navigating here From the dashboard sidebar, open **Agentic Workflows → MCP Servers**. The page lists your registered servers in a card or list view, with **Add MCP Server** in the top right. > Every MCP server is automatically available to your whole company — there is no per-user sharing step for servers themselves. ## Key concepts ### Server registration A server registration is just a name, the server's URL, an optional description, and how to authenticate to it. Autessa supports four authentication types plus unauthenticated servers: | Authentication type | What you provide | | --- | --- | | No Authentication | Nothing — public or network-trusted servers | | API Key | The key plus the header name to send it in (defaults to `X-API-Key`) | | Bearer Token | A token sent as `Authorization: Bearer ...` | | Basic Auth | Username and password | | Custom Headers | Any number of header name/value pairs | Credentials are encrypted server-side and never returned to the browser. When you edit a server, entering new authentication details replaces the stored credentials; the UI only ever shows *whether* auth is configured (a green **Authenticated** badge), not the secret itself. ### Imported tools are regular tools Importing creates ordinary tools that live on your [Tools](/docs/guide/agentic/tools.md) page with the MCP tool type. Deleting a server later does **not** delete tools already created from it — they keep working as long as the server is reachable with the stored credentials. ### Tools, resources, and prompts over the API Beyond tools, MCP servers can expose resources and prompts. The builder UI focuses on tool import; the API additionally lets you test a server's connection and enumerate its tools, resources, and prompts (`/mcp/servers/test-connection`, `/list-tools`, `/list-resources`, `/list-prompts`). See the [MCP Poweruser API](/docs/api/poweruser/mcp.md). ## How to connect an MCP server 1. Go to **Agentic Workflows → MCP Servers** and click **Add MCP Server**. 2. Enter a **Server Name** (e.g. "GitHub MCP Server") and the **Server URL** (e.g. `https://mcp.example.com`). 3. Pick the **Authentication Type** and fill in the credentials the server expects. 4. Optionally add a description, then save. The server is immediately available to your whole company. ## How to import a server's tools 1. On the MCP Servers page, find the server card and click its **Import tools** action — or, from the Tools page, choose **Create Tools from MCP Server** and select the server. 2. Click **Create Tools**. Autessa connects to the server, reads every tool it exposes, and creates one Autessa tool per MCP tool. 3. A confirmation shows how many tools were created. Find and refine them on the [Tools](/docs/guide/agentic/tools.md) page — descriptions and argument docs carry over from the server, but you can edit them like any other tool. Import is all-or-nothing per server; delete any imported tools you don't want afterwards. ## How to update credentials 1. Open the server's edit dialog from the MCP Servers page. 2. Choose the authentication type and enter the new credentials — saving replaces the existing stored credentials entirely. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [MCP tool type](/docs/tools/mcp.md) - [MCP Poweruser API](/docs/api/poweruser/mcp.md) --- # Environment Variables Source: https://docs.autessa.com/docs/guide/agentic/environment-variables > Reusable variables injected into agent and tool executions via @@{VAR_NAME} placeholders. # Environment Variables Environment variables are named values — API keys, base URLs, IDs — that are automatically included in all of your agent and tool executions. Reference them anywhere a tool config or instruction accepts text using the @@{VAR_NAME} placeholder syntax, instead of pasting the same value into every config. ## What it's for - **Keep values out of configs** — a tool's API-call config can send `@@{MY_API_KEY}` in a header rather than embedding the literal key in the saved tool. - **Change once, apply everywhere** — update the variable and every tool or agent referencing it picks up the new value on its next execution. - **Move between environments** — export your variables as JSON and import them on another machine or share a starter set with a teammate. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Environment Variables**. The page is a simple key/value table with search, **Add Variable**, and **Export JSON** / **Import JSON** actions. ## Key concepts ### Naming and referencing Keys follow standard environment-variable conventions: uppercase letters, numbers, and underscores, starting with a letter or underscore (e.g. `MY_API_KEY`, `DATABASE_URL`). Anywhere the builder supports variables — tool configurations, agent and manager instructions, webhook handler configs — typing `@@` opens an autocomplete of available variables and inserts a `@@{VAR_NAME}` placeholder. The placeholder is resolved to the variable's value at execution time. Every [tool type](/docs/tools.md) resolves these placeholders in its configuration fields, and tools created or updated through the [Tools Poweruser API](/docs/api/poweruser/tools.md) consume them exactly the same way — `@@{VAR_NAME}` in a config is a contract, not an editor feature. ### Where the values live Variables are stored locally in your browser and automatically included with every agent and tool execution you run. They are per-browser, not synced across your devices or teammates — use **Export JSON** / **Import JSON** to move a set of variables to another machine or person. A variable's key cannot be renamed after creation (delete and recreate instead), and deleting a variable means it is no longer available to executions. ### Environment variables vs private arguments Both keep sensitive values out of stored configs, but they solve different problems: | | Environment variables | Private arguments | | --- | --- | --- | | Who supplies the value | You, the builder, ahead of time | The end user, at runtime | | Typical contents | Your API keys, service URLs, account IDs | The user's card number, SSN, personal secrets | | Referenced via | `@@{VAR_NAME}` in configs and instructions | Declared per-tool on the Private arguments tab | | Storage | Saved in your browser, injected into executions | Never stored in any Autessa history; collected in-memory only | Rule of thumb: *your* credentials go in environment variables; *your user's* sensitive inputs go in [private arguments](/docs/guide/agentic/tools.md#private-arguments). ### Built-in variables Autessa injects a small set of variables into every execution automatically — no setup, nothing to create. They resolve anywhere `@@{...}` placeholders work: agent and multi-agent instructions, every tool configuration field, and phone call configs. | Variable | Resolves to | Example | | --- | --- | --- | | `@@{currentDate}` | The current date in UTC, human-readable | `January 8, 2026` | | `@@{currentTime}` | The current time in UTC, human-readable | `2:30 PM` | They shine in prompts and instructions — an agent told `Today is @@{currentDate}.` stops guessing dates from its training data, and an LLM Call tool can stamp output with `Generated on @@{currentDate} at @@{currentTime} UTC.` Built-ins have the *lowest* precedence: if you define your own variable named `currentDate` (or pass one at invocation time), your value wins and the built-in stays out of the way. Both values are UTC — if your workflow needs a specific timezone, pass your own formatted value as a prompt template variable instead. #### Phone call and text variables When an agent or multi-agent handles a call or text on one of your [phone numbers](/docs/guide/agentic/phone-numbers.md), it also gets variables describing it. They resolve in the agent's instructions, its greeting, and every tool it runs during the call. Unlike the date and time built-ins, these always take the real value: a variable of your own with the same name doesn't replace them. | Variable | Resolves to | Available | Example | | --- | --- | --- | --- | | `@@{callerPhone}` | The other person's number: who called or texted you, or who your outgoing call is dialing | Calls (incoming and outgoing) and incoming texts | `+14155550100` | | `@@{agentPhone}` | Your number the call or text is on | Calls and incoming texts | `+12065550142` | | `@@{callDirection}` | `INBOUND` or `OUTBOUND` | Calls and incoming texts | `INBOUND` | | `@@{callerName}` | The caller's name from carrier caller-ID records | Incoming calls, when the number has [caller-name lookup](/docs/guide/agentic/phone-numbers.md#caller-info) on and a name is listed | `JANE DOE` | | `@@{callerCity}` | The city where the caller's number is registered | Incoming calls, when the carrier reports it | `SEATTLE` | | `@@{callerState}` | The state or region where the caller's number is registered | Incoming calls, when the carrier reports it | `WA` | | `@@{callerCountry}` | The country where the caller's number is registered | Incoming calls, when the carrier reports it | `US` | | `@@{forwardedFrom}` | The number the call was forwarded from | Incoming calls that were forwarded to you, when the carrier reports it | `+12065550199` | When a value isn't known, the variable resolves to an empty string, so write instructions that still read well without it, for example `The caller's name, if known, is @@{callerName}.` Carrier-reported names and places are often in capital letters and can be out of date, so treat them as hints, not facts. ## How to add a variable 1. Go to **Agentic Workflows → Environment Variables** and click **Add Variable**. 2. Enter the **Variable Key** (e.g. `MY_API_KEY`) — uppercase letters, numbers, and underscores only. 3. Enter the **Variable Value** and save. 4. In any tool config or instruction field, type `@@` and pick the variable to insert `@@{MY_API_KEY}`. ## How to move variables between machines 1. Click **Export JSON** — this downloads a dated JSON file of all your key/value pairs. 2. On the other browser or machine, click **Import JSON** and drop or paste the file. The dialog previews how many variables will be imported before you confirm with **Import Variables**. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Tool Types reference](/docs/tools.md) - [Tools Poweruser API](/docs/api/poweruser/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Connectors](/docs/guide/connectors.md) - [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md) --- # Agent History Source: https://docs.autessa.com/docs/guide/agentic/agent-history > Browse past agent conversations, evaluate quality, and sync transcripts to AutessaDB. # Agent History Agent History is where you review everything your agents have actually done — full conversation transcripts, every tool call with its inputs and outputs, and success evaluations. Use it to measure quality version by version, debug unexpected behavior, and export or sync conversations for downstream analysis. ## What it's for - **Quality evaluation** — per-month stats (total conversations, evaluated, successful, success rate) and a version-by-version performance breakdown tell you whether your latest published version is better than the last. - **Debugging** — the conversation viewer shows exactly what the user said, what the agent replied, and every tool request/response pair, so you can pinpoint where a run went wrong. - **Downstream workflows** — export history as JSON or CSV, or configure Conversation Sync to land transcripts in an AutessaDB table automatically. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Agent History**. Pick an agent in the **Select Agent** dropdown (owned, shared, and company agents are all listed), set a **Start Date / End Date** range (defaults to the current month), and optionally tick **Include My Sandbox Conversations** to see your own test runs alongside real traffic. ## Key concepts ### Stats and version breakdown The **This Month's Stats** dashboard summarizes the selected range: total conversations, how many were evaluated, how many succeeded (including a "successful with caveats" bucket), and the overall success rate. The collapsible **Version Performance Breakdown** splits those numbers per published version (and the draft), with a **Browse** shortcut to open that agent version — so a regression after a publish is immediately visible. See [Versions](/docs/guide/agentic/versions.md). ### Evaluations Each conversation row carries an evaluation status: successful, successful with caveats, unsuccessful, or not yet evaluated. Evaluations come from two places — automated evaluation (scored on dimensions like groundedness, task completeness, argument faithfulness, and efficiency) and human evaluation, which you record in the viewer's **Evaluation Details** panel with statuses like *Successful*, *Successful (Not Efficient)*, *Successful (Not All Steps)*, or *Unsuccessful*, plus notes. A human evaluation always takes precedence over the automated one. ### The conversation viewer Clicking **View Details** on a row opens the conversation viewer. It shows: - An overview card — conversation ID, close date, agent version, API key, message and tool counts. - The full transcript — user messages (including audio transcriptions and attached-image markers) and agent replies in order. - **Tool call cards** — each tool request and its response grouped into one expandable card with the tool's input variables, the result (with a raw/JSON pretty-print toggle), and debug logs. - For Critic agents, each rejected attempt with the critic's reasoning and failed criteria, followed by the approved final message. - A toggleable **Evaluation Details** side panel for reviewing or recording evaluations. ### Conversation Sync The **Sync with AutessaDB** button opens Conversation Sync, where you define configurations that automatically write closed conversations into AutessaDB tables. Each configuration filters by agent (or all agents, optionally including sandbox conversations) and maps conversations onto one or more tables: a **match column** holding the conversation ID, a column receiving the **conversation JSON** (with or without tool calls — including them can significantly increase stored size), an insert-or-skip behavior when no matching row exists, and any additional columns filled from sync variables such as `@@{conversationId}`, `@@{closeDate}`, `@@{agentId}`, `@@{agentVersion}`, `@@{apiKeyId}`, `@@{conversation}`, and `@@{shortened_conversation}`. > Because synced conversations land in a normal AutessaDB table, everything downstream is available: triggers can process each new transcript, knowledge bases can make them searchable, and scheduled jobs can report on them. ## How to review a conversation 1. Open **Agentic Workflows → Agent History** and select the agent. 2. Adjust the date range if needed, then find the conversation row — the icon shows its evaluation status at a glance. 3. Click **View Details** to open the transcript. Expand any tool card to inspect its exact inputs and outputs. 4. Optionally open **Show Evaluation** to record a human evaluation and notes. ## How to export history 1. On the Agent History page, open the export dialog. 2. Pick a time range (last one, two, or three months). 3. Choose **JSON** for full conversation details including logs and evaluations, or **CSV** for a summary table (date, version, message and tool counts, API key, conversation ID). The file downloads immediately. ## How to sync conversations to AutessaDB 1. Click **Sync with AutessaDB** on the Agent History page. 2. Click **Create New Configuration**, name it, and choose which agents it covers (leave the filter empty for all agents). 3. Add a **Table Update**: pick the target table, the match column for the conversation ID (a `SHORT_TEXT` or `LONG_TEXT` column), and the column to receive the conversation JSON. 4. Decide whether unmatched conversations should insert new rows (upsert) or be skipped, map any additional columns using the sync variables, and save. New conversations sync automatically while the configuration is enabled. ## Related - [Agents](/docs/guide/agentic/agents.md) - [Manager History](/docs/guide/agentic/manager-history.md) - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [Conversation Sync API](/docs/api/poweruser/conversation-sync.md) - [Agent Testing](/docs/guide/autobots/testing.md) --- # Manager History Source: https://docs.autessa.com/docs/guide/agentic/manager-history > Review multi-agent runs: delegations, sub-agent transcripts, and orchestration outcomes. # Manager History Manager History (shown as Multi-Agent History in the builder) is the review surface for your multi-agent workflows. It shows every orchestration run — which sub-agents the manager delegated to, what task it gave each one, how each delegation went, and the full nested transcript of every sub-agent conversation. ## What it's for Multi-agent runs have more moving parts than a single agent conversation: the manager reasons about the request, picks sub-agents, phrases a task for each, and synthesizes their results — possibly over several iterations. Manager History lets you: - **Audit delegation decisions** — see which agent got which subtask and why the manager's reasoning led there. - **Debug failures at the right layer** — distinguish a manager that delegated poorly from a sub-agent that executed poorly, by drilling from the orchestration view into each sub-agent's own transcript and tool calls. - **Track quality per version** — the same monthly stats and version performance breakdown as agent history, so you can compare published manager versions. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Multi-Agent History**. Pick a multi-agent in the **Select Multi-Agent** dropdown, set the date range (defaults to the current month), and optionally include your own sandbox conversations. ## Key concepts ### Run rows Each conversation row shows its evaluation status, close date, manager version, message count, **Agents Used** (how many sub-agent invocations occurred), **Iterations** (how many orchestration cycles the manager ran), and the conversation ID. ### The orchestration viewer **View Details** opens the manager conversation viewer. Alongside the user's inputs and the manager's replies and reasoning, each delegation appears as an **Agent Invocation card** showing: - the sub-agent's name and the exact task description the manager gave it, - the delegation status (complete, partial, or failed), - request details — the variables passed to the sub-agent, - the sub-agent's response summary and its own conversation ID, - an expandable **Agent Conversation Context** — the sub-agent's full nested transcript, including its tool requests and responses. An evaluation side panel mirrors the one in [Agent History](/docs/guide/agentic/agent-history.md): automated scores plus human evaluations that take precedence. > Billing note: a manager conversation is billed based on the number of sub-agents configured on the manager — the sub-agent invocations inside the run are part of that one manager conversation, not separately billed agent conversations. ## How to debug a bad multi-agent run 1. Open **Agentic Workflows → Multi-Agent History**, select the manager, and find the run (unsuccessful runs are marked with a red icon). 2. Click **View Details** and read the manager's reasoning before each delegation — was the right sub-agent chosen, and was the task phrased correctly? If not, the fix is in the *manager's* instructions or the sub-agents' descriptions. 3. If the delegation looked right, expand the invocation's **Agent Conversation Context** and inspect the sub-agent's own transcript and tool calls. A failure there is fixed on the *sub-agent* (instructions, tools, or arguments). 4. Record a human evaluation with notes so the run counts correctly in the stats. ## Related - [Managers](/docs/guide/agentic/managers.md) - [Agent History](/docs/guide/agentic/agent-history.md) - [Versions](/docs/guide/agentic/versions.md) - [Managers API](/docs/api/managers.md) --- # Phone Numbers Source: https://docs.autessa.com/docs/guide/agentic/phone-numbers > Buying, linking, or hosting phone numbers; deciding who answers (AI agents, people, keypad menus, voicemail) with business hours and holidays; voice, language, and your own recordings; ringing options; AI call behavior; recording, transcripts, retention, and blocking; the Phone calls table; and after-call automations. Texting with or without AI: who answers texts, auto-replies, keyword replies, the shared inbox, the Text messages table, and forwarding texts. # Phone Numbers Phone numbers put your business on the public telephone network. Buy a number, link one from your own Twilio account, or host your existing business number. Then decide who answers it: an AI agent, a multi-agent, your team's phones, a keypad menu, or voicemail, with different answers for business hours and after hours. Every call, with or without AI, lands in your company's Phone calls table with its status, caller info, transcript, recording, and voicemail. Texts work the same way: an AI agent can answer them, or your team can read and reply from a shared inbox, and every text lands in a Text messages table. ## What it's for - **Voice agents**: an agent (or [multi-agent](/docs/guide/agentic/managers.md)) answers calls, speaks in its configured voice, and uses its tools mid-call, including tools to hang up, transfer, and take a message. - **Business lines with no AI**: ring your team's cell phones or browser phones, play a keypad menu, follow business hours, and take voicemail. AI is optional. - **A mix of both**: for example, people answer during business hours and an AI agent answers after hours. - **Call records and follow-up**: every call is logged in a [Phone calls table](#the-phone-calls-table) you can search, edit, and automate, such as texting back missed calls or emailing new voicemails. - **Texting, with or without AI**: the same number carries text conversations. An AI agent can answer them, or your team can read and reply from a [shared inbox](#the-shared-inbox), with auto-replies and keyword replies for common questions. Texting needs your company's [Text Messaging registration](/docs/guide/agentic/sms-setup.md). ## Navigating here From the dashboard sidebar, open **Agentic Workflows → Phone Numbers**. The table lists every number with what answers it, **Source**, **Texting** status, and sharing level; search matches the number or the linked agent/multi-agent name. Click a row to open the number's detail page. [View Phone Numbers →](https://builder.autessa.com/agentic/phone-numbers) ## Settings at a glance Everything below is optional. A new number works with sensible defaults, and you change only what you need. | You want to… | Setting | Section | | --- | --- | --- | | Decide who answers, by time of day | Call flow, business hours, closed dates | [Who answers](#who-answers), [Business hours and holidays](#business-hours-and-holidays) | | Pick the voice and language callers hear | Voice, language | [Voice & language](#voice--language) | | Play your own recorded greeting or menu | Recordings | [Using your own recordings](#using-your-own-recordings) | | Ring your team's phones | Ring time, labels, press 1 to accept, whisper, caller ID, wait audio, overall time limit | [Ringing people](#ringing-people) | | Offer "press 1 for sales" | Prompt, wait time, messages, retries | [Keypad menus](#keypad-menus) | | Take messages | Greeting, length, finish key, silence, transcription | [Voicemail](#voicemail) | | Tune how an AI agent talks on the phone | Interruptions, end-of-turn speed, hang-up phrases, key presses | [AI call behavior](#ai-call-behavior) | | Record and transcribe calls | Recording, transcripts, disclosure | [Recording, transcripts, and limits](#recording-transcripts-and-limits) | | Delete recordings and transcripts after a while | Keep for N days | [Deleting old recordings and transcripts](#deleting-old-recordings-and-transcripts) | | Remove card numbers and other personal info | Call Redaction | [Removing personal info from recordings](#removing-personal-info-from-recordings) | | End long or silent calls | Maximum length, warning, silence timeout, prompt repeats | [Call limits](#call-limits) | | Know who's calling | Caller info, name lookup | [Caller info](#caller-info) | | Stop unwanted calls | Blocked numbers, anonymous callers | [Blocking callers](#blocking-callers) | | Detect answering machines on calls you place | Answering machine detection | [Answering machines on outgoing calls](#answering-machines-on-outgoing-calls) | | Follow up after calls | Text back missed calls, email voicemails, your own triggers | [Automations](#automations) | | Handle texts, with or without AI | Who answers texts, text hours, auto-replies, keyword replies, shared inbox, forwarding | [Texts](#texts) | ## Key concepts ### Where a number comes from The **Source** column shows how each number got here: | Source | Meaning | | --- | --- | | **Bought through Autessa** | You got it here. It's free for calls, and it's released back to the carrier when you release it. | | **Your Twilio account** | Linked from your own Twilio account. Twilio bills you for it, and unlinking never deletes it from your account. | | **Hosted from your carrier** | Your existing number: calls stay with your current carrier, and texting runs through Autessa. | ### Texting status The **Texting** column shows whether a number can send texts: | Status | Meaning | | --- | --- | | **Not registered** | Texting isn't turned on for this number, or your company hasn't finished [Text Messaging registration](/docs/guide/agentic/sms-setup.md). | | **Pending** | The registration is in carrier review. | | **Ready** | The number can text. | | **Action needed** | The registration was rejected and needs a fix. | | **Ready (your account)** | A number from your own Twilio account whose registration there is approved. | | **Unverified (your account)** | A number from your own Twilio account whose registration we couldn't confirm. Texts still send, but carriers may filter them. | Calls don't depend on texting status. A bought number takes calls right away. ### Sharing Numbers follow the standard sharing model: private, shared with specific members, or company-wide, with read/write/admin permission levels. To call out from a number in the browser, you need permission to use it. ## Who answers A number either sends every call straight to one agent or multi-agent, or follows a **call flow** that you build on the number's detail page under **Call flow**. A call flow can send calls to any of these: | Destination | What the caller experiences | | --- | --- | | **AI agent** | An agent answers and talks with the caller. | | **Multi-agent** | A [multi-agent](/docs/guide/agentic/managers.md) answers, handing the conversation between its agents. | | **People** | Your team's phones ring. See [Ringing people](#ringing-people). | | **Keypad menu** | The caller hears options ("Press 1 for sales, 2 for support") and is sent where they choose. If they don't press anything, the options repeat a set number of times, then the call goes to the destination you chose for no input. See [Keypad menus](#keypad-menus). | | **Voicemail** | The caller hears a greeting and leaves a message. See [Voicemail](#voicemail). | | **Hang up** | A message is played (optional) and the call ends. Useful for "We're closed for the holiday." | Any destination can play a short **message** first, and the flow as a whole can start with a **greeting** that every caller hears. Messages are spoken in a natural voice from the text you type, in the number's [voice and language](#voice--language), or you can [upload your own recording](#using-your-own-recordings) instead. Destinations nest. A keypad menu option can ring people, and if nobody answers, go to voicemail. A business-hours flow can send closed-hours calls to an AI agent. Some common flows: - **Plain business line**: ring the front desk, then voicemail if nobody answers. - **AI receptionist**: an AI agent answers every call and transfers to a person when needed, using the [Transfer Call tool](/docs/tools/phone-sms.md#transfer-call-). - **People by day, AI by night**: during business hours ring the team; when closed, an AI agent answers. - **Department menu**: press 1 to ring sales, press 2 for the support agent, or stay on the line for voicemail. Numbers set up before call flows existed keep working exactly as before. A number that used the older **Press digit** routing now runs as a keypad menu. ### Numbers with nothing set up A number that doesn't send calls anywhere yet, or whose agent or flow no longer exists, still takes messages. Callers go straight to voicemail with a standard greeting, and the call is logged in the [Phone calls table](#the-phone-calls-table) with `handled_by` set to "Voicemail (number not set up)". Texts are saved to the [shared inbox](#the-shared-inbox) without a reply. Nothing is lost while you finish setting the number up, and turning on [Email me new voicemails](#quick-automations) or [forwarding texts](#forwarding-texts-and-other-automations) makes sure someone sees them. ### Using a number with no AI Choose **People (no AI)** or **Keypad menu** when you add a number, or build a call flow later, and the number works as an ordinary business line. Calls ring your team, callers can leave voicemail, and every call is still recorded in the [Phone calls table](#the-phone-calls-table). If you turn on transcripts, calls handled by people are transcribed from the recording after the call ends. Texts can work without AI too: choose **No AI** under [Texts](#texts) and your team answers from the shared inbox. ### Using your own recordings Anywhere the number speaks text you type, you can play a recording instead: the flow's greeting, a destination's message, a keypad menu's prompt, a voicemail greeting, and the audio callers hear while your team's phones ring. Upload an MP3 or WAV file next to the text box, and use the play button to check it. When a recording is set, it plays instead of the text. Remove the recording to go back to the spoken text. Recordings are a good fit for a professionally voiced greeting, hold music, or a message in your own voice. Text is easier to change, and it follows the number's [voice and language](#voice--language). From the API, CLI, or a script, you can also point these settings at an audio file you host at an `https://` link. See [Upload an audio file](/docs/api/poweruser/phone-numbers.md#get-an-audio-upload-link) for the scripted upload steps. ### Business hours and holidays Add business hours to a call flow to send calls somewhere else when you're closed. Set a time zone, and for each day of the week an open and close time (for example Monday to Friday, 09:00 to 17:00). Calls during those hours go to the flow's main destination; calls at any other time go to the **closed** destination, such as an AI agent, voicemail, or a "we're closed" message. Days with no hours are treated as closed all day. Daylight saving time is handled for you. Add **closed dates** for holidays and other days you're closed all day. Each one is a single date or a range of dates (a start and an end date), with an optional name such as "Thanksgiving" so you can tell them apart. On a closed date, every call goes to the closed destination, even during your usual hours. Closed dates follow the flow's time zone. ### Ringing people A **People** destination rings a list of phones: - **Cell or desk phones**: any phone number, in E.164 format (for example `+14155550100`). - **Browser phones**: teammates who are signed in to Autessa with their browser registered for calls. Pick them from your team. Give each phone an optional **label**, such as "Front desk" or "Dana's cell", so the list is easy to read. Choose how they ring: - **All at once**: every phone rings at the same time. The first person to answer gets the call, and the other phones stop ringing. - **One by one**: each phone rings in order for its ring time, then the next one rings. **Ring time** is how long a phone rings before it counts as not answered. It's 20 seconds unless you change it, and you can give any phone its own ring time, for example longer for a desk phone that's across the room. Then fine-tune what happens while phones ring: - **Press 1 to accept**: the person who picks up hears a short prompt and must press 1 before the caller is connected. Turn this on when you ring cell phones. When a cell phone isn't answered, its own voicemail picks up after a few rings, and without this setting that personal voicemail would take your caller. Voicemail can't press 1, so the call moves on to the next person or to your **no answer** destination instead. - **Whisper**: a short message only the person answering hears before they're connected, such as "Call from the sales line." The caller doesn't hear it. It helps people who get calls from several numbers know how to answer. - **Caller ID**: what the rung phone shows. **Business number** (the default) shows your number, so your team knows it's a work call. **Caller's number** shows who's calling, so your team can recognize them. If the carrier won't show the caller's number, your business number is shown instead. The caller ID *name* can't be set per call; carriers show the name registered for the number. - **Wait audio**: what the caller hears while phones ring. By default it's a ringing tone. Upload music or a message (for example "Please hold while we find someone to help you.") and it plays on a loop until someone answers. - **Overall time limit**: for **one by one** ringing, the most time to spend ringing everyone. When it runs out, the call goes to your **no answer** destination even if some phones haven't rung yet, so callers aren't left waiting through a long list. If nobody answers, the call goes to the **no answer** destination you choose: voicemail, an AI agent, a message, or hang up. If you don't choose one, the caller goes to voicemail. ### Keypad menus A **Keypad menu** reads a prompt, such as "Press 1 for sales, 2 for support," and sends the caller wherever the key they press leads. Each key can lead to any destination, including another menu. You can set: - **Prompt**: the text read to the caller, or a [recording](#using-your-own-recordings). - **Wait time**: how long to wait for a key press after the prompt ends, from 2 to 30 seconds (default 6). - **Wrong key message**: what callers hear when they press a key that isn't an option, such as "Sorry, that's not an option." The prompt then plays again. - **No key message**: what callers hear when they don't press anything before the wait time ends, such as "We didn't get your choice." - **Retries**: how many times the prompt repeats before giving up. - **No input**: where the call goes after the last retry, for example voicemail or the front desk. ### Voicemail A **Voicemail** destination plays your greeting (or a standard one), beeps, and records the caller's message. The message's audio and transcript are saved in the call's row in the Phone calls table, and the call's status becomes **Voicemail received**. Turn on [Email me new voicemails](#quick-automations) to get each one in your inbox. You can set: - **Greeting**: typed text, or a [recording](#using-your-own-recordings) of your own. - **Maximum length**: the longest message a caller can leave, 2 minutes by default. - **Finish key**: the key callers press to finish their message. It's `#` by default. Choose none and the message ends when the caller hangs up, goes quiet, or reaches the maximum length. - **Silence**: how many seconds of silence end the message, from 1 to 30 (default 5). Raise it for callers who pause while they think. - **Transcribe**: turn this off if you only want the audio. It's on by default. Agents can send a caller to voicemail mid-call with the [Take a Voicemail tool](/docs/tools/phone-sms.md#take-a-voicemail-). ### AI call settings When an AI agent or multi-agent answers, these settings apply to it (most apply to inbound calls only): - **Environment variables** and **prompt template variables**: values passed to the agent for every call and text on this number. - **Initial message**: a static greeting spoken when someone calls (defaults to "How can I help you?"). - **Initial message generation prompt**: instead of a static greeting, a prompt the LLM answers to generate the opening line. Setting it also turns on automatic voicemail detection, and it takes precedence over the static message. - **Conference participants**: extra parties added to every inbound call: external phone numbers in E.164 format, or teammates' browser phones picked from your team. - **Client display message**: the text shown in the builder's incoming-call notification for browser-phone participants. The agent also gets [built-in variables](/docs/guide/agentic/environment-variables.md#built-in-variables) describing the call, such as `@@{callerPhone}` and `@@{callerName}`. ### AI call behavior These settings change how an AI agent or multi-agent handles the conversation on this number's calls, incoming and outgoing: - **Interruptions**: on by default, so a caller can talk over the agent and the agent stops to listen, the way a person would. Turn it off and the agent always finishes what it's saying, which suits calls where it reads out something important, like terms or instructions. - **Interruption sensitivity**: how easily the caller's voice interrupts the agent. **Low** ignores short sounds like "mm-hmm" and background noise, which helps on noisy lines. **Medium** is the default. **High** stops the agent as soon as the caller starts speaking. - **End-of-turn speed**: how long the agent waits after the caller stops talking before it answers. **Fast** feels snappy for quick back-and-forth. **Normal** is the default. **Patient** waits longer, for callers who pause mid-sentence, such as while reading out an account number or thinking about an answer. - **Hang-up phrases**: phrases like "Goodbye" or "Have a great day". When the agent says one of them, the call ends right after it finishes speaking. It's a simple way to end calls cleanly without giving the agent the [Hang Up Call tool](/docs/tools/phone-sms.md#hang-up-call-). Choose phrases the agent only says at the end of a call. ### Giving an agent call tools On their own, agents can only talk. To let one hang up, transfer, take a message, press keys, pause the recording, or look up the caller, add the [call-control tools](/docs/tools/phone-sms.md#call-controls) to it. The number's **Call tools** card shows which of these tools the answering agent has, and **Add to agent** adds the rest. Then tell the agent when to use them in its instructions, for example: "If the caller asks for a person, transfer them to the front desk. If nobody answers, take a voicemail." These tools do nothing outside a live call, so the same agent can safely answer chats and texts too. ### Taking and making calls in the builder The header of the builder has a phone icon. Click it to register your browser as a phone (a green dot means it's ready to receive calls). When a call rings your browser, because a call flow rings you or an agent transfers to you: - an **incoming-call notification** slides in with the caller, who it's from, and any display message, with **Accept** and **Decline**; - accepting shows the **active-call bar** pinned to the bottom of the page, with the elapsed time, a mute toggle, and an **End** button. You talk through your browser. You can also **call out** from your browser. Open the dial pad from the phone icon, pick which business number to call from, and dial. The person you call sees your business number, not your personal one. You can also click **Call** on a call in the number's **Recent calls** card to call that person back. You need permission to use the number you call from, and outgoing calls are logged in the Phone calls table like any other call. ## Texts Once a number can text (see [Turn on texting](#turn-on-texting)), you decide what happens when someone texts it, on the number's detail page under **Texts**. AI is optional here too: an AI agent can answer, or texts can wait in a shared inbox for your team, with automatic replies for the common cases. ### What happens by default You don't have to set anything up for texts to work: - If the number sends calls straight to one AI agent, that agent answers texts too. - Otherwise, including numbers with a call flow and numbers with nothing set up yet, there's no AI and no automatic reply. Texts are saved to the [shared inbox](#the-shared-inbox) and the [Text messages table](#the-text-messages-table), where your team can read and answer them. Change any of this with the settings below. ### Who answers texts Choose one of these for texts to the number: - **AI agent**: an agent answers. Pick the agent, or leave it on the number's own agent (the one calls go to when they go straight to an agent). Each sender gets an ongoing conversation, so the agent remembers earlier messages from the same person. A sender can text **CLEAR** to start a fresh conversation. - **No AI**: nobody answers automatically. Texts go to the [shared inbox](#the-shared-inbox) for your team, with an optional [auto-reply](#auto-replies). Texts follow their own setting, separate from the [call flow](#who-answers). For example, people can answer calls while an AI agent answers texts, or the reverse. ### Business hours for texts Texts can be handled one way while you're open and another way while you're closed, for example your team answers during the day and an AI agent answers after hours, or texts sent overnight get "We're closed, we'll reply in the morning." - **Hours**: set hours just for texts (a time zone, open and close times for each day of the week, and [closed dates](#business-hours-and-holidays) for holidays), or choose **Same as calls** to use the hours from the number's call flow. When you change the call flow's hours, text hours change with them. - **When closed**: choose who answers texts outside those hours, AI agent or no AI, with its own auto-reply. If you don't set it, texts are handled the same way at all hours. Without hours, the open setting applies all the time. ### Auto-replies With **No AI**, you can send an automatic reply to each texter, such as "Thanks for your message. We'll text you back within one business day." To avoid flooding someone who sends several texts in a row, the auto-reply goes to the same person **at most once every N hours**. It's 24 hours unless you change it; set it to 0 to reply to every text, or up to 720 hours (30 days). You can write a different auto-reply for open and closed hours. AI agents write their own replies, so auto-replies apply only to **No AI**. ### Keyword replies A keyword reply answers a specific word with a fixed message, for example texting **HOURS** gets "We're open Monday to Friday, 9 to 5." A text matches when the whole message is the keyword; capital letters and spaces around it don't matter, so "hours" and " Hours " both match, but "what are your hours" doesn't. Keyword replies work whoever answers the number, AI or not, and at any hour. A matching text gets the keyword reply instead of going to the agent or the auto-reply. **STOP**, **START**, and **HELP** are always handled first, the same way, for compliance: **STOP** opts the sender out (recorded as an [SMS consent](/docs/guide/agentic/sms-consent.md) opt-out), **START** opts them back in, and **HELP** gets the help reply from your [registration](/docs/guide/agentic/sms-setup.md). You can't override them with keyword replies. ### The shared inbox The number's inbox shows texts as threads, one per person who has texted the number or been texted from it, with the newest at the top. Open a thread to see the whole conversation, including replies sent by an agent, an auto-reply, a keyword reply, or a teammate. Reply from the thread and your message is sent **as the business number**, not from your own phone, and the thread shows which teammate sent it. Replies from the inbox follow the same rules as any other text: the number must be able to text under your [Text Messaging registration](/docs/guide/agentic/sms-setup.md), and the person must be allowed to receive texts from you (they texted you within the last year, or have an approved [consent record](/docs/guide/agentic/sms-consent.md)). People who replied STOP can't be texted until they reply START. The inbox shows texts whoever handled them, so you can read along with an AI agent's conversations too. ### Texts you start When someone texts your number for the first time, a user-initiated [consent record](/docs/guide/agentic/sms-consent.md) is created for them automatically, so you can reply. Texts your team or agents *start*, with the [Send SMS tool](/docs/tools/phone-sms.md#send-sms-) for example, need an approved consent record for the recipient or a text from them within the last year. Opted-out numbers are never texted. ### The Text messages table Every text on your company's numbers, incoming and outgoing, with or without AI, is logged in one [AutessaDB table](/docs/guide/autessadb/tables.md) named **Text messages**. It's created the first time a text needs it and is shared by all your numbers. Like the Phone calls table, it's a normal table you can search, edit, query, and add [triggers](/docs/guide/autessadb/triggers.md) to. | Column | What it holds | | --- | --- | | `message_id` | A unique ID for the text. | | `direction` | `INBOUND` or `OUTBOUND`. | | `business_number` | Your number the text was on. | | `other_number` | The other person's number. | | `body` | The message. | | `media_urls` | Links to any pictures or files sent with the text, one per line. | | `status` | `RECEIVED`, `SENT`, `DELIVERED`, `FAILED`, or `BLOCKED` (not sent, for example because the person opted out). | | `handled_by` | Who answered or sent it: the agent's name, `Auto-reply`, `Keyword reply`, `Staff:` and the teammate's name (such as `Staff: Dana Lee`), or `Automation`. | | `sent_at` | When the text was sent or received, in UTC (ISO-8601). | | `error` | A plain-language note when a text couldn't be sent. | ### Forwarding texts and other automations The number's **Quick automations** card can also send every incoming text on to your team: - **Forward texts to phones**: each incoming text is sent on to up to 5 phone numbers as a text, starting with who it's from, such as "From +14155550100: Is the shop open Saturday?" Forwarded texts are sent from this number, so the phones you forward to must be allowed to receive texts from it: the simplest way is to text the business number once from each phone. Texts from those phones are never forwarded, so nothing loops. - **Forward texts to email**: each incoming text is emailed to up to 5 people. Like the call automations, each one is a regular trigger on the Text messages table, so you can open it, see what it does, and change it. For anything else, add your own triggers on the Text messages table: for example, trigger on new rows where `direction` equals `INBOUND` to post texts to Slack, add the sender to your CRM, or have an agent tag each text with a topic in a column you added. ## Voice & language Everything the number itself says is spoken in one voice and language that you pick for the number: the greeting, messages, keypad menu prompts, voicemail greetings, the recording disclosure, the "Are you still there?" prompt, and the announcement before a transfer. - **Voice**: pick from the same voices your agents use, including your company's [custom voices](/docs/guide/custom-voices.md). If you don't pick one, the number uses the voice of the agent the call is going to, or a standard voice when no agent is involved. An AI agent always talks in its own voice; this setting is for what the number says around it. - **Language**: for example English (US) or Spanish (US). It sets how the number's messages are pronounced and what language keypad menus listen in. On AI calls, it also tells speech recognition what language to expect, so set it to the language your callers speak. Only languages we fully support are offered. Write your greetings, menu prompts, and the agent's instructions in the same language you choose here. If you serve callers in more than one language, give each language its own number. ## Recording, transcripts, and limits The number's recording, transcript, Call Redaction, call limit, caller info, blocking, AI call behavior, and answering machine settings apply to every call on the number, incoming and outgoing, whether an AI agent or a person handles it. A call placed with the [Make Phone Call tool](/docs/tools/phone-sms.md#settings-for-this-call) can override them for that one call. Changing a setting never affects a call already in progress. ### Transcripts Transcripts are **on** by default. Each call's transcript is saved in its row in the Phone calls table when the call ends: - **AI calls** are transcribed as they happen, as a back-and-forth of "Caller:" and "Agent:" lines. After a warm transfer, the rest of the conversation is included too. - **Calls handled by people** are transcribed from the call audio after the call ends. This works even when you don't keep recordings: the audio is used for the transcript and then deleted. ### Recording Recordings are **off** by default. Turn on **Save recordings** and each call's audio is saved in its row, where you can play it back on the number's **Recent calls** card or in the table. When recording is on, callers hear a short disclosure before the call connects. It defaults to "This call may be recorded." You can change the wording, or clear it if you give notice another way. ### Deleting old recordings and transcripts Recordings and transcripts are kept until you delete them, unless you set how long to keep them. Set **Keep recordings for** to a number of days, and each call's recording and voicemail recording are deleted that many days after the call. **Keep transcripts for** does the same for the call's transcript and voicemail transcript. The rest of the call's row (who called, when, how long, and how it ended) stays in the Phone calls table, so your call history and reports still add up. Use this to meet a retention policy, or to keep sensitive audio only as long as you need it. Leave the setting empty to keep everything. > Recording laws vary by state and country, and some require every party's consent. You're responsible for meeting the rules that apply to your calls. ### Removing personal info from recordings Turn on **Remove personal info before saving** and every recording and transcript on the number goes through **Call Redaction** before it's saved. Call Redaction finds personal info in what was said and removes it: from the transcript, and from the audio, either as silence or as a tone, whichever you choose. Only the cleaned-up version is saved; the original audio is never stored in the Phone calls table. Voicemails go through it too. You choose what to remove. By default it's all of these: | Setting | What it removes | | --- | --- | | Card number, expiry, and security code | Payment card details | | Social Security number | SSNs | | Bank account and account number | Bank and customer account numbers | | Date of birth | Birth dates | | Phone, email, and address | Contact details | Call Redaction costs **$0.01 per call**, whatever its length (a call whose recording and voicemail are both redacted counts once), billed as a **Call redaction** line on your invoice. While it runs, the call's `redaction_status` shows **Pending**; it changes to **Done** when the cleaned-up recording and transcript are saved. > Call Redaction cleans the recording and the transcript in the Phone calls table. It doesn't change what the AI agent heard during the call, so the agent's [conversation history](/docs/guide/agentic/agent-history.md) still contains what was said live. Limit who can see that history, and use the [Pause or Resume Call Recording tool](/docs/tools/phone-sms.md#pause-or-resume-call-recording-) or careful agent instructions where that matters. ### Call limits Call limits end calls that run too long or go quiet: - **Maximum call length**: the call ends after this many minutes. You can set a warning, such as "We need to wrap up in a minute," spoken before the limit. **Warn this long before** sets when the warning plays, from 10 to 300 seconds before the end (default 30), so match it to your wording. - **Silence timeout**: if nobody says anything for this many seconds, the caller is asked "Are you still there?" (you can change the wording). **Times to ask** sets how many times the question is asked before the call ends, from 0 to 3 (default 1). With 0, the call ends at the timeout without asking. The Phone calls table records why each call ended, for example `SILENCE_TIMEOUT` or `MAX_DURATION`. ### Caller info For incoming calls, the carrier tells us where the caller's number is registered (city, state, and country) and, when the call was forwarded to you, the number it was forwarded from. This is saved with every call and given to the answering agent as [built-in variables](/docs/guide/agentic/environment-variables.md#built-in-variables). Turn on **Look up the caller’s name** to also get the caller's name from carrier caller-ID records, when one is listed. The carrier charges a small fee for each lookup, which is included in the call's price. An agent can get all of this, plus the caller's recent calls with you, using the [Get Caller Info tool](/docs/tools/phone-sms.md#get-caller-info-). ### Blocking callers Stop unwanted incoming calls before anyone answers them: - **Blocked numbers**: a list of phone numbers, in E.164 format, that can't reach you on this number. - **Block anonymous callers**: turn this on to also block callers who hide their number (shown as "Unknown", "Private", or "Anonymous"). - **Blocked message**: what blocked callers hear before the call ends, such as "We're unable to take your call." Leave it empty and blocked callers get a busy signal instead, without the call being answered. Blocked calls never reach an agent or ring your team, and AI time isn't used on them. ### Answering machines on outgoing calls When you or an agent place a call, **Answering machine detection** decides whether to check if a person or a machine picked up: - **Off**: no check. The call starts as soon as it's answered, which is fastest. - **On**: a quick check right after the call is answered. The call's `answered_by` is set to `HUMAN` or `MACHINE`, and if a machine answered, your voicemail message is left. - **Wait for the beep**: if a machine answers, wait until its greeting finishes and it beeps before leaving your voicemail message, so the whole message is recorded. **Detection time limit** is how long to listen before deciding, from 2 to 60 seconds. When you leave this setting unset, detection turns on by itself for calls that have a voicemail message or a generated opening line (see the [Make Phone Call tool](/docs/tools/phone-sms.md#make-phone-call-)). ## The Phone calls table Every call on your company's numbers, incoming and outgoing, with or without AI, is logged in one [AutessaDB table](/docs/guide/autessadb/tables.md) named **Phone calls**. It's created the first time a call needs it and is shared by all your numbers. A row is added when the call starts and filled in as the call progresses, so you can see calls that are still ringing or in progress. To see it, open a number's detail page: the **Recent calls** card shows the latest calls with their status, length, a player for the recording, and the transcript. Click **View all** to open the full table. It's a normal AutessaDB table, so you can search and filter it, edit rows, add your own columns, query it from [AutessaScript](/docs/autessascript.md) or the [SQL Workbench](/docs/guide/autessadb/sql-workbench.md), and add [triggers](/docs/guide/autessadb/triggers.md) to it. | Column | What it holds | | --- | --- | | `call_id` | A unique ID for the call. | | `direction` | `INBOUND` or `OUTBOUND`. | | `business_number` | Your number the call was on. | | `other_number` | The other person's number. | | `caller_name` | The caller's name, when [caller-name lookup](#caller-info) found one. | | `caller_location` | Where the other person's number is registered, such as "Seattle, WA, US". | | `forwarded_from` | The number the call was forwarded from, if any. | | `handled_by` | Who handled the call: the agent's name, "People", the menu choices the caller made, the teammate who called out, or "Voicemail (number not set up)" for a number with [nothing set up](#numbers-with-nothing-set-up). | | `conversation_id` | The agent conversation for AI calls, so you can open it in [agent history](/docs/guide/agentic/agent-history.md). | | `started_at`, `ended_at` | When the call started and ended, in UTC (ISO-8601). | | `duration_seconds` | How long the call lasted. | | `status` | `RINGING`, `IN_PROGRESS`, `COMPLETED`, `NO_ANSWER`, `BUSY`, `FAILED`, `VOICEMAIL_RECEIVED` (the caller left you a message), or `VOICEMAIL_LEFT` (your outgoing call left a message on a machine). | | `answered_by` | `HUMAN`, `MACHINE` (an answering machine picked up your outgoing call), or `PERSON` (one of your team answered when their phone rang). | | `ended_by` | `CALLER`, `AGENT`, `SILENCE_TIMEOUT`, `MAX_DURATION`, `TRANSFERRED`, or `ERROR`. | | `transferred_to` | Who the call was transferred to. | | `keypad_response` | The option chosen on an outgoing [play-a-message call](/docs/tools/phone-sms.md#call-modes) with a key press. | | `transcript` | The call's transcript, when transcripts are on. | | `recording` | The call's audio, when recordings are on. | | `voicemail_recording`, `voicemail_transcript` | The voicemail the caller left, and its transcript. | | `redaction_status` | `NOT_REQUESTED`, `PENDING`, `DONE`, or `FAILED`. See [Removing personal info from recordings](#removing-personal-info-from-recordings). | | `processing_error` | A plain-language note if the recording or transcript couldn't be processed. | Rows are yours to edit, for example to correct a caller's name or add notes in a column you added. ## Automations ### Quick automations The number's **Quick automations** card turns on common follow-ups in one click: - **Text back missed calls**: when an incoming call isn't answered, gets a busy signal, or goes to voicemail, the caller gets a text from this number, such as "Sorry we missed your call. We'll call you back shortly." You write the message. This is offered only when the number can text; see [Turn on texting](#turn-on-texting). - **Email me new voicemails**: when a caller leaves a voicemail, up to 5 people you choose get an email with the transcript and a link to the call. Each one is a regular [trigger](/docs/guide/autessadb/triggers.md) on the Phone calls table that runs a regular tool, so you can open it, see exactly what it does, and change it. Turning a quick automation off disables its trigger without deleting it. ### Build your own automations Click **Add your own** on the Quick automations card to open the Phone calls table's triggers. Any trigger can use the call's columns as `@@{column_name}` variables. A few recipes: - **Summarize every call with an agent**: add a column `summary`. Create an **Invoke Agent** trigger on `UPDATE` when `transcript` becomes non-null, with a prompt like `Summarize this call in three bullet points and list any follow-ups: @@{transcript}`. Give the agent an AutessaDB update tool so it writes its answer into `summary`. - **Post missed calls to Slack**: create an **Invoke Tool** trigger on `UPDATE` when `status` equals `NO_ANSWER` and `direction` equals `INBOUND`. Use an [API call tool](/docs/tools/core.md) that posts to a Slack incoming webhook, with text like `Missed call from @@{caller_name} (@@{other_number}) at @@{started_at}`. - **Update a CRM row**: create an **Update AutessaDB** trigger on `UPDATE` when `ended_at` becomes non-null. Point it at your contacts table, match on the phone number column with `@@{other_number}`, and set its "last contacted" column to `@@{ended_at}`. - **Follow up on a key press**: after an outgoing reminder call that asks callers to press 2 to reschedule, trigger on `keypad_response` equals `Reschedule` to email your scheduling team or start a rescheduling agent. Trigger on the column you actually need, such as `transcript` or `voicemail_transcript` becoming non-null, rather than on `status`. Transcripts and recordings are saved after the call ends, so they arrive a little after the status changes. ## How to add a number On the Phone Numbers page, click **Add number** and choose where the number comes from. ### Buy a number Numbers are free to get and use for calls. No card is needed. 1. Enter an area code and pick a number from the list. 2. Choose who answers it: an agent, a multi-agent, **People (no AI)**, or a **Keypad menu**, with its settings. You can change this later from the number's detail page. Until a number is set up, callers can [leave voicemail](#numbers-with-nothing-set-up). 3. Confirm. The number takes calls immediately. Texting is off until you [turn it on](#turn-on-texting). ### Use my Twilio account Link numbers you already have in your own Twilio account. Your registration in Twilio covers their texting, so you don't register again here. 1. Enter your Twilio **Account SID**, an **API Key SID** and **API Key Secret**, and your **Auth Token**. The auth token is needed because Twilio signs the texts and calls it forwards to Autessa with it, and that signature is how incoming messages are verified. 2. Choose which of your numbers to link. For each one, turn on **Enable calls** if you want calls handled here too; otherwise only texts are. 3. Choose who answers, as for a bought number. We show each linked number's registration status from your Twilio account but don't block texts because of it. Unlinking a number disconnects it from Autessa and restores its previous settings in Twilio. It never deletes the number from your account. ### Host my existing number Keep your current carrier for calls and move only texting to Autessa, which is useful when a number is already printed on signs or cards. Hosting uses your business address, so finish the **Business** step of [Text Messaging registration](/docs/guide/agentic/sms-setup.md) first. 1. Enter the number (US or Canada, not toll-free) and a contact name, email, and title. 2. Twilio emails the contact a **Letter of Authorization**. Sign it from that email. 3. Click **Call me**. Twilio calls the number; enter the code shown on your screen using the phone's keypad. 4. The carrier moves texting over, usually in 1–7 business days. After that the number appears in your list and registers like a bought number. The hosting request has its own page showing which step you're on. See [Hosting your existing number](/docs/guide/agentic/sms-setup.md#hosting-your-existing-number) for the full walkthrough. ## The number detail page Click any number to open its detail page. From there you can: - **Change who answers**: pick a different agent or multi-agent and edit its [AI call settings](#ai-call-settings), or build a [call flow](#who-answers) with people, menus, business hours, and voicemail. - **Pick the voice and language**: see [Voice & language](#voice--language). - **Set recording, transcripts, limits, blocking, and AI call behavior**: see [Recording, transcripts, and limits](#recording-transcripts-and-limits) and [AI call behavior](#ai-call-behavior). - **Decide how texts are handled and answer them**: see [Texts](#texts). - **Turn on quick automations**: see [Quick automations](#quick-automations) and [Forwarding texts](#forwarding-texts-and-other-automations). - **Give the agent call tools**: see [Giving an agent call tools](#giving-an-agent-call-tools). - **See recent calls**: the latest calls with recordings and transcripts, and a link to the full [Phone calls table](#the-phone-calls-table). - **Check texting status**: whether the number can text and, if not, what it's waiting on. - **Turn texting on or off**: see below. - **Release or unlink**: a bought number is released back to the carrier (it may not be recoverable, and any texting fee stops from the next month); a hosted number stops being hosted; a number from your Twilio account is only unlinked and stays in your account. ### Turn on texting A number is free while it's only used for calls. To let it send and receive texts, click **Turn on texting** on its detail page. The flow takes you through everything still needed: 1. Your company's [business registration](/docs/guide/agentic/sms-setup.md#how-to-register-your-business), if it isn't approved yet. Its one-time fees are shown in the **Review & fees** step. 2. The number's monthly texting fee, shown before you confirm. A card on file is required. 3. The number is added to your registration and starts texting, and billing, once the registration is approved. Turning texting off keeps the number working for calls; its texting fee stops from the next month. See [Fees](/docs/guide/agentic/sms-setup.md#fees) for everything texting costs. Everything here is also scriptable through the [Phone Numbers Poweruser API](/docs/api/poweruser/phone-numbers.md), the [CLI](/docs/cli/phone.md), and [AutessaScript](/docs/autessascript/sdk/twilio.md). ## Related - [Phone & SMS tool types](/docs/tools/phone-sms.md) - [Agents](/docs/guide/agentic/agents.md) - [Managers](/docs/guide/agentic/managers.md) - [AutessaDB triggers](/docs/guide/autessadb/triggers.md) - [Text Messaging Setup](/docs/guide/agentic/sms-setup.md) - [SMS Consent](/docs/guide/agentic/sms-consent.md) - [Custom Voices](/docs/guide/custom-voices.md) - [Phone Numbers Poweruser API](/docs/api/poweruser/phone-numbers.md) --- # Text Messaging Setup Source: https://docs.autessa.com/docs/guide/agentic/sms-setup > Register your business for texting, get a phone number that can text, and send texts that carriers deliver. # Text Messaging Setup US carriers only deliver business texts from numbers tied to a registered business. Text Messaging is where your company registers once — your business, how customers opt in, and what you'll send — so your phone numbers can text. Phone numbers are free to get and use for calls. You pay only when you turn texting on: carrier registration fees for your business, and a monthly texting fee for each number that texts. ## What it's for Any text an agent, tool, or script sends from one of your [phone numbers](/docs/guide/agentic/phone-numbers.md) goes out under your company's registration. Without one, carriers block the message. Registering covers three things: - **Your business** — who is sending, verified against public records. - **Your campaign** — what kind of messages you send, with examples, and how people agree to receive them. - **Your numbers** — each number you [turn on texting](#turning-on-texting-for-a-number) for is added to the registration, so it can text once the registration is approved. Each company has **one** registration. It covers every number the company turns on texting for. ## Navigating here From the dashboard sidebar, open **Company → Text Messaging**. The page shows where your registration stands, what the carriers are waiting on, any fees, and the texting status of each of your numbers. Anyone in the company can view it; only the company owner can submit or change the registration. [View Text Messaging →](https://builder.autessa.com/company/messaging) ## Before you start Gather these before you open the form — having them ready is the difference between a registration that goes through the first time and one that comes back for changes: - **Your legal business name exactly as it appears on your IRS records** (your EIN letter), including punctuation and suffixes like "LLC" or "Inc." - **Your EIN** (US employer identification number). Sole proprietors without an EIN can register differently — see [Business or sole proprietor](#business-or-sole-proprietor). - **A working business website that shows your business name.** Reviewers visit it. - **A representative with a company-domain email address** — an address at your own domain, not a free email service. - **How your customers opt in, and a public link that proves it** — the web form, checkout checkbox, paper form, or conversation where people agree to receive your texts. You'll describe it and give a link carriers can open without signing in: the live sign-up page, or a screenshot, photo, or transcript of it (a Google Drive or OneDrive file shared so anyone with the link can view works). This is required for every registration. - **A public privacy policy link and a texting terms link.** Both are required. The privacy policy must say that mobile numbers and texting consent are never shared with third parties for marketing; the terms must cover message frequency, "Message and data rates may apply," and STOP and HELP. The form shows what each needs and gives you text you can copy into them. - **Two to five sample messages** that look like what you'll really send. Each should include your business name and opt-out wording such as "Reply STOP to opt out." - **A card on file** in [Billing](/docs/guide/company/billing.md). Registration fees are charged when you submit, and each number's monthly texting fee is charged to the same card. ## Business or sole proprietor The first choice in the form is what kind of sender you are: | | Registered business | Sole proprietor | | --- | --- | --- | | **Who it's for** | Companies with an EIN | Individuals without an EIN | | **How you're verified** | Your business details are checked against public records | A verification text is sent to the owner's mobile phone, and the owner replies to confirm | | **Phone numbers** | Any number of numbers | One number | | **Volume** | Standard carrier limits | Low daily volume | If you have an EIN, register as a business — sole-proprietor registrations are for people who genuinely don't have one, and carriers reject businesses that use them. ## How to register your business Open **Company → Text Messaging** and click **Register**. The form has five steps, and your progress is saved as a draft as you go, so you can leave and come back. 1. **Business** — legal name, EIN, business type, website, industry, and address. Sole proprietors enter their name and mobile number instead of an EIN. 2. **Representative** — the person carriers can contact about the registration: name, title, company-domain email, and phone number. 3. **Use case & samples** — what kind of messages you send (appointment reminders, customer care, account notifications, marketing, and so on), a description of your program, how people opt in with your opt-in proof link, and your sample messages. Picking a use case offers a template for any fields you've left empty, and **What counts as opt-in proof?** shows what to link and example wording for web forms, text-in keywords, in-person or phone consent, and paper forms. 4. **Opt-in & keywords** — the confirmation people get when they opt in, the replies people get when they text STOP, START, or HELP, and your privacy policy and texting terms links. 5. **Review & fees** — everything you entered, the fees that apply, and the card they'll be charged to. Submit when it all looks right. The form checks the common rejection reasons before you submit — for example, sample messages that don't mention your business name or don't include opt-out wording, or a representative email on a free email service — and points you at the field to fix. ## Proving how people opt in Carriers approve a registration only when a reviewer can see for themselves how people agree to your texts. The form asks for an **opt-in proof link** — a public link anyone can open without signing in — and adds it to your opt-in description. If people opt in more than one way, describe every way and link to one page or shared folder that shows each. | How people opt in | What to link | What it must show | | --- | --- | --- | | **A form on your website** | The live page with the form. If it's behind a login, a screenshot of it. | A phone number field; a texting consent checkbox that is unchecked and optional; your business name, what you'll text about and how often; "Msg & data rates may apply"; "Reply STOP to opt out, HELP for help"; links to your privacy policy and terms. | | **Texting your number first, or a keyword** | Where your number or keyword is advertised: a web page, or a photo or screenshot of the sign, flyer, receipt or post. | The number (and keyword), your business name, what you'll text about, message frequency, "Msg & data rates may apply", STOP and HELP instructions, and where to find your policies. | | **Verbal: in store, on a call, or a phone menu (IVR)** | A transcript of the exact script your staff read or your phone menu plays, posted publicly — a page on your website, or a Google Drive or OneDrive file shared so anyone with the link can view. | Where and when the script is used; the word-for-word consent question and the customer's "yes" (or key press); your business name, what you'll text about and how often; "Msg & data rates may apply"; STOP and HELP; and how you record the consent. | | **A paper form** | A photo or scan of the blank form, shared publicly. | A phone number field and a separate, optional texting consent checkbox, plus the same disclosures as a web form. | The form's **What counts as opt-in proof?** panel shows this guidance with example wording for each, filled in with your business details. ### Privacy policy and texting terms Both links are required, for businesses and sole proprietors alike, and both must open without signing in. - **Privacy policy** — says what you collect for texting and how you use it, and states that mobile numbers and texting consent are never shared with or sold to third parties for marketing. - **Texting terms** — your program name and what the texts are about, how often you text, "Message and data rates may apply", how to opt out (STOP) and get help (HELP) with a support contact, and that carriers aren't liable for delayed or undelivered messages. Your terms of service page works if it covers texting. The form gives you text you can copy into each page. No website? Publish them as shared documents (Google Docs or OneDrive) set so anyone with the link can view; a page on your own website is the strongest option. ## Registration status After you submit, the Text Messaging page shows a timeline of where the registration is. Carriers do the reviewing, so most of this is waiting; you don't need to do anything unless the status is **Action needed**. | Status | What it means | | --- | --- | | **Draft** | You've started the form but haven't submitted it. | | **Submitted** | Received; being sent to the carriers. | | **Business profile review** | Your business details are being checked. Usually quick. | | **Brand registration** | Carriers are registering your business as a sender. | | **Verification text sent** | Sole proprietors only: the owner's mobile has a verification text waiting for a reply. If it didn't arrive, you can resend it. | | **Campaign review** | Carriers are reviewing your use case, samples, and opt-in. This is the longest wait — often one to three weeks. | | **Ready to text** | Approved. Your numbers can text. | | **Action needed** | Something was rejected. The carrier's reason is shown — see [Fixing a rejection](#fixing-a-rejection). | | **Suspended** | Carriers stopped the registration, usually after complaints or messages that don't match what you registered. Contact support. | We email you when your registration is submitted, when it's approved, whenever the carriers ask for changes, and if anything on our side holds it up, so you don't have to keep checking. > You can use a number for calls while the registration is in review. Only texting waits for approval. ## Fixing a rejection When the status is **Action needed**, the page shows the carrier's reason word for word, and **Fix and resubmit** takes you straight to the part of the form it concerns. Rejections usually come down to a few things: - The business name or EIN doesn't match IRS records exactly. - The website doesn't load, doesn't show the business name, or doesn't describe how people opt in. - Sample messages don't include your business name or opt-out wording, or don't match the use case you picked. - The opt-in description doesn't explain clearly where and how people agree to receive texts, or the opt-in proof link doesn't open or doesn't show the consent wording. - The privacy policy or terms link is missing, private, or doesn't cover texting. Make the change and resubmit. Your other answers are kept. If resubmitting carries another carrier fee, it's shown before you confirm. ## Fees Getting a phone number and using it for calls is free, and no card is needed to buy one. Your company is charged only when you turn texting on: - **One-time registration fees** when you submit your business registration: a brand registration fee and a campaign vetting fee. Carriers charge these for reviewing your business, and they're passed through. - **A small monthly campaign fee** while the registration is active. - **A monthly texting fee for each number** that has texting turned on. The registration fees are shown in the **Review & fees** step before you submit, and each number's texting fee is shown on the number's page before you confirm. Everything is charged to your company's card on file, so add a card in [Billing](/docs/guide/company/billing.md) before you start. Registration fees pay for the carriers' review itself, so they aren't refunded if the registration is rejected. When you turn texting off for a number, its texting fee stops from the next month. Charges appear on your invoices alongside your other Autessa charges. ## Getting a number that can text A number can come from one of three places, all started from **Phone Numbers → Add number**. Details for each are on the [Phone Numbers](/docs/guide/agentic/phone-numbers.md#how-to-add-a-number) page. | Source | Calls | Texting | | --- | --- | --- | | **Buy through Autessa** | Free, immediately | When you turn texting on and your registration is approved | | **Your own Twilio account** | Optional, per number | Under your own Twilio registration | | **Host your existing number** | Stay with your current carrier | When your registration is approved and the carrier hands texting over | ## Turning on texting for a number Texting is off for a new number until you turn it on. Open the number from **Phone Numbers** and click **Turn on texting**. The flow walks you through whatever is still missing: 1. **Business registration** — if your company's registration isn't approved yet, you fill in or finish the [registration form](#how-to-register-your-business) first. If it's already approved, this step is skipped. 2. **Confirm the texting fee** — the number's monthly texting fee is shown before you confirm, and a card on file is required. 3. **Wait for approval** — the number is added to your registration. It starts texting, and its monthly fee starts, once the registration is approved. If the registration is already approved, that happens right away. To stop, open the number and turn texting off. The number keeps working for calls, and its texting fee stops from the next month. Once a number can text, decide who answers its texts: an AI agent, or your team from a shared inbox with optional auto-replies and keyword replies. Until you choose, texts to a number whose calls go straight to an AI agent are answered by that agent, and texts to any other number are saved to the inbox. See [Texts](/docs/guide/agentic/phone-numbers.md#texts). ### Texting status of a number The Phone Numbers list and each number's detail page show whether that number can text: | Status | What it means | | --- | --- | | **Not registered** | Your company hasn't finished Text Messaging registration yet. | | **Pending** | Your registration is in review. | | **Ready** | The number can text. | | **Action needed** | Your registration needs a fix — see [Fixing a rejection](#fixing-a-rejection). | | **Ready (your account)** | The number is from your own Twilio account, and its registration there is approved. | | **Unverified (your account)** | The number is from your own Twilio account, and we couldn't confirm it's registered there. Texts still send, but carriers may filter them. | ## Using your own Twilio account If you already have a Twilio account with registered numbers, you can link those numbers instead of buying new ones. Your own registration in Twilio applies — you don't need to register again in Autessa, and we don't block texts based on it; we show its status so you can spot problems. You'll need four things from the Twilio console: - **Account SID** - **API Key SID** and **API Key Secret** — used to manage your numbers. - **Auth Token** — Twilio signs the messages and calls it forwards to Autessa with your account's auth token, and that signature is how we confirm an incoming text really came from Twilio. There's no way to verify them without it. Credentials are verified when you connect and stored encrypted. After connecting, pick which numbers to link. For each one you can turn on **Enable calls** to route calls to your agents as well as texts. If a number was already pointing somewhere else in Twilio, you're warned before it's changed. Unlinking a number only disconnects it from Autessa and puts back its previous settings. It never deletes the number from your Twilio account. ## Hosting your existing number If you have a business number with another carrier and want to keep it — for example, it's printed on your signs — you can move just its **texting** to Autessa while calls keep ringing on your current carrier. This is Twilio's hosted texting service. 1. Finish the **Business** step of Text Messaging registration first — hosting uses your business address. 2. On **Phone Numbers → Add number**, choose **Host my existing number**. Enter the number (US or Canada, not toll-free) and a contact name, email, and title. 3. **Sign the Letter of Authorization.** Twilio emails the contact a letter authorizing the move. Sign it from that email. 4. **Verify you own the number.** Back in Autessa, click **Call me**. Twilio calls the number, and you enter the code shown on your screen on the phone's keypad. If you miss the call you can try again, up to three times. 5. **Wait for the carrier.** Your current carrier moves texting over, usually within 1–7 business days. You can cancel any time before this step starts. 6. **Done.** The number appears in your list with texting turned on and is added to your registration, so it can text once the registration is approved. Like any number that texts, it carries the monthly texting fee. The request page shows which step you're on. A request that isn't finished within 30 days expires, and you can start a new one. ## Consent and opt-outs Registration lets your numbers text; consent decides **who** they can text. Every text is checked before it's sent: - **Replies and conversations people start** — if the person texted your number within the last year, you can text them. - **Texts you start** — the person needs an approved consent record on the [SMS Consent](/docs/guide/agentic/sms-consent.md) page. - **Opt-outs always win** — anyone who replied STOP is never texted until they reply START, whatever their consent record says. STOP, START, and HELP replies are handled automatically using the replies you set in the **Opt-in & keywords** step. They're always handled first, before any agent, auto-reply, or [keyword reply](/docs/guide/agentic/phone-numbers.md#keyword-replies) of your own. A text that fails any of these checks isn't sent, and the agent or script that tried gets a plain-language reason — see the [Send SMS tool](/docs/tools/phone-sms.md#send-sms-). ## Related - [Phone Numbers](/docs/guide/agentic/phone-numbers.md) - [SMS Consent](/docs/guide/agentic/sms-consent.md) - [Phone & SMS tool types](/docs/tools/phone-sms.md) - [Billing](/docs/guide/company/billing.md) - [Phone Numbers Poweruser API](/docs/api/poweruser/phone-numbers.md) --- # SMS Consent Source: https://docs.autessa.com/docs/guide/agentic/sms-consent > Consent records for A2P 10DLC compliance: why they're required, how to submit them, and evidence uploads. # SMS Consent SMS consent records prove that a phone number's owner agreed to receive texts from you. US carriers require this proof for application-to-person messaging (A2P 10DLC), so Autessa keeps a per-company consent registry: before your agents message a number, that number needs an approved consent record. ## What it's for Carriers filter and can block business SMS traffic that lacks documented opt-in. The consent registry keeps your [phone-number](/docs/guide/agentic/phone-numbers.md) messaging compliant: - **Inbound-first conversations are automatic.** When someone texts your number first, a *user-initiated* consent record is created and auto-approved on the spot — nothing to do. A text from them within the last year is enough for your agents, or your team replying from the [shared inbox](/docs/guide/agentic/phone-numbers.md#the-shared-inbox), to text them. - **Outbound-first messaging needs an approved record.** If you want agents to text people who haven't texted you, submit consent documentation for each number and wait for approval. Until then, texts to that number are refused with a plain-language reason. - **Opt-outs are enforced.** A recipient texting **STOP** flips their record to opted-out, and opted-out numbers are never texted; **START** re-subscribes them. **HELP** gets the help reply from your registration. These keywords are handled automatically. > Consent decides *who* you can text. Your company also needs a [Text Messaging registration](/docs/guide/agentic/sms-setup.md) so carriers deliver your texts at all — the two are separate, and a text needs both. ## Navigating here From the dashboard sidebar, open **Agentic Workflows → SMS Consent**. The page has two panels: **Submit Consent** to add a phone number, and a **search** to check any number's current status. [View SMS Consent →](https://builder.autessa.com/agentic/sms-consent) ## Key concepts ### Consent methods Every record declares *how* consent was collected: | Method | Meaning | Approval | | --- | --- | --- | | **User Initiated** | The person texted your number first | Auto-approved | | **Web Form** | Consent given via a form on your website | Admin review | | **Verbal** | Spoken consent — a recording is required | Admin review | | **Written** | Written consent — documentation is required | Admin review | | **Other** | Any other collection method | Admin review | ### What a record holds A consent record stores the phone number (E.164 format, e.g. `+14155551234`; stored encrypted and displayed masked), the consent method, a description of how consent was collected, an optional **evidence file**, and links to the **terms of service** and **privacy policy** the person agreed to. Stronger documentation means faster approval — for Verbal and Written methods, evidence is expected. ### The review lifecycle Submitted records start unapproved and go to platform review. An approved record lets your agents start texts to the number; a rejected one comes back with feedback explaining what was missing. Resubmitting for the same number updates the existing record and resets it to pending review. Opt-out status is independent of approval — an approved number that texted STOP stays off-limits until it texts START. ## How to submit a consent record 1. On the SMS Consent page, open **Submit Consent**. 2. Enter the phone number in E.164 format and pick the consent method. 3. Describe how consent was collected, and attach evidence if you have it — PNG, JPG, JPEG, or PDF up to 10 MB (a screenshot of the signup form, the signed document, etc.). The file uploads directly to secure storage. 4. Add your terms-of-service and privacy-policy URLs, then submit. User-initiated records are approved immediately; everything else enters review. ## How to check a number's status 1. Enter the number in the search panel (E.164 format) and search. 2. The result shows whether a record exists, its approval state, opt-out state, consent method, and — for rejected records — the reviewer's feedback, with a one-click **resubmit** that pre-fills the form. > Checking and creating consent records is also available programmatically, so your signup flow can register web-form consent the moment a customer opts in — see the [SMS Consent Poweruser API](/docs/api/poweruser/sms-consent.md). ## Related - [Phone Numbers](/docs/guide/agentic/phone-numbers.md) - [Text Messaging Setup](/docs/guide/agentic/sms-setup.md) - [Send SMS tool](/docs/tools/phone-sms.md#send-sms-) - [Agents](/docs/guide/agentic/agents.md) - [SMS Consent Poweruser API](/docs/api/poweruser/sms-consent.md) - [Phone Numbers Poweruser API](/docs/api/poweruser/phone-numbers.md) --- # Versions Source: https://docs.autessa.com/docs/guide/agentic/versions > How draft and published versions keep production workflows stable while you iterate. # Versions Every tool, agent, and manager on Autessa is versioned. You iterate freely in a draft sandbox, publish a snapshot when it's ready, and roll back instantly if a release misbehaves — so nothing you're experimenting with ever reaches a customer by accident. ## What it's for LLM workflows have nondeterminism baked in, and model or prompt changes can shift behavior in ways you only discover in production. Versioning gives you deterministic control over what your end customers actually run: - **Safe iteration** — everything you do in the sandbox never impacts a customer until you hit publish. - **Deterministic releases** — a published version is an immutable snapshot; it doesn't change when you keep editing the draft. - **Instant rollback** — if a published version doesn't work so great in production, point the published pointer back at a previous release and the change takes effect immediately. ## Navigating here Versioning isn't a separate page — it lives on each resource's detail page. Open any tool (**Agentic Workflows → Tools**), agent (**Agentic Workflows → Agents**), or multi-agent (**Agentic Workflows → Multi-Agents**) and you'll find the version selector and publish controls on its detail view. ## Key concepts ### Draft (sandbox) vs published Each resource has one **draft** version — the sandbox where all your edits land — and zero or more **published** versions, which are numbered, immutable snapshots. API consumers and end customers get the currently published version by default; the draft is only visible to builders testing in the platform. The lifecycle looks like this: | Action | Draft (sandbox) | Published (what customers get) | | --- | --- | --- | | Create resource | v1 draft | — (nothing published yet) | | Edit and save | v1 draft changes | — | | **Publish** | new draft opens (v2) | **v1** | | Edit and save | v2 draft changes | v1 (unaffected) | | **Publish** | new draft opens (v3) | **v2** | | **Rollback to v1** | v3 draft (unaffected) | **v1** | | **Publish** | new draft opens (v4) | **v3** | Two things to notice: publishing freezes the current draft as the next numbered version and opens a fresh draft on top of it, and rolling back only moves the *published* pointer — your in-progress draft keeps its edits, so you can continue development and publish it when it's ready. > A rollback never deletes versions. Every published version stays browsable, so you can compare releases and re-publish forward again at any time. ### Access levels Resources are shared with per-user access levels: `READ`, `WRITE`, `PUBLISH`, and `ADMIN`. Anyone with `WRITE` can edit the draft, but publishing or rolling back requires **PUBLISH** or **ADMIN** access on that resource. This lets a team iterate on drafts while a smaller group controls what ships. ## How to publish a version 1. Open the resource's detail page and make sure the draft is saved. 2. Test the draft — drafts are fully testable without affecting the published version. 3. Click **Publish**. The draft is frozen as the next version number and becomes the published version customers receive. 4. Keep editing — your changes now accumulate in a fresh draft. ## How to roll back 1. On the resource's detail page, open the version list to see all previously published versions. 2. Browse a prior version to review its configuration and confirm it's the one you want. 3. Choose to roll back the published version to it. The change takes effect immediately for all consumers. 4. Fix the problem in your draft at your own pace, then publish again when ready. ## Versioning over the API Everything above is also scriptable. Publishing, rollback, and version listing live in the Poweruser API for each resource type — see [Agents](/docs/api/poweruser/agents.md), [Tools](/docs/api/poweruser/tools.md), and [Multi-Agents](/docs/api/poweruser/managers.md) — so you can wire releases into your own CI or admin tooling. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Managers](/docs/guide/agentic/managers.md) - [Agents Poweruser API](/docs/api/poweruser/agents.md) - [Tools Poweruser API](/docs/api/poweruser/tools.md) - [Managers Poweruser API](/docs/api/poweruser/managers.md) - [Team & permissions](/docs/guide/company/team.md) - [Marketplace publishing](/docs/guide/marketplace/publishing.md) --- # Custom Voices Source: https://docs.autessa.com/docs/guide/custom-voices > Cloning your own voice from a short sample and using it in voice agents. # Custom Voices Custom voices let your agents speak in a voice you provide. Record or upload a short sample, pick the best ten seconds, and the resulting instant voice clone is immediately selectable wherever an agent speaks — phone calls, real-time voice, and audio responses. ## What it's for Built-in voices are fine for prototypes, but a customer-facing voice agent usually needs to sound like *your* brand — a specific person, tone, and accent. An instant clone captures that from a single short sample and behaves exactly like a built-in voice afterwards: pick it once on the agent and every spoken interaction uses it. ## Navigating here From the dashboard sidebar, open **Custom Voices → Instant Clones**. The list has the standard ownership tabs (**Owned By Me / Shared With Me / Company Wide**); clicking a voice opens its detail page, where you can rename it and manage tags. [View Custom Voices →](https://builder.autessa.com/custom-voices/instant-clones) > Voice cloning is gated by your company's voice settings. If the Custom Voices section is missing from your sidebar (or the page says voice cloning is not enabled), your company doesn't have the feature turned on — ask your administrator. ## Key concepts ### Instant voice clones An instant clone is built from a single ten-second segment of clean speech — no training job, no waiting. Quality tracks the sample: one speaker, no background noise or music, natural conversational delivery. Recording at least 30 seconds and then choosing the best ten gives the model more to pick from. ### Using a cloned voice Cloned voices appear in the agent (and multi-agent) editor's voice picker: choose the **Medium** voice model, and your clones are listed alongside the built-in voices under **Voice Name**. From there the voice applies everywhere that agent speaks — [phone numbers](/docs/guide/agentic/phone-numbers.md), the test window's audio mode, and real-time voice sessions. ### Sharing Voices follow the standard sharing model: private by default, shareable with specific teammates or company-wide, with transferable ownership. Share a clone company-wide and every agent builder on the team can select it. ### Fine-tuned voices A second tier — fine-tuned voice clones trained on larger datasets for higher fidelity — exists in the product but is currently disabled in the builder UI. Instant clones are the supported path today. ## How to create an instant voice clone 1. On the Instant Clones page, click **Create Instant Clone** and name the voice. 2. Provide the sample, either way: - **Record** — capture audio from your microphone; aim for at least 30 seconds of clear, natural speech. - **Upload** — drop in an existing MP3, WAV, OGG, or M4A file. 3. On the trim step, drag the selector across the waveform to choose the best **10-second segment** — the cleanest, most representative stretch of speech. 4. Click **Create Voice Clone**. The clone is ready immediately. 5. Open an agent, set the voice model to **Medium**, select your clone as the voice name, and use the test window's audio mode to hear it before going live. > Only clone voices you have the right to use. Cloning someone's voice without their permission is a misuse of the feature. ## Sharing and access levels Every voice clone starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. Voices use a simple slice of the standard [access levels](/docs/guide/concepts.md): | Level | What it lets a teammate do | | --- | --- | | READ | Use the voice on their agents | | WRITE / PUBLISH | Nothing beyond READ — voices have no draft or publish step | | ADMIN | Share, delete, and transfer the voice | The owner always has ADMIN implicitly; any ADMIN can transfer a voice clone's ownership. ## Related - [Agents](/docs/guide/agentic/agents.md) - [Managers](/docs/guide/agentic/managers.md) - [Phone Numbers](/docs/guide/agentic/phone-numbers.md) - [Voice Cloning Poweruser API](/docs/api/poweruser/voice-cloning.md) --- # AutessaDB Tables Source: https://docs.autessa.com/docs/guide/autessadb/tables > Creating tables and knowledge bases, column types, computed columns, and semantic search. # AutessaDB Tables AutessaDB is the platform's built-in database. Create tables with typed columns for any structured data — a CRM, an order log, a content pipeline — or knowledge bases optimized for document storage and semantic search. Agents read and write the same tables you edit by hand, and triggers turn row changes into automated pipelines. ## Why AutessaDB is different It looks like a spreadsheet-simple table editor, but three capabilities set AutessaDB apart from an ordinary data store. ### AI-searchable Flip vectorization on for any text, file, or audio column and every insert and update is indexed automatically in the background — no re-index jobs, no separate pipeline. And it's more than raw text going into vectors: an LLM enriches each value with summaries, key facts, and the questions someone would likely ask of it, so search matches meaning rather than exact words. File columns get an auto-generated companion notes column describing each document, and audio columns get a transcript column. Semantic [search](/docs/api/autessadb.md) then works alongside structured queries — the same table answers "which calls mention pricing complaints?" and `status = 'open'`. Knowledge bases are a one-click preset for bulk file ingestion, but search works on any vectorized column of any table, not only knowledge bases. ### Event-driven Any insert, update, or delete can fire [triggers](/docs/guide/autessadb/triggers.md) that invoke tools, agents, or multi-agents — or write rows into other tables, whose own triggers run the next step. That's how tables chain into full processing pipelines without queues or glue code. Before you commit to a change, a design-time [impact analyzer](/docs/api/poweruser/autessadb-tables.md) maps the trigger chain a database event would set off and warns you about potential cycles. ### Everything recorded Every row carries created and last-updated timestamps. Every trigger execution is logged with encrypted before/after payloads you can drill into from the Trigger Execution History. And per-column indexing metadata shows exactly what was vectorized, and when — so you always know what your search index reflects. _(interactive UI demo omitted)_ ## Navigating here From the dashboard sidebar, open **AutessaDB → Tables**. The list shows all tables with ownership tabs (**All Tables / My Tables / Company Shared / Shared With Me**), search, and tag filters. The **New** menu offers four types — **Table** (define a custom schema), **Knowledge base** (upload documents for semantic search), **[Drive](/docs/guide/autessadb/drive.md)** (a shared file drive with folders and sharing), and **[Analytics table](/docs/guide/autessadb/analytics.md)** (columnar storage queried with SQL) — and the **Autobot** button opens the DB Copilot. A type badge on each table shows which kind it is, and the list can be filtered by type. ## Key concepts ### Tables and system columns A table is a named schema of typed columns. Table and column names use `snake_case`. Every table automatically gets three read-only system columns managed by the platform: `id` (integer), `created_at`, and `last_updated`. ### Column types The column editor offers these types: | Type | Holds | | --- | --- | | Short Text | Text up to 32K characters | | Long Text | Text up to 128K characters | | File | Documents — PDF, DOCX, XLSX, TXT, and similar | | Image | PNG / JPG uploads with thumbnail preview | | Video | MP4, AVI, MOV, and other video formats | | Audio | MP3, WAV, M4A with an inline player | | Integer | Whole numbers | | Decimal | Floating-point numbers | | Boolean | True/false values | | Single Select | One value from a defined option list | | Multi Select | Multiple values from a defined option list | Per-column options include a **Required** flag (not available on media columns), min/max length or value constraints for text and numbers, an option-list editor for the select types, and on Integer columns a **foreign key** link to a row in another table. > Store timestamps as **Short Text** containing UTC ISO-8601 strings (`yyyy-MM-ddTHH:mm:ssZ`). The only DateTime columns in a table are the system-managed `created_at` and `last_updated`; user-defined date fields follow the Short Text convention so agents, triggers, and scripts can parse them consistently. ### Semantic search and vectorization Mark a Short Text, Long Text, File, or Audio column as **Semantic Search** to turn on the background indexing described above. The table then gains a working **Semantic Search** tab: enter a query, pick the vectorized column, and get ranked results with similarity scores. Search agents (see [Agents](/docs/guide/agentic/agents.md)) query these columns at runtime. ### Knowledge bases A knowledge base is the one-click preset for bulk file ingestion: a table pre-configured for document storage and semantic search. The creation wizard lets you drag-and-drop files or folders; they're uploaded, indexed, and immediately searchable. A knowledge base's table view includes a **Create Agent** shortcut that provisions a ready-made Search agent over the table. ### Computed columns A column's default value can be **Static** or **Computed**. Computed values run an [AutessaScript](/docs/autessascript.md) snippet: read sibling values with `variables.get('column_name')` and set the result with `response.setResponse(...)`. Dependencies are detected automatically from the columns your script reads. See [Computed Columns](/docs/autessascript/computed-columns.md) for the scripting reference. ### DB Copilot (Autobot) The **Autobot** button opens an AI copilot that manages your database from natural language — describe what data you need to store and it proposes table creations and schema updates as reviewable change cards you explicitly apply or reject. See [DB Copilot](/docs/guide/autobots/db-copilot.md). ## How to create a table 1. Go to **AutessaDB → Tables**, click **Create**, and choose **Create Table**. 2. **Table Info** — name the table in `snake_case` and describe it. 3. **Columns** — add each column with a name, type, and description. Set Required, constraints, select options, semantic search, or a default value as needed. 4. **Review** — confirm the schema summary and click **Create Table**. To change the schema later, open the table's **Manage → Update Schema** dialog, which supports adding columns, deleting columns, and appending options to existing select columns. ## How to create a knowledge base 1. Click **Create → Create Knowledge Base**. 2. Name it, then drag in the documents (files or whole folders) on the **Upload Files** step. 3. Review and create — files are indexed automatically and you land on the table view, ready for the **Semantic Search** tab or the **Create Agent** shortcut. ## How to edit rows 1. Open a table and use the **Browse Data** tab. Click **Add Row** to insert — each column gets a typed input, media columns get an upload dropzone with preview, and columns with defaults offer **Use Default** / **Use Computed Value** toggles. 2. To edit or delete an existing row, right-click it in the grid and choose **View/Edit Record** or **Delete Record**. 3. Use the column-settings gear to hide, show, and reorder grid columns; media cells offer per-file **Download** buttons. ## Sharing and access levels Every table starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. The levels carry table-specific meanings: | Level | What it lets a teammate do | | --- | --- | | READ | View the schema and read rows — get, query, join, and semantic search | | WRITE | Insert, update, and delete rows | | PUBLISH | Add triggers to the table | | ADMIN | Change the schema, share the table, and delete it | > PUBLISH does **not** grant row writes on a table — writing rows takes WRITE or ADMIN. And while PUBLISH can *add* a trigger, editing, deleting, enabling, or disabling one requires ADMIN — see [Triggers](/docs/guide/autessadb/triggers.md). Deletion is two-step: the delete request returns a short-lived confirmation token, and confirming re-checks ADMIN. The owner always has ADMIN implicitly; transfer of ownership is owner-only. Tables auto-created by a [connector](/docs/guide/connectors.md) can't be shared, transferred, or deleted directly — manage the connector instead. ## Related - [AutessaDB Triggers](/docs/guide/autessadb/triggers.md) - [AutessaDB Drive](/docs/guide/autessadb/drive.md) - [AutessaDB Analytics Tables](/docs/guide/autessadb/analytics.md) - [DB Copilot](/docs/guide/autobots/db-copilot.md) - [Computed Columns](/docs/autessascript/computed-columns.md) - [AutessaDB tool type](/docs/tools/autessadb.md) - [AutessaDB API](/docs/api/autessadb.md) - [AutessaDB Tables Poweruser API](/docs/api/poweruser/autessadb-tables.md) --- # AutessaDB Triggers Source: https://docs.autessa.com/docs/guide/autessadb/triggers > Run tools, agents, or database updates automatically when table rows change. # AutessaDB Triggers Triggers turn AutessaDB tables into automation surfaces: when a row is inserted, updated, or deleted, a trigger can invoke a tool, an agent, or a multi-agent — or update another table — with the row's column values available as variables. Chain triggers across tables and you have a full processing pipeline, visible and editable right where the data lives. _(interactive UI demo omitted)_ ## What it's for - **React to data changes** — send a welcome email when a signup row lands, kick off an agent when a document is uploaded, recalculate a rollup when a status flips. - **Build pipelines without queues** — a trigger on table A writes a row into table B, whose own trigger runs the next step. Each stage's inputs and outputs are ordinary rows you can inspect and correct. - **Keep automation observable** — every firing is recorded in the Trigger Execution History with the before/after row snapshot and the action's response. ## Navigating here Triggers are defined per table: open a table under **AutessaDB → Tables** and switch to its **Triggers** tab — what you can do there depends on your access level on the table (see below). Execution history has its own page at **AutessaDB → Trigger Execution History**. ## Key concepts ### Anatomy of a trigger A trigger has four parts: 1. **Identity** — a name, optional description, and an enabled/disabled switch. 2. **Event** — fires on `INSERT`, `UPDATE`, or `DELETE` of rows in the table. 3. **Condition** — a filter mode of **Any Column** (fire on every matching event) or **Specified Columns** with one or more column filters combined with AND/OR. Operators cover change detection (*Changed*, *Became Null*, *Became Non-Null*), value comparison (*Equals*, *Not Equals*, *Is Null / Is Not Null*), numeric comparisons, text matching (*Contains*, *Starts With*, *Ends With*), multi-select operators (*Value Added*, *Value Removed*, *Contains Value*), and boolean checks. Comparison values can be literals or references to other columns. 4. **Action** — one of four types: - **Invoke Tool** — pick any tool you can access and map each of its arguments to a literal or a column reference. - **Invoke Agent** — pick an agent and compose its input prompt, e.g. `Analyze this feedback: @@{feedback_text}`; prompt template variables are filled the same way. - **Invoke Multi-Agent** — the same, but delegating to a [manager](/docs/guide/agentic/managers.md); each firing starts a fresh, single-turn conversation. - **Update AutessaDB** — insert or update a row in a target table, mapping its column values from the source row. ### Row variables Anywhere an action accepts a value, `@@{column_name}` inserts the row's current (new) value, and `@@{old.column_name}` inserts the previous value (available on UPDATE and DELETE). Typing `@@` opens autocomplete over the table's columns. This is how a trigger passes data into its tool, agent prompt, or target table. > Key triggers on *artifact* columns rather than status flags where you can: "fire when `transcript` became non-null" is more robust than "fire when `status` equals `transcribed`", and it coexists cleanly with rows that users edit by hand. ### Trigger chains as pipelines Because an Update AutessaDB action can write into another table — and tools, agents, and multi-agents can themselves write rows — trigger firings compose into multi-stage pipelines. A common shape: an intake table's INSERT trigger invokes a processing tool; the tool writes its result column back; a second trigger on that column *becoming non-null* runs the next step or copies the finished record to an output table. Chains can get intricate, so a design-time [impact analyzer](/docs/api/poweruser/autessadb-tables.md) is available: give it a table and an event type and it statically maps which triggers would match, which tables, tools, and agents the cascade could touch, and warns you about potential cycles in the trigger graph — nothing is executed. ### Execution history The **Trigger Execution History** page shows every firing for a table, month by month: the event ID, trigger name, trigger type, the database event (INSERT/UPDATE/DELETE), the response status, and the timestamp. Three drill-downs are available per row: - **Trigger Definition** — a historical snapshot of the trigger as it was when it fired (flagged if the trigger has since been deleted). - **Database Event** — the row's before and after values, field by field. - **Trigger Response** — the action's outcome, including a link to the agent conversation for agent-invoking triggers. ## How to create a trigger 1. Open the table and go to its **Triggers** tab, then create a new trigger. 2. Name it (e.g. "Send Welcome Email") and pick the **Trigger Type** — Invoke Tool, Invoke Agent, or Update AutessaDB. 3. Choose the **Event** (INSERT / UPDATE / DELETE) and the **Filter Mode**. For Specified Columns, add filters — for example `transcript` + *Became Non-Null*. 4. Configure the action, mapping arguments or column values with `@@{column}` references. 5. Save with **Create Trigger**. Triggers can be cloned, disabled, and re-enabled from the list at any time. ## How to debug a trigger 1. Go to **AutessaDB → Trigger Execution History** and select the table and month. 2. Find the firing — failed responses are badged red. 3. Open the **Database Event** to confirm the row change looked as expected, then the **Trigger Response** to read the error or, for agent triggers, jump into the agent conversation. 4. If the trigger definition itself was wrong, the **Trigger Definition** snapshot shows exactly what configuration fired. ## Access levels Triggers don't have their own sharing — what you can do follows your [access level](/docs/guide/concepts.md) on the table: - **READ** — view the table's triggers and their execution history. - **PUBLISH** (or ADMIN) — add a new trigger. - **ADMIN** — edit, delete, enable, or disable an existing trigger. ## Related - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Scheduled Jobs](/docs/guide/scheduled-jobs.md) - [AutessaDB Triggers Poweruser API](/docs/api/poweruser/autessadb-triggers.md) --- # AutessaDB Drive Source: https://docs.autessa.com/docs/guide/autessadb/drive > A shared file drive for your company: folders, uploads, ranked and exact-text search (optionally meaning-based), Google Drive-style sharing, and tools that let agents browse and cite your documents. # AutessaDB Drive A Drive is an AutessaDB table built for files and folders. You get a real file explorer with folder upload, previews, versions, and trash; every document is extracted so you can search its words or its exact text (or its meaning, once semantic search is turned on); and sharing works the way it does in Google Drive. Agents get the same view through a small set of tools (list, find, grep, read, search), so they can find the right page of the right file and cite it. ## What it's for - **A company document store.** Contracts, policies, reports, scanned forms, and exports, organized in folders and shared with the people and teams who need them. - **Grounding agents on documents.** An agent bound to a Drive can browse folders, grep for an exact clause, run a ranked search for a topic, and read the matching page before it quotes it. Answers come back with locators such as `/Contracts/acme-msa.pdf p.4 L.12`. - **File intake for apps and pipelines.** Custom apps, scripts, and the CLI upload into the same folders you browse by hand, and table triggers fire on every new or changed file. A Drive is different from a [knowledge base](/docs/guide/autessadb/tables.md#knowledge-bases). A knowledge base is a flat table of files indexed for semantic search; a Drive has real folders, line- and page-level text search, versions, trash, per-folder sharing, and a file explorer instead of the row grid. ## Navigating here From the dashboard sidebar, open **AutessaDB → Drive**. If your company has one Drive it opens directly; otherwise you land on the tables list filtered to Drives. To create one, open **AutessaDB → Tables**, click **New**, and choose **Drive**. The explorer has three parts: - **Folder tree** on the left. Its top entry is the Drive itself (named after the Drive), followed by **Shared with me** and **Trash**, with a storage meter at the bottom. - **File list** in the center, with breadcrumbs, a toolbar (a **New** menu for folders, documents, and uploads), a search box, a list or grid toggle, and sort options. - **Quick look** (press Space on a file) shows a thumbnail, details, and an Open button. Opening a file (double-click or Enter) goes to the full-page viewer described below. The header carries **Share** (add Drive members) and **Settings**. ## Key concepts ### Files, folders, and paths Every item has a path such as `/Finance/2026/q1-report.pdf`. Folders are real items: they can be empty, shared, moved, and trashed. Names are unique within a folder regardless of case, so `Report.pdf` and `report.pdf` cannot sit side by side. A path can be up to 64 folders deep, and each name can be up to 255 bytes. Under the hood each file or folder is a row, so you can add your own columns (a client ID, a tag, a status) and use them in [triggers](/docs/guide/autessadb/triggers.md). The Drive's built-in columns (`path`, `name`, `file`, `size_bytes`, `version`, and the rest) are managed for you and cannot be removed or retyped. ### Who can see a Drive A new Drive is **empty and private**: only the person who created it and its Drive admins can see it. There are no folders until you create or upload some, and you organize it however you like. Opening a Drive up takes two steps, because sharing the Drive and sharing what is in it are separate: 1. **Add members.** Share the Drive itself from the **Share** button in the Drive's header. Pick people, teams, or **Anyone at your company**, as Viewer or Editor. Members see the Drive in their list and can open it, but they **don't see any folder or file in it yet**. An Editor member can also add folders and files at the top level of the Drive; they own what they add. 2. **Share folders and files.** Share each folder or file from inside the Drive. Folders and files never inherit the Drive's members: each one is visible only to the people it is shared with (and, for a folder, everything in it). To show a folder to the whole team in one step, use **Share with drive members** in its **Share** dialog. A member who opens a Drive where nothing is shared with them yet sees "You're a member of this drive. Folders and files appear here once they're shared with you." | Who | What they see | | --- | --- | | **The Drive's creator and Drive admins** | Everything in the Drive. Drive admins are listed in **Settings**. | | **Members** | The Drive in their list, plus the folders and files shared with them and anything they added themselves. | | **People with access to some items** | Only those items, under **Shared with me**. The Drive also appears in their list. | | **Everyone else in your company** | Nothing. The Drive does not appear in their list of Drives. | ### Extraction and search readiness After a file uploads, its text is extracted in the background. Text, code, CSV, JSON, Markdown, and similar files keep their real line numbers. PDFs, Word documents, spreadsheets, and presentations are extracted page by page, so results point at a page and a line on that page. A small dot next to each file shows its status: | Status | Meaning | | --- | --- | | Processing | Extraction or indexing is still running. The file can be previewed and downloaded but may not appear in search yet. | | Searchable | The file's text is ready for search. | | Some pages aren't searchable | Some scanned pages couldn't be read, so they have no searchable text. The preview lists those pages. | | Not searchable | The format has no extractable text (for example, a ZIP archive), extraction failed, or the file is too large to index fully. The first part of a very large file stays searchable. | Identical files are extracted only once, even if they are uploaded to several folders. ### Scanned pages Scanned PDFs and images are read with OCR (optical character recognition) during extraction, so their text is searchable as soon as the file is. A page OCR reads with low confidence, such as handwriting or a poor scan, keeps the text OCR found: it is searchable, and the preview flags it as read with low confidence ("Unclear pages are kept as read"). A page with no readable text at all is listed as not searchable. OCR'd pages count toward the Drive's monthly extracted-pages allowance like any other extracted page; they are not billed as AI tasks. In the Drive's **Settings**, under **Scanned documents**, set the OCR **language** as a language code, such as `eng` for English, or several joined with `+`, such as `eng+fra`. ### Versions and trash Uploading a file over an existing one with **Replace** keeps the old content as a previous version. Open a file's details to see its versions and restore one. The newest 10 versions are kept by default. Deleting moves items to **Trash**, where they stay for 30 days by default. Restoring puts them back at their original path (with a numbered name if something new now occupies it). **Empty trash** deletes permanently. Version and trash retention follow your plan's [usage limits](/docs/guide/company/usage-limits.md). ### Storage Each company has a Drive storage allowance and a per-file size limit, shown in the storage meter under the folder tree. Uploads reserve space up front, so an upload that would exceed the allowance is refused before any bytes are sent. See [Usage Limits](/docs/guide/company/usage-limits.md). ## Sharing Sharing in a Drive has two levels. Sharing the **Drive** makes people **members**: it puts the Drive in their list and decides whether they can add items at the top level. Sharing a **folder or file** gives access to that item. Membership never gives access to an item by itself. ### What you can share, and with whom You can add **members to the Drive**, or share **any folder** or **any file**, with: - **People** in your company. - **Teams**. Everyone on the team gets access, and people who join the team later get it too. - **Anyone at your company**, as one entry. - **A link** (see [Share by link](#how-to-share-by-link)). Each share has a role: | Role | On a folder or file | As a Drive member | | --- | --- | --- | | **Viewer** | Open, preview, download, and search the item. | See the Drive and browse what is shared with them. | | **Commenter** | Everything a Viewer can do, plus add and reply to comments. | See the Drive, browse what is shared with them, and comment on it. | | **Editor** | Everything a Viewer can do, plus upload, create folders, rename, move, trash and restore, and edit your custom columns. | Also add folders and files at the top level. They own what they add, and nobody else sees it until they share it. | The person who created an item is its **Owner**. Owners and Drive managers can share. Editors can also share, up to the Editor role, when **Editors can share** is on in the Drive's settings (it is on by default, as in Google Drive). Nobody can give a role higher than their own. ### Inheritance Sharing a folder shares everything inside it, including subfolders and files added later. Drive membership does **not** flow down: members see a folder or file only when it is shared with them. Access only flows **down**: sharing `/Finance/2026` does not reveal `/Finance` or its other folders. A person who has access to a shared folder sees it under **Shared with me** and browses from there. When someone has access to an item through more than one share (say, Viewer on a folder through their team and Editor on a file inside it directly), the stronger role wins. ### Moving changes access Because access follows folders, moving an item changes who can see it, exactly as in Google Drive: - **Moving into** a shared folder gives the item that folder's access. - **Moving out** of a shared folder removes the access it inherited from there. - Shares made directly on the item travel with it. Before a move that would change access, the explorer shows a confirmation listing who will gain and who will lose access. Moving requires Editor on the item and Editor on the destination folder. A folder containing items you cannot see cannot be moved by you; ask an Owner or a Drive manager. ### Who has access The **Share** dialog of a folder or file lists everyone who can reach the item: the owner, Drive managers, direct shares, and shares inherited from a folder above, with that folder (for example, "Finance team · Editor · via /Finance"). Remove an inherited share at the folder it comes from. Drive members are not listed there, because membership doesn't give access to the item. When only the owner can see an item, the dialog says **Drive members don't automatically see this item**. The Drive's own **Share** dialog lists its members in one line (for example, "Members: Atlas Core (Editor), Anyone at Acme (Viewer)"). ### Search respects access Search, exact-text search, and agent tools only ever return files the person (or the agent's acting identity) can open. A file that was shared with you and later unshared stops appearing immediately. ## How to create a Drive 1. Go to **AutessaDB → Tables**, click **New**, and choose **Drive**. 2. Name the Drive and add a description. 3. Optionally add Drive admins, who can see and manage everything in it. 4. Click **Create**. You land in the new, empty Drive. It is private to you (and any Drive admins) until you share it; use **Upload** to add files or **Share drive** to add your team as members, then share the folders they should see. ## How to upload files and folders 1. Open the folder you want to upload into. 2. Drag files or whole folders from your computer onto the file list, or use **Upload files** / **Upload folder** in the toolbar. Folder uploads recreate the folder structure. 3. If a name already exists, choose **Keep both** (adds a number to the new file's name) or **Replace** (saves the old content as a version). Tick **Apply to all** to use the same choice for the rest of the batch. 4. Watch progress in the upload panel. You can cancel individual files; closing the tab warns you while uploads are still running. Large files upload in parts and resume automatically after brief network interruptions. ## How to create a new document Open the **New** menu in the toolbar and pick **Word document**, **Spreadsheet**, **Slide deck**, or **Text document**. The file is created in the current folder with a placeholder name you can change on the spot, and it opens straight in its editor. New text documents are Markdown files that open as rich text, so you can write headings, lists, tables, and links without touching any syntax. Everything created this way is a normal file: it has versions, can be shared, and is found by search once its text is extracted. ## How to view and edit files Opening a file takes you to a full-page viewer with a calm top bar: back to the folder, the file name (click to rename), a version badge with history, **Edit**, **Share**, **Download**, a **Details** panel, and a **Comments** panel. **Download** saves the file under the name you see in the Drive, never an internal identifier. Use the arrow keys to move between files in the folder, Esc to go back, and F for full screen. | File type | What you get | | --- | --- | | Word documents (.docx) | Real pages as Word lays them out: styles, lists, tables, images, headers and footers, footnotes, landscape sections. **Edit** gives a Word-style toolbar (styles, fonts, bold/italic, colors, lists, tables, pictures, links, page breaks), find, and **Track changes** with accept and reject. Saving keeps everything you did not touch exactly as it was, including parts that are shown but not yet editable: equations, content controls, charts, text boxes, headers and footers. | | Spreadsheets (.xlsx) and CSV | An editable grid with the file's fonts, colors, fills, borders, number formats, merged cells, frozen panes, and sheet tabs. Formulas recalculate as you type, including spilling array formulas. Saving keeps formatting, charts, pictures, conditional formatting, and data validations; chart sheets, slicers, and form controls are not kept, and the editor says so. Legacy `.xls` files can be downloaded but not opened. | | PDFs | A reader with page thumbnails, zoom, selectable text, and in-document search. | | Images | A lightbox with fit, 1:1, rotate, zoom, and a filmstrip of the folder's images. | | Markdown and plain text (.md, .txt) | Markdown opens as a rich-text document by default, with headings, lists, tables, links, and pictures shown and edited as formatted text; switch to **Source** to see or edit the raw Markdown, or **Split** to see both. Plain text opens in a simple editor. Both support live co-editing. | | JSON, CSV, and code | A code editor with syntax highlighting. These files also support live co-editing. | | Audio and video | Players in the app. | | Presentations (.pptx, .ppsx) | A slide viewer with a slide sorter, present mode (F or F5, arrow keys, swipe), speaker notes, and zoom. **Edit** lets you move, resize, add and delete shapes and text boxes, edit text in place with a formatting toolbar, insert pictures, and add, duplicate, reorder, and delete slides using the deck's own layouts, with undo and redo. Saving rewrites only the slides you changed, so everything else in the file stays exactly as it was. Tables and grouped shapes display but cannot be edited yet; charts without a stored picture show a placeholder; animations are kept but not played. Legacy `.ppt` files can be downloaded but not opened. | | ZIP archives up to 50 MB | Browse the folders and files inside without downloading: sizes, dates, a name search, the folder tree, breadcrumbs, and arrow keys. Text, code, Markdown, images, PDFs, Word and Excel files, CSVs, and media open right inside the archive, and any entry can be downloaded on its own. Larger or password-protected archives can be downloaded. | | Legacy Word (.doc, .rtf) | The extracted text plus **Download**. | Every save creates a new version; open **Details** to see history and restore an earlier one. If someone else saved while you were editing, the viewer offers to reload or save your copy as a new file. ## How to find something The search box has three modes: | Mode | Use it for | | --- | --- | | **Search** | A few words about what you need ("termination for convenience", "Q1 budget approval"). Files that contain more of your words rank first, then files with more matches, earlier pages, and matching names. Common words such as "the" are ignored. | | **Exact text** | A specific string, clause number, or ID. Optionally **Match case**. | | **File name** | Names and patterns such as `*.pdf` or `invoice-2026-*`. | Results are grouped by file with highlighted snippets and their page and line. Click a snippet to open the preview at that page. Use the scope chip to search the current folder only or the whole Drive. ### Semantic search By default, **Search** ranks files by the words they contain. To also match meaning (so "cancel the contract early" finds "termination for convenience"), a Drive owner or admin can turn on **Semantic search** in the Drive's **Settings**, under **Search**. The first mode in the search box is then labeled **Smart**, and it combines meaning-based and keyword ranking. - Semantic search indexes each file's text with an embedding model. Turning it on indexes the files already in the Drive in the background; until a file is indexed, it is found by its words. - Turning it off stops indexing and removes the index. Search goes back to ranking by words. - Semantic searches count toward your company's semantic search limits. ## How to add members to a Drive 1. Open the Drive and click **Share** in the header. 2. Under **Add members**, search for a person or team, pick **Viewer** or **Editor**, and click **Share**. To add everyone in your company, use the **Anyone at your company** row. 3. Members now see the Drive in their list of Drives, but none of its folders or files. Share the folders they need next (see [Share a folder with every member](#how-to-share-a-folder-with-every-member)). 4. To change or remove a member, use the role menu next to their name. From the CLI, `/` means the Drive itself: ```bash autessa drive share / --team Finance --role editor autessa drive share / --company --role viewer ``` ## How to share a folder with every member This is the usual way to give a team a shared folder. 1. Select the folder and click **Share** (or right-click and choose **Share**). 2. In the **Drive members** row, pick **Viewer** or **Editor** and click **Share with drive members**. 3. Every current member gets their own share on the folder, so you can later remove one member without affecting the others. People who become members later don't get the folder automatically; share it with them, or use **Share with drive members** again. From the CLI: ```bash autessa drive share Finance --members --role viewer ``` ## How to share a file or folder 1. Select the item and click **Share** (or right-click and choose **Share**). 2. Under **People and teams**, search for a person or team, pick **Viewer** or **Editor**, and click **Share**. To share with everyone in your company, use the **Anyone at your company** row. 3. Review **Who has access** to confirm the result, including inherited access. 4. To change or remove someone's access, use the role menu next to their name. ## How to share by link 1. In the **Share** dialog, open **Link sharing** and click **Create link**. 2. Choose who the link works for: **Anyone with the link**, or **Only people at your company** (they must be signed in). 3. Choose whether link visitors can download files, and optionally set an expiry date. 4. Copy the link. Revoking it from the same section stops it working immediately. A link on a folder opens a read-only view of that folder and everything under it. A link on the Drive itself (from the Drive's **Share** dialog) opens a read-only view of everything in the Drive. Links keep working if the item is moved or renamed, and stop working if it is trashed. ## How to comment on a file Open a file and press **C**, or click **Comments** in the top bar. Select text in a document, a page range in a PDF, cells in a spreadsheet, or nothing at all to comment on the whole file, then write your note. Comments are threaded: reply, resolve when done, and show resolved threads again any time. Each comment shows an excerpt of exactly what was selected, and that selection stays highlighted in the document for everyone who can see the comment. Clicking a comment jumps to the place it refers to, and clicking a highlight opens its thread. Comment counts show in the file's details and as a badge in the folder list. Commenting needs the **Commenter** role or higher on the item. Authors can edit and delete their own comments; Editors and owners can resolve any thread; Drive managers can delete any comment. Agents can read and leave comments too, with the same locators, so a review note from an agent lands on the right page. Comments stay with the file across versions. When a new version changes the text a comment pointed at, the comment moves to where that text now sits, or is marked as needing a look if the text is gone. ## Editing together in real time Text, Markdown, and CSV files up to 2 MB open as live documents: when two people edit the same file, each sees the other's changes and named cursor as they type, and changes merge without anyone losing work. Saves happen automatically after a short pause and create a new version, marked as a live save in the version history. Viewers and Commenters can watch a live session but not type. If live collaboration is unavailable, the editor falls back to the usual edit-and-save flow and says so. Word documents and spreadsheets open in their own editors; live co-editing for them is coming next. ## How to move, rename, and delete - **Rename:** select an item and press **F2**, or right-click and choose **Rename**. - **Move:** drag items onto a folder in the list, the tree, or the breadcrumbs, or use **Move to** from the right-click menu. Confirm the access change if one is shown. - **Delete:** press **Delete** or choose **Move to trash**. An **Undo** toast appears for a few seconds. Restore later from **Trash**. Keyboard shortcuts: **Enter** opens, **Backspace** goes up a folder, **Space** previews, **Ctrl+A** selects all, **/** focuses search, and **Esc** clears the selection. ## How to work with files from the CLI The [`autessa` CLI](/docs/cli/autessadb.md#shorthand-autessa-drive) has short, shell-like commands for Drives. Pick the Drive once, then use paths relative to the Drive root: ```bash autessa drive use Docs # remember the Drive; 'autessa drive use' lists yours autessa drive touch notes/memory.md # create an empty file autessa drive write notes/memory.md "# Memory" # create or replace a text file echo "- Acme prefers Net 45" | autessa drive append notes/memory.md autessa drive edit notes/memory.md --old "Net 45" --new "Net 60" autessa drive cat notes/memory.md # print the whole file autessa drive grep "Net 60" notes/ # path:page:line: text autessa drive push ./reports reports/2026 # upload a local folder autessa drive pull reports ./backup # download a Drive folder ``` `write`, `append` and `edit` work on text files up to 5 MB, and every change is a new version you can see and restore in the preview's **Version history**. The new text is searchable right away. Run `autessa drive` for the full list. ## How to give an agent access to a Drive 1. Create a tool of type [`AUTESSADB_DRIVE`](/docs/tools/autessadb.md#drive) for the Drive, or pick the Drive as a Search agent's **Knowledge Base Table**. 2. Optionally set a **folder scope** (for example `/Legal`) so the agent can only see that part of the Drive, even if its identity could see more. 3. The agent receives narrow tools for listing, finding, grepping, reading, searching, and fetching files. It uses search for concepts, grep for exact strings, and reads the page or line range before quoting, citing results as `path p.N L.M`. To look at a photo, chart, or scanned page itself, it fetches the file with `view` on. ## Agents writing files Drive-bound agents can also write: create and replace text files, append to them, make exact edits, create folders, and move or trash items. Writing needs **Editor** access on the folder for the person the agent acts for. An agent running on its own (no person) acts as your company, so give its tools a folder scope. A common pattern is a **memory file**: one text file the agent keeps across conversations, in a folder it creates for itself, such as `/agent-memory/notes.md`. Writing a file creates any missing folders on the way. 1. At the start of a task, the agent reads the file. 2. When it learns something worth keeping, it appends a line (`drive_write` with `mode: append`). Appending never overwrites, and two runs can append at the same time. 3. When a fact changes, it edits that line with the exact old text (`drive_edit`), then reads the file again to check. Every write is a new version, so you can see what the agent changed and restore an earlier version from the preview's **Version history**. An agent acting for a user sees exactly what that user can see. See [Agents](/docs/guide/agentic/agents.md) and [Tools](/docs/guide/agentic/tools.md). ## Related - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [AutessaDB Triggers](/docs/guide/autessadb/triggers.md) - [Analytics tables](/docs/guide/autessadb/analytics.md) - [Drive API](/docs/api/autessadb-drive.md) - [Drive tool type](/docs/tools/autessadb.md#drive) - [Drive commands in the CLI](/docs/cli/autessadb.md#how-to-work-with-a-drive) - [`autessa.autessadb` SDK](/docs/autessascript/sdk/autessadb.md) --- # AutessaDB Analytics Tables Source: https://docs.autessa.com/docs/guide/autessadb/analytics > Columnar analytics tables for millions to billions of rows: load CSV and Parquet files, query with read-only SQL, describe the data for agents, and share it like any AutessaDB table. # AutessaDB Analytics Tables An analytics table is an AutessaDB table stored in columnar analytics storage and queried with SQL. It is built for the workloads a row table handles poorly: aggregations, group-bys, window functions, and joins over millions to billions of rows. You load data in bulk from files or other tables, ask questions in the SQL Workbench or through agents, and share it with the same access levels as every other table. ## What it's for - **Reporting over large data.** Sales history, event logs, sensor readings, call records, and exports from other systems that are too large or too analytical for row-by-row access. - **Agents that answer data questions.** An agent can look up what a table contains, write a SQL query, run it, and page through the result. A semantic layer (column descriptions, named measures, and verified example queries) makes those answers consistent. - **Dashboards in custom apps.** Apps run the same read-only queries and render the results. Use a regular [table](/docs/guide/autessadb/tables.md) when people edit individual rows, when triggers should fire per row, or when you need semantic search over text. Use an analytics table when data arrives in batches and is mostly read in aggregate. ## Navigating here Open **AutessaDB → Tables**, click **New**, and choose **Analytics table** to create one. Existing analytics tables appear in the tables list with an **Analytics** badge; filter the list by type to see only them. **AutessaDB → SQL Workbench** opens the [SQL Workbench](/docs/guide/autessadb/sql-workbench.md) for querying one or several tables. A table's detail page stacks its information top to bottom: - **Header** with **Open workbench**, **Load data**, **Ask**, **Share**, and **Edit**. - **Summary** with row count, storage size, and when data was last loaded. - **Schema**, including partition columns. - **Semantic layer**: description, measures, joins, and verified queries. - **Preview** of the first 50 rows. - **Loads** and **Recent queries**. ## Key concepts ### Analytics tables are read-only to SQL Queries are a single read-only `SELECT` statement (common table expressions with `WITH`, subqueries, joins, unions, and window functions are all fine), written in **ANSI SQL**. Standard SQL works as written; for date and approximate functions use the analytical names, such as `date_trunc('month', order_date)`, `approx_distinct(customer_id)`, and `format_datetime(order_ts, 'yyyy-MM')`. You change data by **loading** or **appending**, never with `INSERT`, `UPDATE`, or `DELETE` in a query. Analytics tables do not support row-by-row edits, triggers, semantic search, or the row grid. ### Column types When you create a table you choose an analytics type for each column. If you start from a file, types are inferred for you and you can adjust them. | Type | Holds | | --- | --- | | `STRING` | Text | | `INT` / `LONG` | 32-bit / 64-bit whole numbers | | `DOUBLE` | Floating-point numbers | | `DECIMAL(p,s)` | Exact decimals, for money and quantities | | `BOOLEAN` | True/false | | `DATE` | Calendar dates | | `TIMESTAMP` / `TIMESTAMPTZ` | Date and time, without or with a time zone (stored in UTC) | | `STRING_LIST` | A list of text values | | `JSON` | Nested JSON values | Column names are lowercase `snake_case` (letters, digits, and underscores, starting with a letter or underscore, up to 63 characters). Every table also gets two system columns: `_loaded_at` (when the row was loaded) and `_load_id` (which load added it). There is no built-in `id` column. Large integers and `DECIMAL` values are returned as strings in JSON results so no precision is lost; timestamps are returned as ISO-8601 UTC strings. When types are inferred from a file, numbers with at most two decimal places (money amounts) are suggested as `DECIMAL(18,2)` so totals add up exactly. Pick `DOUBLE` instead if the column is a measurement rather than an amount. ### Partitioning A partition spec tells the storage how to group rows on disk, so a query that filters on the partition column reads only the matching groups. Choose up to two partition fields, each a column plus a transform: | Transform | Groups rows by | | --- | --- | | `YEAR`, `MONTH`, `DAY`, `HOUR` | The date or timestamp truncated to that unit | | `IDENTITY` | The exact value (good for low-cardinality columns such as `region`) | | `BUCKET(n)` | A hash of the value into `n` buckets | | `TRUNCATE(w)` | A text prefix or number range of width `w` | The most common choice is the month or day of the main date column. Queries should then filter on that column: `WHERE order_date >= DATE '2026-01-01'` scans a fraction of what an unfiltered query scans. ### SQL name Each analytics table has a SQL name, unique within your company, that queries use in `FROM` and `JOIN`. It defaults to the table name in `snake_case` (a table called "Order Lines" becomes `order_lines`). Queries can reference any analytics tables you have READ access to by their SQL names. ### Semantic layer The semantic layer is optional metadata that helps people and agents query a table correctly: - **Description and grain.** What one row represents, for example "one row per order line". - **Column descriptions and synonyms.** "`net_amount` is revenue after discounts; also called net sales". - **Measures.** Named SQL expressions such as `revenue := SUM(net_amount)`, offered as completions in the workbench and listed for agents. - **Joins.** How this table relates to others, for example `order_lines.customer_id = customers.id`, many-to-one. - **Verified queries.** A question paired with SQL someone has checked. Agents prefer a verified query when a question matches one. Saved queries are re-validated when the schema changes. #### Draft it with AI On a table's Edit page, click **Suggest with AI**. Autessa reads the table's columns, statistics, and a 50-row sample, as you are allowed to see them, and drafts a description, what one row represents, column descriptions and other names, measures, example verified queries, and joins. Suggestions are drafts: each one sits next to the current value with a checkbox. Verified queries show whether they passed their check, and you can **Test** them. **Apply selected** fills the form; nothing is saved until you click **Save semantic layer**. By default only empty fields get suggestions. When creating a table from a file, **Suggest descriptions with AI** drafts the table and column descriptions before you create it. Suggestions do not count toward AI usage. ### Queries run as jobs Every query is a job that runs asynchronously on the analytics engine. Most queries take a few seconds; large scans take longer. While a query runs, you (or an agent) can wait for it or check back for the result later. Results are paged and can be downloaded as CSV or Parquet. A job can be cancelled while it runs. Results are kept for 7 days. Before running a heavy query you can **estimate** it, which reports roughly how much data it would scan without running it. ### Access and security Analytics tables use the same [sharing and access levels](/docs/guide/autessadb/tables.md#sharing-and-access-levels) as other tables: | Level | On an analytics table | | --- | --- | | READ | Describe the table, preview it, and run queries | | WRITE | Load and append data | | ADMIN | Change the schema and semantic layer, share, and delete | A query that joins several tables needs READ on every one of them. A table you cannot read behaves as if it does not exist: the error says "unknown table or no access". Row-level and field-level security apply to queries too: hidden columns cannot be selected, masked columns return masked values, and row rules filter what each person's queries see. ### Masked columns Mark a text column **Masked** when you create an analytics table (or add the column later) to store its values encrypted. Masked columns still work in SQL for exact matches and counting: `=`, `<>`, `IN`, `IS NULL`, `GROUP BY`, `DISTINCT`, `COUNT(DISTINCT ...)`, `MIN`/`MAX`, joins with other masked columns, and `ORDER BY`. Sorting follows an internal code rather than the alphabet. Searching inside values (`LIKE`), ranges, and other functions are not available on masked columns. You cannot partition or sort a table by a masked column, and you cannot turn masking on or off for an existing column. People see masked values as `••••••` unless a database role says otherwise: - **Visible**: the person can reveal values one at a time (eye button) or all on a page (**Show values**). Every reveal is recorded in the access log. - **Masked**: the person always sees the mask you choose: fully redacted, the last four characters, or a code that looks the same for equal values. - **Hidden**: the column is not there for that person at all. Table owners and admins can always reveal values. Downloads contain mask text unless you choose **with values shown**. Masked columns must be enabled for your company. ### Limits Each plan sets limits on analytics storage, data scanned per query and per day, concurrent queries, query run time (up to 15 minutes), result rows, and data loaded per day. A query whose estimate exceeds the per-query scan limit is refused with a hint naming the partition columns to filter on. See [Usage Limits](/docs/guide/company/usage-limits.md). ## How to create an analytics table 1. Go to **AutessaDB → Tables**, click **New**, and choose **Analytics table**. 2. Enter a name and description. 3. Choose how to define the columns: - **Start from a file**: drop a sample CSV or Parquet file. Columns and types are inferred, a sample of rows is shown, and any problems (mixed types, unparseable dates) are listed as warnings. - **Define columns**: add each column with a name, type, whether it may be empty, and a description. - **Copy from an AutessaDB table**: start from an existing table's columns. 4. Optionally open **Partitioning**. The editor suggests a partition, such as "Partition by month of `order_date`", when your columns include a date. 5. If you started from a file, leave **Load this file after creating** checked to load it right away. 6. Click **Create**. You land on the table's detail page. ## How to load data 1. On the table's detail page, click **Load data**. 2. Choose a source: - **Upload file**: a CSV or Parquet file from your computer. Large files upload in parts. - **From AutessaDB table**: copy rows from a regular table, mapping its columns to this table's columns. 3. Choose **Append** (add the rows) or **Replace** (swap the table's contents for the new rows). 4. Click **Load**. Progress appears in the **Loads** card; rows that cannot be parsed are reported rather than silently dropped. Scripts and apps can also append small batches of rows (up to 10,000 rows or 5 MB per call). Appends that arrive close together are committed together every few seconds. ## How to change the schema Open **Edit** on the detail page. You can add columns (they are empty for existing rows) and widen a column's type safely, for example `INT` to `LONG`. Renaming, removing, and narrowing columns are not supported; add a new column and load into it instead. ## How to describe a table for agents 1. Open **Edit → Semantic layer**, or the **Semantic layer** card on the detail page. 2. Write the table's description and grain, and describe each column. Add synonyms for the words people actually use. 3. Add measures for the numbers people ask about, such as revenue, order count, or average handle time. 4. Add joins to related analytics tables. 5. Save good queries as verified queries, either here or from the workbench with **Save as verified query**. ## How to let an agent query analytics tables 1. Create three tools for the tables the agent should use: [`ANALYTICS_DESCRIBE`](/docs/tools/autessadb.md#describe-analytics-tables), [`ANALYTICS_QUERY`](/docs/tools/autessadb.md#query-analytics-tables), and [`ANALYTICS_GET_RESULT`](/docs/tools/autessadb.md#get-analytics-result). The **Create agent tools** action on the detail page creates all three, bound to the tables you pick. 2. Attach them to an agent. The agent describes the tables first, prefers verified queries, filters on partition columns, and pages through long results. Agents never receive download links; they read results through the tools. ## Related - [SQL Workbench](/docs/guide/autessadb/sql-workbench.md) - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [AutessaDB Drive](/docs/guide/autessadb/drive.md) - [Analytics API](/docs/api/autessadb-analytics.md) - [Analytics Poweruser API](/docs/api/poweruser/autessadb-analytics.md) - [Analytics tool types](/docs/tools/autessadb.md#describe-analytics-tables) - [Analytics commands in the CLI](/docs/cli/autessadb.md#how-to-load-and-query-analytics-tables) --- # SQL Workbench Source: https://docs.autessa.com/docs/guide/autessadb/sql-workbench > Write, run, estimate, and save read-only SQL over your AutessaDB analytics tables, with schema-aware completion and a results grid built for large results. # SQL Workbench The SQL Workbench is where you query [analytics tables](/docs/guide/autessadb/analytics.md). It pairs a SQL editor that knows your tables, columns, and measures with a results grid that scrolls smoothly through hundreds of thousands of rows. Queries run asynchronously as background jobs, and typically take a few seconds. A long query keeps going if you navigate away, and you can come back to it from your history. ## What it's for - Exploring a new dataset and checking that a load landed correctly. - Answering one-off questions that are easier to write as SQL than to build a report for. - Writing and checking the verified queries that agents rely on. - Exporting a result to CSV or Parquet. ## Navigating here - **AutessaDB → SQL Workbench** opens the workbench with every analytics table you can read in the schema sidebar, for queries that join several tables. - **Open workbench** on an analytics table's detail page opens it focused on that table. ## Key concepts ### Layout - **Schema sidebar** on the left: your analytics tables, each expanding to its columns (with types) and measures. Click a name to insert it at the cursor. - **Editor** in the center: SQL highlighting and completion for table names, column names, and measures. - **Run controls** above the results: **Run**, **Run selection**, **Estimate**, **Cancel**, and a row limit. - **Results** below: a grid with a sticky header, resizable columns, and horizontal scrolling for wide results. - **History** opens as a drawer on the right. ### What SQL you can write Each run is one read-only statement: a `SELECT`, optionally with `WITH` common table expressions, `UNION`, joins, subqueries, and window functions. The dialect is **ANSI SQL**: standard SQL works as written, and analytical functions follow the common names, for example `date_trunc('month', order_date)` to group by month, `approx_distinct(customer_id)` for a fast distinct count, and `format_datetime(order_ts, 'yyyy-MM')` to format a timestamp. Date literals are written `DATE '2026-01-01'`. Statements that change data or settings are rejected before they run. Refer to tables by their SQL names; tables you cannot read are reported as unknown. ### Estimates **Estimate** reports roughly how much data a query would scan (for example, "Scans ~1.2 GB") without running it. The workbench also refreshes the estimate shortly after you stop typing. Use it to catch a missing partition filter before you run an expensive query. ### Results The grid shows typed values: decimals and very large integers exactly as stored, timestamps in your local time with UTC on hover, and empty values as a muted `null`. The toolbar shows the row count and run time, and offers **Download CSV**, **Download Parquet**, **Copy**, and **Save as verified query**. The row limit caps how many rows come back. If a result was cut off, the toolbar says so; add filters or aggregation, or raise the limit. ### Drafts and history Your unsaved SQL is kept as a draft for each table in this browser. **History** lists your recent runs with their status, run time, and rows; click one to reopen its SQL and result. Results are kept for 7 days. ## How to run a query 1. Open the workbench. 2. Write a query, for example: ```sql SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue FROM order_lines WHERE order_date >= DATE '2026-01-01' GROUP BY 1, 2 ORDER BY 2, 1 ``` 3. Press **Ctrl+Enter** (**Cmd+Enter** on Mac) or click **Run**. To run only part of the editor, select it and press **Ctrl+Shift+Enter** or click **Run selection**. 4. Results appear in the grid as soon as the job finishes, typically within a few seconds. While it runs you can click **Cancel**, or leave the page and come back to it from **History**. ## How to check a query's cost 1. Write the query and watch the estimate next to the run controls, or click **Estimate**. 2. If the estimate is large, add a filter on a partition column (shown in the schema sidebar) or narrow the columns you select. ## How to download results 1. Run the query. 2. Click **Download CSV** or **Download Parquet** in the results toolbar. The download contains the full result up to the row limit, not only the rows on screen. ## How to save a verified query 1. Run the query and check the result. 2. Click **Save as verified query**, and write the question it answers in plain language ("Monthly revenue by region this year"). 3. The query appears in the table's semantic layer, where agents can find and reuse it. ## How to ask in plain language 1. Click **Ask** and describe what you want to know. 2. The assistant proposes SQL and inserts it into the editor. Nothing runs until you click **Run**. ## Related - [AutessaDB Analytics Tables](/docs/guide/autessadb/analytics.md) - [Analytics API](/docs/api/autessadb-analytics.md) - [Analytics tool types](/docs/tools/autessadb.md#query-analytics-tables) - [Analytics commands in the CLI](/docs/cli/autessadb.md#how-to-load-and-query-analytics-tables) --- # Scheduled Jobs Source: https://docs.autessa.com/docs/guide/scheduled-jobs > Run tools, agents, and AutessaDB updates on recurring, cron, or one-time schedules. # Scheduled Jobs Scheduled jobs run work on a timer: invoke a tool, invoke an agent, or update an AutessaDB table on a recurring interval, a cron schedule, or once at a specific time. They're the "when" half of automation — pair them with triggers, which are the "on data change" half. ## What it's for - **Recurring reports and digests** — an agent that summarizes yesterday's activity every morning at 9. - **Data syncs and maintenance** — a tool that pulls from an external API every 15 minutes, or a nightly row cleanup. - **Future-dated one-offs** — run something once at a specific wall-clock time. ## Navigating here From the dashboard sidebar, open **Scheduled Jobs → Jobs** for the job list and **Scheduled Jobs → History** for the execution history. The job list has ownership tabs (**Owned By Me / Shared With Me / Company Wide**), plus filters by action type (Agent / Tool / Database), status, and sort order. Each row shows the job's next execution, last execution, and total execution count, with quick pause/resume and delete actions. ## Key concepts ### Schedules A job runs on one of three schedule patterns: | Pattern | Configuration | Timezone | | --- | --- | --- | | **Recurring (every X)** | Run every N minutes, hours, or days | None needed — interval-based | | **Custom schedule (cron)** | Daily / Weekly / Monthly builders (hour, minute, days), or an **Advanced** raw cron expression | Required | | **Run once** | A single future date and time | Required | The cron builder shows the generated expression and previews the next three runs in your chosen timezone. Advanced mode takes a 6-field expression (`minute hour day-of-month month day-of-week year`, with `?` in exactly one of day-of-month or day-of-week — AWS cron format). ### Actions The same three action types as [AutessaDB triggers](/docs/guide/autessadb/triggers.md): - **Invoke Tool** — pick a target tool and fill its arguments; optionally set environment variables for the run and a **Continue on error** flag. - **Invoke Agent** — pick a target agent, write its input prompt, and fill any prompt template variables the agent defines. - **Update AutessaDB** — insert or update a row in a target table, choosing which columns to set. Values in any action can reference the job variables `@@{jobName}`, `@@{triggerDate}` (the run date, `YYYY-MM-DD`), and `@@{triggerTime}` (the full ISO-8601 run timestamp) — handy for stamping rows or parameterizing prompts with "today". The action type is fixed once the job is created. ### Job lifecycle and sharing Jobs are created enabled and can be paused/resumed from the list or the job's detail page, which also shows execution stats and metadata. Like other resources, jobs are shareable with specific teammates or company-wide, with transfer of ownership by the owner. ### Execution history **Scheduled Jobs → History** aggregates every run across all jobs, with success/failure counts and a success-rate summary for the selected month. Each row shows the job name, resource type, start/end time, duration, status, and attempt number; clicking a row opens the full execution details — error message, response JSON, and the payload that was sent. ## How to schedule an agent to run daily 1. Go to **Scheduled Jobs → Jobs** and create a new job; give it a name and description. 2. Under **Schedule Configuration**, pick **Custom schedule → Daily**, set the hour and minute, and choose your timezone. Check the "next 3 runs" preview. 3. Under **Action Configuration**, choose **Invoke Agent**, select the agent, and write its input — e.g. `Summarize activity for @@{triggerDate} and email the digest.` 4. Fill any prompt template variables, then click **Create Job**. The job starts enabled; watch its first run on the History page. ## How to investigate a failed run 1. Open **Scheduled Jobs → History** and filter to **Failed** for the relevant month. 2. Click the run to open **Execution Details** — the error message, the response JSON, and the exact payload are all there. 3. Fix the job's action config (or the underlying tool/agent) and either wait for the next scheduled run or temporarily switch the schedule to test sooner. ## Sharing and access levels Every job starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. What each level allows: | Level | What it lets a teammate do | | --- | --- | | READ | View the job | | WRITE | Edit the job and enable/disable it | | PUBLISH | Nothing beyond READ — jobs have no publish step, and job edits require WRITE | | ADMIN | Share and delete the job | The owner always has ADMIN implicitly; transferring ownership is owner-only. ## Related - [AutessaDB Triggers](/docs/guide/autessadb/triggers.md) - [Agents](/docs/guide/agentic/agents.md) - [Tools](/docs/guide/agentic/tools.md) - [Scheduled Events Poweruser API](/docs/api/poweruser/scheduled-events.md) - [Scheduled Events API](/docs/api/scheduled-events.md) --- # Connectors Source: https://docs.autessa.com/docs/guide/connectors > Linking external accounts — Gmail, QuickBooks, Notion, and 20+ more — that power connector-backed tools. # Connectors A connector is a stored, authorized link to an external account — a Gmail inbox, a QuickBooks company, a Notion workspace. Connect an account once, and every connector-backed tool can act on it: the tool config just points at the connector, and Autessa handles the credentials (including refreshing OAuth tokens automatically before each call). ## What it's for Agents become genuinely useful when they can touch your real systems. Connectors separate *authorization* from *automation*: an admin links the account, and tool builders reference it without ever seeing tokens or keys. Each connector unlocks a whole family of tool types — a single Gmail connector powers read, send, label, and search tools; a QuickBooks connector powers invoices, payments, reports, and more. See the [Tool Types reference](/docs/tools.md) for every family. ## Navigating here From the dashboard sidebar, open **Connectors**. The page is a searchable grid grouped by category — Google Services, Social Media, Content Marketing, File Storage, Ticketing Platforms, ERP Systems, CRM Systems, Accounting / Payments, Microsoft Services. Click a card to open that connector's own page, where its connected accounts are listed and managed. [View Connectors →](https://builder.autessa.com/connectors) ## Key concepts ### The full catalog | Connector | Category | Authentication | | --- | --- | --- | | Gmail | Google Services | OAuth | | Google Calendar | Google Services | OAuth | | Google Analytics | Google Services | OAuth | | Outlook | Microsoft Services | OAuth | | Outlook Calendar | Microsoft Services | OAuth | | LinkedIn | Social Media | OAuth | | X (Twitter) | Social Media | OAuth | | Facebook | Social Media | OAuth | | Instagram | Social Media | OAuth | | Reddit | Social Media | OAuth | | Threads | Social Media | OAuth | | Bluesky | Social Media | Handle + app password | | YouTube | Social Media | OAuth | | TikTok | Social Media | OAuth | | Ghost | Content Marketing | API key | | Mailchimp | Content Marketing | OAuth (access token only) | | Beehiiv | Content Marketing | API key | | Box | File Storage | OAuth | | Notion | File Storage | OAuth | | Zendesk | Ticketing Platforms | OAuth | | Acumatica | ERP Systems | OAuth | | HubSpot | CRM Systems | OAuth | | Zoho CRM | CRM Systems | OAuth | | QuickBooks | Accounting / Payments | OAuth | | Autessa Payments | Accounting / Payments | Special — embedded onboarding | | Meeting Bot | — | Special — provisioned by Meeting Recorder | Two entries are special: **Autessa Payments** is a payments account onboarded through a secure verification flow rather than an OAuth redirect (agents can then invoice, charge, and refund your customers), and **Meeting Bot** is the bot identity created by the [Meeting Recorder](/docs/guide/platform/meeting-recorder.md) rather than something you connect from this page. The exact cards shown on your Connectors page can vary — a few connectors are enabled per environment. ### The connect flow OAuth connectors follow the standard redirect dance: you name the connector, get sent to the provider's consent screen, approve the requested scopes, and land back on the connector's page with the new account listed. API-key connectors (Ghost, Beehiiv) skip the redirect — you paste the key. Bluesky takes your handle plus an app password and authenticates at call time. You can connect **multiple accounts per connector type** — e.g. a support inbox and a billing inbox as two Gmail connectors — and each tool picks which one it uses. ### Reconnection If the external provider revokes or expires the grant (password change, admin policy, long inactivity), tools using the connector start failing with auth errors. **Reconnect** on the connector's card re-runs the OAuth flow for the *same* connector — its identity and every tool referencing it stay intact. Prefer reconnecting over deleting and re-adding. ### How connectors power tools A connector-backed [tool](/docs/guide/agentic/tools.md) names its connector in its configuration; at execution time Autessa loads the credentials, refreshes tokens if needed, and makes the call. Connector pages also offer a bulk **Create Tools** action that generates a ready-made set of tools for that account in one step — the fastest way from "account connected" to "agent can use it." ### Sharing A connector is private to its creator by default. Share it with specific teammates or company-wide, with read/write/admin levels — sharing the connector is what lets others build and run tools against that account without ever holding its credentials. Ownership can be transferred. **Deleting a connector also removes the tools that use it**, so audit dependents first. ## How to connect an account 1. On the **Connectors** page, find the service (search or browse by category) and open its page. 2. Click connect and name the connector — something that identifies the account, like `SupportInbox`. 3. Complete the provider's flow (OAuth consent, API key, or credentials). You'll return to the connector page with a success confirmation. 4. Optionally use **Create Tools** to generate the standard toolset for the account, or reference the connector from individual tools. 5. Share the connector with your team if others should build against it. ## How to fix a broken connector 1. If connector-backed tools start failing with authorization errors, open the connector's page. 2. Click **Reconnect** on the affected account and re-approve access at the provider. 3. Re-run a failing tool with **Execute Tool** to confirm the fix — no tool configuration changes needed. ## Sharing and access levels Every connector starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. Sharing is what lets teammates' tools act through the connection without ever holding its credentials. What each level allows: | Level | What it lets a teammate do | | --- | --- | | READ | Use the connection — their tools can act through it, and it appears in their connector list | | WRITE | Edit the connection (rename it, activate or deactivate it) | | PUBLISH | Nothing beyond READ — connectors have no publish step, and edits require WRITE | | ADMIN | Share the connector | Deleting a connector and transferring its ownership are both owner-only. The owner always has ADMIN implicitly. Deleting a connector removes the tools that use it **and its auto-created tables** — those tables can't be shared, transferred, or deleted directly; manage the connector instead. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Tool Types reference](/docs/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Meeting Recorder](/docs/guide/platform/meeting-recorder.md) - [Connectors Poweruser API](/docs/api/poweruser/connectors.md) --- # API Keys Source: https://docs.autessa.com/docs/guide/api-management/api-keys > Creating resource-scoped client API keys with rate limits and origin restrictions for your integrations. # API Keys Client API keys let your applications execute the resources you've built on Autessa — run tools, chat with agents, and read or write AutessaDB tables through the Client API. Each key carries its own resource scope, rate limits, and optional origin allow-list, so a production integration touches exactly what you intended and nothing else. ## What it's for A client key is the credential you hand to your own backend (or a partner's) so it can *use* what you've published — as opposed to a [poweruser key](/docs/guide/api-management/poweruser-keys.md), which *manages* your workspace. Because scope, limits, and origins are set per key, the right pattern is one key per integration: a narrowly scoped key for the website chatbot, another for the data pipeline, each revocable on its own. ## Navigating here Open **API Management → API Keys** in the sidebar (the section appears when your role has API access). The page lists your keys in a table — name, truncated key ID, **Enabled**/**Disabled** status, an **All resources**/**Scoped** badge, this month's request count, and success rate. Expanding a row shows detailed usage stats with **View History**, **Reset Token**, and **Edit** actions; the kebab menu on each row also offers **Edit**, **Reset token**, **View details**, and **Delete**. ## Key concepts - **The key is shown once.** After **Create API Key**, the full key value appears a single time with a **Copy** button — it cannot be retrieved later. Requests pass the raw key in the `Authorization` header (no `Bearer` prefix); WebSocket endpoints take it as an `?authorization=` query parameter instead. See [Authentication](/docs/guide/authentication.md). - **Resource access** — a key either has access to **all resources in your account** or a hand-picked scope across three resource types: **Tools**, **Agents**, and **AutessaDB Tables**. Within each type you can grant everything (e.g. "All Tools") or tick individual resources. - **Per-table permissions** — every AutessaDB table in a key's scope carries its own **READ**, **WRITE**, and **DELETE** checkboxes (or one set of operations applied to all tables), so a key can read a catalog table while being unable to modify it. - **Rate limits** — each key sets **Requests per Minute** (default 60) and **Requests per Day** (default 5000). Requests beyond the limit are rejected until the window resets. - **Allowed origins** — an optional allow-list of web origins (e.g. `https://app.example.com`, `http://localhost:3000`). Leave it empty to accept requests from anywhere; add entries to restrict. Origins are exact scheme + host + port — wildcards aren't supported. - **Usage stats, not just logs** — the expanded row shows total, successful, and failed requests with per-endpoint, per-agent, and per-tool breakdowns; **View History** opens the full transaction log. > Origin restrictions are a hardening layer, not a substitute for secrecy — the key itself still belongs on a server, never in client-side code. ## How to create an API key 1. On the API Keys page, click **Create API Key**. 2. Name the key after the integration it serves and add a description. Leave **Enable this API key** checked. 3. Set the per-minute and per-day rate limits, and add allowed origins if the calling app runs in a browser context you control. 4. Under resource access, uncheck **Grant access to all resources in your account** and select the specific tools, agents, and tables the integration needs — including READ/WRITE/DELETE per table. 5. Click **Create API Key**, then **copy the key value** from the success screen. It will not be shown again. ## How to rotate a key 1. Open the key's row menu and choose **Reset token** (or **Reset Token** in the expanded detail panel). 2. Confirm the warning — the old token is invalidated **immediately**, so anything still using it will stop working. 3. Copy the new key value from the success screen and update every application that used the old one. The key keeps its ID, scope, limits, and stats; only the secret changes. To pause a key without rotating, uncheck **Enable this API key** in **Edit**; to remove it permanently, choose **Delete** (irreversible). ## The in-app API docs The builder ships a quick-start reference at `/keys/docs` covering exactly what client keys can call: `POST` and WebSocket variants of tool execution and agent execution, with request formats, streaming message shapes, audio notes, and copyable cURL and JavaScript samples. For the complete endpoint reference, use the [Client API docs](/docs/api.md). ## Related - [Authentication](/docs/guide/authentication.md) - [Client API Reference](/docs/api.md) - [Poweruser Keys](/docs/guide/api-management/poweruser-keys.md) - [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md) - [Tables](/docs/guide/autessadb/tables.md) --- # Webhook Handlers Source: https://docs.autessa.com/docs/guide/api-management/webhook-handlers > Receiving external webhooks and routing them to tools, agents, managers, or AutessaDB. # Webhook Handlers Webhook handlers give you public HTTPS endpoints that external systems — Stripe, GitHub, Shopify, your own services — can call when something happens. Each handler validates the incoming request, extracts variables from the payload, and executes a resource you choose: a tool, an agent, a manager, or a direct AutessaDB operation. ## What it's for Agents and tools normally run when you (or your API) invoke them. Webhook handlers flip the direction: the outside world initiates. A payment processor reports a charge, a CRM announces a new lead, a CI pipeline finishes a build — the handler turns that HTTP call into an execution inside Autessa, with fields from the payload mapped into the resource's inputs. No client API key is involved; each webhook carries its own dedicated key. ## Navigating here Open **API Management → Webhook Handlers** in the sidebar. The page lists your handlers in a table — name, an **Enabled**/**Disabled** status badge, a **View History** button, and **View Calling Instructions** — with a **Create Webhook** button at the top. Click a row to open its configuration. ## Key concepts - **One trigger URL per handler** — every handler is called through the shared endpoint `https://api.autessa.com/clients/custom-webhooks?resourceId={webhookId}`, which accepts any HTTP method (GET, POST, PUT, DELETE, PATCH) so it fits whatever the sending system insists on. You don't configure allowed methods; the method used is simply recorded per delivery. See the [Custom Webhooks API](/docs/api/custom-webhooks.md) for the full request contract. - **Dedicated webhook key** — each handler gets its own `wh:{keyId}:{keySecret}` credential, shown **once** in the "Webhook Created Successfully" modal. Pass it in the `Authorization` header, or as an `authorization` query parameter when the sender can't set headers. Lost it? **View Calling Instructions → Reset API Key** issues a new one and immediately invalidates the old. - **Four target types** — the **Resource Execution** section selects what runs: a **Tool** (with argument mappings, environment variables, and private variables that are never logged), an **Agent** or **Manager** (with a user message and prompt template variables), or **AutessaDB** (a direct Insert or Update on a table, no agent involved). - **Payload transformation** — incoming requests are reduced to a flat set of variables, either with **JSONPath** mappings (`orderId` ← `$.order.id`) or an **AutessaScript** transform that receives the raw `payload`, `headers`, and `queryParams` and returns an object. Everywhere else in the form, `@@{variableName}` references substitute those variables into tool arguments, user messages, and column values. - **Request validation** — an optional AutessaScript that runs on every delivery after key authentication. It receives `headers`, `body`, and `queryParams` and must return `true` or `false` — use it to verify sender signatures (a Stripe or GitHub HMAC, for example). A `false` return rejects the delivery with `401`. - **Asynchronous execution** — a valid delivery returns `202 Accepted` with an `executionId` immediately; the resource runs in the background under the handler's **Execution Settings** (timeout, default 300 seconds; max retries, default 3; retry delay, default 5 seconds). The caller never receives the resource's output — you inspect it in execution history. ## How to create a webhook handler 1. On the Webhook Handlers page, click **Create Webhook**. 2. Under **Basic Information**, name the handler (e.g. "Stripe Payment Webhook"), add a description, and leave **Enabled** checked. 3. Optionally enable **Request Validation** and write the signature-check script. 4. Under **Payload Transformation**, map the payload fields you need — JSONPath pairs for straightforward JSON bodies, an AutessaScript transform for anything more involved. JSONPath mode requires at least one mapping. 5. Under **Resource Execution**, pick the resource type and target, then wire the extracted variables into its inputs with `@@{variableName}` references. 6. Adjust **Execution Settings** if the defaults don't fit, then click **Create Webhook**. 7. The success modal shows the full trigger URL and the API key — **copy the key now**; this is the only time it's displayed. Confirm with **I've Saved My API Key - Continue**. 8. Paste the trigger URL and key into the external system's webhook settings. ## How to review deliveries 1. Click **View History** on the handler's row. 2. The table lists each execution with its ID, status (**QUEUED**, **SUCCESS**, or **FAILED**), resource type, queued time, and duration. 3. Click **View** on a row to open **Execution Details**: the HTTP method and source IP, the request headers and payload, the **Transformed Variables** your transform produced, and the **Resource Response** from the tool, agent, manager, or database operation. > There is no in-app "send test request" button — the way to test a handler is to call its trigger URL with `curl` (or the sender's test-delivery feature) and read the outcome in execution history. Handlers can also be shared from the edit page (**Share**): keep private, share company-wide read-only, or company-wide editable. Only the owner can delete a handler, and disabled handlers reject all deliveries. ## Sharing and access levels Every handler starts **private** to its owner. Share it with **specific teammates** — each at their own access level — or **company-wide** at a single level for everyone. What each level allows: | Level | What it lets a teammate do | | --- | --- | | READ | View the handler | | WRITE | Edit the handler | | PUBLISH | Same as WRITE — handlers have no publish step | | ADMIN | Share the handler and regenerate its API key | Deleting a handler is owner-only — ADMIN is not enough. The owner always has ADMIN implicitly. ## Related - [Custom Webhooks API](/docs/api/custom-webhooks.md) — trigger URL, authentication, and the execution pipeline - [Custom Webhooks (Poweruser API)](/docs/api/poweruser/custom-webhooks.md) — managing handlers programmatically - [API Keys](/docs/guide/api-management/api-keys.md) - [Tools](/docs/guide/agentic/tools.md) - [Triggers](/docs/guide/autessadb/triggers.md) --- # Poweruser Keys Source: https://docs.autessa.com/docs/guide/api-management/poweruser-keys > Personal full-management credentials for the Poweruser API — permanent keys, 15-minute temporary keys, rotation, and revocation. # Poweruser Keys A poweruser key is a personal credential that lets scripts and integrations act as you against the Poweruser API — creating agents, editing tools, managing tables, and everything else your account can do in the builder. Each user holds at most one active permanent key, plus on-demand temporary keys that expire after 15 minutes. ## What it's for Client API keys execute what you've published; poweruser keys manage the workspace itself. Reach for one when you're automating administration: provisioning resources from CI, running migrations, syncing configuration, or wiring back-office scripts. Because a poweruser key inherits your full permissions, treat it like your password — it belongs in a secrets vault, never in client-side code or version control. ## Navigating here Open **API Management → Poweruser Keys** in the sidebar. If you have no key yet, the page offers **Generate Poweruser Key** and **Generate Temporary Key (15 min)**. Once a permanent key exists, an **Active Poweruser Key** card shows its name, truncated key ID, and an **Enabled**/**Disabled** status, with **Generate Temporary Key (15 min)**, **Rotate Key**, and **Revoke Key** actions. ## Key concepts - **Acts as you.** Every call authenticated with the key runs with your identity — the same resources, roles, and company scope you have in the builder. There is no narrower resource picker on this page; the key covers everything you can touch. - **Permanent keys** have the shape `pwrusr:{id}:{secret}` and are passed in the `Authorization` header. The full value is displayed exactly once, masked behind a Show/Hide toggle with a **Copy Key** button; after you click **I've Saved My Key - Continue** it cannot be retrieved again. - **One active permanent key.** You can hold only one permanent key at a time (per company) — the backend refuses to mint a second, and the UI replaces the Generate button with Rotate/Revoke once a key exists. To change the secret, rotate; to start over, revoke first. - **Temporary keys** have the shape `pwrusr_temp:{jwt}` and expire 15 minutes after issue — the lifetime is fixed, with no options. They behave identically to permanent keys while valid, don't count against the one-key rule, and are ideal for one-off jobs or handing a short-lived credential to a support session. Generate as many as you need. - **Endpoint scoping is for delegated credentials, not this page.** Keys generated here are full-scope. When poweruser access is delegated to something you don't fully control — for example a hosted [custom app](/docs/guide/platform/app-builder.md) calling admin endpoints on the owner's behalf — the credential is scoped to an explicit list of allowed poweruser endpoints instead. You configure that in the app's settings, not here. ## How to generate and use a key 1. On the Poweruser Keys page, click **Generate Poweruser Key** (or **Generate Temporary Key (15 min)** for a short-lived credential). 2. Copy the key from the success modal — heed the warning: **"This is the only time you'll see this key."** 3. Store it in a secrets manager and pass it in the `Authorization` header of Poweruser API calls. See the [Poweruser API reference](/docs/api/poweruser.md) for the base URL and request shape. ## How to rotate or revoke 1. Click **Rotate Key** to invalidate the current secret and get a new one — the confirmation modal requires ticking **"I understand this will invalidate my current key"**. The key ID stays the same; every application using the old value stops working immediately, so update them with the new secret from the copy-once modal. 2. Click **Revoke Key** to kill the key outright (confirmation checkbox required, irreversible). The page returns to its empty state and you can generate a fresh key whenever you're ready. > Rotation is the right response to any suspected exposure — it takes effect immediately and the old secret can never be recovered or re-enabled. The generation modal also lists the security basics worth repeating: keep the key in a password manager or secrets vault, never commit it to version control, don't share it over email or chat, and rotate periodically. ## Related - [Poweruser API Reference](/docs/api/poweruser.md) - [Authentication](/docs/guide/authentication.md) - [API Keys](/docs/guide/api-management/api-keys.md) - [App Builder](/docs/guide/platform/app-builder.md) --- # OAuth Clients Source: https://docs.autessa.com/docs/guide/api-management/oauth-clients > Registering OAuth 2.0 clients so third-party applications can sign in Autessa users. # OAuth Clients OAuth clients make Autessa an OAuth 2.0 provider for your own applications. Register a client, point your app at Autessa's hosted sign-in page, and users authenticate with their Autessa account — your app receives access and refresh tokens without ever handling their password. ## What it's for API keys and poweruser keys authenticate *you*; an OAuth client authenticates *your users*. If you're building an external product — a companion mobile app, a partner portal, a browser extension — that needs to know which Autessa user is on the other end, register it as an OAuth client. Autessa hosts the sign-in screen (email/password or Google), validates your client and redirect URI, and hands your app a token pair for that user. For how OAuth tokens fit alongside API keys and poweruser keys — and which credential each part of the [API reference](/docs/api.md) expects — see [Authentication](/docs/guide/authentication.md). ## Navigating here Open **API Management → OAuth Clients** in the sidebar. The page lists your registered clients in a table — client name, copyable client ID, redirect URIs, an **Approved**/**Pending** status badge, and creation date — with **Edit**, **Rotate Secret** (confidential clients only), and **Delete** actions per row, and a **Register OAuth Client** button at the top. ## Key concepts - **Public vs. confidential** — chosen at registration and not editable afterwards. **Public (SPA / Browser Extension)** clients get no secret — suitable for code that runs where a secret can't be kept. **Confidential (Server-side)** clients receive a client secret, shown once at creation, that must accompany token and refresh requests. Only confidential clients can rotate their secret. - **Redirect URIs** — a comma-separated list of allowed callback URLs, each starting with `http://` or `https://`. At sign-in time the requested `redirect_uri` must exactly match a registered one — no wildcards or prefix matching. - **Admin approval** — new clients start as **Pending** and must be approved by an Autessa platform administrator before general use. While pending, only you (the creator) can sign in through the client — enough to build and test — and other users are told the application hasn't been approved for public use. - **The hosted sign-in page** — your app sends users to Autessa's `/oauth/login` page with your `client_id` and `redirect_uri`. The page shows "Sign in to connect **", offers Google and email/password sign-in, and on success redirects back to your URI with `access_token`, `refresh_token`, `expires_in`, and `token_type` in the URL fragment. There is no scope picker — a token represents the signed-in user's identity, full stop. - **Token lifetimes** — access tokens live 24 hours; refresh tokens live 7 days and rotate on every refresh. Refresh (`/user/oauth/refresh`) is bound to the issuing client — confidential clients must present their secret again — and sign-out revokes the refresh token via `/user/oauth/revoke`. ## How to register a client 1. On the OAuth Clients page, click **Register OAuth Client**. 2. Enter a **Client Name** — this is what users see on the sign-in screen, so name it after your application. 3. Pick the **Client Type**: *Public (SPA / Browser Extension)* or *Confidential (Server-side)*. 4. Enter the **Redirect URIs** your app will use, comma-separated. 5. Click **Register Client**. The success modal shows your client ID and — for confidential clients — the client secret with the warning **"Save this secret now. It will not be shown again."** 6. If the modal notes the client is pending admin approval, you can test it with your own account immediately; wait for approval before rolling out to other users. ## How to rotate a confidential client's secret 1. Click **Rotate Secret** on the client's row. 2. Confirm — the current secret is invalidated immediately, and any application still using it loses access until updated. 3. Copy the new secret from the modal; like the original, it's shown only once. Editing a client (**Edit**) can change its name and redirect URIs, but not its type. **Delete** permanently retires the client — irreversible, and any application using it loses access. > Tokens are delivered to your redirect URI in the URL fragment, so register exact, HTTPS callback URLs you control. Treat a confidential client's secret like any server credential — never ship it in frontend code. ## Related - [Authentication](/docs/guide/authentication.md) - [API Reference](/docs/api.md) - [API Keys](/docs/guide/api-management/api-keys.md) - [Poweruser Keys](/docs/guide/api-management/poweruser-keys.md) - [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md) --- # Company Source: https://docs.autessa.com/docs/guide/company > A company is your tenant in Autessa — the isolation boundary for people, resources, data, and encryption keys. # Company A company is your tenant in Autessa. Everything you create — agents, tools, tables, connectors, apps, keys — lives inside exactly one company, and every request you make executes inside exactly one company. It is the platform's isolation boundary: for people, for resources, for data, and for encryption keys. ## The tenant model When you sign up you get a personal company, and you can create or be invited into others. You always work inside one company at a time — your active **scope**, switchable from the header — and switching re-scopes the entire builder and all APIs to that company's resources, members, and limits. Nothing crosses the boundary. A resource in one company cannot see, call, or reference a resource in another; API credentials are minted inside a company and only ever reach that company's resources; and members see only the companies they belong to. When two companies genuinely need to exchange something, that's what the [Marketplace](/docs/guide/marketplace.md) is for — an explicit, reviewed packaging step, never ambient access. ## Tenant-specific encryption Your company isn't just logically separated — it's cryptographically separated. Sensitive data is encrypted at rest with **AES-256**, using **encryption keys unique to your tenant**: keys are derived per company, so the key material protecting your data is not the key material protecting anyone else's. Tenant-specific keys protect, among other things: - **Connector credentials** — the OAuth tokens and secrets behind every connected account (Gmail, QuickBooks, Notion, and the rest). - **Conversation and execution history** — agent and multi-agent conversation logs, copilot sessions, and transaction records. - **Automation payloads** — the before/after payloads captured in trigger execution history, webhook execution details, and scheduled-job run history. - **Feature secrets** — MCP server credentials, phone configurations, e-signature verification codes, and app authentication material. All storage is additionally encrypted at rest at the infrastructure level, and every request is company-scoped before it touches any data — the tenant key is the innermost of several layers, not the only one. ## Sharing resources inside the tenant Within the company, every resource answers two independent questions: **who can see it** (the share level) and **what they can do with it** (the access level). **Share level** — chosen on the resource's Share dialog: | Share level | Who gets access | | --- | --- | | **Private** | Only the owner. Every resource starts here. | | **Specific members** | Named teammates, each with their own access level. | | **Company-wide** | Every member of the company, all at one access level you choose. | **Access level** — what a grant allows, in increasing order: | Level | In general | | --- | --- | | **READ** | See and use the resource — view an agent and test-run it, read a table's rows, launch an app. | | **WRITE** | Change its working state — edit an agent's draft, write a table's rows, edit an app's files. | | **PUBLISH** | Ship it — publish and roll back versions. | | **ADMIN** | Govern it — share it, delete it, change its structure. | The owner always holds ADMIN implicitly, and for most resource types only the owner can transfer ownership. The exact meaning of each level varies a little by resource — on a table WRITE means writing rows, on an app READ means launching it — so every resource's guide page carries its own "Sharing and access levels" section; the general model is in [Core Concepts](/docs/guide/concepts.md). Roles and access levels compose: a [team role](/docs/guide/company/team.md) decides whether a member can enter an area of the product at all, and access levels decide what they can do with each individual resource inside it. ## What lives at the company level | Concern | Where it's managed | | --- | --- | | **Members, roles, and the owner** | [Team](/docs/guide/company/team.md) — who's in the tenant and which product areas each person can use | | **Resources and sharing** | Every resource is owned by a member and private, shared, or company-wide — see [access levels](/docs/guide/concepts.md) | | **Connected accounts** | [Connectors](/docs/guide/connectors.md) belong to the company and power its tools | | **API credentials** | [API keys, poweruser keys, webhook keys, and OAuth clients](/docs/guide/api-management/api-keys.md) are all company-scoped | | **Usage and limits** | Metering, budgets, and plan limits are per-company — [Usage & Limits](/docs/guide/company/usage-limits.md) | | **Billing** | One bill per company — [Billing](/docs/guide/company/billing.md) | ## Related - [Team](/docs/guide/company/team.md) — members, roles, and permissions - [Usage & Limits](/docs/guide/company/usage-limits.md) - [Billing](/docs/guide/company/billing.md) - [Core Concepts](/docs/guide/concepts.md) — scopes, sharing, and access levels - [Authentication](/docs/guide/authentication.md) — company-scoped credentials --- # Team Source: https://docs.autessa.com/docs/guide/company/team > Inviting teammates, assigning roles, and managing who can access your company. # Team The Team page is where you manage who belongs to your company and what they are allowed to do. Invite teammates by email, assign them roles as you invite them, and remove members who should no longer have access. ## What it's for Every Autessa company is a shared workspace. The Team page lists everyone who has accepted an invitation to your company and gives members with the right permission the controls to grow or trim that list. It is also where company-level settings live: renaming the company, choosing how long members stay signed in and, for the owner, transferring ownership to another member. ## Navigating here From the builder sidebar, open the **Company** section and click **Team**. The page header shows the current company name along with two buttons: - **Company Settings** — rename the company and set how long members stay signed in; the owner also finds the ownership-transfer option here. - **Invite Team Member** — visible only if you have the team-management permission. If a consultant manages your company, a **Managed by** banner appears at the top of the page; the company owner can revoke the consultant's access from that banner. ## Key concepts - **Members** — the Team Members table lists each member's name, user ID, and email. Your own row is marked so you cannot remove yourself. The list paginates at 10 members per page. - **Consultants** — a consultant is a company-level designation, not a member role. A consultant granted access to your company appears in the Managed-by banner, and the owner can revoke that access at any time. ## Roles and permissions A role is a **named bundle of permissions**. Autessa ships built-in roles available to every company (tagged **Built-in** in the invite dialog), and your company can define custom roles of its own. A member can hold more than one role — their permissions are the **union** of everything those roles grant. Permissions gate whole areas of the product; if a member lacks one, the corresponding sidebar sections and actions are hidden for them: | Permission | What it unlocks | | --- | --- | | Agentic Workflows | The Tools, Agents, and Multi-Agents sections | | AutessaDB | Tables and trigger execution history | | Scheduled Jobs | Scheduled jobs | | API Keys | API key and webhook-handler management | | App Builder | Building apps in the App Builder (implies App Viewer) | | App Viewer | Using published apps only — the sidebar shows **Apps** instead of **App Builder** | | E-Signature | Document signing | | Meeting Recorder | The meeting recorder | | Billing | The company's billing pages | | Manage Team Members | Inviting and removing members, assigning roles, and editing company settings | ### Built-in roles The five built-in roles map to those permissions like this: | Permission | Admin | Developer | Manager | Accountant | User | | --- | :---: | :---: | :---: | :---: | :---: | | Agentic Workflows | ✓ | ✓ | | | | | AutessaDB | ✓ | ✓ | | | | | Scheduled Jobs | ✓ | ✓ | | | | | API Keys | ✓ | ✓ | | | | | App Builder | ✓ | ✓ | | | | | App Viewer | ✓ | ✓ | | | ✓ | | E-Signature | ✓ | ✓ | ✓ | ✓ | ✓ | | Meeting Recorder | ✓ | ✓ | ✓ | ✓ | ✓ | | Billing | ✓ | | | ✓ | | | Manage Team Members | ✓ | | ✓ | | | In practice: **Admin** holds everything; **Developer** builds across the whole Autessa suite but doesn't touch the team or the bill; **Manager** runs the team; **Accountant** handles billing; **User** consumes what's been built — launching apps rather than building them. Document signing and the meeting recorder are everyday collaboration features, so every built-in role includes them. When none of these fit, define a custom role with exactly the permissions you want. Only members with **Manage Team Members** can invite, remove, or re-role members — and an inviter cannot grant roles carrying permissions they don't hold themselves, so an invitation can never hand out more access than the inviter has. The **company owner** sits above roles: the owner always has full access and cannot be removed. Ownership can be transferred only by the owner, and only to a member who already holds **Manage Team Members** and **Billing**. On the Companies page, each workspace is badged as **Personal**, **Owner**, or **Member** to show your relationship to it. > Roles decide which parts of the product a member can use at all. Access to *individual resources* within those areas — a specific agent, table, or app — is governed separately by [access levels](/docs/guide/concepts.md). ## How to invite a teammate 1. On the Team page, click **Invite Team Member**. 2. Enter the person's **Email Address**. 3. In the **Roles** checklist, select at least one role. Built-in roles are tagged, and each role shows a description of what it grants. 4. Click **Send Invitation**. Autessa emails the person a link; once they accept, they appear in the Team Members table. ## How to remove a member 1. Find the member in the Team Members table. The **Actions** column is visible only if you have the team-management permission. 2. Click **Remove** on their row. 3. Confirm in the **Remove Team Member** dialog. Removal is immediate and cannot be undone — the person loses access to the company's resources. ## How to rename the company or transfer ownership 1. Click **Company Settings** in the page header. 2. Edit the **Company Name** field to rename the workspace. 3. If you are the owner, use the **Transfer Ownership** dropdown to pick another confirmed member — they must already hold the **Manage Team Members** and **Billing** permissions. Transferring ownership removes your admin access to the company, so read the warning before you confirm. 4. Click **Update Company**. ## How to change how long members stay signed in Autessa signs a member out after a period without activity. By default that's 1 hour, and any activity in Autessa starts the clock again. The same sign-in covers team-only apps built in the App Builder. 1. Click **Company Settings** in the page header. 2. Pick a length in **Sign out after inactivity**, from 15 minutes to 30 days, or **Default**. 3. Click **Update Company**. The setting applies to everyone in the company, including people who are already signed in. A member who belongs to several companies follows the setting of the company they're working in. Longer lengths are convenient on trusted devices but leave an unattended computer signed in for longer. Sign-in is also tied to the network a member signed in from, so switching networks, for example moving from Wi-Fi to a phone connection, still asks them to sign in again. ## Related - [Company](/docs/guide/company.md) - [Companies](/docs/guide/platform/companies.md) - [Usage & Limits](/docs/guide/company/usage-limits.md) - [Billing](/docs/guide/company/billing.md) - [Consultant](/docs/guide/partners/consultant.md) --- # Usage & Limits Source: https://docs.autessa.com/docs/guide/company/usage-limits > Monitoring metered usage, understanding what counts as a task, and reading your company's limits. # Usage & Limits The Usage & Limits page shows how much of everything your company has consumed — AI tasks, executions, credits, storage, phone minutes — and how much headroom each limit leaves. It is the first place to look when something is blocked or you want to understand what drives your bill. ## What it's for Almost everything in Autessa is metered: how many resources you can create, how often agents and tools run, how many LLM credits copilots consume, how much you store. This page collects all of those meters in one place, grouped into collapsible category cards with progress bars per metric. It refreshes on demand and shows fixed windows (this minute, today, this month) rather than custom date ranges. ## Navigating here From the builder sidebar, open the **Company** section and click **Usage & Limits**. The page header shows when the data was last fetched and a **Refresh** button. For a per-task operational log (which agent ran, when, with what flags), see **AI Tasks** under **Agentic Workflows** in the sidebar. ## Key concepts ### What counts as a task AI tasks are the primary billing unit: - A conversation with an **agent**, or a standalone LLM or tool run, counts as **one task**. - A **multi-agent (manager)** conversation counts as its configured number of sub-agents — the sub-agents do not bill separately on top. - A task with **evaluation** enabled adds one extra task for the evaluation pass. - **Voice** minutes and **semantic search (RAG)** calls are metered surcharges attached to a task, not separate task counts. Voice time is rounded up to whole minutes. Each task is also tagged with a coverage state: covered by your free monthly sandbox allowance, covered by prepaid credit, or billable at month end. Sandbox and credit-covered tasks cost nothing extra. ### The category cards - **AI Tasks** — total tasks this month, billable (overage) tasks, voice tasks and minutes, semantic search calls, and evaluated tasks, alongside cost-so-far and projection tiles. Shown when billing is enabled for your company. - **Resource Limits** — how many **Tools**, **Agents**, **Multi-Agents**, **Custom Apps**, and **Database Tables** you have created against your company's caps. - **Execution Limits** — agent, tool, and multi-agent executions, each with a per-minute rate limit and a monthly quota. - **LLM Credits** — daily, weekly, and monthly credit meters for AI features, computed from input and output tokens with the weights shown on the card. - **Phone Calls** — phone numbers in use and call minutes this month. - **Storage** — storage units consumed, with the per-column-type cost grid (regular, vectorizable, file, and autocomputed columns are weighted differently). - **Semantic Search** — search requests this minute, today, and this month. - **Browser & Scraping** — web search, basic scraping, and JavaScript scraping usage, plus the per-minute rate limit. - **Vibecode (AI Coding)** — daily and weekly credit meters for the App Builder AutoBot, when enabled. > Rate-limit windows reset on their own schedule — per-minute meters clear within a minute, monthly meters on the first of the month. If a run is rejected for a limit, check the matching category here before assuming an error. ## How to check why something is blocked 1. Open **Company → Usage & Limits** and click **Refresh**. 2. Find the category matching the failing action — creating a resource maps to **Resource Limits**, a rejected run maps to **Execution Limits** or **LLM Credits**, a copilot refusal maps to **LLM Credits** or **Vibecode**. 3. Look for meters at or near 100%. Per-minute limits clear on their own; monthly caps and resource caps need a higher limit or cleanup. 4. If the **AI Tasks** card shows a budget or hard-limit badge, your own spending caps are the blocker — adjust them on the Billing page. ## How to audit individual AI tasks 1. In the sidebar, open **Agentic Workflows → AI Tasks**. 2. Pick a month from the period selector (the last six months are available). 3. Scan the table — each row shows when the task ran, its type, model, who ran it, status, and flags for RAG, evaluation, and voice. 4. Click a row for the full task detail. ## Related - [Billing](/docs/guide/company/billing.md) - [Team](/docs/guide/company/team.md) - [Agent History](/docs/guide/agentic/agent-history.md) - [Companies](/docs/guide/platform/companies.md) --- # Billing Source: https://docs.autessa.com/docs/guide/company/billing > The billing portal: plan, budget caps, payment methods, invoices, and hosted invoice pages. # Billing The Billing page is a single portal for everything money-related: your plan, spending caps and alerts, payment methods, and invoices — including a live preview of what the current month will cost. ## What it's for Autessa bills for AI-task usage. The billing portal shows where this month stands (tasks so far, estimated month-end invoice, credit balance, your per-task rate) and lets you manage the levers around it: switch between pay-as-you-go and a monthly commitment, set hard caps and email alerts, add cards or bank accounts, and view or pay invoices. ## Navigating here From the builder sidebar, open the **Company** section and click **Billing**. Access requires the billing permission, and the page may not appear for every company depending on rollout. Sections on the page are anchor-linked: plan and credit, budget and alerts, payment methods, AI tasks, and invoices. ## Key concepts - **Plan & credit** — you either pay per task or commit to a monthly amount (charged on the 1st) in exchange for a lower locked-in per-task rate. The commitment is a slider, not a set of named tiers; a credit-activity ledger records top-ups, task spend, promotional credits, and adjustments. Enterprise companies may instead have a custom rate or a task grant. - **Budget & alerts** — optional hard caps (**Max AI tasks per month** and **Max monthly spend**) block new AI tasks when reached until the next month. Separately, email alerts warn at 50/80/90/100% of your cap without blocking anything; you can add up to five extra alert recipients, and the owner is always emailed. - **Payment methods** — add a card or link a bank account through an embedded Stripe form (Autessa never stores your card details). A billing address is required for sales tax. Top-ups and monthly invoices charge your default method. - **Invoices** — the invoices table lists each period with type (invoice or commitment receipt), billable tasks, total, due date, and status (paid, pending, or payment failed). A live **Preview** card shows the current period as it accrues. **View** opens a hosted invoice page; **Pay now** appears on payable invoices. - **Hosted invoice pages** — every invoice email links to a tokenized public invoice page that anyone with the link can open, download as PDF, and pay — with a saved card or as a guest — without signing in. Links expire. - **AI tasks** — the portal links to the full AI Tasks log, and each task opens a cost receipt itemizing base, voice, semantic search, and evaluation charges, plus whether credit or the free sandbox covered it. > Billing has no public API. Everything is managed through this portal; the only unauthenticated surface is the tokenized hosted invoice page from your billing emails. ## How to add a payment method 1. Open **Company → Billing** and scroll to **Payment methods**. 2. Click **Add card** (or **Link bank** where available). 3. Enter your billing address, then the card or bank details in the embedded Stripe form. 4. The new method appears in the list — use **Make default** to route future charges to it. ## How to pay an invoice 1. In the **Invoices** section, find the row with a **Pending** or **Payment failed** status. 2. Click **Pay now** to charge your default payment method, or **View** to open the hosted invoice page and pay there — the hosted page also accepts a guest card and offers a PDF download. ## Related - [Usage & Limits](/docs/guide/company/usage-limits.md) - [Team](/docs/guide/company/team.md) - [Companies](/docs/guide/platform/companies.md) --- # AutoBots Source: https://docs.autessa.com/docs/guide/autobots > Autessa's built-in AI copilots — where each one lives and what it builds for you. # AutoBots AutoBots are Autessa's built-in AI copilots, embedded right where you work. Each surface has its own specialist: one designs agents and tools, one manages your database, one writes AutessaScript, one builds entire apps, and one drives live test runs. ## What they're for Everything an AutoBot does, you could do by hand in the same screen — the copilots exist to get you there faster. Describe what you want in plain language and the AutoBot explores what you already have, proposes concrete changes, and applies them once you approve. They are strongest for first drafts and structural work (a new agent with the right tools, a table schema, an app scaffold); you refine the result with the normal editors afterwards. _(interactive UI demo omitted)_ ## The five surfaces **Agentic Copilot** — lives on the Tools and Agents pages as an **Autobot** button. It builds agentic workflows from natural language: describe a job and it designs the tools and agents to do it, showing its work through three specialists (Discovery, Builder, Publisher) and proposing creations as change cards you apply or reject. See [Agentic Copilot](/docs/guide/autobots/agentic-copilot.md). **DB Copilot** — the **Autobot** button on the AutessaDB Tables page. It manages your database conversationally: create tables, modify schemas, and query data by asking. Schema changes are always previewed — columns, types, foreign keys — before you apply them. See [DB Copilot](/docs/guide/autobots/db-copilot.md). **Script Copilot** — built into every AutessaScript editor (script tools, webhook handlers, computed columns). A chat tab writes and fixes scripts you can apply straight into the editor, and a Docs tab gives you a searchable reference of every helper and built-in. See [Script Copilot](/docs/guide/autobots/script-copilot.md). **Vibecode** — the **AutoBot** in the App Builder. It generates and edits a complete custom app from chat: it plans first, shows per-file diffs, and refreshes the live preview as changes land. The only copilot with resumable sessions. See [Vibecode](/docs/guide/autobots/vibecode.md). **Streaming Test Window** — the **Autobot (Streaming)** panel behind the Test button on agent and multi-agent pages. It doesn't build anything; it runs your agent live, streaming every message, tool call, and (for multi-agents) each sub-agent delegation so you can see exactly how a run unfolds. See [Testing](/docs/guide/autobots/testing.md). ## Comparison | Surface | Where it lives | What it does | Reach for it when | | --- | --- | --- | --- | | [Agentic Copilot](/docs/guide/autobots/agentic-copilot.md) | Tools & Agents pages | Creates tools and agents from a description | You're starting a workflow and want a working first draft | | [DB Copilot](/docs/guide/autobots/db-copilot.md) | AutessaDB Tables page | Creates tables, modifies schemas, queries data | You know the data you need but not the schema details | | [Script Copilot](/docs/guide/autobots/script-copilot.md) | Every AutessaScript editor | Writes and fixes scripts in place, with a docs reference | A script tool, validation, or computed column needs code | | [Vibecode](/docs/guide/autobots/vibecode.md) | App Builder editor | Generates and edits a full custom app | You want an app built or reworked without writing the code | | [Streaming Test Window](/docs/guide/autobots/testing.md) | Test button on agents & multi-agents | Runs live tests with full tool-call visibility | You need to see why an agent behaves the way it does | ## General guidelines - **Review before applying.** Every AutoBot marks its output as AI-generated and (except when you enable Vibecode's auto-apply) proposes changes as cards with explicit **Apply Changes** / **Reject Changes** buttons. Read the proposal — column types, tool configurations, file diffs — before accepting. - **Copilots act within your permissions.** An AutoBot can only see and change what you can. It won't reach resources your role doesn't grant, and everything it creates is owned like anything you made by hand. - **They consume LLM credits.** The Agentic, DB, and Vibecode copilots draw on your company's daily and weekly AI credit allowances; when a limit is reached, the copilot shows a credit-limit banner until the window resets. See [Usage & Limits](/docs/guide/company/usage-limits.md). - **Sessions are mostly ephemeral.** The Agentic and DB copilots start fresh each time you open them; only Vibecode keeps resumable conversation sessions. ## Related - [Agentic Copilot](/docs/guide/autobots/agentic-copilot.md) - [DB Copilot](/docs/guide/autobots/db-copilot.md) - [Script Copilot](/docs/guide/autobots/script-copilot.md) - [Vibecode](/docs/guide/autobots/vibecode.md) - [Testing](/docs/guide/autobots/testing.md) --- # Agentic Copilot Source: https://docs.autessa.com/docs/guide/autobots/agentic-copilot > The Autobot on the Tools and Agents pages that builds agentic workflows from natural language. # Agentic Copilot The Agentic Copilot is the Autobot on your Tools and Agents pages. Describe the workflow you want — "an agent that triages support emails and files them into a table" — and it explores what you already have, designs the missing pieces, and proposes new tools and agents for you to apply. _(interactive UI demo omitted)_ ## What it's for Building an agentic workflow by hand means creating each tool, wiring its arguments, then creating an agent with the right instructions and tool attachments. The copilot compresses that into a conversation: it knows your existing resource catalog, avoids duplicating tools you already have, and produces complete, publishable drafts. Use it to go from idea to first working version; use the regular editors to fine-tune instructions, models, and configuration afterwards. ## Navigating here Open **Agentic Workflows → Tools** or **Agents** in the sidebar and click the **Autobot** button. The copilot opens as a resizable panel on the right. It's also available on individual tool and agent detail pages, where it automatically knows which resource you're looking at — so "add an argument for the customer's region" works without further context. Each time you open the panel, the conversation starts fresh; closing it discards the chat. ## Key concepts - **Three specialists** — while working, the copilot shows which of its specialists is active: **Discovery** explores your existing resources and the tool catalog, **Builder** creates and edits resources, and **Publisher** publishes the results. The pill indicator ("Discovering…", "Building…", "Publishing…") tells you what phase the copilot is in. - **Tool-call cards** — every action the copilot takes (listing tools, fetching agent details, delegating to a specialist) appears as a collapsible card with a status indicator. Expand a card to see the exact arguments and result — useful when you want to verify what the copilot actually looked at. - **Changes cards** — the copilot never creates anything silently. Proposed tools and agents appear in an amber **changes proposed** card, each expandable to show its type and description, with **Apply Changes** and **Reject Changes** buttons. The chat input locks until you decide. On apply, the new resources appear in your lists immediately. - **Credits** — the copilot draws on your company's daily and weekly AI credit allowances; a banner appears if a limit is reached. ## When to use it vs. building manually Reach for the copilot when you're starting something new or when the shape of the workflow is clearer in words than in configuration — it excels at scaffolding several coordinated resources at once. Build manually (or switch to the editors after applying) when you need precise control: exact tool configuration values, careful instruction wording, model and voice selection, or edits to resources with production traffic. ## How to build a workflow with the copilot 1. On the Tools or Agents page, click **Autobot**. 2. Describe the outcome, not the implementation: what the agent should accomplish, what systems it touches, and what data it reads or writes. 3. Watch the specialist indicator and tool-call cards as it explores your catalog — answer any clarifying questions it asks. 4. When the changes card appears, expand each proposed tool and agent and check the descriptions and types. 5. Click **Apply Changes** (or **Reject Changes** and refine your prompt). 6. Open the created resources in their editors to review instructions and configuration, then test the agent with the [Streaming Test Window](/docs/guide/autobots/testing.md). ## Tips for good prompts - **State the goal and the constraints** — "summarize new rows in the Leads table and email the sales channel every morning" beats "make a sales agent". - **Name the resources you want reused** — mention existing tables, tools, or connectors by name so Discovery links to them instead of creating duplicates. - **One workflow per conversation** — the copilot handles multi-resource builds well, but unrelated asks in one thread muddy the plan. - **Iterate before applying** — you can ask it to adjust the proposal ("make the tool take a date range argument") while the changes card is pending; reject and re-prompt if the design is off. ## Related - [AutoBots overview](/docs/guide/autobots.md) - [Tools](/docs/guide/agentic/tools.md) - [Agents](/docs/guide/agentic/agents.md) - [Testing](/docs/guide/autobots/testing.md) --- # DB Copilot Source: https://docs.autessa.com/docs/guide/autobots/db-copilot > The natural-language database assistant on AutessaDB Tables — create tables, modify schemas, query data. # DB Copilot The DB Copilot is the Autobot on the AutessaDB Tables page. Tell it what data you need to store — or what you want to know about data you already have — and it designs schemas, applies changes with your approval, and answers queries conversationally. ## What it's for Designing a good schema means picking column types, deciding what's required, what should be vectorized for semantic search, and how tables link together. The DB Copilot does that from a plain-language description: "track customer support tickets with a status, priority, and a link to the customer" becomes a concrete table proposal you can inspect column by column. It also modifies existing schemas and queries your data, so it doubles as a quick way to explore what's in a table without building anything. ## Navigating here Open **AutessaDB → Tables** in the sidebar and click the **Autobot** button. The copilot opens as a resizable panel on the right. Conversations start fresh each time you open the panel. ## Key concepts - **Reads run directly, writes need approval.** Exploring — listing tables, fetching schemas, looking up select-column options, querying data — happens straight away, visible as expandable tool-call cards with arguments and results. Anything that changes a schema goes through a confirmation card first. - **Schema change cards** — proposals appear as an amber **table change(s) proposed** card. A **Create** proposal expands to the full column list (name, type, required, vectorized, foreign key), plus tags and select-column options. An **Update** proposal shows exactly which columns will be added or removed and any new select options. **Apply Changes** executes; **Reject Changes** discards. The chat input locks until you decide. - **Design first, then build** — for larger asks the copilot may present a design overview of several related tables before proposing the actual creations, so you can steer the model of your data early. - **Credits** — like the other copilots, it draws on your company's daily and weekly AI credit allowances. ## When to use it - **New data models** — describe the business objects and let it propose types, required flags, and foreign keys; adjust in the table editor afterwards if needed. - **Schema evolution** — "add a column for the contract renewal date" is faster in chat than in the editor, and the update card shows the exact diff before it lands. - **Ad-hoc questions** — ask about your data ("how many open tickets are assigned to nobody?") without writing a query. - Prefer the manual table editor when you're making a single precise change you already know exactly, or when editing triggers and computed columns, which have their own editors. ## How to create a table with the copilot 1. On the Tables page, click **Autobot**. 2. Describe the data: what each record represents, the fields you care about, which text should be semantically searchable, and any relationships to existing tables (name them). 3. Review the **Create** card — check every column's type, the required flags, vectorized columns, and foreign keys. 4. Click **Apply Changes**. The table appears in your list, ready for rows, triggers, and agent access like any hand-made table. ## Related - [AutoBots overview](/docs/guide/autobots.md) - [Tables](/docs/guide/autessadb/tables.md) - [Triggers](/docs/guide/autessadb/triggers.md) - [Script Copilot](/docs/guide/autobots/script-copilot.md) --- # Script Copilot Source: https://docs.autessa.com/docs/guide/autobots/script-copilot > The AutessaScript editor's built-in Autobot chat and searchable docs reference. # Script Copilot Every AutessaScript editor in Autessa carries its own copilot: an Autobot chat tab that writes and fixes scripts you can apply in place, and a Docs tab with a searchable reference of every helper and built-in — so you never leave the editor to look something up. ## What it's for AutessaScript powers the programmable corners of the platform, and the copilot removes the friction of writing it. Ask for a script in plain language, paste an error you're getting, or select a confusing block and ask what it does — the copilot answers with runnable code you can apply to the editor in one click. ## Navigating here The copilot lives wherever the AutessaScript editor appears: - **Script tools** — a tool whose type runs a script (Tools page → tool config editor). - **Webhook handlers** — request-validation and payload-transformation scripts. - **Computed columns** — AutessaDB columns whose values are calculated by a script. Click the **Autobot** toggle at the top-right of any script editor to open the side panel, then switch between the **Autobot** and **Docs** tabs. ## Key concepts - **Selection-aware chat** — select code in the editor before asking and a **Selection** chip attaches it to your question, so "why does this fail?" targets exactly the lines you highlighted. - **Apply to Editor** — when the copilot answers with a code block, an **Apply to Editor** button writes it into the editor, replacing the current script; the button flips to **Applied** once it lands. Nothing changes until you click — and the applied script still isn't saved until you save the resource itself. - **Docs tab** — a **Search documentation** box filters three sections: **Helpers** (the platform objects your script can call), **Built-ins**, and **Notes**. Expanding an entry shows its description and method signatures. This is the same material as the full [AutessaScript reference](/docs/autessascript.md), inlined next to your code. - **Clear conversation** — the chat keeps context within your editing session; use the clear link to start over when switching topics. ## How to write a script with the copilot 1. Open the script editor (tool config, webhook handler, or computed column) and click **Autobot**. 2. Describe what the script should do, including its inputs and what it must return — return-value contracts differ by context (a webhook validator returns true or false; a transformer returns an object; a computed column returns the cell value). 3. Review the generated code. Check the Docs tab if you want to verify a helper's signature. 4. Click **Apply to Editor**, then test the surrounding resource and save. ## How to fix a failing script 1. Select the failing section in the editor (or leave nothing selected to include the whole script). 2. Paste the error message or describe the wrong behavior in the Autobot tab. 3. Apply the corrected version and re-run. ## Related - [AutoBots overview](/docs/guide/autobots.md) - [AutessaScript reference](/docs/autessascript.md) - [Tools](/docs/guide/agentic/tools.md) - [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md) - [Tables](/docs/guide/autessadb/tables.md) --- # Vibecode Source: https://docs.autessa.com/docs/guide/autobots/vibecode > The App Builder's AutoBot — generates and edits a complete custom app from chat, with plans, reviewable diffs, and resumable sessions. # Vibecode Vibecode is the AutoBot inside the App Builder. Describe the app you want — "a client portal where customers chat with my SupportAgent and see their invoices" — and it explores your files and published resources, proposes a plan, and writes the code as per-file diffs you review and apply, with the live preview refreshing as changes land. ## What it's for The [App Builder](/docs/guide/platform/app-builder.md) hosts real React apps, and writing one by hand means files, routing, styling, and wiring calls to your agents, tools, and tables. Vibecode compresses that into a conversation: it reads your existing files, knows your published tools, agents, and AutessaDB schemas, and produces working code you can refine — in chat or directly in the editor, since everything it writes is ordinary app source. It's the only AutoBot with resumable sessions, so a build can span days without losing the thread. ## Navigating here Open an app in the **App Builder** and click the **AutoBot** button in the editor toolbar (in narrow windows it moves into the **More actions** menu). The editor switches to a two-pane layout — chat on the left, live preview on the right — with a draggable divider between them. Minimizing collapses the chat to a **Resume Autobot** pill so you can hand-edit code and pop back in. ## Key concepts - **Sessions** — Vibecode conversations persist per app. The welcome screen offers **Start New Conversation** plus your recent sessions ("Or continue where you left off"), each with an auto-generated summary, timestamps, and a state badge (**active**, **Pending** when changes await your decision, or **closed**). Resuming reloads the full history; the header's conversation switcher jumps between sessions mid-build. - **Plan cards** — before touching code, AutoBot posts an **Implementation Plan** card: a summary, numbered steps, and the files each step affects. The plan is informational — your approval gate comes at the changes card — but it's the moment to redirect the design cheaply, before any code is written. - **Changes cards** — proposed edits arrive as an amber **"N file(s) will be modified"** card. Each file row is badged **CREATE**, **EDIT**, **DELETE**, or **RENAME** and expands to a line-by-line diff (or a preview of new files). **Apply Changes** writes them and refreshes the preview; **Reject Changes** discards. The chat input locks until you decide — or flip on the **Auto apply** toggle in the input footer to have changes land automatically. - **Tool-call cards** — every action AutoBot takes (viewing and searching files, listing your published tools, agents, and tables, web searches, proposing plans) shows as a collapsible card with a status indicator; expand one to see the exact arguments and result. - **Frontend changes only** — a toggle on the session-start screen. When on, AutoBot builds frontend-only and won't read your backend resources (tables, agents, tools) — useful for mocking up UI before the backend exists. It's fixed per conversation; start a new session to change it. - **Credits** — Vibecode draws on your company's daily and weekly AI credit allowances. When a limit is hit, a **Daily/Weekly Credit Limit Reached** banner appears and generation pauses until the window resets. See [Usage & Limits](/docs/guide/company/usage-limits.md). - **Resource registration (`autessa.config.json`)** — every Autessa resource an app uses (tables, agents, tools) is registered by logical name in the app's `autessa.config.json`, and the generated code looks resources up through it rather than hardcoding IDs. You rarely touch this file: when you name a table or agent in chat, AutoBot discovers it with its listing tools and registers it for you. If generated code can't find a resource, this file is the first place to look. ## How to build with Vibecode 1. In your app's editor, click **AutoBot**, then **Start New Conversation** (enable **Frontend changes only** first if you want a mock-data build). 2. Describe the app in terms of outcomes — the screens, who uses them, and which agents, tools, or tables they should connect to. Name existing resources so AutoBot wires to them instead of inventing stand-ins. 3. Review the **Implementation Plan** card; if the structure is off, say so before code gets written. 4. When the changes card appears, expand the diffs, then **Apply Changes** — the file tree updates and the preview reloads. Iterate in chat, or edit any file yourself; press the red stop button anytime to interrupt a generation mid-stream. 5. When you're happy, close AutoBot and [publish the app](/docs/guide/platform/app-builder.md) as usual. > Everything Vibecode writes is marked AI-generated in the panel header — review diffs before applying, especially deletes and renames, exactly as you would a teammate's pull request. ## Tips - **One feature per message** — "add an invoices page reading from my Invoices table" beats a paragraph of five asks; the plan and diff stay reviewable. - **Use sessions as branches of thought** — start a new conversation for a new area of the app rather than steering one giant thread. - **Auto apply is for flow, not for trust** — great when iterating on styling with the preview open; switch it off for structural changes you want to read first. - **Mock first, wire later** — build the UI in a frontend-only session, then start a normal session to connect real agents and tables. ## Related - [Build your first app](/docs/guide/tutorials/first-app.md) — a full guided build using Vibecode end to end - [AutoBots overview](/docs/guide/autobots.md) - [App Builder](/docs/guide/platform/app-builder.md) - [Agents](/docs/guide/agentic/agents.md) - [Tables](/docs/guide/autessadb/tables.md) - [Testing](/docs/guide/autobots/testing.md) --- # Testing Source: https://docs.autessa.com/docs/guide/autobots/testing > The streaming test window on agent and manager pages — live runs with full tool-call, sub-agent, and audio visibility. # Testing The streaming test window is the panel behind the Test button on agent and manager pages. It runs your agent live over a streaming connection and shows everything as it happens — text as it's generated, every tool call, each sub-agent delegation on a manager, and audio responses — so you can see not just what an agent answered, but how it got there. ## What it's for An agent that "mostly works" usually fails somewhere specific: a tool called with the wrong argument, a sub-agent given a vague instruction, a critic loop rejecting good output. The test window exposes those internals for a real run against your current draft — no publishing, no separate harness. Use it after every meaningful change to instructions, tools, or template variables, before traffic (or the [version](/docs/guide/agentic/versions.md) you publish) depends on the behavior. _(interactive UI demo omitted)_ ## Navigating here Open an agent or manager in the editor and click the purple **Test** button (next to **Real Time Voice** and **Save**). A panel slides in from the right — titled **Autobot (Streaming)** for agents, **Test Multi-Agent (Streaming)** for managers — and connects immediately; the colored dot under the title shows the connection status and the current conversation ID. The refresh icon in the header resets everything and reconnects without closing the window; closing it discards the session. ## Key concepts - **Variables input** — if the agent or manager defines prompt template variables, a **Prompt Template Variables** section appears above the input with one field per variable (its description as the placeholder). Values are sent with each message, so you can test how different substitutions change behavior. - **Text or audio output** — a **Text / Audio** toggle above the input selects the response mode. Audio responses render as playable cards ("Audio Response") that fill as chunks stream in, and an **Audio Transcription History** section collects the transcripts. - **Voice input** — the microphone button records 16 kHz audio (with a live duration counter) and attaches it to your message, so you can test how a voice-driven agent handles real speech. A paperclip menu also attaches images and documents where the model supports them. - **A conversation, not one-shots** — messages accumulate in a single conversation until you hit refresh, so multi-turn behavior (memory, follow-ups, clarifying questions) is testable. There's no stop button; the input simply unlocks when the response completes. ## How to read the stream Each run unfolds as a sequence of message types in the transcript: - **Your message** — right-aligned, with chips for any recorded audio, images, or files attached. - **Processing indicator** — bouncing dots ("Agent is processing...") while the agent works before content arrives. - **Tool activity** — collapsible cards grouped per tool invocation, headed **" (N messages)"** with a blue gear icon. Collapsed, they show the latest status line; expanded, every status message with its variables and timestamp. Groups auto-collapse when the run finishes — expand them when a result looks wrong to see exactly what the tool received and reported. - **Sub-agent delegations** (manager window only) — each delegation is a card named for the sub-agent, showing the instruction the manager gave it, its status, and a nested **Tools Used** list with that sub-agent's own tool invocations. This hierarchy — manager → sub-agent → tools — is the main thing to study when a multi-agent run goes sideways: bad final answers are usually bad instructions. - **Critic attempts** — agents with a critic loop show attempt cards: **Initial Attempt**, **Retry N**, and a final **Approved** (green) or **Rejected** (red) verdict with the critic's reasoning. Rejected attempts auto-collapse; the approved one stays open. - **Streaming text** — the reply renders token by token with a cursor until complete. - **Errors** — a red **Streaming Error** card with the raw message; connection problems also turn the status dot red while the window retries. ## How to test an agent 1. Open the agent, make your edits, and click **Save**, then **Test**. 2. Fill in any **Prompt Template Variables** and pick **Text** or **Audio** output. 3. Send a realistic message (Enter to send, Shift+Enter for a new line) — the phrasing a real user would use, not a happy-path prompt. 4. Watch the tool cards as much as the answer: verify each tool fired when expected, with the arguments you intended. 5. Continue the conversation to probe follow-ups and edge cases; click the header's refresh button to reset and try a clean variation. 6. For managers, expand each sub-agent card and check the instruction the manager wrote — then tighten the manager's instructions if delegations are vague. > Test conversations are real runs — tools execute with real side effects (emails send, rows write). Point destructive tools at test data first. ## Related - [AutoBots overview](/docs/guide/autobots.md) - [Agents](/docs/guide/agentic/agents.md) - [Managers](/docs/guide/agentic/managers.md) - [Agent History](/docs/guide/agentic/agent-history.md) - [Versions](/docs/guide/agentic/versions.md) --- # Referrals Source: https://docs.autessa.com/docs/guide/partners/referrals > The Autessa referral program: share your code, refer companies, and earn commission on their usage. # Referrals The referral program pays you commission for companies you bring to Autessa. Accept the affiliate agreement, share your referral code, and track every referred company and its commission from one dashboard. ## What it's for If you recommend Autessa to other companies — as a consultant, an agency, or a happy customer — the referral program turns those recommendations into recurring commission. On the usage track you earn 15% of a referred customer's usage for 12 months; referrals that convert to enterprise agreements move to a separate enterprise track. ## Navigating here From the dashboard, select **Referrals** in the sidebar. [Open Referrals →](https://builder.autessa.com/referrals) ## Key concepts - **Affiliate agreement** — your referral code activates when you read and accept the Autessa Affiliate Partner Agreement, directly on the page. If the agreement is later updated, a banner asks you to re-accept it to keep earning. - **Referral code and share link** — a customizable code (for example `ACMECONSULTING`) with a copyable share link. When a company signs up with your code or link, it appears in your **Referred companies** table. - **Tracks** — each referral is on the **Usage** track (15% of collected usage, with the 12-month window shown as months remaining) or the **Enterprise** track. New referrals show as **Pending Autessa review** before becoming **Active**. - **Payout details and tax form** — Autessa pays earned commission monthly in arrears once your tax form is verified. Bank and tax details are encrypted at rest. ## How to enroll and share your code 1. Open **Referrals** from the sidebar. 2. Review the embedded agreement and click **I have read and accept the Autessa Affiliate Partner Agreement**. Your referral code activates immediately. 3. Optionally click **Customize** on the "Your referral code" card to pick a memorable code — availability is checked as you type. 4. Click **Copy share link** and send it to the companies you're referring. ## How to get paid 1. In **Payout details**, click **Add details** and enter the account holder name, bank name, account number, routing number, and country. 2. Submit your tax form; its status moves from **Tax info needed** to **Tax under review** to **Tax verified**. 3. Once verified, the commission shown per referral is paid monthly in arrears. Paid and reversed amounts are marked on each row. ## Related - [Consultant Access](/docs/guide/partners/consultant.md) - [Companies](/docs/guide/platform/companies.md) - [Billing](/docs/guide/company/billing.md) - [Usage & Limits](/docs/guide/company/usage-limits.md) --- # Consultant Access Source: https://docs.autessa.com/docs/guide/partners/consultant > Operate client companies as a consultant without owning them. # Consultant Access The consultant dashboard lets Autessa-designated consultant companies provision and operate client companies. Clients own their own workspace; your team gets consultant access to build inside it. ## What it's for Agencies and resellers often build and maintain Autessa workspaces on behalf of their customers. Consultant access keeps ownership where it belongs — with the client — while giving your team the access it needs to do the work, plus commission tracking on the client's usage. ## Navigating here The **Consultant** entry appears in the sidebar only for companies Autessa has designated as consultants. If you don't see it and believe you should, contact Autessa. [Open Consultant Dashboard →](https://builder.autessa.com/consultant) ## Key concepts - **Autessa-granted** — consultant status is a flag Autessa sets on your company; it is not self-serve. - **Client owns the workspace** — provisioning a client creates a company owned by the client, with your team granted consultant access. The client must already have an Autessa account. - **Two ways a client joins** — you can provision the client company yourself from the dashboard, or the client signs up on their own using your consultant code, which links their company to you the same way a [referral code](/docs/guide/partners/referrals.md) does. - **Scope switching, not impersonation** — you operate a client company the same way you use any company you belong to: switch into it from the [Companies](/docs/guide/platform/companies.md) page. - **Client-side transparency and control** — the client's company shows a **Managed by** banner, and the client's owner can revoke your access at any time with **Revoke access**. - **Commission** — client companies appear with their usage for the period and your commission, on the same rails as the [referral program](/docs/guide/partners/referrals.md). ## Reading the client list The **Client companies** table has one row per client, with four columns: - **Client** — the client company's name. - **Status** — new clients start as **Pending review** while Autessa confirms the relationship, then flip to **Active**. - **Usage (period)** — the client's collected usage for the current commission period, in dollars. - **Your commission** — what that usage has earned you so far in the period. ## How to provision a client company 1. Open **Consultant** from the sidebar. The **Provision a new client** section is visible to your company's owner. 2. Enter the client's account email and the client company name. 3. Click **Provision client**. The client company is created under the client's ownership, and it appears in your **Client companies** list. ## How to work inside a client company 1. On the consultant dashboard, click **Open a client workspace**. 2. You land on the **Companies** page — select the client company and click **Switch to this company**. 3. Everything you build while switched in belongs to the client's company. Switch back to your own company the same way when you're done. ## Related - [Referrals](/docs/guide/partners/referrals.md) - [Companies](/docs/guide/platform/companies.md) - [Team](/docs/guide/company/team.md) # API Reference --- # API Reference Source: https://docs.autessa.com/docs/api > Complete API reference for Autessa client-facing APIs including Agents, Tools, and AutessaDB. # API Reference Welcome to the Autessa API reference documentation. This guide covers all client-facing APIs that you can use to integrate Autessa into your applications. ## Base URL All API requests are made to: ``` https://api.autessa.com ``` For WebSocket connections: ``` wss://api.autessa.com ``` --- ## Authentication Autessa uses API keys for authentication. You'll need to generate an API key from the builder platform. [Generate API Key →](https://builder.autessa.com/keys) ### HTTP REST APIs For REST API requests, pass your API key in the `Authorization` header: ```bash Authorization: your_api_key_here ``` **Important:** Do NOT prefix with "Bearer" - just pass the API key directly. **cURL Example:** ```bash curl https://api.autessa.com/clients/agents/execute \ -H "Authorization: your_api_key_here" \ -H "Content-Type: application/json" ``` **JavaScript Example:** ```javascript fetch('https://api.autessa.com/clients/agents/execute', { headers: { 'Authorization': 'your_api_key_here', 'Content-Type': 'application/json' } }) ``` **Python Example:** ```python headers = { 'Authorization': 'your_api_key_here', 'Content-Type': 'application/json' } response = requests.post( 'https://api.autessa.com/clients/agents/execute', headers=headers ) ``` ### WebSocket APIs For WebSocket connections, pass your API key as a query parameter: ``` wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key_here ``` **websocat Example:** ```bash websocat "wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key_here&resourceId=123" ``` **JavaScript Example:** ```javascript const ws = new WebSocket( 'wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key_here&resourceId=123' ); ``` **Python Example:** ```python ws = websocket.WebSocket() ws.connect( "wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key_here&resourceId=123" ) ``` --- ## Resource ID Requirement All API requests to agents, tools, and AutessaDB require a `resourceId` query parameter. This ID is used to identify which resource you're interacting with and to track your API usage. ### Finding Your Resource ID 1. Navigate to your resource in the [Autessa Builder](https://builder.autessa.com) 2. Click on the "Details" tab 3. Copy the "ID" field - this is your `resourceId` ### Usage ```bash # For Agents https://api.autessa.com/clients/agents/execute?resourceId=123 # For Tools https://api.autessa.com/clients/tool/execute?resourceId=456 # For AutessaDB https://api.autessa.com/clients/autessadb/insert?resourceId=789 ``` --- ## Common Response Format All API responses follow a consistent structure: - `errors` — "> Array of error messages. Empty if the request succeeded. - `sessionToken` (string) — Optional session token for authenticated requests. - `...data` (object) — Response-specific data fields. **Success Response:** ```json { "errors": [], "conversationId": "conv_abc123", "output": [ { "outputType": "TEXT", "content": "Hello! How can I help you?" } ] } ``` **Error Response:** ```json { "errors": [ "Invalid API key", "Resource not found" ] } ``` --- ## Error Handling ### HTTP Status Codes - `200` — Success - Request completed successfully - `401` — Unauthorized - Invalid or missing API key - `429` — Rate Limit Exceeded - Includes `Retry-After` header - `500` — Internal Server Error - Something went wrong on our end ### Error Response Structure When errors occur, they are returned in the `errors` array: ```json { "errors": [ "Resource with ID 123 not found", "Permission denied: READ access required" ] } ``` --- ## Rate Limiting API requests are rate-limited to ensure fair usage. Rate limit information is included in response headers: - `X-RateLimit-Limit-Minute` (integer) — Maximum requests allowed per minute - `X-RateLimit-Remaining-Minute` (integer) — Remaining requests in the current minute - `X-RateLimit-Limit-Day` (integer) — Maximum requests allowed per day - `X-RateLimit-Remaining-Day` (integer) — Remaining requests for the current day When you exceed the rate limit, you'll receive a `429` status code with a `Retry-After` header indicating how long to wait before retrying. --- ## Versioning By default, all API requests use the **PUBLISHED** version of your resource. You can override this using the `version` parameter in your requests. - `versionState` (enum) — The version state: `PUBLISHED`, `DRAFT`, or `ARCHIVED`. Default is `PUBLISHED`. - `versionNumber` (integer) — Specific version number to use (optional). **REST API Version Override:** ```json { "agentId": 123, "input": [...], "version": { "versionState": "DRAFT" } } ``` **WebSocket Version Override:** ```bash wss://api.autessa.com/ws/clients/agents/execute?authorization=key&resourceId=123&versionState=DRAFT ``` --- ## Available APIs ### Agent API The Agent API allows you to create conversations, execute agents, and manage multi-turn interactions with AI agents. **Key Features:** - Multi-turn conversations with context - Multimodal input (text and audio) - Real-time streaming via WebSockets - Voice mode with automatic transcription - Audio output generation [View the Agent API documentation →](/docs/api/agents.md) ### Tool API The Tool API enables you to execute custom tools that you've built in the Autessa platform. **Key Features:** - Synchronous and streaming execution - Environment variables and private variables - Real-time debugging output - Version control [View the Tool API documentation →](/docs/api/tools.md) ### AutessaDB API The AutessaDB API provides a complete database solution with semantic search capabilities. **Key Features:** - CRUD operations (Create, Read, Update, Delete) - Semantic vector search - Batch operations - Join queries across tables - File upload support - Automatic vectorization [View the AutessaDB API documentation →](/docs/api/autessadb.md) ### AutessaDB Drive API Browse, upload, search, read, and share the files and folders in an AutessaDB Drive. **Key Features:** - Folder listing, glob find, and folder trees - Exact-text grep and ranked search (meaning-based when enabled) with page and line locators - Direct-to-storage uploads, including multipart for large files - Versions, trash, and restore - Sharing with people, teams, your company, or a link [View the Drive API documentation →](/docs/api/autessadb-drive.md) ### AutessaDB Analytics API Run read-only SQL over columnar analytics tables with millions to billions of rows. **Key Features:** - Table descriptions with a semantic layer for agents - Queries as jobs, with paged results and downloads - Scan estimates before you run - File loads and small-batch appends [View the Analytics API documentation →](/docs/api/autessadb-analytics.md) --- ## WebSocket Features All WebSocket connections support: ### Heartbeat Mechanism - Automatic ping/pong handling to keep connections alive - 30-minute idle timeout - Graceful disconnection handling ### Connection Lifecycle 1. **Connect** - Establish WebSocket connection with authentication 2. **Receive Status** - Server sends initial connection status 3. **Send Messages** - Client sends requests to the server 4. **Receive Responses** - Server streams back responses 5. **Disconnect** - Clean connection close --- ## Best Practices ### Security - Never expose your API keys in client-side code - Use environment variables to store API keys - Rotate keys regularly - Use separate keys for development and production ### Performance - Use WebSockets for real-time, multi-turn interactions - Use REST APIs for single-shot requests - Implement exponential backoff for retries - Cache responses when appropriate ### Error Handling - Always check the `errors` array in responses - Implement proper error handling for network failures - Handle rate limit errors with retry logic - Log errors for debugging ### Conversations - Close conversations when done to trigger logging and evaluation - Conversations auto-close after 12 hours of inactivity - Use conversation IDs to maintain context across requests - Don't create unnecessary conversations for single-turn interactions --- ## Support Need help? Here are some resources: [Autessa Builder →](https://builder.autessa.com) · [Join our Discord →](https://discord.gg/autessa) For technical questions or bug reports, please contact our support team at support@autessa.com. --- # Agent API Source: https://docs.autessa.com/docs/api/agents > Complete API reference for executing agents, managing conversations, and handling multimodal interactions. # Agent API The Agent API allows you to create and manage conversations with AI agents, execute them with text or audio input, and receive real-time streaming responses. > All Agent API requests require the `resourceId` query parameter set to your agent ID and an `Authorization` header with your API key. --- ## Base URL ``` REST API: https://api.autessa.com/clients/agents WebSocket: wss://api.autessa.com/ws/clients/agents/execute ``` --- ## Authentication ### HTTP REST APIs Pass your API key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: your_api_key_here ``` ### WebSocket APIs Pass your API key as a query parameter: ``` authorization=your_api_key_here ``` **REST:** ```bash curl https://api.autessa.com/clients/agents/execute-create?resourceId=V1StGXR8Z5 \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" ``` **WebSocket:** ```bash websocat "wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ``` --- ## REST API Endpoints ## Create Conversation **POST** `/execute-create` Creates a new conversation for multi-turn agent interactions. A conversation maintains context across multiple agent executions. You only need to create a conversation if you want multi-turn interactions - single-shot requests don't require conversations. **Note:** Once created, a conversation is tied to a specific agent version. Even if you publish a new version, existing conversations will continue using the original version. ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `agentId` (string, required) — The ID of the agent to create a conversation for. - `version` (CustomVersion) — Optional version override. If not specified, uses the PUBLISHED version. ### Response - `conversationId` (string) — The unique ID for the created conversation. Use this in subsequent execute requests. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/agents/execute-create?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "agentId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/agents/execute-create?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5' }) } ); const data = await response.json(); console.log(data.conversationId); ``` ```json title="JSON" { "conversationId": "conv_a1b2c3d4e5", "errors": [] } ``` --- ## Execute Agent **POST** `/execute` Executes an agent synchronously with multimodal input (text or audio). This endpoint returns the complete response once processing is done. **Note:** Synchronous execution only supports TEXT output. For AUDIO output or real-time streaming, use the WebSocket endpoint. ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `agentId` (string, required) — The ID of the agent to execute. - `conversationId` (string) — The conversation ID from `/execute-create`. Required for multi-turn conversations. - `input` — " required> Array of multimodal inputs. See [Multimodal Input](#multimodal-input). - `environmentVariables` (object) — Key-value pairs for environment variables that won't be stored on the server. - `promptTemplateVariables` (object) — Key-value pairs to inject into the agent's prompt template. Variables are referenced in the agent instructions using `@@{variableName}@@` syntax. - `version` (CustomVersion) — Version override. Ignored if using an existing conversationId. ### Response - `output` — "> Array of output responses from the agent. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5", "input": [ { "inputType": "TEXT", "content": "What is the weather today?" } ], "environmentVariables": { "USER_LOCATION": "Seattle" }, "promptTemplateVariables": { "customerName": "John Doe", "accountType": "Premium" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', conversationId: 'conv_a1b2c3d4e5', input: [ { inputType: 'TEXT', content: 'What is the weather today?' } ], environmentVariables: { USER_LOCATION: 'Seattle' }, promptTemplateVariables: { customerName: 'John Doe', accountType: 'Premium' } }) } ); const data = await response.json(); ``` ```json title="JSON" { "output": [ { "outputType": "TEXT", "content": "The weather in Seattle today is 65°F with partly cloudy skies." } ], "errors": [] } ``` --- ## Close Conversation **POST** `/execute-close` Closes an existing conversation. This triggers logging and any configured auto-evaluation for the conversation. It's safe to call this even for expired conversations. **Important:** Conversations automatically close after 12 hours of inactivity, but it's best practice to explicitly close them when done. ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `agentId` (string, required) — The ID of the agent. - `conversationId` (string, required) — The conversation ID to close. ### Response - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/agents/execute-close?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` **JavaScript:** ```javascript await fetch( 'https://api.autessa.com/clients/agents/execute-close?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', conversationId: 'conv_a1b2c3d4e5' }) } ); ``` ```json title="JSON" { "errors": [] } ``` --- ## Get Conversation **POST** `/conversation` Retrieves the complete conversation history including all messages and evaluation context. ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `agentId` (string, required) — The ID of the agent. - `conversationId` (string, required) — The conversation ID to retrieve. ### Response - `conversation` (AgentConversationDto) — The complete conversation data including logs and evaluation context. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/agents/conversation?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` ```json title="JSON" { "conversation": { "resourceId": "V1StGXR8Z5", "version": "1", "conversationId": "conv_a1b2c3d4e5", "apiKeyId": "key_xyz", "closeDate": null, "logs": [ { "logType": "USER", "userMultimodalInputs": [ { "inputType": "TEXT", "content": "What is the weather in Seattle?" } ] }, { "logType": "TOOL_REQUEST", "executeToolRequest": { "toolCallId": "call_abc123", "name": "GetWeather", "variables": { "location": "Seattle" } } }, { "logType": "TOOL_RESPONSE", "executeToolResponse": { "toolCallId": "call_abc123", "toolName": "GetWeather", "result": { "temperature": 65, "condition": "Partly Cloudy" } } }, { "logType": "MESSAGE_TO_USER", "multimodalOutput": { "outputType": "TEXT", "content": "The weather in Seattle is currently 65°F with partly cloudy skies." } } ] }, "errors": [] } ``` --- ## Conversation Log Structure Conversation logs returned by the `/conversation` endpoint contain detailed information about all interactions in a conversation. Each log entry has a `logType` field that determines which other fields are present. ### Log Types - `USER` — User input to the agent. Contains `userMultimodalInputs` array with text or audio inputs. - `MESSAGE_TO_USER` — Agent's response to the user. Contains `multimodalOutput` with text or audio output. - `TOOL_REQUEST` — Request to execute a tool. Contains `executeToolRequest` with tool name and variables. - `TOOL_RESPONSE` — Result from tool execution. Contains `executeToolResponse` with the tool's result. - `CRITIC_REJECTED_MESSAGE` — A draft response that was rejected by the agent's critic (if critic evaluation is enabled). Contains `multimodalOutput` plus `criticEvaluation` and `criticAttemptNumber` fields for debugging. - `LISTEN` — Voice-mode marker indicating the agent chose to keep listening instead of responding. ### AgentLog Structure Each log entry has one of these structures based on its `logType`: **USER Log:** - `logType` (string) — `"USER"` - `userMultimodalInputs` — "> Array of user inputs (text or audio). See [Multimodal Input](#multimodal-input). **MESSAGE_TO_USER Log:** - `logType` (string) — `"MESSAGE_TO_USER"` - `multimodalOutput` (MultimodalOutput) — The agent's response (text or audio). **TOOL_REQUEST Log:** - `logType` (string) — `"TOOL_REQUEST"` - `executeToolRequest` (object) — Tool execution request details. - `executeToolRequest.toolCallId` (string) — Unique identifier for this tool call. - `executeToolRequest.name` (string) — Name of the tool being invoked. - `executeToolRequest.variables` (object) — Key-value pairs of arguments passed to the tool. **TOOL_RESPONSE Log:** - `logType` (string) — `"TOOL_RESPONSE"` - `executeToolResponse` (object) — Tool execution result. - `executeToolResponse.toolCallId` (string) — Matches the toolCallId from the corresponding TOOL_REQUEST. - `executeToolResponse.toolName` (string) — Name of the tool that was executed. - `executeToolResponse.result` (any) — The result returned by the tool. **USER (Text):** ```json { "logType": "USER", "userMultimodalInputs": [ { "inputType": "TEXT", "content": "What is the weather?" } ] } ``` **USER (Audio):** ```json { "logType": "USER", "userMultimodalInputs": [ { "inputType": "AUDIO", "s3Uri": "s3://autessa-audio/...", "audioFormat": { "sampleRate": 16000, "sampleSizeInBits": 16, "channels": 1, "signed": true, "bigEndian": false }, "transcription": "What is the weather?" } ] } ``` **MESSAGE_TO_USER (Text):** ```json { "logType": "MESSAGE_TO_USER", "multimodalOutput": { "outputType": "TEXT", "content": "It's 65°F and sunny." } } ``` **MESSAGE_TO_USER (Audio):** ```json { "logType": "MESSAGE_TO_USER", "multimodalOutput": { "outputType": "AUDIO", "sampleRate": 16000, "base64Audio": "UklGRiQAAABXQVZF...", "transcription": "It's 65 degrees and sunny.", "isFinalChunk": true } } ``` **TOOL_REQUEST:** ```json { "logType": "TOOL_REQUEST", "executeToolRequest": { "toolCallId": "call_123", "name": "GetWeather", "variables": { "location": "Seattle", "units": "fahrenheit" } } } ``` **TOOL_RESPONSE:** ```json { "logType": "TOOL_RESPONSE", "executeToolResponse": { "toolCallId": "call_123", "toolName": "GetWeather", "result": { "temperature": 65, "condition": "Sunny" } } } ``` --- ## Get Conversation Status **POST** `/conversation-status` Checks the current status of a conversation (IN_PROGRESS, CLOSED, or NOT_FOUND). ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `agentId` (string, required) — The ID of the agent. - `conversationId` (string, required) — The conversation ID to check. ### Response - `conversationId` (string) — The conversation ID that was checked. - `status` (string) — One of: `IN_PROGRESS`, `CLOSED`, `NOT_FOUND` - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/agents/conversation-status?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` ```json title="JSON" { "conversationId": "conv_a1b2c3d4e5", "status": "IN_PROGRESS", "errors": [] } ``` --- ## Revive Conversation **POST** `/revive-conversation` Revives a closed conversation, allowing you to continue the interaction where it left off. ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `agentId` (string, required) — The ID of the agent. - `conversationId` (string, required) — The conversation ID to revive. ### Response - `conversationId` (string) — The revived conversation ID. - `status` (string) — Status message about the revival operation. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/agents/revive-conversation?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` ```json title="JSON" { "conversationId": "conv_a1b2c3d4e5", "status": "REVIVED", "errors": [] } ``` --- ## Generate Audio Upload Link **POST** `/generate-audio-upload-link` Generates a pre-signed S3 upload URL for audio files. Use this when you want to upload audio files separately rather than sending them as base64 in the request. **Note:** Currently supports WAV format only. ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body Currently empty (WAV format is hardcoded). ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the audio file via PUT request. - `s3Uri` (string) — S3 URI to use in AudioInput when referencing this file. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/agents/generate-audio-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{}' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-audio/...", "s3Uri": "s3://autessa-audio/user-123/audio-xyz.wav", "expiresInSeconds": 3600, "errors": [] } ``` ```bash title="Upload the file" # Step 1: Get upload link # Step 2: Upload audio file curl --request PUT \ --url 'https://s3.amazonaws.com/autessa-audio/...' \ --header 'Content-Type: audio/wav' \ --data-binary '@audio.wav' # Step 3: Use s3Uri in AudioInput # { # "inputType": "AUDIO", # "s3Uri": "s3://autessa-audio/user-123/audio-xyz.wav" # } ``` --- ## Generate Image Upload Link **POST** `/generate-image-upload-link` Generates a pre-signed S3 upload URL for image files. Upload the image via PUT, then reference the returned `s3Uri` in an [Image Input](#image-input). ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `fileExtension` (string) — Image file extension: `png`, `jpg`, or `jpeg`. Defaults to `png` if not provided. ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the image file via PUT request. - `s3Uri` (string) — S3 URI to use in ImageInput when referencing this file. - `mimeType` (string) — MIME type of the image (e.g. `image/png`). Use it as the `Content-Type` header when uploading, and in the ImageInput. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/agents/generate-image-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "fileExtension": "png" }' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-images/...", "s3Uri": "s3://autessa-images/user-123/image-xyz.png", "mimeType": "image/png", "expiresInSeconds": 3600, "errors": [] } ``` ```bash title="Upload the file" # Step 1: Get upload link # Step 2: Upload image file curl --request PUT \ --url 'https://s3.amazonaws.com/autessa-images/...' \ --header 'Content-Type: image/png' \ --data-binary '@image.png' # Step 3: Use s3Uri in ImageInput # { # "inputType": "IMAGE", # "s3Uri": "s3://autessa-images/user-123/image-xyz.png", # "mimeType": "image/png" # } ``` --- ## Generate File Upload Link **POST** `/generate-file-upload-link` Generates a pre-signed S3 upload URL for document files (PDFs, text documents, etc.). Upload the file via PUT, then reference the returned `s3Uri` in a [File Input](#file-input). ### Query Parameters - `resourceId` (string, required) — The ID of the agent (e.g. `V1StGXR8Z5`). ### Request Body - `fileExtension` (string) — File extension of the document to upload (e.g. `pdf`, `docx`, `txt`). ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the file via PUT request. - `s3Uri` (string) — S3 URI to use in FileInput when referencing this file. - `mimeType` (string) — MIME type of the file (e.g. `application/pdf`). Use it as the `Content-Type` header when uploading, and in the FileInput. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/agents/generate-file-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "fileExtension": "pdf" }' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/user-123/file-xyz.pdf", "mimeType": "application/pdf", "expiresInSeconds": 3600, "errors": [] } ``` ```bash title="Upload the file" # Step 1: Get upload link # Step 2: Upload document file curl --request PUT \ --url 'https://s3.amazonaws.com/autessa-files/...' \ --header 'Content-Type: application/pdf' \ --data-binary '@document.pdf' # Step 3: Use s3Uri in FileInput # { # "inputType": "FILE", # "s3Uri": "s3://autessa-files/user-123/file-xyz.pdf", # "mimeType": "application/pdf" # } ``` --- ## WebSocket API ## Multimodal Agent Execution **WSS** `/ws/clients/agents/execute` Real-time agent execution with streaming responses. Supports both text and audio input/output with streaming capabilities. ### Connection URL ``` wss://api.autessa.com/ws/clients/agents/execute?authorization={key}&resourceId={agentId} ``` ### Query Parameters - `authorization` (string, required) — Your API key. - `resourceId` (string, required) — The ID of the agent. - `conversationId` (string) — Optional conversation ID to revive and continue. - `voiceModeEnabled` (boolean) — Set to "true" for voice mode with continuous audio streaming. - `versionState` (string) — Optional version state override: `PUBLISHED` (default), `DRAFT`, or `ARCHIVED`. - `versionNumber` (integer) — Optional specific version number to use. ### Connection Lifecycle 1. **Connect** - WebSocket connection established 2. **Status Message** - Server sends conversation status 3. **Send Messages** - Client sends MULTIMODAL or VOICE messages 4. **Receive Streams** - Server streams responses in real-time 5. **Disconnect** - Connection closes, conversation auto-closes **websocat:** ```bash websocat "wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ``` **JavaScript:** ```javascript const ws = new WebSocket( 'wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key&resourceId=V1StGXR8Z5' ); ws.onopen = () => { console.log('Connected to agent'); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('Received:', data); }; ``` **Python:** ```python ws = websocket.WebSocket() ws.connect( "wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ) # Receive status status = json.loads(ws.recv()) print(status) ``` ### Server-to-Client Messages When you connect, the server immediately sends a status message: **New Conversation:** ```json { "conversationId": "conv_abc123", "status": "CREATED", "errors": [] } ``` **Revived Conversation:** ```json { "conversationId": "conv_abc123", "status": "REVIVED", "errors": [] } ``` **Error:** ```json { "conversationId": null, "status": "ERROR", "errors": ["Invalid API key"] } ``` ### Client-to-Server Messages #### MULTIMODAL Message Send a JSON message to execute the agent with multimodal input: - `messageType` (string, required) — Must be `"MULTIMODAL"` - `agentId` (string, required) — The agent ID - `input` — " required> Array of multimodal inputs (text or audio) - `executionOutputMode` (string) — `"TEXT"` or `"AUDIO"` - desired output format. Defaults to `"TEXT"`. - `environmentVariables` (object) — Key-value pairs for environment variables that won't be stored on the server. - `promptTemplateVariables` (object) — Key-value pairs to inject into the agent's prompt template. Variables are referenced in the agent instructions using `@@{variableName}@@` syntax. ```json title="JSON" { "messageType": "MULTIMODAL", "agentId": "V1StGXR8Z5", "executionOutputMode": "TEXT", "input": [ { "inputType": "TEXT", "content": "What's the weather in Seattle?" } ], "environmentVariables": { "USER_LOCATION": "Seattle" }, "promptTemplateVariables": { "customerName": "John Doe", "accountType": "Premium" } } ``` #### VOICE Message Establish voice mode for continuous audio streaming: - `messageType` (string, required) — Must be `"VOICE"` - `agentId` (string, required) — The agent ID - `audioFormat` (AudioFormat, required) — Audio format specification for the audio bytes you'll send - `outputType` (string) — `"TEXT"` or `"AUDIO"` - desired output format - `environmentVariables` (object) — Environment variables ```json title="JSON" { "messageType": "VOICE", "agentId": "V1StGXR8Z5", "audioFormat": { "sampleRate": 16000, "sampleSizeInBits": 16, "channels": 1, "signed": true, "bigEndian": false }, "outputType": "AUDIO", "environmentVariables": {} } ``` After establishing voice mode, send raw audio bytes as binary WebSocket messages. ### Streaming Response Messages The server streams responses in real-time. There are three types of messages: #### 1. Text Stream Messages ```json title="JSON" { "streamedOutput": { "outputType": "TEXT", "content": " Seattle" } } ``` Text is streamed token-by-token. Concatenate the `content` fields to build the full response. #### 2. Tool Stream Messages **Tool Start:** ```json { "streamedToolMessages": { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "inv_123", "publicVars": { "city": "Seattle", "state": "WA" }, "streamMessage": "EXECUTION START: GetWeather" } } ``` **Tool Variables:** ```json { "streamedToolMessages": { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "inv_123", "streamMessage": "EXECUTION VARIABLES: {\"city\":\"Seattle\",\"state\":\"WA\"}" } } ``` **Tool End:** ```json { "streamedToolMessages": { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "inv_123", "streamMessage": "EXECUTION END: GetWeather" } } ``` **Tool Result:** ```json { "streamedToolMessages": { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "inv_123", "streamMessage": "EXECUTION RESULT: \"It is 65°F and partly cloudy\"" } } ``` #### 3. Audio Stream Messages When `executionOutputMode: "AUDIO"` is requested: ```json title="JSON" { "streamedOutput": { "outputType": "AUDIO", "sampleRate": 16000, "base64Audio": "//8AAAMAAAD...", "transcription": "The weather in Seattle is 65 degrees", "isFinalChunk": false } } ``` Audio is streamed in chunks. Decode the `base64Audio` and play it sequentially. #### 4. Final Response After streaming completes, the server sends a final message with the complete output: ```json title="JSON" { "conversationId": "conv_abc123", "output": [ { "outputType": "TEXT", "content": "The weather in Seattle is currently 65°F with partly cloudy skies." } ], "errors": [] } ``` --- ## Multimodal Input Autessa supports multimodal input - you can send text, audio, images, files, or any combination. Each input is distinguished by its `inputType` field: `"TEXT"`, `"AUDIO"`, `"IMAGE"`, `"FILE"`, `"IMAGE_URL"`, or `"FILE_URL"`. ### Text Input - `inputType` (string, required) — Must be `"TEXT"` - `content` (string, required) — The text message from the user ```json title="JSON" { "inputType": "TEXT", "content": "What's the weather today?" } ``` ### Audio Input Audio can be provided in two ways: base64 encoded data or S3 URI. - `inputType` (string, required) — Must be `"AUDIO"` - `base64EncodedData` (string) — Base64 encoded audio data (option 1) - `s3Uri` (string) — S3 URI from `/generate-audio-upload-link` (option 2) - `audioFormat` (AudioFormat, required) — Audio format metadata - `transcription` (string) — Optional pre-transcribed text **Base64:** ```json { "inputType": "AUDIO", "base64EncodedData": "UklGRiQAAABXQVZFZm10...", "audioFormat": { "sampleRate": 16000, "sampleSizeInBits": 16, "channels": 1, "signed": true, "bigEndian": false } } ``` **S3 URI:** ```json { "inputType": "AUDIO", "s3Uri": "s3://autessa-audio/user-123/audio.wav", "audioFormat": { "sampleRate": 16000, "sampleSizeInBits": 16, "channels": 1, "signed": true, "bigEndian": false } } ``` ### Image Input Send an image the agent can see. Upload the image first with [/generate-image-upload-link](#generate-image-upload-link-), or point at an external URL with `directUrl`. - `inputType` (string, required) — Must be `"IMAGE"` - `s3Uri` (string) — S3 URI from `/generate-image-upload-link` - `directUrl` (string) — Direct HTTPS URL to an externally hosted image. If set, it is used instead of `s3Uri`. - `mimeType` (string) — Image MIME type, e.g. `image/png`, `image/jpeg` **S3 URI:** ```json { "inputType": "IMAGE", "s3Uri": "s3://autessa-images/user-123/image-xyz.png", "mimeType": "image/png" } ``` **Direct URL:** ```json { "inputType": "IMAGE", "directUrl": "https://example.com/product-photo.jpg", "mimeType": "image/jpeg" } ``` ### File Input Send a document (PDF, text, etc.) the agent can read. Upload the file first with [/generate-file-upload-link](#generate-file-upload-link-), or point at an external URL with `directUrl`. - `inputType` (string, required) — Must be `"FILE"` - `s3Uri` (string) — S3 URI from `/generate-file-upload-link` - `directUrl` (string) — Direct HTTPS URL to an externally hosted file. If set, it is used instead of `s3Uri`. - `mimeType` (string) — File MIME type, e.g. `application/pdf`, `text/plain` ```json title="JSON" { "inputType": "FILE", "s3Uri": "s3://autessa-files/user-123/file-xyz.pdf", "mimeType": "application/pdf" } ``` ### URL Inputs `IMAGE_URL` and `FILE_URL` inputs reference content by URL directly: - `inputType` (string, required) — `"IMAGE_URL"` or `"FILE_URL"` - `url` (string, required) — Direct URL (`https://...`) or S3 URI (`s3://...`) - `mimeType` (string) — MIME type. Can be inferred from the URL if omitted. - `isS3Uri` (boolean) — Set to `true` if `url` is an `s3://` URI that needs pre-signed URL conversion. ```json title="JSON" { "inputType": "IMAGE_URL", "url": "https://example.com/diagram.png", "mimeType": "image/png", "isS3Uri": false } ``` ### AudioFormat - `sampleRate` (integer, required) — Samples per second in Hz. Common values: 16000 (telephony), 44100 (CD quality), 48000 (professional). - `sampleSizeInBits` (integer, required) — Bits per sample. Typical values: 8, 16, 24, 32. - `channels` (integer, required) — Number of audio channels. 1 = mono, 2 = stereo. - `signed` (boolean, required) — Whether samples are signed (true) or unsigned (false). - `bigEndian` (boolean, required) — Byte order: true = big-endian, false = little-endian. --- ## Multimodal Output Agent responses can be in text or audio format. The output type is distinguished by the `outputType` field, which can be either `"TEXT"` or `"AUDIO"`. ### Text Output - `outputType` (string, required) — Must be `"TEXT"` - `content` (string, required) — The text response from the agent ```json title="JSON" { "outputType": "TEXT", "content": "The weather in Seattle is 65°F with partly cloudy skies." } ``` ### Audio Output Audio output is used when you request `executionOutputMode: "AUDIO"` via WebSocket. - `outputType` (string, required) — Must be `"AUDIO"` - `sampleRate` (integer, required) — Samples per second in Hz (e.g., 16000) - `base64Audio` (string, required) — Base64 encoded audio data - `transcription` (string, required) — Text transcription of the audio - `isFinalChunk` (boolean, required) — Whether this is the final chunk of audio in the stream ```json title="JSON" { "outputType": "AUDIO", "sampleRate": 16000, "base64Audio": "UklGRiQAAABXQVZFZm10...", "transcription": "The weather in Seattle is 65 degrees with partly cloudy skies.", "isFinalChunk": true } ``` --- ## Custom Version Override the default PUBLISHED version of an agent: - `versionState` (string) — Version state: `"PUBLISHED"`, `"DRAFT"`, or `"ARCHIVED"`. Defaults to `"PUBLISHED"`. - `versionNumber` (integer) — Specific version number to use (optional). **REST API:** ```json { "agentId": "V1StGXR8Z5", "version": { "versionState": "DRAFT" }, "input": [...] } ``` **WebSocket:** ```bash wss://api.autessa.com/ws/clients/agents/execute?authorization=key&resourceId=V1StGXR8Z5&versionState=DRAFT ``` --- ## Enumerations ### EConversationStatus - `IN_PROGRESS` - Conversation is active - `CLOSED` - Conversation has been closed - `NOT_FOUND` - Conversation doesn't exist ### ExecutionOutputMode - `TEXT` - Text output (default) - `AUDIO` - Audio output (base64 encoded) ### EInputType - `TEXT` - Text input - `AUDIO` - Audio input - `IMAGE` - Image input (S3 URI or direct URL) - `FILE` - File/document input (S3 URI or direct URL) - `IMAGE_URL` - Image referenced by URL - `FILE_URL` - File referenced by URL ### EOutputType - `TEXT` - Text output - `AUDIO` - Audio output ### EVersionState - `PUBLISHED` - Published version (default) - `DRAFT` - Draft/sandbox version - `ARCHIVED` - Archived version --- ## Rate Limiting Agent execution endpoints are rate limited. Rate limit information is included in response headers: - `X-RateLimit-Limit-Minute` - Requests allowed per minute - `X-RateLimit-Remaining-Minute` - Remaining requests this minute - `X-RateLimit-Limit-Day` - Requests allowed per day - `X-RateLimit-Remaining-Day` - Remaining requests today When rate limited, you'll receive a 429 status code with a `Retry-After` header. --- ## Complete Examples ### Single-Turn Conversation **JavaScript:** ```javascript // Simple one-shot agent execution const response = await fetch( 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', input: [ { inputType: 'TEXT', content: 'Tell me a joke' } ] }) } ); const data = await response.json(); console.log(data.output[0].content); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={ 'agentId': 'V1StGXR8Z5', 'input': [ { 'inputType': 'TEXT', 'content': 'Tell me a joke' } ] } ) data = response.json() print(data['output'][0]['content']) ``` ### Multi-Turn Conversation ```javascript title="JavaScript" // Create conversation const createResp = await fetch( 'https://api.autessa.com/clients/agents/execute-create?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5' }) } ); const { conversationId } = await createResp.json(); // First message const resp1 = await fetch( 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', conversationId, input: [{ inputType: 'TEXT', content: 'Hi, I need help booking a flight' }] }) } ); // Second message (continues conversation) const resp2 = await fetch( 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', conversationId, input: [{ inputType: 'TEXT', content: 'From Seattle to San Francisco' }] }) } ); // Close conversation await fetch( 'https://api.autessa.com/clients/agents/execute-close?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', conversationId }) } ); ``` ### Mixed Text and Audio Input ```javascript title="JavaScript" // Example showing both text and audio inputs in one request const response = await fetch( 'https://api.autessa.com/clients/agents/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', conversationId: 'conv_a1b2c3d4e5', input: [ // Text input { inputType: 'TEXT', content: 'Here is my question:' }, // Audio input { inputType: 'AUDIO', s3Uri: 's3://autessa-audio/user-123/question.wav', audioFormat: { sampleRate: 16000, sampleSizeInBits: 16, channels: 1, signed: true, bigEndian: false } } ] }) } ); const data = await response.json(); // Response has outputType field to distinguish format console.log('Output type:', data.output[0].outputType); // "TEXT" or "AUDIO" ``` ### WebSocket Streaming ```javascript title="JavaScript" const ws = new WebSocket( 'wss://api.autessa.com/ws/clients/agents/execute?authorization=your_api_key&resourceId=V1StGXR8Z5' ); let conversationId; let fullResponse = ''; ws.onopen = () => { console.log('Connected'); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); // Connection status if (data.status) { conversationId = data.conversationId; console.log('Conversation created:', conversationId); // Send first message ws.send(JSON.stringify({ messageType: 'MULTIMODAL', agentId: 'V1StGXR8Z5', executionOutputMode: 'TEXT', input: [ { inputType: 'TEXT', content: 'What is the weather?' } ] })); return; } // Streaming text if (data.streamedOutput) { fullResponse += data.streamedOutput.content; console.log('Stream:', data.streamedOutput.content); } // Tool messages if (data.streamedToolMessages) { console.log('Tool:', data.streamedToolMessages.streamMessage); } // Final output if (data.output) { console.log('Final response:', data.output[0].content); console.log('Full streamed response:', fullResponse); } }; ws.onerror = (error) => { console.error('WebSocket error:', error); }; ws.onclose = () => { console.log('Disconnected'); }; ``` ### Real-time Voice Mode For conversational AI with continuous audio streaming, use the `voiceModeEnabled=true` parameter. This enables bidirectional audio streaming for natural voice conversations. **JavaScript:** ```javascript const agentId = 'V1StGXR8Z5' const apiKey = 'your_api_key' // Connect with voice mode enabled const ws = new WebSocket( `wss://api.autessa.com/ws/clients/agents/execute?authorization=${apiKey}&resourceId=${agentId}&voiceModeEnabled=true` ) let conversationId ws.onopen = () => { console.log('Connected in voice mode') } ws.onmessage = async (event) => { // Handle connection status if (typeof event.data === 'string') { const data = JSON.parse(event.data) if (data.status) { conversationId = data.conversationId console.log('Conversation created:', conversationId) // Send voice establishment message ws.send(JSON.stringify({ messageType: 'VOICE', agentId: agentId, audioFormat: { sampleRate: 16000, sampleSizeInBits: 32, channels: 1, signed: true, bigEndian: false }, outputType: 'AUDIO', environmentVariables: {}, promptTemplateVariables: {} })) console.log('Voice mode established') return } // Handle audio output chunks if (data.streamedOutput?.outputType === 'AUDIO') { const { base64Audio, sampleRate, transcription, isFinalChunk } = data.streamedOutput console.log('Agent:', transcription) // Process audio chunk for playback // See: https://docs.autessa.com/api/audio-playback await audioProcessor.processAudioChunk(base64Audio, sampleRate, transcription) // Auto-start playback const status = audioProcessor.getStatus() if (status.totalItems === 1 && !status.isPlaying) { audioProcessor.play() } if (isFinalChunk) { console.log('Agent finished speaking') } } } } // Start recording and stream audio // See complete implementation at: https://docs.autessa.com/api/audio-record const recorder = new AudioRecorder(16000) recorder.onSpeechStart = () => { console.log('User started speaking') audioProcessor.setInterrupted(true) } recorder.onSpeechEnd = () => { console.log('User stopped speaking') audioProcessor.setInterrupted(false) } // Start continuous recording with WebSocket await recorder.startContinuousRecording( `wss://api.autessa.com/ws/clients/agents/execute?authorization=${apiKey}&resourceId=${agentId}&voiceModeEnabled=true`, { outputType: 'AUDIO', agentId: agentId, environmentVariables: {}, promptTemplateVariables: {} } ) // Later: cleanup recorder.stopRecording() audioProcessor.clear() ws.close() ``` **Python:** ```python agent_id = 'V1StGXR8Z5' api_key = 'your_api_key' # Connect with voice mode ws_url = f"wss://api.autessa.com/ws/clients/agents/execute?authorization={api_key}&resourceId={agent_id}&voiceModeEnabled=true" ws = websocket.create_connection(ws_url) # Receive connection status status = json.loads(ws.recv()) conversation_id = status['conversationId'] print(f"Connected: {conversation_id}") # Send voice establishment message ws.send(json.dumps({ "messageType": "VOICE", "agentId": agent_id, "audioFormat": { "sampleRate": 16000, "sampleSizeInBits": 32, "channels": 1, "signed": True, "bigEndian": False }, "outputType": "AUDIO", "environmentVariables": {}, "promptTemplateVariables": {} })) print("Voice mode established") # Now stream audio bytes # See Python audio recording implementation in docs while True: # Receive audio or text responses response = json.loads(ws.recv()) if 'streamedOutput' in response: output = response['streamedOutput'] if output['outputType'] == 'AUDIO': print(f"Agent: {output['transcription']}") # Process audio chunk # See: https://docs.autessa.com/api/audio-playback ws.close() ``` > **Voice Mode Protocol:** > 1. Connect with `voiceModeEnabled=true` > 2. Wait for connection status message > 3. Send VOICE establishment message with audio format > 4. Stream raw audio bytes as binary WebSocket messages > 5. Receive streamed audio chunks in real-time > > For complete implementation with Voice Activity Detection and audio utilities, see the [Audio Recording](/docs/api/audio-record.md#real-time-voice-mode-websocket) and [Audio Playback](/docs/api/audio-playback.md#real-time-voice-mode-playback) guides. #### Voice Establishment Message After connecting, send this message to establish voice mode: ```json title="VOICE Message" { "messageType": "VOICE", "agentId": "V1StGXR8Z5", "audioFormat": { "sampleRate": 16000, "sampleSizeInBits": 32, "channels": 1, "signed": true, "bigEndian": false }, "outputType": "AUDIO", "environmentVariables": {}, "promptTemplateVariables": {} } ``` **After sending the establishment message:** - Send raw audio bytes as **binary** WebSocket messages (ArrayBuffer/bytes) - Receive JSON messages with audio chunks and transcriptions - Audio format: Float32Array converted to ArrayBuffer (32-bit float, mono, 16kHz) #### Audio Streaming Format **Input Audio (Client → Server):** - Send raw ArrayBuffer from Float32Array - 16kHz sample rate, 32-bit float, mono - Continuous streaming (detected via Voice Activity Detection) **Output Audio (Server → Client):** ```json { "streamedOutput": { "outputType": "AUDIO", "sampleRate": 16000, "base64Audio": "UklGRiQAAABXQVZF...", "transcription": "Hello! How can I help you today?", "isFinalChunk": false } } ``` #### Complete Voice Mode Example This example combines everything for a working voice conversation: ```typescript title="Complete TypeScript Example" class VoiceConversation { private agentId: string private apiKey: string private ws: WebSocket | null = null private recorder: AudioRecorder private audioProcessor: AudioProcessor private conversationId: string | null = null constructor(agentId: string, apiKey: string) { this.agentId = agentId this.apiKey = apiKey this.recorder = new AudioRecorder(16000) this.audioProcessor = new AudioProcessor() this.setupCallbacks() } private setupCallbacks() { // Handle user speech detection this.recorder.onSpeechStart = () => { console.log('👤 User speaking...') this.audioProcessor.setInterrupted(true) } this.recorder.onSpeechEnd = () => { console.log('👤 User stopped') this.audioProcessor.setInterrupted(false) } } async start() { const wsUrl = `wss://api.autessa.com/ws/clients/agents/execute?authorization=${this.apiKey}&resourceId=${this.agentId}&voiceModeEnabled=true` this.ws = new WebSocket(wsUrl) this.ws.onopen = () => { console.log('✅ Connected') } this.ws.onmessage = async (event) => { const data = JSON.parse(event.data) // Connection status if (data.status) { this.conversationId = data.conversationId console.log('💬 Conversation:', this.conversationId) // Establish voice mode await this.recorder.startContinuousRecording(wsUrl, { outputType: 'AUDIO', agentId: this.agentId, environmentVariables: {}, promptTemplateVariables: {} }) console.log('🎤 Voice mode active') return } // Audio responses if (data.streamedOutput?.outputType === 'AUDIO') { const { base64Audio, sampleRate, transcription, isFinalChunk } = data.streamedOutput console.log('🤖 Agent:', transcription) // Play audio await this.audioProcessor.processAudioChunk(base64Audio, sampleRate, transcription) const status = this.audioProcessor.getStatus() if (status.totalItems === 1 && !status.isPlaying) { this.audioProcessor.play() } if (isFinalChunk) { console.log('🤖 Agent finished') } } } this.ws.onerror = (error) => { console.error('❌ Error:', error) } this.ws.onclose = () => { console.log('👋 Disconnected') this.cleanup() } } cleanup() { this.recorder.stopRecording() this.audioProcessor.clear() if (this.ws) { this.ws.close() this.ws = null } } } // Usage const conversation = new VoiceConversation('V1StGXR8Z5', 'your_api_key') await conversation.start() // Later: cleanup conversation.cleanup() ``` > **Production Considerations:** > > 1. **Interruption Handling**: The example above handles user interruptions by pausing agent audio when the user starts speaking > 2. **Voice Activity Detection**: The AudioRecorder includes VAD to detect speech vs silence > 3. **Memory Management**: Always call cleanup methods to free blob URLs and close connections > 4. **Error Handling**: Implement reconnection logic for production use > 5. **Audio Format**: Use 16kHz, mono, Float32 for recording; receive Int16 PCM for playback > > For complete utility class implementations, see: > > - [Audio Recording Guide](/docs/api/audio-record.md) > - [Audio Playback Guide](/docs/api/audio-playback.md) --- ## Related - [Agents guide](/docs/guide/agentic/agents.md) - [Manager API](/docs/api/managers.md) - [Tool Execution API](/docs/api/tools.md) - [Audio Recording](/docs/api/audio-record.md) and [Audio Playback](/docs/api/audio-playback.md) - [API keys](/docs/guide/api-management/api-keys.md) and [Authentication](/docs/guide/authentication.md) --- # Audio Recording Source: https://docs.autessa.com/docs/api/audio-record > Complete guide for recording audio with Autessa - real-time voice mode streaming and multimodal audio upload. # Audio Recording Learn how to record and send audio to Autessa agents using two different approaches: real-time voice mode for conversational AI and multimodal audio upload for asynchronous processing. --- ## Overview Autessa supports two audio input methods: 1. **Real-time Voice Mode**: Stream audio continuously via WebSocket for conversational experiences with `voiceModeEnabled=true` 2. **Multimodal Audio**: Record audio, upload to S3, and send as part of a multimodal request Choose real-time voice mode for interactive conversations with low latency. Use multimodal audio when you need to process pre-recorded audio or don't require real-time interaction. --- ## Real-time Voice Mode (WebSocket) Real-time voice mode enables continuous bidirectional audio streaming for conversational AI experiences. ### Connection Setup Connect to the WebSocket endpoint with the `voiceModeEnabled=true` parameter: ```javascript const agentId = 123 const apiKey = 'your_api_key' const wsUrl = `wss://api.autessa.com/ws/clients/agents/execute?authorization=${apiKey}&resourceId=${agentId}&voiceModeEnabled=true` const websocket = new WebSocket(wsUrl) ``` ### Voice Establishment Message After the WebSocket connection opens, send a voice establishment message to configure audio format and agent parameters: ```javascript websocket.onopen = () => { const establishmentMessage = { messageType: 'VOICE', outputType: 'AUDIO', // or 'TEXT' for transcription only agentId: 123, audioFormat: { sampleRate: 16000, sampleSizeInBits: 32, channels: 1, signed: true, bigEndian: false }, environmentVariables: {}, promptTemplateVariables: {} } websocket.send(JSON.stringify(establishmentMessage)) console.log('Voice mode established') } ``` ### AudioRecorder Utility Class Here's a production-ready utility class for continuous audio recording with WebSocket streaming: ```typescript private sampleRate: number private audioContext: AudioContext | null = null private processor: ScriptProcessorNode | null = null private source: MediaStreamAudioSourceNode | null = null private stream: MediaStream | null = null private isRecording: boolean = false private websocket: WebSocket | null = null private websocketReady: boolean = false // Voice Activity Detection private vadThreshold: number = 0.01 private vadDebounceMs: number = 300 private isSpeaking: boolean = false private silenceStart: number = 0 private rmsBuffer: number[] = [] private readonly rmsBufferSize: number = 5 // Audio accumulation for JSON export private accumulatedAudio: Float32Array[] = [] private isAccumulating: boolean = false constructor(sampleRate: number = 16000) { this.sampleRate = sampleRate } /** * Start continuous recording with optional WebSocket streaming */ async startContinuousRecording( websocketUrl: string | null = null, agentConfig: { outputType: string agentId: number environmentVariables?: Record promptTemplateVariables?: Record } | null = null ): Promise { try { // Set up WebSocket if URL provided if (websocketUrl) { this.websocket = new WebSocket(websocketUrl) this.websocketReady = false await new Promise((resolve, reject) => { this.websocket!.onopen = () => { console.log('WebSocket connected') // Send voice establishment message if (agentConfig) { const establishmentPayload = { messageType: 'VOICE', outputType: agentConfig.outputType, agentId: agentConfig.agentId, audioFormat: { sampleRate: this.sampleRate, sampleSizeInBits: 32, channels: 1, signed: true, bigEndian: false }, environmentVariables: agentConfig.environmentVariables || {}, promptTemplateVariables: agentConfig.promptTemplateVariables || {} } this.websocket!.send(JSON.stringify(establishmentPayload)) console.log('Sent voice establishment message') } else { this.websocketReady = true } resolve(undefined) } this.websocket!.onmessage = (event) => { if (!this.websocketReady) { console.log('WebSocket ready to receive audio') this.websocketReady = true } this.handleWebSocketMessage(event) } this.websocket!.onerror = (error) => reject(error) }) } // Request microphone access this.stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } }) // Create audio context with specified sample rate this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)({ sampleRate: this.sampleRate }) this.source = this.audioContext.createMediaStreamSource(this.stream) // Create processor with 4096 buffer size const bufferSize = 4096 this.processor = this.audioContext.createScriptProcessor(bufferSize, 1, 1) this.source.connect(this.processor) this.processor.connect(this.audioContext.destination) // Process audio in real-time this.processor.onaudioprocess = (event) => { const float32Samples = event.inputBuffer.getChannelData(0) // Detect voice activity this.detectVoiceActivity(float32Samples) // Send to WebSocket if ready if (this.websocket && this.websocketReady && this.isSpeaking) { this.sendAudioViaWebSocket(float32Samples) } // Accumulate for JSON export if requested if (this.isAccumulating) { this.accumulatedAudio.push(new Float32Array(float32Samples)) } } this.isRecording = true console.log('Continuous recording started') } catch (error) { console.error('Error starting recording:', error) throw error } } /** * Detect voice activity using RMS energy */ private detectVoiceActivity(samples: Float32Array): void { // Calculate RMS (Root Mean Square) let sum = 0 for (let i = 0; i < samples.length; i++) { sum += samples[i] * samples[i] } const rms = Math.sqrt(sum / samples.length) // Add to smoothing buffer this.rmsBuffer.push(rms) if (this.rmsBuffer.length > this.rmsBufferSize) { this.rmsBuffer.shift() } // Calculate smoothed RMS const smoothedRms = this.rmsBuffer.reduce((a, b) => a + b, 0) / this.rmsBuffer.length const now = Date.now() if (smoothedRms > this.vadThreshold) { // Voice detected if (!this.isSpeaking) { this.isSpeaking = true this.onSpeechStart?.() } this.silenceStart = now } else { // Silence detected if (this.isSpeaking && (now - this.silenceStart) > this.vadDebounceMs) { this.isSpeaking = false this.onSpeechEnd?.() } } } /** * Send audio data via WebSocket as ArrayBuffer */ private sendAudioViaWebSocket(float32Array: Float32Array): void { if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN || !this.websocketReady) { return } try { // Send as raw ArrayBuffer (Float32Array buffer) this.websocket.send(float32Array.buffer) } catch (error) { console.error('Error sending audio via WebSocket:', error) } } /** * Handle incoming WebSocket messages */ private handleWebSocketMessage(event: MessageEvent): void { // Override this method or set onMessage callback console.log('WebSocket message received:', event.data) } /** * Stop recording and clean up resources */ stopRecording(): void { this.isRecording = false this.isAccumulating = false this.websocketReady = false this.isSpeaking = false if (this.processor) { this.processor.disconnect() this.processor = null } if (this.source) { this.source.disconnect() this.source = null } if (this.audioContext) { this.audioContext.close() this.audioContext = null } if (this.stream) { this.stream.getTracks().forEach(track => track.stop()) this.stream = null } if (this.websocket) { this.websocket.close() this.websocket = null } this.accumulatedAudio = [] this.rmsBuffer = [] console.log('Recording stopped and cleaned up') } /** * Check if currently recording */ isCurrentlyRecording(): boolean { return this.isRecording } /** * Check if voice is detected */ isCurrentlySpeaking(): boolean { return this.isSpeaking } // Optional callbacks onSpeechStart?: () => void onSpeechEnd?: () => void onMessage?: (event: MessageEvent) => void } ``` ### Usage Example: Real-time Voice Mode ```typescript // Initialize recorder const recorder = new AudioRecorder(16000) // Set up callbacks recorder.onSpeechStart = () => { console.log('User started speaking') } recorder.onSpeechEnd = () => { console.log('User stopped speaking') } recorder.onMessage = (event) => { try { const response = JSON.parse(event.data) console.log('Received:', response) // Handle audio chunks - see Audio Playback documentation } catch (e) { // Binary audio data } } // Start recording with WebSocket streaming const agentId = 123 const apiKey = 'your_api_key' const wsUrl = `wss://api.autessa.com/ws/clients/agents/execute?authorization=${apiKey}&resourceId=${agentId}&voiceModeEnabled=true` await recorder.startContinuousRecording(wsUrl, { outputType: 'AUDIO', agentId: agentId, environmentVariables: {}, promptTemplateVariables: {} }) // Later: stop recording recorder.stopRecording() ``` For handling received audio chunks, see the [Audio Playback](/docs/api/audio-playback.md) documentation. --- ## Multimodal Audio Mode (S3 Upload) For non-real-time use cases, record audio, convert to WAV, upload to S3, and send the S3 URI in a multimodal request. ### Step 1: Record and Accumulate Audio ```typescript class AudioAccumulator { private sampleRate: number private audioContext: AudioContext | null = null private processor: ScriptProcessorNode | null = null private source: MediaStreamAudioSourceNode | null = null private stream: MediaStream | null = null private isRecording: boolean = false private chunks: Float32Array[] = [] constructor(sampleRate: number = 16000) { this.sampleRate = sampleRate } async startRecording(): Promise { try { this.stream = await navigator.mediaDevices.getUserMedia({ audio: true }) this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)({ sampleRate: this.sampleRate }) this.source = this.audioContext.createMediaStreamSource(this.stream) this.processor = this.audioContext.createScriptProcessor(4096, 1, 1) this.source.connect(this.processor) this.processor.connect(this.audioContext.destination) this.processor.onaudioprocess = (event) => { const samples = event.inputBuffer.getChannelData(0) this.chunks.push(new Float32Array(samples)) } this.isRecording = true this.chunks = [] } catch (error) { console.error('Error starting recording:', error) throw error } } stopRecording(): Float32Array { this.isRecording = false if (this.processor) this.processor.disconnect() if (this.source) this.source.disconnect() if (this.audioContext) this.audioContext.close() if (this.stream) { this.stream.getTracks().forEach(track => track.stop()) } // Combine all chunks const totalLength = this.chunks.reduce((sum, chunk) => sum + chunk.length, 0) const combined = new Float32Array(totalLength) let offset = 0 for (const chunk of this.chunks) { combined.set(chunk, offset) offset += chunk.length } return combined } } ``` ### Step 2: Convert to WAV Format ```typescript class WavConverter { static float32ToWav(float32Array: Float32Array, sampleRate: number): Blob { // Convert Float32 to Int16 const int16Array = new Int16Array(float32Array.length) for (let i = 0; i < float32Array.length; i++) { const s = Math.max(-1, Math.min(1, float32Array[i])) int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF } const buffer = new ArrayBuffer(44 + int16Array.length * 2) const view = new DataView(buffer) // WAV header const writeString = (offset: number, str: string) => { for (let i = 0; i < str.length; i++) { view.setUint8(offset + i, str.charCodeAt(i)) } } writeString(0, 'RIFF') view.setUint32(4, 36 + int16Array.length * 2, true) writeString(8, 'WAVE') writeString(12, 'fmt ') view.setUint32(16, 16, true) view.setUint16(20, 1, true) view.setUint16(22, 1, true) view.setUint32(24, sampleRate, true) view.setUint32(28, sampleRate * 2, true) view.setUint16(32, 2, true) view.setUint16(34, 16, true) writeString(36, 'data') view.setUint32(40, int16Array.length * 2, true) // Write audio data const wavData = new Uint8Array(buffer) wavData.set(new Uint8Array(int16Array.buffer), 44) return new Blob([wavData], { type: 'audio/wav' }) } } ``` ### Step 3: Upload to S3 ```typescript class AudioUploader { private apiKey: string private baseUrl: string constructor(apiKey: string, baseUrl: string = 'https://api.autessa.com') { this.apiKey = apiKey this.baseUrl = baseUrl } async uploadAudio(agentId: number, wavBlob: Blob): Promise { // Step 1: Get presigned URL const presignedResponse = await fetch( `${this.baseUrl}/clients/agents/generate-audio-upload-link?resourceId=${agentId}`, { method: 'POST', headers: { 'Authorization': this.apiKey, 'Content-Type': 'application/json' } } ) if (!presignedResponse.ok) { throw new Error('Failed to get presigned URL') } const { uploadUrl, s3Uri } = await presignedResponse.json() // Step 2: Upload to S3 const uploadResponse = await fetch(uploadUrl, { method: 'PUT', body: wavBlob, headers: { 'Content-Type': 'audio/wav' } }) if (!uploadResponse.ok) { throw new Error('Failed to upload to S3') } // Step 3: Return S3 URI return s3Uri } } ``` ### Step 4: Use in Multimodal Request ```typescript // Complete workflow async function recordAndSendAudio(agentId: number, apiKey: string) { // 1. Record audio const accumulator = new AudioAccumulator(16000) await accumulator.startRecording() console.log('Recording... (press any key to stop)') // ... wait for user to finish ... const audioData = accumulator.stopRecording() // 2. Convert to WAV const wavBlob = WavConverter.float32ToWav(audioData, 16000) // 3. Upload to S3 const uploader = new AudioUploader(apiKey) const s3Uri = await uploader.uploadAudio(agentId, wavBlob) console.log('Audio uploaded:', s3Uri) // 4. Send multimodal request const response = await fetch( `https://api.autessa.com/clients/agents/execute?resourceId=${agentId}`, { method: 'POST', headers: { 'Authorization': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: agentId, input: [ { inputType: 'AUDIO', s3Uri: s3Uri, audioFormat: { sampleRate: 16000, sampleSizeInBits: 16, channels: 1, signed: true, bigEndian: false } } ], executionOutputMode: 'TEXT' // or 'AUDIO' }) } ) const result = await response.json() console.log('Agent response:', result) } ``` --- ## Audio Format Specifications All audio in Autessa follows these specifications: - `sampleRate` (number) — 16000 Hz (16 kHz) - `channels` (number) — 1 (mono) - `sampleSizeInBits` (number) — 32-bit for recording (Float32), 16-bit for WAV export (Int16) - `signed` (boolean) — true - `bigEndian` (boolean) — false (little-endian) --- ## Best Practices ### Voice Activity Detection The AudioRecorder class includes VAD to detect when the user is speaking: - **Threshold**: Adjust `vadThreshold` (default: 0.01) for sensitivity - **Debounce**: Set `vadDebounceMs` (default: 300ms) to avoid flickering - **Callbacks**: Use `onSpeechStart` and `onSpeechEnd` for UI updates ### Memory Management - Always call `stopRecording()` when done to free resources - For long recordings, consider chunked processing - Use blob URLs sparingly and revoke them after use ### Error Handling ```typescript try { await recorder.startContinuousRecording(wsUrl, config) } catch (error) { if (error.name === 'NotAllowedError') { console.error('Microphone permission denied') } else if (error.name === 'NotFoundError') { console.error('No microphone found') } else { console.error('Failed to start recording:', error) } } ``` ### WebSocket Reconnection For production use, implement reconnection logic: ```typescript let reconnectAttempts = 0 const maxReconnectAttempts = 3 websocket.onclose = () => { if (reconnectAttempts < maxReconnectAttempts) { reconnectAttempts++ setTimeout(() => { console.log(`Reconnecting... (${reconnectAttempts}/${maxReconnectAttempts})`) // Restart recording }, 1000 * reconnectAttempts) } } ``` --- ## Next Steps - Learn how to play received audio in the [Audio Playback](/docs/api/audio-playback.md) guide - See complete examples in the [Agent API](/docs/api/agents.md#real-time-voice-mode) documentation - Explore multimodal capabilities in the [Agent API](/docs/api/agents.md) reference --- # Audio Playback Source: https://docs.autessa.com/docs/api/audio-playback > Complete guide for playing audio received from Autessa - real-time voice mode and standard audio responses. # Audio Playback Learn how to play audio received from Autessa agents in both real-time voice mode and standard audio responses. --- ## Overview Autessa delivers audio in two contexts: 1. **Real-time Voice Mode**: Receive streaming audio chunks via WebSocket during conversational interactions 2. **Standard Audio Response**: Receive audio in HTTP responses from agent executions Both use the same audio format (16kHz, base64-encoded PCM) and the same utility classes for playback. --- ## Audio Response Format Audio received from Autessa has the following structure: ```json { "streamedOutput": { "sampleRate": 16000, "base64Audio": "AAAA...", "transcription": "Would you like any tips on running routes or safety?", "isFinalChunk": false, "outputType": "AUDIO" } } ``` - `sampleRate` (number) — Always 16000 Hz (16 kHz) - `base64Audio` (string) — Base64-encoded PCM audio data (Int16, mono, little-endian) - `transcription` (string) — Text transcription of the audio chunk - `isFinalChunk` (boolean) — Indicates if this is the last chunk in the stream - `outputType` (string) — "AUDIO" for audio responses, "TEXT" for text-only --- ## Utility Classes ### WavUtils - WAV File Creation Converts PCM data to playable WAV format: ```typescript private static writeString(view: DataView, offset: number, str: string): void { for (let i = 0; i < str.length; i++) { view.setUint8(offset + i, str.charCodeAt(i)) } } /** * Create WAV file from PCM data */ static createWavFromPCM(pcmData: Uint8Array, sampleRate: number): Uint8Array { const numChannels = 1 // Mono const bitsPerSample = 16 const byteRate = sampleRate * numChannels * bitsPerSample / 8 const blockAlign = numChannels * bitsPerSample / 8 const dataSize = pcmData.length const fileSize = 44 + dataSize // WAV header is 44 bytes const wavBuffer = new ArrayBuffer(fileSize) const view = new DataView(wavBuffer) // RIFF chunk descriptor this.writeString(view, 0, 'RIFF') view.setUint32(4, fileSize - 8, true) this.writeString(view, 8, 'WAVE') // fmt sub-chunk this.writeString(view, 12, 'fmt ') view.setUint32(16, 16, true) view.setUint16(20, 1, true) view.setUint16(22, numChannels, true) view.setUint32(24, sampleRate, true) view.setUint32(28, byteRate, true) view.setUint16(32, blockAlign, true) view.setUint16(34, bitsPerSample, true) // data sub-chunk this.writeString(view, 36, 'data') view.setUint32(40, dataSize, true) // Copy PCM data const wavData = new Uint8Array(wavBuffer) wavData.set(pcmData, 44) return wavData } /** * Create WAV from Float32Array */ static createWavFromFloat32(float32Array: Float32Array, sampleRate: number): Uint8Array { // Convert Float32 to Int16 const int16Array = new Int16Array(float32Array.length) for (let i = 0; i < float32Array.length; i++) { const s = Math.max(-1, Math.min(1, float32Array[i])) int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF } const pcmBytes = new Uint8Array(int16Array.buffer) return this.createWavFromPCM(pcmBytes, sampleRate) } } ``` ### DataUtils - Data Conversion Handles base64 decoding and blob URL creation: ```typescript /** * Convert base64 string to Uint8Array */ static base64ToUint8Array(base64: string): Uint8Array { if (!base64 || base64.length === 0) { throw new Error('Invalid base64 string provided') } const binaryString = atob(base64) const bytes = new Uint8Array(binaryString.length) for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i) } return bytes } /** * Create blob URL for audio playback */ static createAudioBlobUrl(audioData: Uint8Array, mimeType: string = 'audio/wav'): string { const blob = new Blob([audioData], { type: mimeType }) return URL.createObjectURL(blob) } /** * Revoke blob URL to free memory */ static revokeAudioBlobUrl(url: string): void { if (url.startsWith('blob:')) { URL.revokeObjectURL(url) } } /** * Convert Float32Array to Int16Array for audio */ static convertFloat32ToInt16(float32Array: Float32Array): Int16Array { const int16Array = new Int16Array(float32Array.length) for (let i = 0; i < float32Array.length; i++) { const s = Math.max(-1, Math.min(1, float32Array[i])) int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF } return int16Array } } ``` ### AudioQueue - Queue Management Manages sequential playback of audio chunks: ```typescript url: string transcription: string timestamp: number sampleRate: number } isPlaying: boolean currentIndex: number totalItems: number currentItem: AudioQueueItem | null } private queue: AudioQueueItem[] = [] private currentIndex: number = -1 private audioElement: HTMLAudioElement | null = null private isPlaying: boolean = false private isInterrupted: boolean = false constructor(audioElement?: HTMLAudioElement) { this.audioElement = audioElement || this.createAudioElement() this.setupEventListeners() } private createAudioElement(): HTMLAudioElement { return new Audio() } private setupEventListeners(): void { if (!this.audioElement) return this.audioElement.addEventListener('ended', () => { this.playNext() }) this.audioElement.addEventListener('error', (error) => { console.error('Audio playback error:', error) this.isPlaying = false this.playNext() }) } /** * Add audio item to queue */ add(item: AudioQueueItem): void { this.queue.push(item) } /** * Play next item in queue */ playNext(): void { // Don't advance if interrupted if (this.isInterrupted) { this.isPlaying = false return } const nextIndex = this.currentIndex + 1 if (nextIndex < this.queue.length && this.audioElement) { const nextAudio = this.queue[nextIndex] this.audioElement.src = nextAudio.url this.currentIndex = nextIndex this.isPlaying = true this.audioElement.play().catch(error => { console.error('Error playing audio chunk:', error) this.isPlaying = false this.playNext() }) } else { this.isPlaying = false } } /** * Start playback from beginning */ play(): void { if (this.queue.length > 0) { this.isInterrupted = false this.currentIndex = -1 this.playNext() } } /** * Pause playback */ pause(): void { if (this.audioElement) { this.audioElement.pause() } this.isPlaying = false } /** * Stop playback and reset */ stop(): void { if (this.audioElement) { this.audioElement.pause() this.audioElement.currentTime = 0 } this.isPlaying = false this.currentIndex = -1 this.isInterrupted = false } /** * Set interrupted state (user started speaking) */ setInterrupted(interrupted: boolean): void { this.isInterrupted = interrupted if (interrupted && this.isPlaying) { this.pause() } } /** * Clear queue and free memory */ clear(): void { this.stop() this.queue.forEach(item => { if (item.url.startsWith('blob:')) { DataUtils.revokeAudioBlobUrl(item.url) } }) this.queue = [] } /** * Discard queued items (called when interruption ends) */ discardQueue(): void { // Keep currently playing item, discard rest const currentItem = this.currentIndex >= 0 ? this.queue[this.currentIndex] : null // Free memory for discarded items this.queue.forEach((item, index) => { if (index !== this.currentIndex && item.url.startsWith('blob:')) { DataUtils.revokeAudioBlobUrl(item.url) } }) this.queue = currentItem ? [currentItem] : [] this.currentIndex = currentItem ? 0 : -1 } /** * Get current playback status */ getStatus(): AudioQueueStatus { return { isPlaying: this.isPlaying, currentIndex: this.currentIndex, totalItems: this.queue.length, currentItem: this.currentIndex >= 0 ? this.queue[this.currentIndex] : null } } /** * Check if interrupted */ isCurrentlyInterrupted(): boolean { return this.isInterrupted } } ``` ### AudioProcessor - Main Playback Class Combines all utilities for complete audio playback: ```typescript private audioQueue: AudioQueue constructor(audioElement?: HTMLAudioElement) { this.audioQueue = new AudioQueue(audioElement) } /** * Process incoming audio chunk from Autessa */ async processAudioChunk( base64Audio: string, sampleRate: number, transcription?: string ): Promise { try { const pcmBytes = DataUtils.base64ToUint8Array(base64Audio) const wavData = WavUtils.createWavFromPCM(pcmBytes, sampleRate) const url = DataUtils.createAudioBlobUrl(wavData) const audioItem: AudioQueueItem = { url, transcription: transcription || '', timestamp: Date.now(), sampleRate } this.audioQueue.add(audioItem) return audioItem } catch (error) { console.error('Error processing audio chunk:', error) return null } } /** * Start playback */ play(): void { this.audioQueue.play() } /** * Pause playback */ pause(): void { this.audioQueue.pause() } /** * Stop playback */ stop(): void { this.audioQueue.stop() } /** * Set interrupted state (user is speaking) */ setInterrupted(interrupted: boolean): void { this.audioQueue.setInterrupted(interrupted) // When interruption ends, discard queued audio if (!interrupted) { this.audioQueue.discardQueue() } } /** * Clear all audio and free memory */ clear(): void { this.audioQueue.clear() } /** * Get playback status */ getStatus(): AudioQueueStatus { return this.audioQueue.getStatus() } /** * Check if interrupted */ isInterrupted(): boolean { return this.audioQueue.isCurrentlyInterrupted() } } ``` --- ## Usage Examples ### Real-time Voice Mode Playback For WebSocket voice mode, process incoming audio chunks and handle interruptions: ```typescript // Initialize audio processor const audioProcessor = new AudioProcessor() // Set up WebSocket const agentId = 123 const apiKey = 'your_api_key' const wsUrl = `wss://api.autessa.com/ws/clients/agents/execute?authorization=${apiKey}&resourceId=${agentId}&voiceModeEnabled=true` const websocket = new WebSocket(wsUrl) // Track user speech for interruption handling let userIsSpeaking = false websocket.onmessage = async (event) => { try { const response = JSON.parse(event.data) if (response.streamedOutput?.outputType === 'AUDIO') { const { base64Audio, sampleRate, transcription, isFinalChunk } = response.streamedOutput // Process audio chunk const audioItem = await audioProcessor.processAudioChunk( base64Audio, sampleRate, transcription ) if (audioItem) { console.log('Processed:', transcription) // Start playing if this is the first chunk and user isn't speaking const status = audioProcessor.getStatus() if (status.totalItems === 1 && !status.isPlaying && !userIsSpeaking) { audioProcessor.play() } if (isFinalChunk) { console.log('Audio stream complete') } } } } catch (error) { console.error('Error processing audio:', error) } } // Handle user speech detection (from AudioRecorder) recorder.onSpeechStart = () => { userIsSpeaking = true audioProcessor.setInterrupted(true) console.log('User started speaking - pausing agent audio') } recorder.onSpeechEnd = () => { userIsSpeaking = false audioProcessor.setInterrupted(false) console.log('User stopped speaking - discarding queued agent audio') } // Cleanup when done websocket.onclose = () => { audioProcessor.clear() console.log('Audio processor cleaned up') } ``` ### Standard Audio Response Playback For HTTP responses with audio: ```typescript const audioProcessor = new AudioProcessor() // Execute agent with audio output const response = await fetch( `https://api.autessa.com/clients/agents/execute?resourceId=${agentId}`, { method: 'POST', headers: { 'Authorization': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: agentId, input: [{ inputType: 'TEXT', content: 'Hello' }], executionOutputMode: 'AUDIO' }) } ) const result = await response.json() // Process audio chunks from response if (result.streamedOutput?.outputType === 'AUDIO') { await audioProcessor.processAudioChunk( result.streamedOutput.base64Audio, result.streamedOutput.sampleRate, result.streamedOutput.transcription ) audioProcessor.play() } // Cleanup when done audioProcessor.clear() ``` ### Processing Multiple Chunks When receiving multiple chunks in sequence: ```typescript async function handleAudioStream(chunks: any[]) { const audioProcessor = new AudioProcessor() for (const chunk of chunks) { if (chunk.streamedOutput?.outputType === 'AUDIO') { await audioProcessor.processAudioChunk( chunk.streamedOutput.base64Audio, chunk.streamedOutput.sampleRate, chunk.streamedOutput.transcription ) } } // Start playback after all chunks are queued audioProcessor.play() // Wait for playback to complete return new Promise((resolve) => { const checkStatus = setInterval(() => { const status = audioProcessor.getStatus() if (!status.isPlaying && status.currentIndex >= status.totalItems - 1) { clearInterval(checkStatus) audioProcessor.clear() resolve(undefined) } }, 100) }) } ``` --- ## Interruption Handling For conversational AI, handle user interruptions properly: ### When User Starts Speaking ```typescript // Pause agent audio immediately audioProcessor.setInterrupted(true) // Update UI to show user is speaking updateUI({ userSpeaking: true, agentSpeaking: false }) ``` ### When User Stops Speaking ```typescript // Discard queued agent audio (it's outdated) audioProcessor.setInterrupted(false) // Update UI updateUI({ userSpeaking: false }) ``` ### Why Discard Queued Audio? When the user interrupts, the queued audio becomes contextually irrelevant. The `setInterrupted(false)` method automatically calls `discardQueue()` to: - Stop playing current audio - Remove all queued items except the current one - Free memory by revoking blob URLs --- ## Memory Management ### Automatic Cleanup The AudioProcessor automatically manages memory: ```typescript // Blob URLs are created when processing chunks const audioItem = await audioProcessor.processAudioChunk(base64Audio, sampleRate) // audioItem.url is a blob URL like "blob:https://example.com/uuid" // Blob URLs are automatically revoked when clearing audioProcessor.clear() // Frees all blob URLs ``` ### Manual Cleanup For fine-grained control: ```typescript // Clear specific items DataUtils.revokeAudioBlobUrl(audioItem.url) // Clear entire queue audioProcessor.clear() ``` ### Best Practices 1. **Call `clear()` when done**: Always clean up after playback completes 2. **Handle interruptions**: Use `setInterrupted()` to manage user speech 3. **Monitor memory**: For long sessions, periodically clear old audio 4. **Check playback status**: Use `getStatus()` to know when to clean up --- ## Error Handling ### Robust Audio Processing ```typescript async function robustAudioProcessing(streamedData: any) { try { if (streamedData.streamedOutput?.outputType === 'AUDIO') { const { base64Audio, sampleRate, transcription, isFinalChunk } = streamedData.streamedOutput // Validate required fields if (!base64Audio || !sampleRate) { throw new Error('Missing required audio data') } const audioItem = await audioProcessor.processAudioChunk( base64Audio, sampleRate, transcription ) if (!audioItem) { throw new Error('Failed to process audio chunk') } // Auto-play first chunk const status = audioProcessor.getStatus() if (status.totalItems === 1 && !status.isPlaying) { audioProcessor.play() } if (isFinalChunk) { console.log('Audio stream complete') } return audioItem } } catch (error) { console.error('Error processing audio:', error) // Cleanup on error audioProcessor.clear() return null } } ``` ### Handle Playback Errors ```typescript // The AudioQueue automatically handles playback errors // by logging and attempting to play the next chunk // You can also monitor the audio element directly const audioElement = new Audio() const audioProcessor = new AudioProcessor(audioElement) audioElement.addEventListener('error', (e) => { console.error('Playback error:', e) // Implement retry logic or user notification }) ``` --- ## Advanced Usage ### Custom Audio Element Use a custom audio element for more control: ```typescript const audioElement = document.getElementById('my-audio') as HTMLAudioElement const audioProcessor = new AudioProcessor(audioElement) // Now you have full control over the audio element audioElement.volume = 0.8 audioElement.playbackRate = 1.2 ``` ### Monitor Playback Progress ```typescript const status = audioProcessor.getStatus() console.log(`Playing: ${status.isPlaying}`) console.log(`Current: ${status.currentIndex + 1} of ${status.totalItems}`) console.log(`Transcription: ${status.currentItem?.transcription}`) ``` ### Pause and Resume ```typescript // Pause playback audioProcessor.pause() // Resume playback audioProcessor.play() // Stop and reset audioProcessor.stop() ``` --- ## Performance Optimization ### Chunked Processing For real-time streams, process chunks as they arrive: ```typescript websocket.onmessage = async (event) => { const response = JSON.parse(event.data) // Process immediately without waiting if (response.streamedOutput?.outputType === 'AUDIO') { audioProcessor.processAudioChunk( response.streamedOutput.base64Audio, response.streamedOutput.sampleRate, response.streamedOutput.transcription ).then(audioItem => { // Auto-start on first chunk const status = audioProcessor.getStatus() if (status.totalItems === 1 && !status.isPlaying) { audioProcessor.play() } }) } } ``` ### Limit Queue Size For long-running sessions, limit queue size: ```typescript class AudioProcessor { private maxQueueSize: number = 50 async processAudioChunk(...args) { const item = await super.processAudioChunk(...args) // Trim old items if queue too large const status = this.getStatus() if (status.totalItems > this.maxQueueSize) { // Implementation would remove old items // and revoke their blob URLs } return item } } ``` --- ## Next Steps - Learn how to record audio in the [Audio Recording](/docs/api/audio-record.md) guide - See complete voice mode examples in the [Agent API](/docs/api/agents.md#real-time-voice-mode) documentation - Explore the full Agent API in the [Agent API](/docs/api/agents.md) reference --- # Manager API Source: https://docs.autessa.com/docs/api/managers > Complete API reference for executing managers, orchestrating multi-agent conversations, and streaming sub-agent activity in real time. # Manager API The Manager API lets you run conversations with managers - orchestrators that plan a task, delegate work to their configured sub-agents, and report back to the user. You get the same conversation lifecycle as the Agent API, plus structured visibility into every sub-agent invocation. > All Manager API requests require the `resourceId` query parameter set to your manager ID and an `Authorization` header with your API key. --- ## Base URL ``` REST API: https://api.autessa.com/clients/managers WebSocket: wss://api.autessa.com/ws/clients/managers/execute ``` --- ## Managers vs. Agents A manager does not answer the user directly from a single prompt. Instead it: 1. Reasons about the request and breaks it into tasks 2. Invokes one or more of its configured sub-agents, each running its own full agent conversation 3. Relays sub-agent results, asks the user for clarification when needed (`waitingForUserInput`), and iterates until the task is complete Compared to the [Agent API](/docs/api/agents.md), the request shapes are nearly identical (with `managerId` instead of `agentId`), while responses add manager-specific fields: `waitingForUserInput`, `agentInvocationStatus`, and `subAgentStreamedOutput`. Conversation logs use manager log types (`INVOKE_AGENT_REQUEST`, `INVOKE_AGENT_RESPONSE`, `USER_REQUEST`, ...) instead of agent log types. --- ## Authentication ### HTTP REST APIs Pass your API key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: your_api_key_here ``` ### WebSocket APIs Pass your API key as a query parameter: ``` authorization=your_api_key_here ``` **REST:** ```bash curl https://api.autessa.com/clients/managers/execute-create?resourceId=V1StGXR8Z5 \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" ``` **WebSocket:** ```bash websocat "wss://api.autessa.com/ws/clients/managers/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ``` --- ## REST API Endpoints ## Create Conversation **POST** `/execute-create` Creates a new conversation for multi-turn manager interactions. A conversation maintains the manager's context - including all sub-agent activity - across multiple execute requests. **Note:** Once created, a conversation is tied to a specific manager version. Even if you publish a new version, existing conversations will continue using the original version. ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `managerId` (string, required) — The ID of the manager to create a conversation for. - `version` (CustomVersion) — Optional version override. If not specified, uses the PUBLISHED version. See [Custom Version](#custom-version). ### Response - `conversationId` (string) — The unique ID for the created conversation. Use this in subsequent execute requests. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/managers/execute-create?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "managerId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/managers/execute-create?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ managerId: 'V1StGXR8Z5' }) } ); const data = await response.json(); console.log(data.conversationId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/managers/execute-create?resourceId=V1StGXR8Z5', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={'managerId': 'V1StGXR8Z5'} ) conversation_id = response.json()['conversationId'] ``` ```json title="JSON" { "conversationId": "conv_a1b2c3d4e5", "errors": [] } ``` --- ## Execute Manager **POST** `/execute` Executes a manager synchronously with multimodal input. The manager plans, invokes its sub-agents as needed, and returns the complete response once the orchestration finishes - which can take significantly longer than a single agent execution. **Note:** Synchronous execution only supports TEXT output. For real-time streaming of manager reasoning and sub-agent activity, use the WebSocket endpoint. ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `managerId` (string, required) — The ID of the manager to execute. - `conversationId` (string) — The conversation ID from `/execute-create`. Required for multi-turn conversations. - `input` — " required> Array of multimodal inputs (text, audio, images, files). Same shapes as the Agent API - see [Multimodal Input](/docs/api/agents.md#multimodal-input). - `environmentVariables` (object) — Key-value pairs for environment variables that won't be stored on the server. Forwarded to sub-agent tool executions. - `promptTemplateVariables` (object) — Key-value pairs to inject into the manager's prompt template. Variables are referenced in the manager instructions using `@@{variableName}@@` syntax. - `executionOutputMode` (string) — `"TEXT"` or `"AUDIO"` - desired output format. REST execution supports `"TEXT"` only. ### Response - `output` — "> Array of output responses from the manager. - `waitingForUserInput` (boolean) — `true` if the manager paused to ask you a clarifying question instead of completing the task. Send your answer as the next `/execute` request in the same conversation. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/managers/execute?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "managerId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5", "input": [ { "inputType": "TEXT", "content": "Research our top competitor and draft an outreach email." } ], "promptTemplateVariables": { "companyName": "Acme Corp" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/managers/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ managerId: 'V1StGXR8Z5', conversationId: 'conv_a1b2c3d4e5', input: [ { inputType: 'TEXT', content: 'Research our top competitor and draft an outreach email.' } ], promptTemplateVariables: { companyName: 'Acme Corp' } }) } ); const data = await response.json(); if (data.waitingForUserInput) { // The manager asked a question - reply with another /execute call } ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/managers/execute?resourceId=V1StGXR8Z5', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={ 'managerId': 'V1StGXR8Z5', 'conversationId': 'conv_a1b2c3d4e5', 'input': [ { 'inputType': 'TEXT', 'content': 'Research our top competitor and draft an outreach email.' } ] } ) data = response.json() ``` ```json title="JSON" { "output": [ { "outputType": "TEXT", "content": "I researched the competitor and drafted the outreach email. Here it is: ..." } ], "waitingForUserInput": false, "errors": [] } ``` --- ## Close Conversation **POST** `/execute-close` Closes an existing conversation. This triggers logging and any configured auto-evaluation for the conversation. It's safe to call this even for expired conversations. ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `managerId` (string, required) — The ID of the manager. - `conversationId` (string, required) — The conversation ID to close. ### Response - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/managers/execute-close?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "managerId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` **JavaScript:** ```javascript await fetch( 'https://api.autessa.com/clients/managers/execute-close?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ managerId: 'V1StGXR8Z5', conversationId: 'conv_a1b2c3d4e5' }) } ); ``` **Python:** ```python requests.post( 'https://api.autessa.com/clients/managers/execute-close?resourceId=V1StGXR8Z5', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={ 'managerId': 'V1StGXR8Z5', 'conversationId': 'conv_a1b2c3d4e5' } ) ``` ```json title="JSON" { "errors": [] } ``` --- ## Get Conversation **POST** `/conversation` Retrieves the complete conversation history including manager reasoning, every sub-agent invocation, and evaluation context. ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `managerId` (string, required) — The ID of the manager. - `conversationId` (string, required) — The conversation ID to retrieve. - `expandAgents` (boolean) — When `true`, each `INVOKE_AGENT_RESPONSE` log inlines the full sub-agent conversation (`invokeAgentResponse.agentContext`), so you can see the sub-agent's own tool calls and messages. Defaults to `false`. ### Response - `conversation` (ManagerConversationDto) — The complete conversation data. See [Manager Conversation Structure](#manager-conversation-structure). - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/managers/conversation?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "managerId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5", "expandAgents": false }' ``` ```json title="JSON" { "conversation": { "resourceId": "V1StGXR8Z5", "version": "1", "conversationId": "conv_a1b2c3d4e5", "apiKeyId": "key_xyz", "closeDate": null, "currentIteration": 3, "logs": [ { "logType": "USER_INPUT", "combinedMultimodalInput": [ { "inputType": "TEXT", "content": "Research our top competitor and draft an outreach email." } ] }, { "logType": "INVOKE_AGENT_REQUEST", "invokeAgentRequest": { "invocationId": "inv_9f8e7d", "toolCallId": "call_abc123", "agentId": "aB3dE5fG7h", "agentName": "ResearchAgent", "taskDescription": "Find the top competitor of Acme Corp", "variables": { "company": "Acme Corp" } } }, { "logType": "INVOKE_AGENT_RESPONSE", "invokeAgentResponse": { "invocationId": "inv_9f8e7d", "toolCallId": "call_abc123", "agentId": "aB3dE5fG7h", "agentName": "ResearchAgent", "responseType": "COMPLETE", "summary": "Identified Globex Inc as the top competitor.", "conversationId": "conv_sub_1a2b3c", "success": true } }, { "logType": "MESSAGE_TO_USER", "multimodalOutput": { "outputType": "TEXT", "content": "Your top competitor is Globex Inc. Here is the outreach email draft: ..." } } ] }, "errors": [] } ``` --- ## Manager Conversation Structure The `/conversation` endpoint returns a `ManagerConversationDto`: - `resourceId` (string) — The manager ID. - `version` (string) — Manager version used for this conversation. - `conversationId` (string) — Unique conversation ID. - `apiKeyId` (string) — API key ID that created this conversation, or `"my-sandbox"` for conversations created in the builder. - `closeDate` (datetime) — When the conversation was closed, or `null` if still active. - `currentIteration` (integer) — Number of orchestration iterations the manager has used. - `logs` — "> Manager log entries in chronological order. See log types below. - `evaluationContext` (object) — Human and automated evaluation results for this conversation, if any. ### Log Types Each log entry has a `logType` field that determines which other fields are present: - `USER_INPUT` — User input to the manager. Contains `combinedMultimodalInput` array. - `MANAGER_REASONING` — The manager's planning/thinking output. Contains `multimodalOutput`. - `MESSAGE_TO_USER` — The manager's final message to the user. Contains `multimodalOutput`. - `INVOKE_AGENT_REQUEST` — The manager delegating a task to a sub-agent. Contains `invokeAgentRequest`. - `INVOKE_AGENT_RESPONSE` — The sub-agent's result. Contains `invokeAgentResponse`. - `USER_REQUEST` — The manager asking the user for clarification. Contains `userRequest`. - `USER_RESPONSE` — The user's answer to a `USER_REQUEST`. Contains `userResponse` (string). ### Sub-Agent Invocation Objects **invokeAgentRequest** (on `INVOKE_AGENT_REQUEST` logs): - `invocationId` (string) — System-generated UUID for tracking this invocation. - `toolCallId` (string) — The manager LLM's tool call ID. - `agentId` (string) — Which sub-agent is being invoked. - `agentName` (string) — The sub-agent's name. - `taskDescription` (string) — The task given to the sub-agent. - `context` (string) — Additional context passed to the sub-agent (optional). - `variables` (object) — All tool arguments the manager supplied. - `agentPromptTemplateVariables` (object) — Prompt template variables forwarded to the sub-agent. - `executionOutputMode` (string) — Output mode passed to the sub-agent (`TEXT` or `AUDIO`). - `multimodalInputs` — "> Multimodal inputs (e.g. images) forwarded to the sub-agent. **invokeAgentResponse** (on `INVOKE_AGENT_RESPONSE` logs): - `invocationId` (string) — Matches the `invocationId` from the corresponding request. - `toolCallId` (string) — Matching tool call ID. - `agentId` (string) — Which sub-agent responded. - `agentName` (string) — The sub-agent's name. - `responseType` (string) — `"COMPLETE"` (task finished) or `"HELP"` (the sub-agent needs more information). - `summary` (string) — For COMPLETE: what was accomplished. - `helpRequest` (string) — For HELP: what the sub-agent needs. - `conversationId` (string) — The sub-agent's own conversation ID (usable for resuming). - `success` (boolean) — Whether the sub-agent execution succeeded. - `errorMessage` (string) — Error message if the sub-agent failed. - `agentContext` (object) — Full sub-agent conversation. Only populated when `expandAgents=true` on `/conversation`. **userRequest** (on `USER_REQUEST` logs): - `question` (string) — What the manager is asking the user. - `reason` (string) — Why this information is needed. - `contextualInfo` (string) — Background context for the user. **INVOKE_AGENT_REQUEST:** ```json { "logType": "INVOKE_AGENT_REQUEST", "invokeAgentRequest": { "invocationId": "inv_9f8e7d", "toolCallId": "call_abc123", "agentId": "aB3dE5fG7h", "agentName": "ResearchAgent", "taskDescription": "Find the top competitor of Acme Corp", "variables": { "company": "Acme Corp" }, "executionOutputMode": "TEXT" } } ``` **INVOKE_AGENT_RESPONSE:** ```json { "logType": "INVOKE_AGENT_RESPONSE", "invokeAgentResponse": { "invocationId": "inv_9f8e7d", "toolCallId": "call_abc123", "agentId": "aB3dE5fG7h", "agentName": "ResearchAgent", "responseType": "COMPLETE", "summary": "Identified Globex Inc as the top competitor.", "conversationId": "conv_sub_1a2b3c", "success": true } } ``` **USER_REQUEST:** ```json { "logType": "USER_REQUEST", "userRequest": { "question": "Which product line should the outreach email focus on?", "reason": "The competitor sells in two markets and the draft differs per market.", "contextualInfo": "Globex Inc sells both industrial and consumer robotics." } } ``` **USER_RESPONSE:** ```json { "logType": "USER_RESPONSE", "userResponse": "Focus on the industrial line." } ``` --- ## Get Conversation Status **POST** `/conversation-status` Checks the current status of a conversation (IN_PROGRESS, CLOSED, or NOT_FOUND). ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `managerId` (string, required) — The ID of the manager. - `conversationId` (string, required) — The conversation ID to check. ### Response - `conversationId` (string) — The conversation ID that was checked. - `status` (string) — One of: `IN_PROGRESS`, `CLOSED`, `NOT_FOUND` - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/managers/conversation-status?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "managerId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` ```json title="JSON" { "conversationId": "conv_a1b2c3d4e5", "status": "IN_PROGRESS", "errors": [] } ``` --- ## Revive Conversation **POST** `/revive-conversation` Revives a closed conversation, allowing you to continue the interaction where it left off - including the manager's sub-agent context. ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `managerId` (string, required) — The ID of the manager. - `conversationId` (string, required) — The conversation ID to revive. ### Response - `conversationId` (string) — The revived conversation ID. - `status` (string) — One of: `REVIVED`, `NOT_FOUND`, `ALREADY_ACTIVE`, `INVALID_STATE`, `ERROR` - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/managers/revive-conversation?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "managerId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` ```json title="JSON" { "conversationId": "conv_a1b2c3d4e5", "status": "REVIVED", "errors": [] } ``` --- ## Generate Audio Upload Link **POST** `/generate-audio-upload-link` Generates a pre-signed S3 upload URL for audio files. Use this when you want to upload audio files separately rather than sending them as base64 in the request. **Note:** Currently supports WAV format only. ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body Currently empty (WAV format is hardcoded). ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the audio file via PUT request. - `s3Uri` (string) — S3 URI to use in AudioInput when referencing this file. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/managers/generate-audio-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{}' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-audio/...", "s3Uri": "s3://autessa-audio/user-123/audio-xyz.wav", "expiresInSeconds": 3600, "errors": [] } ``` ```bash title="Upload the file" # Step 1: Get upload link # Step 2: Upload audio file curl --request PUT \ --url 'https://s3.amazonaws.com/autessa-audio/...' \ --header 'Content-Type: audio/wav' \ --data-binary '@audio.wav' # Step 3: Use s3Uri in AudioInput # { # "inputType": "AUDIO", # "s3Uri": "s3://autessa-audio/user-123/audio-xyz.wav" # } ``` --- ## Generate Image Upload Link **POST** `/generate-image-upload-link` Generates a pre-signed S3 upload URL for image files. Upload the image via PUT, then reference the returned `s3Uri` in an [Image Input](/docs/api/agents.md#image-input). ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `fileExtension` (string) — Image file extension: `png`, `jpg`, or `jpeg`. Defaults to `png` if not provided. ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the image file via PUT request. - `s3Uri` (string) — S3 URI to use in ImageInput when referencing this file. - `mimeType` (string) — MIME type of the image (e.g. `image/png`). Use it as the `Content-Type` header when uploading, and in the ImageInput. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/managers/generate-image-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "fileExtension": "png" }' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-images/...", "s3Uri": "s3://autessa-images/user-123/image-xyz.png", "mimeType": "image/png", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Generate File Upload Link **POST** `/generate-file-upload-link` Generates a pre-signed S3 upload URL for document files (PDFs, text documents, etc.). Upload the file via PUT, then reference the returned `s3Uri` in a [File Input](/docs/api/agents.md#file-input). ### Query Parameters - `resourceId` (string, required) — The ID of the manager (e.g. `V1StGXR8Z5`). ### Request Body - `fileExtension` (string) — File extension of the document to upload (e.g. `pdf`, `docx`, `txt`). ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the file via PUT request. - `s3Uri` (string) — S3 URI to use in FileInput when referencing this file. - `mimeType` (string) — MIME type of the file (e.g. `application/pdf`). Use it as the `Content-Type` header when uploading, and in the FileInput. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/managers/generate-file-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "fileExtension": "pdf" }' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/user-123/file-xyz.pdf", "mimeType": "application/pdf", "expiresInSeconds": 3600, "errors": [] } ``` --- ## WebSocket API ## Streaming Manager Execution **WSS** `/ws/clients/managers/execute` Real-time manager execution with streaming responses. As the manager works, you receive its reasoning stream, structured sub-agent invocation status updates, and the sub-agents' own streamed output - then a final response. ### Connection URL ``` wss://api.autessa.com/ws/clients/managers/execute?authorization={key}&resourceId={managerId} ``` ### Query Parameters - `authorization` (string, required) — Your API key. - `resourceId` (string, required) — The ID of the manager. - `conversationId` (string) — Optional conversation ID to revive and continue. - `versionState` (string) — Optional version state override: `PUBLISHED` (default), `DRAFT`, or `ARCHIVED`. - `versionNumber` (integer) — Optional specific version number to use. ### Connection Lifecycle 1. **Connect** - WebSocket connection established; a conversation is created (or revived if `conversationId` was passed) 2. **Status Message** - Server sends conversation status (`CREATED`, `REVIVED`, or `ERROR`) 3. **Send Messages** - Client sends MULTIMODAL messages 4. **Receive Streams** - Server streams manager reasoning, sub-agent status, and sub-agent output in real time 5. **Final Response** - Server sends the complete output for the turn 6. **Disconnect** - Connection closes > Voice mode (`voiceModeEnabled`) is not currently supported on the client Manager WebSocket - it is an Agent API feature. Manager streaming is text/multimodal. **websocat:** ```bash websocat "wss://api.autessa.com/ws/clients/managers/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ``` **JavaScript:** ```javascript const ws = new WebSocket( 'wss://api.autessa.com/ws/clients/managers/execute?authorization=your_api_key&resourceId=V1StGXR8Z5' ); ws.onopen = () => { console.log('Connected to manager'); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('Received:', data); }; ``` **Python:** ```python ws = websocket.WebSocket() ws.connect( "wss://api.autessa.com/ws/clients/managers/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ) # Receive status status = json.loads(ws.recv()) print(status) ``` ### Server-to-Client Status Message When you connect, the server immediately sends a status message: **New Conversation:** ```json { "conversationId": "conv_abc123", "status": "CREATED", "errors": [] } ``` **Revived Conversation:** ```json { "conversationId": "conv_abc123", "status": "REVIVED", "errors": [] } ``` **Error:** ```json { "conversationId": "conv_abc123", "status": "ERROR", "errors": ["Failed to revive conversation"] } ``` ### Client-to-Server Messages Send a JSON message to execute the manager with multimodal input. The conversation ID is managed by the socket session - you don't need to include it. - `messageType` (string, required) — `"MULTIMODAL"` (or its alias `"EXECUTE"`) - `managerId` (string, required) — The manager ID. Must match the connection's `resourceId`. - `input` — " required> Array of multimodal inputs (text, audio, images, files). See [Multimodal Input](/docs/api/agents.md#multimodal-input). - `executionOutputMode` (string) — `"TEXT"` or `"AUDIO"` - desired output format. Defaults to `"TEXT"`. - `environmentVariables` (object) — Key-value pairs for environment variables that won't be stored on the server. - `promptTemplateVariables` (object) — Key-value pairs to inject into the manager's prompt template using `@@{variableName}@@` syntax. ```json title="JSON" { "messageType": "MULTIMODAL", "managerId": "V1StGXR8Z5", "executionOutputMode": "TEXT", "input": [ { "inputType": "TEXT", "content": "Research our top competitor and draft an outreach email." } ], "promptTemplateVariables": { "companyName": "Acme Corp" } } ``` ### Streaming Response Messages While the manager works, the server streams several kinds of messages. Each message is a JSON object; check which field is present. #### 1. Manager Text Stream The manager's own reasoning and response text, streamed token-by-token: ```json title="JSON" { "streamedOutput": { "outputType": "TEXT", "content": " competitor" } } ``` #### 2. Agent Invocation Status Structured updates whenever the manager starts or finishes a sub-agent invocation: ```json title="JSON" { "agentInvocationStatus": { "agentName": "ResearchAgent", "agentId": "aB3dE5fG7h", "agentInvocationId": "call_abc123", "message": "Invoking ResearchAgent...", "instruction": "Find the top competitor of Acme Corp" } } ``` #### 3. Sub-Agent Streamed Output The invoked sub-agent's own streaming output (same shape as the [Agent API's](/docs/api/agents.md) streaming messages), wrapped with agent identification so you can attribute it: ```json title="JSON" { "subAgentStreamedOutput": { "agentName": "ResearchAgent", "agentId": "aB3dE5fG7h", "agentInvocationId": "call_abc123", "streamedOutput": { "outputType": "TEXT", "content": "Searching recent filings..." } } } ``` Sub-agent tool activity arrives the same way, as `subAgentStreamedOutput.streamedToolMessages`. #### 4. Final Response After the orchestration completes (or the manager pauses for your input), the server sends the complete output: ```json title="JSON" { "output": [ { "outputType": "TEXT", "content": "Your top competitor is Globex Inc. Here is the outreach email draft: ..." } ], "waitingForUserInput": false, "errors": [] } ``` If `waitingForUserInput` is `true`, send another MULTIMODAL message with the user's answer to continue the same turn. --- ## Custom Version Override the default PUBLISHED version of a manager: - `versionState` (string) — Version state: `"PUBLISHED"`, `"DRAFT"`, or `"ARCHIVED"`. Defaults to `"PUBLISHED"`. - `versionNumber` (integer) — Specific version number to use (optional). **REST API:** ```json { "managerId": "V1StGXR8Z5", "version": { "versionState": "DRAFT" } } ``` **WebSocket:** ```bash wss://api.autessa.com/ws/clients/managers/execute?authorization=key&resourceId=V1StGXR8Z5&versionState=DRAFT ``` --- ## Enumerations ### EConversationStatus - `IN_PROGRESS` - Conversation is active - `CLOSED` - Conversation has been closed - `NOT_FOUND` - Conversation doesn't exist ### EManagerLogType - `USER_INPUT` - User input to the manager - `MANAGER_REASONING` - Manager's reasoning/thinking output - `MESSAGE_TO_USER` - Manager's final message to the user - `INVOKE_AGENT_REQUEST` - Manager invoking a sub-agent - `INVOKE_AGENT_RESPONSE` - Sub-agent invocation result - `USER_REQUEST` - Manager requesting information from the user - `USER_RESPONSE` - User's response to the manager's request ### ESubAgentResponseType - `COMPLETE` - Sub-agent completed the task successfully - `HELP` - Sub-agent needs help or additional information ### ExecutionOutputMode - `TEXT` - Text output (default) - `AUDIO` - Audio output --- ## Rate Limiting Manager execution endpoints are rate limited per company and per API key. Rate limit information is included in response headers: - `X-RateLimit-Limit-Minute` - Requests allowed per minute - `X-RateLimit-Remaining-Minute` - Remaining requests this minute - `X-RateLimit-Limit-Day` - Requests allowed per day - `X-RateLimit-Remaining-Day` - Remaining requests today When rate limited, you'll receive a 429 status code with a `Retry-After` header. > A manager turn can invoke several sub-agents, each running a full agent conversation with its own tool calls - budget for longer execution times and higher usage than a single-agent request. --- ## Complete Examples ### Multi-Turn Orchestration with Clarification ```javascript title="JavaScript" const base = 'https://api.autessa.com/clients/managers'; const qs = '?resourceId=V1StGXR8Z5'; const headers = { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }; // 1. Create conversation const createResp = await fetch(`${base}/execute-create${qs}`, { method: 'POST', headers, body: JSON.stringify({ managerId: 'V1StGXR8Z5' }) }); const { conversationId } = await createResp.json(); // 2. Execute - the manager may pause with a question let resp = await fetch(`${base}/execute${qs}`, { method: 'POST', headers, body: JSON.stringify({ managerId: 'V1StGXR8Z5', conversationId, input: [{ inputType: 'TEXT', content: 'Research our top competitor and draft an outreach email.' }] }) }); let data = await resp.json(); // 3. Answer clarifying questions until the manager finishes while (data.waitingForUserInput) { console.log('Manager asks:', data.output[0].content); resp = await fetch(`${base}/execute${qs}`, { method: 'POST', headers, body: JSON.stringify({ managerId: 'V1StGXR8Z5', conversationId, input: [{ inputType: 'TEXT', content: 'Focus on the industrial line.' }] }) }); data = await resp.json(); } console.log('Final result:', data.output[0].content); // 4. Close conversation await fetch(`${base}/execute-close${qs}`, { method: 'POST', headers, body: JSON.stringify({ managerId: 'V1StGXR8Z5', conversationId }) }); ``` ### WebSocket Streaming with Sub-Agent Attribution ```javascript title="JavaScript" const ws = new WebSocket( 'wss://api.autessa.com/ws/clients/managers/execute?authorization=your_api_key&resourceId=V1StGXR8Z5' ); let conversationId; ws.onmessage = (event) => { const data = JSON.parse(event.data); // Connection status if (data.status) { conversationId = data.conversationId; ws.send(JSON.stringify({ messageType: 'MULTIMODAL', managerId: 'V1StGXR8Z5', executionOutputMode: 'TEXT', input: [ { inputType: 'TEXT', content: 'Research our top competitor and draft an outreach email.' } ] })); return; } // Manager's own reasoning/response stream if (data.streamedOutput) { process.stdout.write(data.streamedOutput.content); } // Structured sub-agent status updates if (data.agentInvocationStatus) { console.log(`\n[${data.agentInvocationStatus.agentName}] ${data.agentInvocationStatus.message}`); } // Sub-agent streamed output, attributed by agent if (data.subAgentStreamedOutput?.streamedOutput) { const sub = data.subAgentStreamedOutput; console.log(`\n[${sub.agentName}] ${sub.streamedOutput.content}`); } // Final response for this turn if (data.output) { console.log('\nFinal:', data.output[0].content); if (data.waitingForUserInput) { console.log('Manager is waiting for your answer - send another MULTIMODAL message.'); } } }; ``` --- ## Related - [Managers guide](/docs/guide/agentic/managers.md) - [Agent API](/docs/api/agents.md) - single-agent execution and the shared multimodal input/output shapes - [Tool Execution API](/docs/api/tools.md) - [API keys](/docs/guide/api-management/api-keys.md) and [Authentication](/docs/guide/authentication.md) --- # Tool API Source: https://docs.autessa.com/docs/api/tools > Complete API reference for executing custom tools with synchronous and streaming execution modes. # Tool API The Tool API allows you to execute custom tools that you've built in the Autessa platform. Tools can be executed synchronously for immediate results or via WebSocket for real-time streaming output. > All Tool API requests require the `resourceId` query parameter set to your tool ID and an `Authorization` header with your API key. --- ## Base URL ``` REST API: https://api.autessa.com/clients/tool WebSocket: wss://api.autessa.com/ws/clients/tools/execute ``` --- ## Authentication ### HTTP REST APIs Pass your API key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: your_api_key_here ``` ### WebSocket APIs Pass your API key as a query parameter: ``` authorization=your_api_key_here ``` **REST:** ```bash curl https://api.autessa.com/clients/tool/execute?resourceId=V1StGXR8Z5 \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" ``` **WebSocket:** ```bash websocat "wss://api.autessa.com/ws/clients/tools/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ``` --- ## REST API Endpoints ## Execute Tool **POST** `/execute` Executes a tool synchronously and returns the complete result. This endpoint waits for the tool execution to complete before returning. ### Query Parameters - `resourceId` (string, required) — The ID of the tool to execute. ### Request Body - `toolId` (string, required) — The ID of the tool to execute. - `variables` (object) — Public variables (input parameters) for the tool. Key-value pairs where both keys and values are strings. - `privateVariables` (object) — Private variables that won't be exposed in the output or logs. String key-value pairs. - `environmentVariables` (object) — Environment variables that won't be stored on the server. Useful for secrets. String key-value pairs. - `version` (CustomVersion) — Optional version override. If not specified, uses the PUBLISHED version. ### Response - `responseType` (string) — Always `"EXECUTE_TOOL"` for tool responses. - `toolName` (string) — Name of the tool that was executed. - `result` (any) — The result data from the tool execution. Can be any JSON structure. - `publicVars` (object) — Public variables returned by the tool. - `debuggingInfo` (object) — Debugging information including success status, logs, and final response. - `invocationId` (string) — Unique UUID identifying this execution. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/tool/execute?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "toolId": "V1StGXR8Z5", "variables": { "city": "Seattle", "state": "WA" }, "environmentVariables": { "API_KEY": "secret_key" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/tool/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ toolId: 'V1StGXR8Z5', variables: { city: 'Seattle', state: 'WA' } }) } ); const data = await response.json(); console.log(data.result); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/tool/execute?resourceId=V1StGXR8Z5', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={ 'toolId': 'V1StGXR8Z5', 'variables': { 'city': 'Seattle', 'state': 'WA' } } ) data = response.json() print(data['result']) ``` **Success:** ```json { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "result": "It is 65 degrees Fahrenheit with partly cloudy skies", "publicVars": { "city": "Seattle", "state": "WA" }, "debuggingInfo": { "success": true, "finalResponse": "It is 65 degrees Fahrenheit with partly cloudy skies", "logs": [ "[2025-01-29 15:23:41.123] [LOG] Fetching weather data...", "[2025-01-29 15:23:42.456] [LOG] Weather data retrieved successfully" ] }, "errors": [] } ``` **Error:** ```json { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "result": null, "debuggingInfo": { "success": false, "logs": [ "[2025-01-29 15:23:41.123] [ERROR] Failed to fetch weather data" ] }, "errors": ["Tool execution failed"] } ``` --- ## WebSocket API ## Stream Tool Execution **WSS** `/ws/clients/tools/execute` Real-time tool execution with streaming output. Allows you to receive intermediate results and debugging messages as the tool executes. ### Connection URL ``` wss://api.autessa.com/ws/clients/tools/execute?authorization={key}&resourceId={toolId} ``` ### Query Parameters - `authorization` (string, required) — Your API key. - `resourceId` (string, required) — The ID of the tool to execute. This determines which tool runs — any `toolId` in the message body is ignored. ### Connection Lifecycle 1. **Connect** - WebSocket connection established 2. **Send Message** - Client sends tool execution request 3. **Receive Streams** - Server streams execution messages in real-time 4. **Final Result** - Server sends final execution result 5. **Disconnect** - Connection closes after execution completes **websocat:** ```bash websocat "wss://api.autessa.com/ws/clients/tools/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ``` **JavaScript:** ```javascript const ws = new WebSocket( 'wss://api.autessa.com/ws/clients/tools/execute?authorization=your_api_key&resourceId=V1StGXR8Z5' ); ws.onopen = () => { console.log('Connected to tool'); // Send execution request ws.send(JSON.stringify({ variables: { city: 'Seattle', state: 'WA' } })); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('Received:', data); }; ``` **Python:** ```python ws = websocket.WebSocket() ws.connect( "wss://api.autessa.com/ws/clients/tools/execute?authorization=your_api_key&resourceId=V1StGXR8Z5" ) # Send execution request ws.send(json.dumps({ 'variables': { 'city': 'Seattle', 'state': 'WA' } })) # Receive messages while True: message = ws.recv() data = json.loads(message) print(data) if data.get('result'): break # Final result received ``` ### Client-to-Server Message Send a JSON message to execute the tool: - `variables` (object) — Public variables (input parameters). String key-value pairs. - `privateVariables` (object) — Private variables. String key-value pairs. - `environmentVariables` (object) — Environment variables. String key-value pairs. > The executed tool is always the one identified by the `resourceId` connection parameter — a `toolId` field in the message body is ignored. Version overrides are not supported over WebSocket; the PUBLISHED version always runs. ```json title="JSON" { "variables": { "city": "Seattle", "state": "WA" }, "privateVariables": {}, "environmentVariables": { "API_KEY": "secret_key" } } ``` ### Server-to-Client Messages The server sends multiple messages during tool execution: #### 1. Execution Start Sent when the tool begins executing: ```json title="JSON" { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "publicVars": { "city": "Seattle", "state": "WA" }, "streamMessage": "EXECUTION START: GetWeather" } ``` #### 2. Variables Preview Shows the public variables being used: ```json title="JSON" { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "streamMessage": "EXECUTION VARIABLES: {\"city\":\"Seattle\",\"state\":\"WA\"}" } ``` #### 3. Custom Stream Messages Any custom messages your tool sends via console.log or stream APIs: ```json title="JSON" { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "streamMessage": "Fetching weather data from API..." } ``` #### 4. Execution End Sent when the tool finishes executing: ```json title="JSON" { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "streamMessage": "EXECUTION END: GetWeather" } ``` #### 5. Execution Result Preview Preview of the tool's return value: ```json title="JSON" { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "streamMessage": "EXECUTION RESULT: \"It is 65 degrees Fahrenheit with partly cloudy skies\"" } ``` #### 6. Final Result The complete execution result with debugging info: ```json title="JSON" { "responseType": "EXECUTE_TOOL", "toolName": "GetWeather", "invocationId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "result": "It is 65 degrees Fahrenheit with partly cloudy skies", "publicVars": { "city": "Seattle", "state": "WA" }, "debuggingInfo": { "success": true, "finalResponse": "It is 65 degrees Fahrenheit with partly cloudy skies", "logs": [ "[2025-01-29 15:23:41.123] [LOG] Fetching weather data...", "[2025-01-29 15:23:42.456] [LOG] Weather data retrieved successfully" ] } } ``` --- ## Built-in Stream Message Prefixes The Tool API uses built-in prefixes for system messages. Avoid using these prefixes in your custom stream messages: - `EXECUTION START` — Sent when tool execution begins - `EXECUTION VARIABLES` — Shows the public variables being used - `EXECUTION END` — Sent when tool execution completes - `EXECUTION RESULT` — Preview of the tool's return value --- ## Custom Version Override the default PUBLISHED version of a tool. Version overrides apply to the REST endpoint only — WebSocket executions always run the PUBLISHED version. - `versionState` (string) — Version state: `"PUBLISHED"`, `"DRAFT"`, or `"ARCHIVED"`. Defaults to `"PUBLISHED"`. - `versionNumber` (integer) — Specific version number to use (optional). ```json title="REST API" { "toolId": "V1StGXR8Z5", "version": { "versionState": "DRAFT", "versionNumber": 3 }, "variables": {...} } ``` --- ## Variables Guide ### Public Variables Public variables are the main input parameters for your tool. They are: - Visible in debugging output - Logged for monitoring and evaluation - Returned in the `publicVars` field Use these for normal input parameters like IDs, search queries, etc. ### Private Variables Private variables are not exposed in outputs or logs. Use these for: - Intermediate values you want to pass - Data that should not be logged - Internal processing parameters **Note:** Private variables are still visible during execution, just not in logs. ### Environment Variables Environment variables are not stored on the server. Use these for: - API keys and secrets - Credentials - Temporary configuration values **Important:** Environment variables are passed with each request and never persisted. --- ## Rate Limiting Tool execution is rate limited at two levels: per API key (the per-minute and per-day limits configured on the key) and per company. Both the REST and WebSocket endpoints enforce these limits. When rate limited, you'll receive a 429 status code with the reason in the `errors` array of the response body. --- ## Complete Examples ### Simple Tool Execution **JavaScript:** ```javascript async function executeTool(toolId, variables) { const response = await fetch( `https://api.autessa.com/clients/tool/execute?resourceId=${toolId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ toolId, variables }) } ); const data = await response.json(); if (data.errors && data.errors.length > 0) { throw new Error(data.errors.join(', ')); } return data.result; } // Usage const weather = await executeTool('V1StGXR8Z5', { city: 'Seattle', state: 'WA' }); console.log(weather); // Output: "It is 65 degrees Fahrenheit with partly cloudy skies" ``` **Python:** ```python def execute_tool(tool_id, variables): response = requests.post( f'https://api.autessa.com/clients/tool/execute?resourceId={tool_id}', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={ 'toolId': tool_id, 'variables': variables } ) data = response.json() if data.get('errors'): raise Exception(', '.join(data['errors'])) return data['result'] # Usage weather = execute_tool('V1StGXR8Z5', { 'city': 'Seattle', 'state': 'WA' }) print(weather) # Output: "It is 65 degrees Fahrenheit with partly cloudy skies" ``` ### Streaming Tool Execution **JavaScript:** ```javascript function executeToolStreaming(toolId, variables) { return new Promise((resolve, reject) => { const ws = new WebSocket( `wss://api.autessa.com/ws/clients/tools/execute?authorization=your_api_key&resourceId=${toolId}` ); const streamMessages = []; let finalResult = null; ws.onopen = () => { ws.send(JSON.stringify({ variables })); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); // Collect stream messages if (data.streamMessage) { console.log('Stream:', data.streamMessage); streamMessages.push(data.streamMessage); } // Final result if (data.result !== undefined) { finalResult = data.result; ws.close(); } }; ws.onclose = () => { resolve({ result: finalResult, streamMessages }); }; ws.onerror = (error) => { reject(error); }; }); } // Usage const { result, streamMessages } = await executeToolStreaming('V1StGXR8Z5', { city: 'Seattle', state: 'WA' }); console.log('Final result:', result); console.log('Stream messages:', streamMessages); ``` **Python:** ```python def execute_tool_streaming(tool_id, variables): ws = websocket.WebSocket() ws.connect( f"wss://api.autessa.com/ws/clients/tools/execute?authorization=your_api_key&resourceId={tool_id}" ) # Send execution request ws.send(json.dumps({ 'variables': variables })) stream_messages = [] final_result = None # Receive messages while True: message = ws.recv() data = json.loads(message) # Stream messages if 'streamMessage' in data: print(f"Stream: {data['streamMessage']}") stream_messages.append(data['streamMessage']) # Final result if 'result' in data and data['result'] is not None: final_result = data['result'] break ws.close() return { 'result': final_result, 'stream_messages': stream_messages } # Usage response = execute_tool_streaming('V1StGXR8Z5', { 'city': 'Seattle', 'state': 'WA' }) print(f"Final result: {response['result']}") print(f"Stream messages: {response['stream_messages']}") ``` ### Tool with Environment Variables ```javascript title="JavaScript" async function executeToolWithSecrets(toolId, variables, secrets) { const response = await fetch( `https://api.autessa.com/clients/tool/execute?resourceId=${toolId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ toolId, variables, environmentVariables: secrets }) } ); const data = await response.json(); return data.result; } // Usage - secrets are never logged const result = await executeToolWithSecrets( 'V1StGXR8Z5', { city: 'Seattle', state: 'WA' }, { WEATHER_API_KEY: 'secret_key_123' } ); ``` --- ## Error Handling Always check for errors in the response: ```javascript title="JavaScript" const response = await fetch( 'https://api.autessa.com/clients/tool/execute?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ toolId: 'V1StGXR8Z5', variables: { city: 'Seattle' } }) } ); const data = await response.json(); if (data.errors && data.errors.length > 0) { console.error('Tool execution failed:', data.errors); // Handle error } else { console.log('Result:', data.result); // Handle success } ``` --- ## Best Practices ### Security - Use `environmentVariables` for API keys and secrets - Never log sensitive data in stream messages - Rotate API keys regularly ### Performance - Use WebSocket for long-running tools that benefit from streaming - Use REST API for quick, simple tool executions - Implement exponential backoff for retries ### Debugging - Check `debuggingInfo` for detailed execution logs - Use stream messages to provide progress updates - Back off when you receive 429 responses to avoid throttling ### Error Handling - Always check the `errors` array - Check `debuggingInfo.success` for execution status - Implement proper error handling for network failures --- ## Related - [API Overview](/docs/api.md) - [Agent API](/docs/api/agents.md) - [Scheduled Events API](/docs/api/scheduled-events.md) - [Authentication](/docs/guide/authentication.md) - [Building Tools](/docs/guide/agentic/tools.md) - [Tool Types](/docs/tools.md) --- # AutessaDB API Source: https://docs.autessa.com/docs/api/autessadb > Complete API reference for AutessaDB including CRUD operations, semantic search, batch operations, and join queries. # AutessaDB API AutessaDB is a complete database solution with built-in semantic search capabilities. Perform CRUD operations, complex queries, batch updates, join tables, and leverage powerful vector search across your data. > All AutessaDB API requests require the `resourceId` query parameter set to your table's ID (e.g. `V1StGXR8Z5`) and an `Authorization` header with your API key. Your API key must have the appropriate permissions (READ, WRITE, DELETE) for the requested operation. --- ## Base URL ``` https://api.autessa.com/clients/autessadb ``` --- ## Authentication Pass your API key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: your_api_key_here ``` ```bash title="cURL" curl https://api.autessa.com/clients/autessadb/get-data?resourceId=V1StGXR8Z5 \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" ``` --- ## Permissions Each API key can have different permissions for each table: - `READ` — Required for: get-data, query, join-query, aggregate, get-single-row, search - `WRITE` — Required for: insert, update, batch-update, bulk-insert, bulk-update-by-ids, generate-upload-link, index-record - `DELETE` — Required for: delete-row, batch-delete, bulk-delete-by-ids `/create-knowledge-base` creates a new table, so no table-level permission applies — the API key authenticates at the company level instead. Configure permissions for your API key in the [Autessa Builder](https://builder.autessa.com/keys). --- ## Knowledge Bases ## Create Knowledge Base **POST** `/create-knowledge-base` Creates a knowledge base table pre-configured for semantic search and inserts one row per file. The new table has three columns: `full_path` (SHORT_TEXT), `file_name` (SHORT_TEXT, computed automatically from `full_path`), and `file` (FILE, vectorized for semantic search). **Permission Required:** None at the table level — the endpoint creates a new table, so the API key authenticates at the company level. ### Query Parameters - `resourceId` (string, required) — ID of an AutessaDB table the API key has access to. Required by the gateway even though a new table is created. ### Request Body - `tableName` (string, required) — Name for the new knowledge base table. - `files` — " required> Files to insert into the knowledge base, at least one. Each file has `fullPath` (string, the file's full path including folders) and `s3Uri` (string, the S3 URI of the uploaded file). Files are copied into the knowledge base's permanent storage. ### Response - `tableId` (string) — ID of the newly created knowledge base table. - `tableName` (string) — Name of the created table. - `filesInserted` (integer) — Number of files successfully inserted as rows. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/create-knowledge-base?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableName": "Product Docs", "files": [ { "fullPath": "docs/getting-started.pdf", "s3Uri": "s3://autessa-files/uploads/getting-started.pdf" }, { "fullPath": "docs/api-reference.pdf", "s3Uri": "s3://autessa-files/uploads/api-reference.pdf" } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/create-knowledge-base?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableName: 'Product Docs', files: [ { fullPath: 'docs/getting-started.pdf', s3Uri: 's3://autessa-files/uploads/getting-started.pdf' } ] }) } ); const data = await response.json(); console.log('Knowledge base table:', data.tableId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/create-knowledge-base?resourceId=V1StGXR8Z5', headers={ 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, json={ 'tableName': 'Product Docs', 'files': [ { 'fullPath': 'docs/getting-started.pdf', 's3Uri': 's3://autessa-files/uploads/getting-started.pdf' } ] } ) data = response.json() print('Knowledge base table:', data['tableId']) ``` ```json title="JSON" { "tableId": "kzT9pLm3Qw", "tableName": "Product Docs", "filesInserted": 2, "errors": [] } ``` --- ## Data Manipulation ## Insert Row **POST** `/insert` Inserts a new row into a table. **Permission Required:** WRITE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table (same as resourceId). - `values` (object, required) — Column values to insert. Keys are column names, values are AutessaDbColumnValue objects. ### Response - `insertedId` (integer) — The ID of the newly inserted row. - `newRow` (object) — The complete inserted row data with all column values. - `computationStatus` (string) — `"COMPUTING"` if computed fields are being calculated in the background. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/insert?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "values": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 }, "email": { "columnType": "SHORT_TEXT", "textValue": "john@example.com" } } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/insert?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', values: { name: { columnType: 'SHORT_TEXT', textValue: 'John Doe' }, age: { columnType: 'INTEGER', integerValue: 30 } } }) } ); const data = await response.json(); console.log('Inserted ID:', data.insertedId); ``` ```json title="JSON" { "insertedId": 12345, "newRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 }, "email": { "columnType": "SHORT_TEXT", "textValue": "john@example.com" } }, "errors": [] } ``` --- ## Update Row **POST** `/update` Updates an existing row in a table. Only the specified columns will be updated. **Permission Required:** WRITE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `rowId` (integer, required) — The ID of the row to update. - `values` (object, required) — Column values to update. Only specified columns will be modified. ### Response - `updatedId` (integer) — The ID of the updated row. - `oldRow` (object) — Row data before the update. - `newRow` (object) — Row data after the update. - `computationStatus` (string) — `"COMPUTING"` if computed fields are being calculated. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/update?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "rowId": 12345, "values": { "age": { "columnType": "INTEGER", "integerValue": 31 } } }' ``` ```json title="JSON" { "updatedId": 12345, "oldRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 } }, "newRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 31 } }, "errors": [] } ``` --- ## Batch Update Rows **POST** `/batch-update` Updates multiple rows that match the specified WHERE conditions. All matching rows will be updated with the same values. **Permission Required:** WRITE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `values` (object, required) — Column values to update for all matching rows. - `whereGroups` — " required> Array of WHERE condition groups to identify rows to update. - `groupsLogicalOperator` (string) — How to combine WHERE groups: `"AND"` or `"OR"`. Defaults to `"OR"`. ### Response - `updatedCount` (integer) — Number of rows that were updated. - `results` — "> Detailed results for each updated row (rowId, oldRow, newRow). - `computationStatus` (string) — `"COMPUTING"` if computed fields are being calculated. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/batch-update?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "active" } }, "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "age", "operator": "GREATER_THAN", "value": 25 } ] } ] }' ``` ```json title="JSON" { "updatedCount": 5, "results": [ { "rowId": 12345, "oldRow": {...}, "newRow": {...} }, { "rowId": 12346, "oldRow": {...}, "newRow": {...} } ], "errors": [] } ``` --- ## Delete Row **POST** `/delete-row` Deletes a single row from a table. **Permission Required:** DELETE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `rowId` (integer, required) — The ID of the row to delete. ### Response - `deletedId` (integer) — The ID of the deleted row. - `oldRow` (object) — The deleted row data. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/delete-row?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "rowId": 12345 }' ``` ```json title="JSON" { "deletedId": 12345, "oldRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 } }, "errors": [] } ``` --- ## Batch Delete Rows **POST** `/batch-delete` Deletes multiple rows that match the specified WHERE conditions. **Permission Required:** DELETE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `whereGroups` — " required> Array of WHERE condition groups to identify rows to delete. - `groupsLogicalOperator` (string) — How to combine WHERE groups: `"AND"` or `"OR"`. Defaults to `"OR"`. ### Response - `deletedCount` (integer) — Number of rows that were deleted. - `results` — "> Detailed results for each deleted row (rowId, oldRow). - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/batch-delete?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "inactive" }, { "columnName": "lastLogin", "operator": "IS_NULL" } ] } ] }' ``` ```json title="JSON" { "deletedCount": 3, "results": [ { "rowId": 12345, "oldRow": {...} }, { "rowId": 12347, "oldRow": {...} }, { "rowId": 12350, "oldRow": {...} } ], "errors": [] } ``` --- ## Bulk Operations The bulk endpoints write many rows in a single call — insert, update-by-id, and delete-by-id. Each returns a per-row result list so you can correlate individual outcomes, and each accepts mode fields that control failure handling and side effects. > Bulk writes **suppress table triggers by default** — a deliberate divergence from single-row writes, whose triggers always fire. Bulk loads are typically ingestion where firing per-row triggers thousands of times is rarely intended. Set `triggerMode` to `"PER_ROW"` or `"BATCH_CONTEXT"` to opt in. **Common mode fields** (all optional): - `atomicity` (string) — `"ALL_OR_NOTHING"` (default) writes the whole call as one transaction — if any row fails a structural check, nothing is written. `"BEST_EFFORT_PER_CHUNK"` writes in chunks; a failing chunk is reported per-row and skipped while earlier chunks stay committed. - `triggerMode` (string) — `"SUPPRESS"` (default), `"PER_ROW"`, or `"BATCH_CONTEXT"`. See the note above. - `indexingMode` (string) — `"IMMEDIATE"` (default), `"DEFERRED"`, or `"SKIP"` — when semantic-search indexing of the written rows happens. (Insert and update only.) - `computeMode` (string) — `"IMMEDIATE"` (default), `"DEFERRED"`, or `"SKIP"` — when computed fields are calculated. (Insert and update only.) > The number of rows per bulk call is capped (a generous first-party limit). Requests above the cap are rejected; split very large loads across multiple calls. Read and aggregate endpoints are additionally rate-limited per company (see [Rate Limits](#rate-limits)). ## Bulk Insert Rows **POST** `/bulk-insert` Inserts many rows into a table in a single call. **Permission Required:** WRITE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `rows` — " required> List of rows to insert. Each entry is a column-name → column-value map, the same shape as a single insert's `values`. - `atomicity` (string) — See common mode fields above. - `indexingMode` (string) — See common mode fields above. - `computeMode` (string) — See common mode fields above. - `triggerMode` (string) — See common mode fields above. ### Response - `insertedCount` (integer) — Number of rows actually written. - `results` — "> One entry per input row: `{ inputIndex, insertedId, newRow, errors }`. `inputIndex` is the row's position in the request so you can correlate; on failure `errors` is populated and `insertedId` is null. - `computationStatus` (string) — `"COMPUTING"` if computed fields are being calculated in the background. - `errors` — "> Top-level errors when the whole call was rejected (e.g. an `ALL_OR_NOTHING` validation failure). ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/bulk-insert?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "rows": [ { "name": { "columnType": "SHORT_TEXT", "textValue": "Ada" } }, { "name": { "columnType": "SHORT_TEXT", "textValue": "Grace" } } ], "atomicity": "ALL_OR_NOTHING" }' ``` ```json title="JSON" { "insertedCount": 2, "results": [ { "inputIndex": 0, "insertedId": 12345, "newRow": {...}, "errors": [] }, { "inputIndex": 1, "insertedId": 12346, "newRow": {...}, "errors": [] } ], "computationStatus": null, "errors": [] } ``` --- ## Bulk Update Rows By IDs **POST** `/bulk-update-by-ids` Updates many rows by id in a single call. Each update is a partial patch — only the columns you supply change. **Permission Required:** WRITE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `updates` — " required> List of per-row updates. Each entry is `{ "id": , "values": { ... } }`, where `values` is a partial column-name → column-value map. - `atomicity` (string) — See common mode fields above. - `indexingMode` (string) — See common mode fields above. - `computeMode` (string) — See common mode fields above. - `triggerMode` (string) — See common mode fields above. ### Response - `updatedCount` (integer) — Number of rows actually written. - `results` — "> One entry per input row: `{ inputIndex, updatedId, oldRow, newRow, errors }`. An id that is not found or outside your access is reported per-row in `errors`. - `computationStatus` (string) — `"COMPUTING"` if computed fields are being recalculated in the background. - `errors` — "> Top-level errors when the whole call was rejected. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/bulk-update-by-ids?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "updates": [ { "id": 12345, "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "done" } } }, { "id": 12346, "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "open" } } } ] }' ``` ```json title="JSON" { "updatedCount": 2, "results": [ { "inputIndex": 0, "updatedId": 12345, "oldRow": {...}, "newRow": {...}, "errors": [] }, { "inputIndex": 1, "updatedId": 12346, "oldRow": {...}, "newRow": {...}, "errors": [] } ], "errors": [] } ``` --- ## Bulk Delete Rows By IDs **POST** `/bulk-delete-by-ids` Deletes many rows by id in a single call. Only `atomicity` and `triggerMode` apply (there is nothing to index or compute on a delete). **Permission Required:** DELETE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `ids` — " required> List of row ids to delete. - `atomicity` (string) — See common mode fields above. - `triggerMode` (string) — See common mode fields above. ### Response - `deletedCount` (integer) — Number of rows actually removed. - `results` — "> One entry per input id: `{ inputIndex, deletedId, errors }`. An id that is not found or outside your access is reported per-row in `errors`. - `errors` — "> Top-level errors when the whole call was rejected. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/bulk-delete-by-ids?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "ids": [12345, 12346, 12347] }' ``` ```json title="JSON" { "deletedCount": 3, "results": [ { "inputIndex": 0, "deletedId": 12345, "errors": [] }, { "inputIndex": 1, "deletedId": 12346, "errors": [] }, { "inputIndex": 2, "deletedId": 12347, "errors": [] } ], "errors": [] } ``` --- ## Data Retrieval ## Get Table Data **POST** `/get-data` Retrieves paginated data from a table. **Permission Required:** READ ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `orderBy` — "> Sort terms in priority order. Each is `{ "columnName": "...", "direction": "ASC" | "DESC" }` (direction defaults to `DESC`). Defaults to newest-updated first when omitted. - `page` (integer) — Page number (1-based). Defaults to 1. Ignored when `cursor` is set. - `pageSize` (integer) — Number of records per page. Defaults to 50. Maximum 100. - `cursor` (string) — Opaque cursor from a prior response's `nextCursor`, for efficient seek pagination through large tables. When set, `page` is ignored and the same `orderBy` must be reused. A cursor is bound to the sort that produced it. - `includeTotal` (boolean) — Whether to compute `totalRows`/`totalPages`. Offset requests default to `true`; cursor requests default to `false` (set `true` to force the count). ### Response - `schema` (AutessaDbSchema) — The table schema definition. - `rows` — "> Array of table rows with column data and metadata. - `currentPage` (integer) — Current page number. - `pageSize` (integer) — Number of rows per page. - `totalRows` (integer) — Total number of rows matching the request. `null` on a cursor request unless `includeTotal` was set. - `totalPages` (integer) — Total number of pages. `null` on a cursor request unless `includeTotal` was set. - `nextCursor` (string) — Cursor to fetch the next page; `null` when there are no more rows. - `hasMore` (boolean) — Whether another page exists beyond this one. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/get-data?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "page": 1, "pageSize": 50 }' ``` ```json title="JSON" { "schema": { "tableName": "users", "columnDefinitions": [ { "columnName": "name", "columnType": "SHORT_TEXT", "required": true }, { "columnName": "age", "columnType": "INTEGER", "required": false } ], "dbType": "DEFAULT" }, "rows": [ { "id": 12345, "columnData": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 } }, "createdAt": "2025-01-29T10:00:00", "updatedAt": "2025-01-29T15:30:00" } ], "currentPage": 1, "pageSize": 50, "totalRows": 150, "totalPages": 3, "errors": [] } ``` --- ## Query Table Data **POST** `/query` Queries table data with WHERE conditions and pagination. **Permission Required:** READ ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `whereGroups` — " required> Array of WHERE condition groups for filtering. - `groupsLogicalOperator` (string) — How to combine WHERE groups: `"AND"` or `"OR"`. Defaults to `"OR"`. - `orderBy` — "> Sort terms in priority order. Each is `{ "columnName": "...", "direction": "ASC" | "DESC" }` (direction defaults to `DESC`). - `page` (integer) — Page number (1-based). Defaults to 1. Ignored when `cursor` is set. - `pageSize` (integer) — Records per page. Defaults to 50. Maximum 100. - `cursor` (string) — Opaque cursor from a prior response's `nextCursor` for seek pagination. When set, `page` is ignored and the same `orderBy` must be reused. - `includeTotal` (boolean) — Whether to compute `totalRows`/`totalPages`. Offset requests default to `true`; cursor requests default to `false`. ### Response Same structure as Get Table Data, but filtered by WHERE conditions — including `nextCursor` / `hasMore` for cursor pagination and nullable `totalRows` / `totalPages` on cursor requests. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/query?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "age", "operator": "GREATER_THAN_OR_EQUAL", "value": 18 }, { "columnName": "status", "operator": "EQUALS", "value": "active" } ] } ], "page": 1, "pageSize": 50 }' ``` ```json title="JSON" { "schema": {...}, "rows": [ { "id": 12345, "columnData": {...}, "createdAt": "2025-01-29T10:00:00", "updatedAt": "2025-01-29T15:30:00" } ], "currentPage": 1, "pageSize": 50, "totalRows": 25, "totalPages": 1, "errors": [] } ``` --- ## Join Query **POST** `/join-query` Performs JOIN operations across multiple tables with optional WHERE conditions. **Permission Required:** READ (on all tables in the join) ### Query Parameters - `resourceId` (string, required) — The ID of the main/left table. ### Request Body - `tableId` (string, required) — The ID of the main/left table. - `joins` — " required> Array of tables to join with and their join conditions. - `whereGroups` — "> Optional WHERE conditions applied after the join. - `groupsLogicalOperator` (string) — How to combine WHERE groups: `"AND"` or `"OR"`. Defaults to `"OR"`. - `orderBy` — "> Sort terms (main-table columns) in priority order. Each is `{ "columnName": "...", "direction": "ASC" | "DESC" }`. - `page` (integer) — Page number. Defaults to 1. Ignored when `cursor` is set. - `pageSize` (integer) — Records per page. Defaults to 50. Maximum 100. - `cursor` (string) — Opaque cursor from a prior response's `nextCursor` for seek pagination. When set, `page` is ignored and the same `orderBy` must be reused. - `includeTotal` (boolean) — Whether to compute `totalRows`/`totalPages`. Offset requests default to `true`; cursor requests default to `false`. ### Response Includes `nextCursor` / `hasMore` for cursor pagination; `totalRows` / `totalPages` are `null` on a cursor request unless `includeTotal` was set. - `mainSchema` (AutessaDbSchema) — Schema of the main/source table. - `joinedSchemas` (object) — Map of joined table ID to schema. Keys are the joined tables' raw numeric IDs. - `rows` — "> Joined rows with columns prefixed by table name. Each row includes `sourceTableId` (string), `sourceRowId` (integer), `columnData`, `createdAt`, and `updatedAt`. - `currentPage` (integer) — Current page number. - `pageSize` (integer) — Number of rows per page. - `totalRows` (integer) — Total number of joined rows. - `totalPages` (integer) — Total number of pages. - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/join-query?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "joins": [ { "rightTableId": "3oPtLQmZ7v", "joinType": "INNER", "leftColumnName": "user_id", "rightColumnName": "id" } ], "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "active" } ] } ] }' ``` ```json title="JSON" { "mainSchema": {...}, "joinedSchemas": { "790": {...} }, "rows": [ { "sourceTableId": "V1StGXR8Z5", "sourceRowId": 12345, "columnData": { "orders.order_id": {...}, "orders.user_id": {...}, "users.id": {...}, "users.name": {...} }, "createdAt": "2025-01-29T10:00:00", "updatedAt": "2025-01-29T15:30:00" } ], "currentPage": 1, "totalRows": 42, "totalPages": 1, "errors": [] } ``` --- ## Aggregate **POST** `/aggregate` Runs a grouped aggregation over a table: filter, group by up to a few columns, compute aggregates, optionally filter groups and sort, and return a capped set of groups. **Permission Required:** READ ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `whereGroups` — "> Optional pre-aggregation filter (same shape as `query`). - `groupsLogicalOperator` (string) — How to combine WHERE groups: `"AND"` or `"OR"`. Defaults to `"OR"`. - `groupBy` — "> Columns to group by (a small maximum applies). Omit to aggregate the whole filtered set into a single group. - `aggregates` — " required> The aggregates to compute. Each is `{ "function": "COUNT" | "SUM" | "AVG" | "MIN" | "MAX", "column": "...", "alias": "..." }`. `column` may be omitted for `COUNT` (counts rows). `alias` is required, must be unique, and is how the value is returned and referenced by `having` / `orderBy`. - `having` — "> Post-aggregation filters on computed aggregates, referenced by `alias`. Each is `{ "alias": "...", "operator": "=" | "!=" | ">" | "<" | ">=" | "<=", "value": }`. Multiple conditions are ANDed. - `orderBy` — "> Sort by a `groupBy` column or an aggregate alias. - `limit` (integer) — Maximum number of groups to return (clamped to the configured maximum). ### Response - `groups` — "> One entry per group: `{ "key": { : }, "values": { : } }`. `key` is empty when there is no `groupBy`. - `errors` — "> Array of error messages if any occurred. > Columns hidden from your access are treated as not existing, and columns you may only see in masked form cannot be grouped or aggregated (the request is rejected). Aggregation is additionally rate-limited per company (see [Rate Limits](#rate-limits)). ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/aggregate?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "groupBy": ["status"], "aggregates": [ { "function": "COUNT", "alias": "count" }, { "function": "AVG", "column": "age", "alias": "avg_age" } ], "orderBy": [{ "columnName": "count", "direction": "DESC" }] }' ``` ```json title="JSON" { "groups": [ { "key": { "status": "active" }, "values": { "count": 120, "avg_age": 34.2 } }, { "key": { "status": "inactive" }, "values": { "count": 30, "avg_age": 41.7 } } ], "errors": [] } ``` --- ## Get Single Row **POST** `/get-single-row` Retrieves a single row by ID with pre-signed URLs for FILE/IMAGE columns. **Permission Required:** READ ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `rowId` (integer, required) — The ID of the row to retrieve. ### Response - `schema` (AutessaDbSchema) — The table schema. - `row` (TableRowDto) — Single row with presigned URLs for FILE/IMAGE columns (10-minute expiry). - `errors` — "> Array of error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/get-single-row?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "rowId": 12345 }' ``` ```json title="JSON" { "schema": {...}, "row": { "id": 12345, "columnData": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "avatar": { "columnType": "IMAGE", "filePath": "s3://bucket/avatar.jpg", "presignedUrl": "https://s3.amazonaws.com/..." } }, "createdAt": "2025-01-29T10:00:00", "updatedAt": "2025-01-29T15:30:00" }, "errors": [] } ``` --- ## Semantic Search ## Search Table **POST** `/search` Performs semantic vector search on a table column using embeddings. Searches use different strategies based on column type (SHORT_TEXT vs LONG_TEXT). **Permission Required:** READ **Note:** Columns must be indexed before they can be searched. Use `/index-record` to index rows. ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `query` (string, required) — The search query text. - `columnName` (string, required) — The column name to search within. - `limit` (integer) — Maximum number of results. Default: 10. Maximum: 100. - `scoreThreshold` (number) — Minimum similarity score (0.0 to 1.0). Default: 0.0. - `page` (integer) — Page number (1-based). Default: 1. - `includeDetails` (boolean) — Whether to include detailed match information. Default: false. ### Response - `schema` (AutessaDbSchema) — The table schema. - `results` — "> Search results ordered by relevance (highest score first). - `totalResults` (integer) — Total number of results found (before pagination). - `currentPage` (integer) — Current page number. - `totalPages` (integer) — Total number of pages. - `metadata` (SearchMetadata) — Search metadata including query, column, strategy, execution time. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/search?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "query": "machine learning engineer", "columnName": "job_description", "limit": 10, "scoreThreshold": 0.7 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/search?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', query: 'machine learning engineer', columnName: 'job_description', limit: 10, scoreThreshold: 0.7 }) } ); const data = await response.json(); data.results.forEach(result => { console.log(`Score: ${result.score}, ID: ${result.recordId}`); }); ``` ```json title="JSON" { "schema": {...}, "results": [ { "recordId": 12345, "score": 0.92, "rank": 1, "contentHash": "hash_abc", "rowData": { "job_title": { "columnType": "SHORT_TEXT", "textValue": "Senior ML Engineer" }, "job_description": { "columnType": "LONG_TEXT", "textValue": "We are looking for..." } }, "createdAt": "2025-01-29T10:00:00", "updatedAt": "2025-01-29T15:30:00", "details": { "embedType": "FULL", "allMatches": [ { "embedType": "FULL", "score": 0.92 } ] } } ], "totalResults": 25, "currentPage": 1, "totalPages": 3, "metadata": { "query": "machine learning engineer", "columnName": "job_description", "columnType": "LONG_TEXT", "searchStrategy": "HYBRID", "targetEmbeddingTypes": ["FULL", "CHUNK"], "executionTimeMs": 45 }, "errors": [] } ``` --- ## File Upload ## Generate Upload Link **POST** `/generate-upload-link` Generates a pre-signed S3 upload URL for file/image/video columns. Upload your file to the returned URL, then use the S3 URI in insert/update operations. **Permission Required:** WRITE ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `fileType` (string, required) — File extension. Supported: `DOCX`, `DOC`, `PDF`, `PPTX`, `XLSX`, `XLS`, `TXT`, `JSON`, `YAML`, `YML`, `XML`, `HTML`, `HTM`, `CSV`, `EML`, `MD`, `MARKDOWN`, `PNG`, `JPG`, `JPEG`, `GIF`, `WEBP`, `AVIF`, `BMP`, `ICO`, `TIFF`, `TIF`, `HEIC`, `HEIF`, `MP4`, `AVI`, `MOV`, `WMV`, `FLV`, `MKV`, `WEBM`, `MPEG`, `MPG`, `MP3`, `M4A`, `WAV` - `columnName` (string, required) — The column name where the file will be stored. ### Response - `uploadUrl` (string) — Pre-signed URL for uploading the file via PUT request. - `s3Uri` (string) — S3 URI to use in insert/update requests. - `expiresInSeconds` (integer) — Time in seconds until the upload URL expires. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/generate-upload-link?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "fileType": "PDF", "columnName": "resume" }' ``` ```json title="JSON" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/uploads/resume-xyz.pdf", "expiresInSeconds": 3600, "errors": [] } ``` **1. Get Upload Link:** ```bash # Already done above ``` **2. Upload File to S3:** ```bash curl --request PUT \ --url 'https://s3.amazonaws.com/autessa-files/...' \ --header 'Content-Type: application/pdf' \ --data-binary '@resume.pdf' ``` **3. Use S3 URI in Insert:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/insert?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "values": { "resume": { "columnType": "FILE", "filePath": "s3://autessa-files/uploads/resume-xyz.pdf" } } }' ``` --- ## Indexing ## Index Record **POST** `/index-record` Indexes a row for semantic search. Creates vector embeddings for SHORT_TEXT and LONG_TEXT columns. Required before searching. **Permission Required:** WRITE **Note:** Indexing happens automatically on insert/update for most columns, but you can manually trigger it with this endpoint. ### Query Parameters - `resourceId` (string, required) — The ID of the table. ### Request Body - `tableId` (string, required) — The ID of the table. - `rowId` (integer, required) — The ID of the row to index. ### Response - `recordId` (integer) — The ID of the indexed record. - `columnsIndexed` (integer) — Number of columns that were indexed. - `vectorsCreated` (integer) — Total number of vectors created. - `indexingDurationMs` (integer) — Time taken for indexing in milliseconds. - `indexedColumns` — "> Names of columns that were indexed. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/autessadb/index-record?resourceId=V1StGXR8Z5' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "tableId": "V1StGXR8Z5", "rowId": 12345 }' ``` ```json title="JSON" { "recordId": 12345, "columnsIndexed": 3, "vectorsCreated": 15, "indexingDurationMs": 234, "indexedColumns": [ "job_title", "job_description", "requirements" ], "errors": [] } ``` --- ## Rate Limits Read and aggregation endpoints (`get-data`, `query`, `join-query`, `aggregate`) are rate-limited per company on a rolling per-minute and per-day basis. The ceiling is generous for normal first-party use; if you exceed it the request is rejected and you should retry after a short back-off. Write endpoints are governed by the per-call bulk row cap rather than a request-rate limit. --- ## Column Types AutessaDB supports various column types with different features: - `SHORT_TEXT` — Short text (max 32,000 chars). **Vectorization enabled** for semantic search. - `LONG_TEXT` — Long text (max 128,000 chars). **Vectorization enabled** for semantic search. - `FILE` — File attachment. Use `/generate-upload-link` to upload. - `IMAGE` — Image file (PNG, JPG, JPEG, GIF, WEBP, AVIF, BMP, ICO, TIFF, TIF, HEIC, HEIF). Every format except TIFF/TIF and HEIC/HEIF renders natively in an `` tag, so you can point one at the cell's `presignedUrl` directly; for TIFF and HEIC offer a download instead, since most browsers will not paint them. Animated GIF and WEBP are stored and served intact and animate in the browser, but agent/LLM extraction reads the first frame only. SVG is not an IMAGE type — it is an executable document, so it belongs in a FILE column. Use `/generate-upload-link` to upload. - `VIDEO` — Video file (MP4, AVI, MOV, etc.). Use `/generate-upload-link` to upload. - `INTEGER` — Integer number. - `FLOAT` — Floating point number. - `BOOLEAN` — True/False value. - `SINGLE_SELECT` — Single selection from predefined options. - `MULTI_SELECT` — Multiple selections from predefined options. - `DATETIME` — Legacy, read-only date/time type. Existing `DATETIME` columns (and the built-in `created_at`/`updated_at`) still read back their value, but new columns cannot be created with it — store timestamps as `SHORT_TEXT` holding UTC ISO-8601 strings (e.g. `2026-07-15T14:30:00Z`) instead. --- ## Append-Only Tables A table can be configured as **append-only** (immutable / audit-grade): rows may be inserted and read, but never updated or deleted. When a table is append-only, every write beyond insert is rejected for all callers — `update`, `batch-update`, `bulk-update-by-ids`, `delete-row`, `batch-delete`, and `bulk-delete-by-ids` all return an error. This makes append-only tables well suited to audit trails and event logs where records must be tamper-evident. The `schema` returned on every read call carries an `appendOnly` boolean so your app can tell whether a table is immutable before offering edit or delete actions. Append-only is a one-way setting — once on, it cannot be turned off — and is configured when the table is created (via the [poweruser Tables API](/docs/api/poweruser/autessadb-tables.md) or the builder), not through this row-level API. --- ## Indexed Columns Columns can carry an **equality index** for faster filtering in `query` / `join-query` / `aggregate`. `INTEGER`, `FLOAT`, `BOOLEAN`, `SINGLE_SELECT`, and foreign-key columns are always indexed. `SHORT_TEXT` columns can opt in — once a `SHORT_TEXT` column is indexed, its values are length-capped to 2000 characters. `LONG_TEXT`, `MULTI_SELECT`, and media columns cannot be indexed. Each column definition in a returned `schema` carries an `indexed` boolean. Indexes are configured at table creation or via a schema update (see the [poweruser Tables API](/docs/api/poweruser/autessadb-tables.md)); on a large table a newly requested index builds in the background and becomes usable once its build finishes. ### Column Value Format Each column value is an object with the column type and appropriate value field: **SHORT_TEXT / LONG_TEXT:** ```json { "columnType": "SHORT_TEXT", "textValue": "Hello world" } ``` **INTEGER:** ```json { "columnType": "INTEGER", "integerValue": 42 } ``` **FLOAT:** ```json { "columnType": "FLOAT", "floatValue": 3.14159 } ``` **BOOLEAN:** ```json { "columnType": "BOOLEAN", "booleanValue": true } ``` **SINGLE_SELECT:** ```json { "columnType": "SINGLE_SELECT", "textValue": "option1" } ``` **MULTI_SELECT:** ```json { "columnType": "MULTI_SELECT", "multiSelectValue": ["option1", "option2", "option3"] } ``` **DATETIME:** ```json { "columnType": "DATETIME", "textValue": "2025-01-29T15:30:00" } ``` **FILE / IMAGE / VIDEO:** ```json { "columnType": "IMAGE", "filePath": "s3://bucket/path/to/image.jpg" } ``` --- ## WHERE Conditions ### WhereGroupDto - `logicalOperator` (string, required) — How to combine conditions within this group: `"AND"` or `"OR"` - `conditions` — " required> List of conditions in this group ### WhereConditionDto - `columnName` (string, required) — Column to filter on - `operator` (string, required) — Comparison operator (see below) - `value` (any) — Value to compare against (not needed for IS_NULL/IS_NOT_NULL) ### Supported Operators - `EQUALS` — Exact match (=) - `NOT_EQUALS` — Not equal (!=) - `GREATER_THAN` — Greater than (>) - `LESS_THAN` — Less than (<) - `GREATER_THAN_OR_EQUAL` — Greater than or equal (>=) - `LESS_THAN_OR_EQUAL` — Less than or equal (<=) - `IN` — Value in list (value must be an array) - `NOT_IN` — Value not in list (value must be an array) - `IS_NULL` — Value is null (no value needed) - `IS_NOT_NULL` — Value is not null (no value needed) - `CONTAINS` — String contains substring - `STARTS_WITH` — String starts with - `ENDS_WITH` — String ends with These 13 operators are the complete set — the query builder rejects anything else. The same operators (and per-column-type support rules) are documented in the [Poweruser AutessaDB reference](/docs/api/poweruser/autessadb-query.md). ### WHERE Examples **Simple Condition:** ```json { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "age", "operator": "GREATER_THAN", "value": 18 } ] } ] } ``` **Multiple Conditions (AND):** ```json { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "age", "operator": "GREATER_THAN_OR_EQUAL", "value": 18 }, { "columnName": "status", "operator": "EQUALS", "value": "active" } ] } ] } ``` **Multiple Groups (OR):** ```json { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "role", "operator": "EQUALS", "value": "admin" } ] }, { "logicalOperator": "AND", "conditions": [ { "columnName": "role", "operator": "EQUALS", "value": "moderator" } ] } ], "groupsLogicalOperator": "OR" } ``` **IS_NULL Check:** ```json { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "deletedAt", "operator": "IS_NULL" } ] } ] } ``` **String Operations:** ```json { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "email", "operator": "ENDS_WITH", "value": "@example.com" }, { "columnName": "name", "operator": "CONTAINS", "value": "John" } ] } ] } ``` **IN Operator:** ```json { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "IN", "value": ["active", "pending", "verified"] } ] } ] } ``` --- ## JOIN Operations ### JoinConditionDto - `rightTableId` (string, required) — The table to join with - `joinType` (string) — Type of join: `"INNER"`, `"LEFT"`, `"RIGHT"`. Defaults to `"INNER"`. - `leftColumnName` (string, required) — Column from the main/left table - `rightColumnName` (string, required) — Column from the right table to join on ### JOIN Examples **Simple INNER JOIN:** ```json { "tableId": "V1StGXR8Z5", "joins": [ { "rightTableId": "3oPtLQmZ7v", "joinType": "INNER", "leftColumnName": "user_id", "rightColumnName": "id" } ] } ``` **Multiple JOINs:** ```json { "tableId": "V1StGXR8Z5", "joins": [ { "rightTableId": "3oPtLQmZ7v", "joinType": "INNER", "leftColumnName": "user_id", "rightColumnName": "id" }, { "rightTableId": "9uYwXn4RtK", "joinType": "LEFT", "leftColumnName": "product_id", "rightColumnName": "id" } ] } ``` **JOIN with WHERE:** ```json { "tableId": "V1StGXR8Z5", "joins": [ { "rightTableId": "3oPtLQmZ7v", "joinType": "INNER", "leftColumnName": "user_id", "rightColumnName": "id" } ], "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "completed" } ] } ] } ``` --- ## Complete Examples ### Create and Search Records ```javascript title="JavaScript" // 1. Insert a record const insertResp = await fetch( 'https://api.autessa.com/clients/autessadb/insert?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', values: { job_title: { columnType: 'SHORT_TEXT', textValue: 'Senior Machine Learning Engineer' }, job_description: { columnType: 'LONG_TEXT', textValue: 'We are seeking an experienced ML engineer to join our team...' }, salary: { columnType: 'INTEGER', integerValue: 150000 } } }) } ); const { insertedId } = await insertResp.json(); // 2. Index the record for search await fetch( 'https://api.autessa.com/clients/autessadb/index-record?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', rowId: insertedId }) } ); // 3. Search for similar jobs const searchResp = await fetch( 'https://api.autessa.com/clients/autessadb/search?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', query: 'machine learning position', columnName: 'job_description', limit: 10, scoreThreshold: 0.7 }) } ); const searchData = await searchResp.json(); console.log('Found', searchData.totalResults, 'matching jobs'); searchData.results.forEach(result => { console.log(`${result.rowData.job_title.textValue} - Score: ${result.score}`); }); ``` ### Batch Operations ```python title="Python" headers = { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' } # Batch update all inactive users update_resp = requests.post( 'https://api.autessa.com/clients/autessadb/batch-update?resourceId=V1StGXR8Z5', headers=headers, json={ 'tableId': 'V1StGXR8Z5', 'values': { 'status': { 'columnType': 'SINGLE_SELECT', 'textValue': 'archived' } }, 'whereGroups': [ { 'logicalOperator': 'AND', 'conditions': [ { 'columnName': 'status', 'operator': 'EQUALS', 'value': 'inactive' }, { 'columnName': 'lastLogin', 'operator': 'IS_NULL' } ] } ] } ) data = update_resp.json() print(f"Updated {data['updatedCount']} users") ``` ### File Upload Workflow ```javascript title="JavaScript" // 1. Generate upload link const linkResp = await fetch( 'https://api.autessa.com/clients/autessadb/generate-upload-link?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', fileType: 'PDF', columnName: 'resume' }) } ); const { uploadUrl, s3Uri } = await linkResp.json(); // 2. Upload file to S3 const fileData = await fetch('resume.pdf').then(r => r.blob()); await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': 'application/pdf' }, body: fileData }); // 3. Create record with file reference await fetch( 'https://api.autessa.com/clients/autessadb/insert?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'your_api_key' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', values: { name: { columnType: 'SHORT_TEXT', textValue: 'John Doe' }, resume: { columnType: 'FILE', filePath: s3Uri } } }) } ); ``` --- ## Best Practices ### Performance - Use pagination for large datasets - Set appropriate `scoreThreshold` for search to filter low-quality results - Use batch operations for multiple updates/deletes - Index records immediately after insert for search availability ### Security - Configure minimal required permissions on API keys - Use environment variables for sensitive data - Rotate API keys regularly - Never expose API keys in client-side code ### Data Integrity - Always check the `errors` array in responses - Validate data before insertion - Use transactions for related operations - Monitor `computationStatus` for async operations ### Search Optimization - Index all searchable text columns - Use appropriate column types (SHORT_TEXT vs LONG_TEXT) - Set reasonable `limit` values - Use `scoreThreshold` to filter irrelevant results - Check `metadata.executionTimeMs` to monitor performance --- ## Related - [API Overview](/docs/api.md) - [Agent API](/docs/api/agents.md) - [Manager API](/docs/api/managers.md) - [Tool API](/docs/api/tools.md) - [Authentication](/docs/guide/authentication.md) - [API Keys](/docs/guide/api-management/api-keys.md) --- # AutessaDB Drive API Source: https://docs.autessa.com/docs/api/autessadb-drive > Client API for AutessaDB Drives: browse folders, upload files, search exact text and ranked keywords (or meaning), read pages and lines, manage versions and trash, and share files and folders. # AutessaDB Drive API Programmatic access to an [AutessaDB Drive](/docs/guide/autessadb/drive.md): list and find files, grep their text and run ranked search over it (meaning-based when semantic search is on), read exact pages and lines, upload whole folders, and share files and folders with people, teams, your company, or a link. > Every request is a `POST` with a JSON body to `https://api.autessa.com/clients/autessadb-drive/`, with the Drive's ID in the `resourceId` query parameter and your API key in the `Authorization` header (no `Bearer` prefix). The body's `tableId` must match `resourceId`. ## Permissions Two layers apply to every call: 1. **The API key's permission on the Drive**, as for any table: **READ** for browsing, searching, reading, and downloading; **WRITE** for uploads, folders, moves, trash and restore, versions, sharing, and links; **DELETE** for permanent deletion. 2. **Sharing inside the Drive.** Requests run under the key's acting identity and only reach the files and folders that identity can open, with the role it has on each (Owner, Editor, or Viewer). Items it cannot open behave as if they do not exist. See [Sharing](/docs/guide/autessadb/drive.md#sharing). ## Paths Paths are absolute and start with `/`, for example `/Shared/Legal/Contracts/acme-msa.pdf`. Backslashes are treated as `/`, repeated slashes are collapsed, and `.` and `..` segments are rejected. Names are compared without regard to case, are at most 255 bytes, and paths are at most 64 levels deep. The Drive's root is `/`; a new Drive is empty, and every folder in it is one someone created. Calls that take an item accept either `path` or `nodeId`; calls that take several items accept `paths` or `nodeIds`. An item's `nodeId` never changes, even when it is moved or renamed. ## The item object Listings, searches, and most write calls return items in this shape: - `nodeId` (string) — The item's ID (a decimal string). - `path` (string) — Current absolute path. - `name` (string) — The last segment of the path. - `nodeType` (string) — `FILE` or `FOLDER`. - `parentId` (string) — ID of the containing folder, or `null` at the top level. - `sizeBytes` (integer) — File size. Files only. - `mimeType` (string) — File type. Files only. - `version` (integer) — Current version number, starting at 1. Files only. - `contentUpdatedAt` (string) — When the file's content last changed (ISO-8601 UTC). Files only. - `updatedAt` (string) — When the item last changed, including renames and moves. - `ownerUserId` (string) — The owner's user ID. - `ownerName` (string) — The owner's name. - `extractionStatus` (string) — Files only: `PENDING` or `EXTRACTING` while text is being extracted, `EXTRACTED` when it is searchable, `FAILED`, `UNSUPPORTED` (no extractable text), or `TOO_LARGE` (only the first part is searchable). - `trashedAt` (string) — When the item was trashed. Present only when listing with `includeTrashed`. - `customColumns` (object) — Values of any custom columns you added to the Drive, as a map of column name to typed column value (the same shape as [AutessaDB rows](/docs/api/autessadb.md)). ## Paging and truncation Nothing is dropped silently. Every listing and search response says whether it is complete: - `truncated` is `false` when you have everything, or a string naming the cap that stopped it: `"limit"` (page full), `"max_entries"` (tree size), `"max_matches_per_file"` (grep per-file cap), or `"timeout"` (grep time budget). - `nextCursor` continues from where the response stopped. Send it back as `cursor` with the **same** other arguments; a cursor replayed with different arguments is rejected with `INVALID_CURSOR`. ## Uploading files Uploads take three steps, so file bytes go straight to storage and large files can be sent in parallel parts: 1. [Start uploads](#start-uploads) with the paths and sizes. Storage is reserved and each file gets an upload link. 2. `PUT` each file's bytes to its `url` with the returned `headers`, or for multipart uploads, `PUT` each part to its [part link](#get-part-links) and then [complete](#complete-a-multipart-upload) it. 3. [Commit](#commit-uploads) the finished uploads. Files appear in the Drive and processing starts. The [CLI](/docs/cli/autessadb.md#how-to-work-with-a-drive) wraps all three steps in `autessa autessadb drive push` and `upload`. --- ## List a folder **POST** `/ls` Lists the direct children of a folder: folders first, then files, each group sorted by the chosen order. Only items the caller can open are returned. Omit both `path` and `nodeId` to list the top level of the Drive (or of the folder an agent tool is scoped to). **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. - `sort` (string) — `name` (default), `modified`, or `size`. Folders always come first. - `includeTrashed` (boolean) — Include items in the trash. Default `false`. - `limit` (integer) — Maximum entries to return. Default 200, maximum 1000. - `cursor` (string) — The `nextCursor` from the previous page. Send the same other arguments as the first call. ### Response - `driveRole` (string) — Your Drive membership: `OWNER` (the Drive's owner or a Drive admin), `EDITOR` or `VIEWER` (a member: the Drive itself is shared with you), or `NONE` (only items inside it are shared with you). Membership does not show any item by itself: members see what they own and what is shared with them. `OWNER` and `EDITOR` can add items at the top level. - `folder` (object) — The folder being listed, or `null` for the top level. - `items` — "> The folder's children. See [The item object](#the-item-object). - `nextCursor` (string) — Cursor for the next page, or `null` when there is nothing more. - `truncated` (boolean | string) — `false` when the response is complete; otherwise the reason it stopped early. See [Paging and truncation](#paging-and-truncation). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/ls?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal/Contracts", "limit": 2 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/ls?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal/Contracts', limit: 2 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/ls?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal/Contracts', 'limit': 2 } ) data = response.json() ``` ```json title="Response" { "folder": { "nodeId": "4170", "path": "/Shared/Legal/Contracts", "name": "Contracts", "nodeType": "FOLDER", "parentId": "4101", "updatedAt": "2026-09-21T09:12:40Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" } }, "items": [ { "nodeId": "4190", "path": "/Shared/Legal/Contracts/Archive", "name": "Archive", "nodeType": "FOLDER", "parentId": "4170", "updatedAt": "2026-09-21T09:12:40Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" } }, { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "name": "acme-msa.pdf", "nodeType": "FILE", "parentId": "4170", "sizeBytes": 482113, "mimeType": "application/pdf", "version": 2, "contentUpdatedAt": "2026-09-28T17:04:11Z", "updatedAt": "2026-09-28T17:04:11Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "extractionStatus": "EXTRACTED" } ], "nextCursor": "eyJrIjpbImFjbWUtbXNhLnBkZiIsNDE4Ml19", "truncated": "limit" } ``` --- ## Get a folder tree **POST** `/tree` Returns a folder and its descendants down to a depth, as a flat list in path order. Useful for getting an overview of a folder in one call. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Folder to start from. Defaults to the top level. - `maxDepth` (integer) — Levels below `path` to include. Default 3, maximum 20. - `maxEntries` (integer) — Maximum entries to return. Default 500, maximum 5000. ### Response - `root` (string) — The starting path. - `entries` — "> One entry per item: `nodeId`, `path`, `nodeType`, `depth` (relative to `root`), and for files `sizeBytes`. - `truncated` (boolean | string) — `"max_entries"` if more items exist than were returned. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/tree?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal", "maxDepth": 2 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/tree?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal', maxDepth: 2 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/tree?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal', 'maxDepth': 2 } ) data = response.json() ``` ```json title="Response" { "root": "/Shared/Legal", "entries": [ { "nodeId": "4170", "path": "/Shared/Legal/Contracts", "nodeType": "FOLDER", "depth": 1 }, { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "nodeType": "FILE", "depth": 2, "sizeBytes": 482113 }, { "nodeId": "4175", "path": "/Shared/Legal/Policies", "nodeType": "FOLDER", "depth": 1 } ], "truncated": false } ``` --- ## Find items **POST** `/find` Finds files and folders by name, pattern, type, date, or size anywhere under a folder. Glob patterns support `*`, `**` (any number of folders), `?`, character classes like `[0-9]`, and alternatives like `{pdf,docx}`. A pattern with no `/` (such as `*.pdf`) matches the name at any depth. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Folder to search under. Defaults to everything the caller can see. - `glob` (string) — Glob pattern matched against paths, e.g. `**/2026/*.pdf` or `invoice-*`. - `nameRegex` (string) — Regular expression matched against names. - `type` (string) — `FILE` or `FOLDER`. - `mime` (string) — Exact MIME type, or a prefix ending in `/`, e.g. `image/`. - `modifiedAfter` (string) — ISO-8601 UTC timestamp. Only items modified at or after it. - `modifiedBefore` (string) — ISO-8601 UTC timestamp. Only items modified before it. - `sizeGt` (integer) — Minimum file size in bytes. - `sizeLt` (integer) — Maximum file size in bytes. - `limit` (integer) — Maximum entries to return. Default 200, maximum 1000. - `cursor` (string) — The `nextCursor` from the previous page. Send the same other arguments as the first call. ### Response - `items` — "> Matching items in path order. See [The item object](#the-item-object). - `nextCursor` (string) — Cursor for the next page, or `null` when there is nothing more. - `truncated` (boolean | string) — `false` when the response is complete; otherwise the reason it stopped early. See [Paging and truncation](#paging-and-truncation). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/find?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal", "glob": "*.pdf", "modifiedAfter": "2026-09-01T00:00:00Z" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/find?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal', glob: '*.pdf', modifiedAfter: '2026-09-01T00:00:00Z' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/find?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal', 'glob': '*.pdf', 'modifiedAfter': '2026-09-01T00:00:00Z' } ) data = response.json() ``` ```json title="Response" { "items": [ { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "name": "acme-msa.pdf", "nodeType": "FILE", "parentId": "4170", "sizeBytes": 482113, "mimeType": "application/pdf", "version": 2, "contentUpdatedAt": "2026-09-28T17:04:11Z", "updatedAt": "2026-09-28T17:04:11Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "extractionStatus": "EXTRACTED" } ], "nextCursor": null, "truncated": false } ``` --- ## Get item details **POST** `/stat` Returns one item with its processing status, page and line counts, recent versions, and the caller's role on it. Callers who can manage sharing on the item also get its shares. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. ### Response The item's own fields (`nodeId`, `path`, `name`, `nodeType`, `sizeBytes`, `version`, …) at the top level, as in [The item object](#the-item-object), plus: - `effectiveRole` (string) — The caller's effective role: `OWNER`, `EDITOR`, or `VIEWER`. - `extraction` (object) — For files: `status`, `indexStatus`, `textKind` (`NATIVE` for text files with real line numbers, `PAGED` for documents extracted page by page), `pageCount`, `lineCount`, `ocrNeededPages` (scanned pages OCR was unsure of), `ocrStatus` (`DONE` when scanned pages were read by OCR, otherwise `NONE`), and `ocr`: `engine`, `pagesOcr` (pages read by OCR), `pagesLowConfidence` and `lowConfidencePages` (pages read with low confidence; their text is kept as read and is searchable), and `pagesUnreadable` and `unreadablePages` (pages with no searchable text). - `versionsCount` (integer) — How many previous versions the file has. List them with [List versions](#list-versions). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/stat?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal/Contracts/acme-msa.pdf" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/stat?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal/Contracts/acme-msa.pdf' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/stat?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal/Contracts/acme-msa.pdf' } ) data = response.json() ``` ```json title="Response" { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "name": "acme-msa.pdf", "parentId": "4170", "nodeType": "FILE", "depth": 4, "sizeBytes": 482113, "mimeType": "application/pdf", "version": 2, "contentUpdatedAt": "2026-09-28T17:04:11Z", "ownerUserId": "u8Kd2LmQ4x", "ownerName": "Dana Ortiz", "updatedAt": "2026-09-28T17:04:11Z", "extractionStatus": "EXTRACTED", "extraction": { "status": "EXTRACTED", "indexStatus": "NONE", "textKind": "PAGED", "pageCount": 14, "lineCount": 612, "ocrNeededPages": [9], "ocrStatus": "DONE", "ocr": { "engine": "tesseract", "pagesOcr": 3, "pagesLowConfidence": 1, "pagesUnreadable": 0, "lowConfidencePages": [9], "unreadablePages": [] } }, "versionsCount": 1, "effectiveRole": "EDITOR" } ``` --- ## Search exact text **POST** `/grep` Finds lines that contain a string or match a regular expression, across every searchable file under a folder. Matching is per line. Each match carries its page and line, so you can [read](#read-a-file) around it or cite it. Files that are not searchable yet are counted in `stats.filesUnsearchable` rather than silently skipped. Regular expressions support character classes, `\d`, `\w`, `\s`, alternation, anchors, and quantifiers. A regular expression must contain at least 3 literal characters unless the search is scoped to 2,000 files or fewer. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `pattern` (string, required) — Text or regular expression to find. Up to 1,000 characters. - `regex` (boolean) — Treat `pattern` as a regular expression. Default `false` (literal text). - `caseInsensitive` (boolean) — Ignore case. Default `false`. - `path` (string) — Folder or file to search. Defaults to everything the caller can see. - `glob` (string) — Only search files whose path matches this glob, e.g. `*.pdf`. - `outputMode` (string) — `matches` (default) returns each matching line; `files` returns only the files with a match; `count` returns the match count per file. - `context` (integer) — Lines of context before and after each match, 0 to 10. Default 0. - `maxMatchesPerFile` (integer) — Maximum matches returned per file. Default 20. - `timeoutMs` (integer) — Time budget in milliseconds. Default 5000, maximum 15000. - `limit` (integer) — Maximum entries to return. Default 100, maximum 1000. - `cursor` (string) — The `nextCursor` from the previous page. Send the same other arguments as the first call. ### Response - `matches` — "> In `matches` mode: `nodeId`, `path`, `version`, `page` and `pageLine` (for paged documents), `line`, `text`, `submatches` (character `start`/`end` of each hit in `text`), and `before`/`after` context lines. - `files` — "> In `files` mode: `files`, each with `nodeId`, `path` and `matchCount`; in `count` mode: `count` with `files` and `matches`. - `stats` (object) — `filesUnsearchable`: counts of files in scope that could not be searched, by reason (`pending`, `failed`, `unsupported`, `tooLarge`), with `samplePaths`. - `nextCursor` (string) — Cursor for the next page, or `null` when there is nothing more. - `truncated` (boolean | string) — `false` when the response is complete; otherwise the reason it stopped early. See [Paging and truncation](#paging-and-truncation). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/grep?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "pattern": "termination for convenience", "caseInsensitive": true, "path": "/Shared/Legal", "context": 1 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/grep?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', pattern: 'termination for convenience', caseInsensitive: true, path: '/Shared/Legal', context: 1 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/grep?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'pattern': 'termination for convenience', 'caseInsensitive': True, 'path': '/Shared/Legal', 'context': 1 } ) data = response.json() ``` ```json title="Response" { "matches": [ { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "version": 2, "page": 4, "pageLine": 12, "line": 171, "text": "12.2 Termination for Convenience. Either party may terminate this Agreement", "submatches": [ { "start": 5, "end": 32 } ], "before": [ "12.1 Term. This Agreement begins on the Effective Date." ], "after": [ "upon sixty (60) days written notice to the other party." ] } ], "stats": { "filesUnsearchable": { "pending": 1, "failed": 0, "unsupported": 0, "tooLarge": 0, "samplePaths": [ "/Shared/Legal/Contracts/globex-sow.docx" ] } }, "nextCursor": null, "truncated": false } ``` --- ## Read a file **POST** `/read` Returns a file's extracted text for a page range or a line range. Use it to read around a match before quoting. A single call returns at most 20 pages or 2,000 lines, and at most `maxChars` characters; when the range is cut short, `nextRange` tells you where to continue. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. - `pages` (string) — Page range for paged documents, e.g. `"4"` or `"3-5"`. - `lines` (string) — Line range, e.g. `"100-250"`. Send `pages` or `lines`; with neither, reading starts at the beginning. - `maxChars` (integer) — Maximum characters to return. Default 20,000, maximum 100,000. ### Response - `path` (string) — The file's current path. - `version` (integer) — The version that was read. - `kind` (string) — `NATIVE` or `PAGED`. - `lines` — "> The lines read: `line`, `text`, and for paged documents `page` and `pageLine`. Empty lines are included with empty `text`. - `nextRange` (string) — The range to request next, or `null` at the end of the file. - `truncated` (boolean | string) — `"limit"` when the range was cut short by `maxChars` or the per-call caps. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/read?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "pages": "4" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/read?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal/Contracts/acme-msa.pdf', pages: '4' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/read?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal/Contracts/acme-msa.pdf', 'pages': '4' } ) data = response.json() ``` ```json title="Response" { "path": "/Shared/Legal/Contracts/acme-msa.pdf", "version": 2, "kind": "PAGED", "lines": [ { "line": 160, "page": 4, "pageLine": 1, "text": "12. TERM AND TERMINATION" }, { "line": 171, "page": 4, "pageLine": 12, "text": "12.2 Termination for Convenience. Either party may terminate this Agreement" } ], "nextRange": "5", "truncated": false } ``` --- ## Search file contents **POST** `/search` Ranked keyword search over file contents; meaning-based when the Drive's **Semantic search** setting is on. Each result is a passage with its page and line range and a snippet. - **Semantic search off (the default):** files that contain more of the query's words rank first, then files with more matches, earlier pages, and matching names. Common words are ignored. Snippets mark the matched words as `**word**`, results have `source: "keyword"`, and `stats.mode` is `keyword`. Every `mode` behaves this way. - **Semantic search on:** `hybrid` (the default) combines meaning-based and keyword ranking, which works well for both questions and specific terms. Semantic search usage counts toward your company's search limits. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `query` (string, required) — What to look for, in plain language or keywords. - `mode` (string) — `hybrid` (default), `semantic` (meaning only), `lexical` (keywords only), or `keyword`. Applies when semantic search is on; otherwise every mode is a ranked keyword search. - `path` (string) — Folder or file to search. Defaults to everything the caller can see. - `glob` (string) — Only search files whose path matches this glob. - `topK` (integer) — Number of results. Default 10, maximum 50. ### Response - `results` — "> Best passages first: `nodeId`, `path`, `score`, `page` (for paged documents), `lineStart`, `lineEnd`, `snippet`, and `source` (`keyword`, or with semantic search on `hybrid`, `dense`, or `bm25`). - `stats` (object) — How the search ran, including `mode` (`keyword` when semantic search is off). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/search?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "query": "can either side end the contract early?", "path": "/Shared/Legal/Contracts", "topK": 3 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/search?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', query: 'can either side end the contract early?', path: '/Shared/Legal/Contracts', topK: 3 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/search?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'query': 'can either side end the contract early?', 'path': '/Shared/Legal/Contracts', 'topK': 3 } ) data = response.json() ``` ```json title="Response" { "results": [ { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "score": 0.83, "page": 4, "lineStart": 168, "lineEnd": 179, "snippet": "12.2 Termination for Convenience. Either party may terminate this Agreement upon sixty (60) days written notice...", "source": "hybrid" } ], "stats": { "mode": "hybrid" } } ``` --- ## Get a download link **POST** `/download-url` Returns a short-lived link to download a file's content, or one of its previous versions. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. - `version` (integer) — A previous version to download. Defaults to the current version. ### Response - `url` (string) — The download link. - `expiresAt` (string) — When the link stops working (ISO-8601 UTC). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/download-url?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeId": "4182" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/download-url?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeId: '4182' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/download-url?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeId': '4182' } ) data = response.json() ``` ```json title="Response" { "url": "https://files.autessa.com/d/9f3c1a7e...", "expiresAt": "2026-10-02T15:10:00Z" } ``` --- ## Fetch a file **POST** `/fetch` Returns a 10-minute download link for a file, or one of its previous versions, together with the file's name, type, size and version. Same as [`/download-url`](#get-a-download-link), which stays available, plus the file's details. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the file, e.g. `/Reports/q1.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The file's ID. - `version` (integer) — A previous version. Defaults to the current version. ### Response - `url` (string) — The download link, valid for 10 minutes. - `expiresAt` (string) — When the link stops working (ISO-8601 UTC). - `nodeId` (string) — The file's ID. - `path` (string) — The file's path. - `name` (string) — The file's name. - `mime` (string) — MIME type of the version served. - `sizeBytes` (integer) — Size of the version served. - `version` (integer) — The version served. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/fetch?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"tableId": "V1StGXR8Z5", "path": "/Reports/q1.pdf"}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/fetch?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({'tableId': 'V1StGXR8Z5', 'path': '/Reports/q1.pdf'}) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/fetch?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={'tableId': 'V1StGXR8Z5', 'path': '/Reports/q1.pdf'} ) data = response.json() ``` ```json title="Response" { "url": "https://files.autessa.com/d/9f3c1a7e...", "expiresAt": "2026-10-03T15:10:00Z", "nodeId": "4182", "path": "/Reports/q1.pdf", "name": "q1.pdf", "mime": "application/pdf", "sizeBytes": 248112, "version": 2 } ``` --- ## Get processing status **POST** `/status` Summarizes how many files in the Drive are searchable, still processing, or not searchable. Use it after a large upload to wait until everything is searchable. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. ### Response - `extraction` (object) — File counts by text status (`PENDING`, `EXTRACTING`, `EXTRACTED`, `FAILED`, `UNSUPPORTED`, `TOO_LARGE`). - `index` (object) — File counts by semantic-search index status (used only while the Drive's semantic search is on). - `files` (integer) — Files in the Drive. - `folders` (integer) — Folders in the Drive. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/status?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/status?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/status?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5' } ) data = response.json() ``` ```json title="Response" { "extraction": { "PENDING": 0, "EXTRACTING": 2, "EXTRACTED": 318, "FAILED": 1, "UNSUPPORTED": 4, "TOO_LARGE": 0 }, "index": { "NONE": 325 }, "files": 325, "folders": 41 } ``` --- ## Get storage usage **POST** `/usage` Returns your company's Drive storage and processing usage against its limits. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. ### Response - `bytesUsed` (integer) — Bytes stored, including previous versions and trash. - `bytesLimit` (integer) — Storage allowance in bytes. - `filesCount` (integer) — Files in the Drive. - `maxFileBytes` (integer) — Largest file you can upload, in bytes. - `extractedPagesThisMonth` (integer) — Pages extracted this calendar month. - `extractedPagesLimit` (integer) — Monthly page allowance, or `null` if unlimited. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/usage?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/usage?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/usage?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5' } ) data = response.json() ``` ```json title="Response" { "bytesUsed": 18253611008, "bytesLimit": 53687091200, "filesCount": 12408, "maxFileBytes": 5368709120, "extractedPagesThisMonth": 4120, "extractedPagesLimit": 50000 } ``` --- ## Write a file **POST** `/write` Writes text to a file: creates it, replaces its content, or appends to its end. Missing folders are created. A replaced file keeps its previous content in its [version history](#list-versions), and the new text is searchable with [grep](#search-exact-text) as soon as the call returns. Text files only (by extension or MIME type), up to 5 MB; upload anything else with [uploads](#start-uploads). **Permission:** WRITE, and Editor on the file (or on its folder for a new file) ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string, required) — Absolute path of the file, e.g. `/Shared/notes/memory.md`. - `content` (string) — The text, UTF-8. Absent or empty writes an empty file. - `mode` (string) — `overwrite` (default) replaces the file, creating it when missing. `create` fails with `NAME_CONFLICT` when the file exists. `append` adds `content` to the end, starting a new line when the file does not end with one, and creates the file when missing. - `mime` (string) — MIME type. Default: the existing file's, else inferred from the extension (`.md` text/markdown, `.json` application/json, `.csv` text/csv), else text/plain. - `expectedVersion` (integer) — Only write when the file is still at this version (`0`: it must not exist yet). Otherwise the call fails with `VERSION_CONFLICT`, and `error.details.currentVersion` holds the current version. ### Response - `node` (object) — The file after the write (see [The item object](#the-item-object)). - `version` (integer) — The file's version after the write (`1` for a new file). - `bytes` (integer) — Size of the file after the write. - `created` (boolean) — `true` when the write created the file. - `mode` (string) — The mode applied. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/write?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"tableId": "V1StGXR8Z5", "path": "/Shared/notes/memory.md", "content": "- Acme prefers Net 45", "mode": "append"}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/write?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({'tableId': 'V1StGXR8Z5', 'path': '/Shared/notes/memory.md', 'content': '- Acme prefers Net 45', 'mode': 'append'}) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/write?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={'tableId': 'V1StGXR8Z5', 'path': '/Shared/notes/memory.md', 'content': '- Acme prefers Net 45', 'mode': 'append'} ) data = response.json() ``` ```json title="Response" { "node": { "nodeId": "5120", "path": "/Shared/notes/memory.md", "version": 4, "mimeType": "text/markdown" }, "version": 4, "bytes": 112, "created": false, "mode": "append" } ``` --- ## Edit a file **POST** `/edit` Replaces an exact piece of text in a text file, like an editor's find-and-replace. `oldText` must occur exactly once unless `replaceAll` is `true`. The change is saved as a new version. Files with Windows line endings also match an `oldText` written with plain line breaks. Text files up to 5 MB. **Permission:** WRITE, and Editor on the file ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the file. Send `path` or `nodeId`. - `nodeId` (string) — The file's ID. - `oldText` (string, required) — The exact text to replace, including spaces and line breaks. - `newText` (string, required) — The replacement. An empty string deletes `oldText`. - `replaceAll` (boolean) — Replace every occurrence instead of requiring exactly one. Default `false`. - `expectedVersion` (integer) — Only edit when the file is still at this version; otherwise `VERSION_CONFLICT`. ### Response - `node` (object) — The file after the edit. - `version` (integer) — The file's new version. - `replacements` (integer) — How many places were changed. - `bytes` (integer) — Size of the file after the edit. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/edit?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"tableId": "V1StGXR8Z5", "path": "/Shared/notes/plan.md", "oldText": "Status: draft", "newText": "Status: approved"}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/edit?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({'tableId': 'V1StGXR8Z5', 'path': '/Shared/notes/plan.md', 'oldText': 'Status: draft', 'newText': 'Status: approved'}) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/edit?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={'tableId': 'V1StGXR8Z5', 'path': '/Shared/notes/plan.md', 'oldText': 'Status: draft', 'newText': 'Status: approved'} ) data = response.json() ``` ```json title="Response" { "node": { "nodeId": "5121", "path": "/Shared/notes/plan.md", "version": 3 }, "version": 3, "replacements": 1, "bytes": 2048 } ``` --- ## Create folders **POST** `/mkdir` Creates one or more folders, along with any missing parent folders. Existing folders are left as they are, so the call is safe to repeat. **Permission:** WRITE, and Editor on the parent folder ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `paths` — " required> Folders to create (up to 1,000), e.g. `["/Shared/Reports/2026"]`. - `parents` (boolean) — Create missing parent folders too. Default `true`. ### Response - `folders` — "> Every requested folder, existing or new. See [The item object](#the-item-object). - `created` — "> The folders this call created. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/mkdir?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "paths": [ "/Shared/Legal/Contracts/2026/Q4", "/Shared/Legal/Contracts/2026/Q3" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/mkdir?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', paths: ['/Shared/Legal/Contracts/2026/Q4', '/Shared/Legal/Contracts/2026/Q3'] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/mkdir?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'paths': ['/Shared/Legal/Contracts/2026/Q4', '/Shared/Legal/Contracts/2026/Q3'] } ) data = response.json() ``` ```json title="Response" { "created": [ { "nodeId": "4201", "paths": ["/Shared/Legal/Contracts/2026"], "name": "2026", "nodeType": "FOLDER", "parentId": "4101", "updatedAt": "2026-09-21T09:12:40Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" } }, { "nodeId": "4202", "paths": ["/Shared/Legal/Contracts/2026/Q4"], "name": "Q4", "nodeType": "FOLDER", "parentId": "4201", "updatedAt": "2026-09-21T09:12:40Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" } }, { "nodeId": "4203", "paths": ["/Shared/Legal/Contracts/2026/Q3"], "name": "Q3", "nodeType": "FOLDER", "parentId": "4201", "updatedAt": "2026-09-21T09:12:40Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" } } ] } ``` --- ## Move or rename **POST** `/move` Moves an item into another folder, renames it, or both. Moving a folder moves everything inside it. Because access follows folders, a move can change who sees the item. If it would, and `confirm` is not `true`, nothing moves and the response lists who would gain and lose access; repeat the call with `confirm: true` to go ahead. A folder that contains items the caller cannot see cannot be moved by that caller. **Permission:** WRITE, Editor on the item, and Editor on the destination folder ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `paths` — "> Paths of the items to move, e.g. `["/Inbox/acme-msa.pdf"]`. Send `paths`, `nodeIds`, or both. With one item named by path and a `destinationPath` that does not exist yet (inside an existing folder), the item is moved there under that name, like `mv a b`. - `nodeIds` — "> IDs of the items to move. - `destinationPath` (string, required) — The folder to move into. - `newName` (string) — New name, when moving a single item. - `confirm` (boolean) — Proceed even if the move changes who has access. Default `false`. ### Response - `moved` — "> The moved items at their new paths, when the move happened. - `requiresConfirmation` (boolean) — `true` when nothing moved because access would change. - `accessChanges` (object) — `gaining` and `losing`: the people, teams, and company-wide shares that would gain or lose access, each with their `role` and the folder it comes from. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/move?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "paths": ["/Drafts/globex-sow.docx"], "destinationPath": "/Shared/Legal/Contracts" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/move?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', paths: ['/Drafts/globex-sow.docx'], destinationPath: '/Shared/Legal/Contracts' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/move?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'paths': ['/Drafts/globex-sow.docx'], 'destinationPath': '/Shared/Legal/Contracts' } ) data = response.json() ``` ```json title="Response (confirmation needed)" { "requiresConfirmation": true, "accessChanges": { "gaining": [ { "principal": { "type": "TEAM", "teamId": "3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903", "name": "Legal" }, "role": "EDITOR" } ], "losing": [] } } ``` --- ## Move to trash **POST** `/trash` Moves items to the trash. A trashed folder takes everything inside it along. Trashed items keep their shares, are hidden from listings and search, and can be restored until the trash retention period ends. **Permission:** WRITE, and Editor on each item ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `paths` — "> Paths of the items to trash. Send `paths`, `nodeIds`, or both. - `nodeIds` — "> IDs of the items to trash. - `permanent` (boolean) — Delete the items for good right after trashing them; they cannot be restored. Needs the key's DELETE permission. Default `false`. ### Response - `trashed` (integer) — How many items were trashed. - `items` — "> The trashed items. - `purged` (integer) — With `permanent`: rows deleted for good, including everything inside folders. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/trash?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeIds": [ "4182", "4190" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/trash?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeIds: ['4182', '4190'] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/trash?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeIds': ['4182', '4190'] } ) data = response.json() ``` ```json title="Response" { "trashed": 2, "items": [ { "nodeId": "4182", "path": "/Shared/Legal/Contracts/acme-msa.pdf", "nodeType": "FILE" }, { "nodeId": "4190", "path": "/Shared/Legal/Contracts/Archive", "nodeType": "FOLDER" } ] } ``` --- ## Restore from trash **POST** `/restore` Restores trashed items to their original paths. If something now occupies a path, the restored item gets a numbered name. Missing parent folders are recreated. **Permission:** WRITE, and Editor on each item ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `nodeIds` — " required> Trashed items to restore. ### Response - `restored` — "> One entry per item: `nodeId` and `path` (the final path, which differs from the original if it was renamed). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/restore?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeIds": [ "4190" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/restore?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeIds: ['4190'] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/restore?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeIds': ['4190'] } ) data = response.json() ``` ```json title="Response" { "restored": [ { "nodeId": "4190", "path": "/Shared/Legal/Contracts/Archive" } ] } ``` --- ## Delete permanently **POST** `/purge` Permanently deletes trashed items, their versions, and their shares and links. This cannot be undone. To empty the trash, call `/empty-trash` (no other arguments). **Permission:** DELETE, and Owner on each item (or Drive manager) ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `nodeIds` — "> Trashed items to delete. ### Response - `purged` (integer) — Items permanently deleted, including folder contents. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/purge?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeIds": [ "4182" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/purge?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeIds: ['4182'] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/purge?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeIds': ['4182'] } ) data = response.json() ``` ```json title="Response" { "purgedCount": 1, "bytesFreed": 962033 } ``` --- ## Start uploads **POST** `/uploads/create` Registers up to 1,000 files to upload and returns where to send each one. Paths, permissions, file size, and storage are checked up front, and storage is reserved, so a file that cannot be stored is refused before you send it. Files up to 100 MB use one `PUT` to `url`; larger files use multipart upload with [part links](#get-part-links). Then [commit](#commit-uploads) the finished uploads. **Permission:** WRITE, and Editor on each destination folder ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `basePath` (string) — Folder that relative `files[].path` values are resolved against. Missing folders are created on commit. - `conflict` (string) — What to do when a file already exists at a path: `FAIL` (default), `RENAME` (keep both, adding a number), or `NEW_VERSION` (replace, keeping the old content as a version). - `files` — " required> Files to upload: `path` (absolute, or relative to `basePath`), `size` in bytes, and optionally `mimeType` and `sha256` (hex). When `sha256` is sent, the upload is rejected if the content does not match. ### Response - `uploads` — "> One entry per accepted file: `uploadId`, `path`, `method` (`SINGLE` or `MULTIPART`), and `expiresAt`. `SINGLE` entries carry `url` and `headers` to send with the `PUT`; `MULTIPART` entries carry `partSize` and `partCount`. - `errors` — "> Files that were refused: `path`, `errorCode`, and `message`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/uploads/create?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "basePath": "/Shared/Legal/Contracts/2026/Q4", "conflict": "RENAME", "files": [ { "path": "initech-msa.pdf", "size": 913442, "mimeType": "application/pdf" } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/uploads/create?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', basePath: '/Shared/Legal/Contracts/2026/Q4', conflict: 'RENAME', files: [ { path: 'initech-msa.pdf', size: 913442, mimeType: 'application/pdf' } ] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/uploads/create?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'basePath': '/Shared/Legal/Contracts/2026/Q4', 'conflict': 'RENAME', 'files': [ { 'path': 'initech-msa.pdf', 'size': 913442, 'mimeType': 'application/pdf' } ] } ) data = response.json() ``` ```json title="Response" { "uploads": [ { "uploadId": "5d0a7c2e-1b9f-4e3a-8f61-0c2d9b7a4e15", "path": "/Shared/Legal/Contracts/2026/Q4/initech-msa.pdf", "method": "SINGLE", "url": "https://uploads.autessa.com/u/5d0a7c2e...", "headers": { "Content-Type": "application/pdf" }, "expiresAt": "2026-10-02T16:00:00Z" } ], "errors": [] } ``` --- ## Get part links **POST** `/uploads/part-urls` For a multipart upload, returns links to `PUT` each part to. Every part except the last must be exactly `partSize` bytes. Keep the `ETag` response header from each part for [completion](#complete-a-multipart-upload). **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `uploadId` (string, required) — The multipart upload. - `partNumbers` — " required> Part numbers to get links for, starting at 1. ### Response - `partUrls` (object) — Map of part number to upload link. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/uploads/part-urls?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "uploadId": "8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64", "partNumbers": [ 1, 2, 3 ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/uploads/part-urls?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', uploadId: '8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64', partNumbers: [1, 2, 3] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/uploads/part-urls?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'uploadId': '8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64', 'partNumbers': [1, 2, 3] } ) data = response.json() ``` ```json title="Response" { "partUrls": { "1": "https://uploads.autessa.com/u/8e4b2f10.../1", "2": "https://uploads.autessa.com/u/8e4b2f10.../2", "3": "https://uploads.autessa.com/u/8e4b2f10.../3" } } ``` --- ## Complete a multipart upload **POST** `/uploads/complete-multipart` Assembles the uploaded parts of a multipart upload. Then commit it like any other upload. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `uploadId` (string, required) — The multipart upload. - `parts` — " required> Every part: `partNumber` and the `etag` returned when it was uploaded. ### Response - `uploadId` (string) — The upload, now ready to commit. - `status` (string) — `UPLOADED`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/uploads/complete-multipart?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "uploadId": "8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64", "parts": [ { "partNumber": 1, "etag": "\"a54357aff0632cce46d942af68356b38\"" }, { "partNumber": 2, "etag": "\"0c78aef83f66abc1fa1e8477f296d394\"" }, { "partNumber": 3, "etag": "\"acbd18db4cc2f85cedef654fccc4a4d8\"" } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/uploads/complete-multipart?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', uploadId: '8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64', parts: [ { partNumber: 1, etag: '"a54357aff0632cce46d942af68356b38"' }, { partNumber: 2, etag: '"0c78aef83f66abc1fa1e8477f296d394"' }, { partNumber: 3, etag: '"acbd18db4cc2f85cedef654fccc4a4d8"' } ] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/uploads/complete-multipart?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'uploadId': '8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64', 'parts': [ { 'partNumber': 1, 'etag': '"a54357aff0632cce46d942af68356b38"' }, { 'partNumber': 2, 'etag': '"0c78aef83f66abc1fa1e8477f296d394"' }, { 'partNumber': 3, 'etag': '"acbd18db4cc2f85cedef654fccc4a4d8"' } ] } ) data = response.json() ``` ```json title="Response" { "uploadId": "8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64", "status": "UPLOADED" } ``` --- ## Commit uploads **POST** `/uploads/commit` Turns finished uploads into Drive files, creating missing folders and applying the conflict rule chosen at start. Up to 100 uploads per call; each succeeds or fails on its own. Committed files appear immediately and become searchable once processing finishes. Insert or update triggers on the Drive fire for each file. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `uploadIds` — " required> Uploads to commit, up to 100. ### Response - `results` — "> One entry per upload: `uploadId`, `status` (`COMMITTED` or `FAILED`), `node` when committed, and `errorCode`/`message` when not. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/uploads/commit?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "uploadIds": [ "5d0a7c2e-1b9f-4e3a-8f61-0c2d9b7a4e15" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/uploads/commit?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', uploadIds: ['5d0a7c2e-1b9f-4e3a-8f61-0c2d9b7a4e15'] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/uploads/commit?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'uploadIds': ['5d0a7c2e-1b9f-4e3a-8f61-0c2d9b7a4e15'] } ) data = response.json() ``` ```json title="Response" { "results": [ { "uploadId": "5d0a7c2e-1b9f-4e3a-8f61-0c2d9b7a4e15", "status": "COMMITTED", "node": { "nodeId": "4210", "path": "/Shared/Legal/Contracts/2026/Q4/initech-msa.pdf", "name": "initech-msa.pdf", "nodeType": "FILE", "parentId": "4202", "sizeBytes": 913442, "mimeType": "application/pdf", "version": 1, "contentUpdatedAt": "2026-09-28T17:04:11Z", "updatedAt": "2026-09-28T17:04:11Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "extractionStatus": "PENDING" } } ] } ``` --- ## Cancel uploads **POST** `/uploads/abort` Cancels uploads that have not been committed and releases their reserved storage. Uploads that are never committed expire on their own. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `uploadIds` — " required> Uploads to cancel. ### Response - `aborted` — "> The uploads that were cancelled. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/uploads/abort?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "uploadIds": [ "8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/uploads/abort?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', uploadIds: ['8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64'] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/uploads/abort?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'uploadIds': ['8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64'] } ) data = response.json() ``` ```json title="Response" { "aborted": [ "8e4b2f10-6a3d-4c7e-b5a9-2f1e0d8c7b64" ] } ``` --- ## List versions **POST** `/versions/list` Lists a file's versions, newest first. The current content is the highest version. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. ### Response - `versions` — "> `version`, `sizeBytes`, `sha256`, `createdAt`, `createdBy`, and `current` (`true` for the current content). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/versions/list?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeId": "4182" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/versions/list?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeId: '4182' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/versions/list?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeId': '4182' } ) data = response.json() ``` ```json title="Response" { "versions": [ { "version": 2, "sizeBytes": 482113, "sha256": "9b74c9897bac770ffc029102a200c5de...", "createdAt": "2026-09-28T17:04:11Z", "createdBy": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "current": true }, { "version": 1, "sizeBytes": 479920, "sha256": "3f786850e387550fdab836ed7e6dc881...", "createdAt": "2026-09-02T11:30:05Z", "createdBy": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "current": false } ] } ``` --- ## Restore a version **POST** `/versions/restore` Makes a previous version current again. The restore itself creates a new version, so nothing is lost. **Permission:** WRITE, and Editor on the file ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `nodeId` (string, required) — The file's ID. - `version` (integer, required) — The version to restore. ### Response - `node` (object) — The file with its new `version`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/versions/restore?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeId": "4182", "version": 1 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/versions/restore?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeId: '4182', version: 1 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/versions/restore?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeId': '4182', 'version': 1 } ) data = response.json() ``` ```json title="Response" { "node": { "nodeId": "4182", "name": "acme-msa.pdf", "nodeType": "FILE", "parentId": "4170", "sizeBytes": 479920, "mimeType": "application/pdf", "version": 3, "contentUpdatedAt": "2026-09-28T17:04:11Z", "updatedAt": "2026-09-28T17:04:11Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "extractionStatus": "EXTRACTED" } } ``` --- ## Who has access **POST** `/shares/list` Lists everyone who can reach an item: its owner, Drive managers, direct shares, shares inherited from parent folders (deepest folder first, with the folder they come from), and active links. Drive members are listed separately in `members`, because membership doesn't give access to an item. Send `path: "/"` to list the Drive's members. **Permission:** READ, and the ability to see the item ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. ### Response - `owner` (object) — The item's owner: `userId` and `name`. - `managers` — "> Drive managers, who can see and manage everything. - `shares` — "> One entry per person, team, or company-wide share that gives access to this item: `principal` (`type` of `USER`, `TEAM`, or `COMPANY`, with `userId` or `teamId` and `name`), `role`, `direct` (`true` if shared on this item), `inheritedFrom` (`nodeId` and `path` of the folder it comes from; `null` when direct), `expiresAt`, and `alsoVia` (other folders that grant the same principal a weaker or equal role). With `path: "/"`, the Drive's members. - `links` — "> Active links on this item or a parent folder. Same shape as [List links](#list-links). - `members` — "> The Drive's members, in the same shape as `shares`, with each member's Drive role. Members see the Drive in their list but only the items shared with them. Returned to members and Drive managers; absent for callers who only have items shared with them. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/shares/list?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeId": "4182" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/shares/list?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeId: '4182' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/shares/list?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeId': '4182' } ) data = response.json() ``` ```json title="Response" { "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "managers": [ { "userId": "k3Jd8sLq2W", "name": "Priya Natarajan" } ], "shares": [ { "principal": { "type": "USER", "userId": "pQ7wR2tY9z", "name": "Sam Lee" }, "role": "EDITOR", "direct": true, "inheritedFrom": null, "expiresAt": null, "alsoVia": [] }, { "principal": { "type": "TEAM", "teamId": "3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903", "name": "Legal" }, "role": "VIEWER", "direct": false, "inheritedFrom": { "nodeId": "4101", "path": "/Shared/Legal" }, "expiresAt": null, "alsoVia": [] }, { "principal": { "type": "COMPANY", "name": "Anyone at Acme Corp" }, "role": "VIEWER", "direct": false, "inheritedFrom": { "nodeId": "4175", "path": "/Shared/Legal/Policies" }, "expiresAt": null, "alsoVia": [] } ], "links": [], "members": [ { "principal": { "type": "TEAM", "teamId": "3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903", "name": "Legal" }, "role": "VIEWER", "direct": true, "inheritedFrom": null, "expiresAt": null, "alsoVia": [] } ] } ``` --- ## Share an item **POST** `/shares/upsert` Shares a folder or a file with people, teams, or everyone at your company, adds members to the Drive, or changes the role of an existing share. Sharing a folder shares everything inside it, including items added later. Sharing the Drive itself (`path: "/"`, or `scope: "drive"`) adds **members**: the Drive appears in their list of Drives and an `EDITOR` member can add items at the top level, but members see no folder or file until it is shared with them. To share a folder or file with every current member, send `"principals": "members"`; each member gets their own share on the item. You can grant up to your own role. **Permission:** WRITE, and Owner on the item, Drive manager, or Editor when Editors can share is on ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. - `scope` (string) — `drive` to add members to the Drive. Same as sending `path: "/"`. - `principals` — | string" required> Who to share with. Each entry is `{"type": "USER", "userId": ...}`, `{"type": "TEAM", "teamId": ...}`, or `{"type": "COMPANY"}`. Users and teams must belong to your company. Send the string `"members"` (or an entry `{"type": "MEMBERS"}`) to share a folder or file with every current member of the Drive; not allowed with `path: "/"`. - `role` (string, required) — `VIEWER` or `EDITOR`. - `expiresAt` (string) — ISO-8601 UTC time when the share ends. ### Response - `shares` — "> The resulting shares, same shape as [Who has access](#who-has-access). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/shares/upsert?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal/Contracts", "principals": [ { "type": "TEAM", "teamId": "3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903" }, { "type": "USER", "userId": "pQ7wR2tY9z" } ], "role": "EDITOR" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/shares/upsert?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal/Contracts', principals: [ { type: 'TEAM', teamId: '3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903' }, { type: 'USER', userId: 'pQ7wR2tY9z' } ], role: 'EDITOR' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/shares/upsert?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal/Contracts', 'principals': [ { 'type': 'TEAM', 'teamId': '3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903' }, { 'type': 'USER', 'userId': 'pQ7wR2tY9z' } ], 'role': 'EDITOR' } ) data = response.json() ``` ```json title="Response" { "shares": [ { "principal": { "type": "TEAM", "teamId": "3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903", "name": "Legal" }, "role": "EDITOR", "direct": true, "inheritedFrom": null, "expiresAt": null, "alsoVia": [] }, { "principal": { "type": "USER", "userId": "pQ7wR2tY9z", "name": "Sam Lee" }, "role": "EDITOR", "direct": true, "inheritedFrom": null, "expiresAt": null, "alsoVia": [] } ] } ``` --- ## Stop sharing **POST** `/shares/remove` Removes one direct share, by its `grantId` (from [Who has access](#who-has-access)). Access ends immediately. To remove inherited access, remove the share on the folder it comes from. **Permission:** WRITE, and the same rights as sharing ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `grantId` (string, required) — The share to remove: a `grantId` from [Who has access](#who-has-access). ### Response - `ok` (boolean) — `true` when the share was removed. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/shares/remove?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "grantId": "6f1c2b9e-4d7a-4f0e-9b2a-1c3d5e7f9a0b" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/shares/remove?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', grantId: '6f1c2b9e-4d7a-4f0e-9b2a-1c3d5e7f9a0b' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/shares/remove?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'grantId': '6f1c2b9e-4d7a-4f0e-9b2a-1c3d5e7f9a0b' } ) data = response.json() ``` ```json title="Response" { "ok": true } ``` --- ## Shared with me **POST** `/shared-with-me` Lists items other people have shared with the caller, directly, through one of their teams, or with everyone at the company. Only the shared item itself is listed, not its contents; browse into a shared folder with [List a folder](#list-a-folder). When the caller is a member of the Drive, the Drive is listed as one entry whose `node` is the Drive itself (`path` `/`, named after the Drive) and whose `role` is the caller's Drive role. Membership shows only the Drive, not its contents. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `limit` (integer) — Maximum entries to return. Default 200, maximum 1000. - `cursor` (string) — The `nextCursor` from the previous page. Send the same other arguments as the first call. ### Response - `items` — "> One entry per shared item: `node` (see [The item object](#the-item-object)), `role` (your role), `grantedBy` and `createdAt`. - `nextCursor` (string) — Cursor for the next page; absent on the last page. - `hasMore` (boolean) — `true` when another page exists. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/shared-with-me?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/shared-with-me?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/shared-with-me?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5' } ) data = response.json() ``` ```json title="Response" { "items": [ { "node": { "nodeId": "4101", "path": "/Shared/Legal", "name": "Legal", "nodeType": "FOLDER", "parentId": "4001", "updatedAt": "2026-09-21T09:12:40Z", "owner": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" } }, "role": "VIEWER", "sharedBy": { "userId": "k3Jd8sLq2W", "name": "Priya Natarajan" }, "sharedAt": "2026-08-14T10:02:00Z", "via": { "type": "TEAM", "name": "Legal" } } ], "nextCursor": null, "truncated": false } ``` --- ## Check access **POST** `/access` Returns the caller's effective role on an item and their Drive membership. To see every share on an item, use [Who has access](#who-has-access). **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. ### Response - `effectiveRole` (string) — `OWNER`, `EDITOR`, `VIEWER`, or `NONE`. - `driveRole` (string) — The caller's Drive membership: `OWNER` (owner or Drive admin), `EDITOR` or `VIEWER` (a member), or `NONE`. Membership alone gives no access to files and folders. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/access?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeId": "4182" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/access?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeId: '4182' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/access?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeId': '4182' } ) data = response.json() ``` ```json title="Response" { "effectiveRole": "EDITOR", "driveRole": "VIEWER" } ``` --- ## Transfer ownership **POST** `/transfer-owner` Makes another person in your company the owner of an item. The previous owner keeps Editor access. **Permission:** WRITE, and Owner on the item or Drive manager ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `nodeId` (string, required) — The item's ID. - `newOwnerUserId` (string, required) — The new owner: a member of your company. ### Response - `node` (object) — The item with its new owner. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/transfer-owner?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "nodeId": "4182", "newOwnerUserId": "pQ7wR2tY9z" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/transfer-owner?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', nodeId: '4182', newOwnerUserId: 'pQ7wR2tY9z' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/transfer-owner?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'nodeId': '4182', 'newOwnerUserId': 'pQ7wR2tY9z' } ) data = response.json() ``` ```json title="Response" { "node": { "nodeId": "4182", "name": "acme-msa.pdf", "nodeType": "FILE", "parentId": "4170", "sizeBytes": 482113, "mimeType": "application/pdf", "version": 2, "contentUpdatedAt": "2026-09-28T17:04:11Z", "updatedAt": "2026-09-28T17:04:11Z", "owner": { "newOwnerUserId": "pQ7wR2tY9z", "name": "Sam Lee" }, "extractionStatus": "EXTRACTED" } } ``` --- ## Create a link **POST** `/links/create` Creates a link to a file, a folder, or the whole Drive (`path: "/"`). A folder or Drive link opens a read-only view of everything inside it. Links keep working when the item is moved or renamed and stop working when it is trashed. The link `url` is returned only by this call. Autessa does not store it in readable form, so save it if you need it again; otherwise revoke the link and create a new one. **Permission:** WRITE, and the same rights as sharing ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Absolute path of the item, e.g. `/Contracts/acme-msa.pdf`. Send `path` or `nodeId`. - `nodeId` (string) — The item's ID. Takes precedence over `path` when both are sent. - `scope` (string) — `drive` to link the whole Drive. Same as sending `path: "/"`. - `audience` (string) — `COMPANY` (default): only signed-in people at your company. `ANYONE`: anyone with the link, no sign-in. - `role` (string) — `VIEWER` (default). - `allowDownload` (boolean) — Let link visitors download files. Default `false` (preview only). - `allowSearch` (boolean) — Let link visitors search inside the shared folder. Default `true`. - `expiresAt` (string) — ISO-8601 UTC time when the link stops working. ### Response - `url` (string) — The link. Shown only once. - `link` (object) — The link's settings, same shape as [List links](#list-links). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/links/create?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal/Contracts", "audience": "ANYONE", "allowDownload": false, "expiresAt": "2026-12-31T23:59:59Z" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/links/create?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal/Contracts', audience: 'ANYONE', allowDownload: false, expiresAt: '2026-12-31T23:59:59Z' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/links/create?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal/Contracts', 'audience': 'ANYONE', 'allowDownload': False, 'expiresAt': '2026-12-31T23:59:59Z' } ) data = response.json() ``` ```json title="Response" { "url": "https://app.autessa.com/public/drive/Xk9vQ2...", "link": { "linkId": "c6e2d9a4-0f3b-4a71-9e58-7d1b2c4f6a80", "target": { "nodeId": "4170", "path": "/Shared/Legal/Contracts" }, "role": "VIEWER", "audience": "ANYONE", "allowDownload": false, "allowSearch": true, "expiresAt": "2026-12-31T23:59:59Z", "createdBy": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "createdAt": "2026-10-02T14:58:00Z", "accessCount": 0, "lastAccessedAt": null } } ``` --- ## List links **POST** `/links/list` Lists active links, for one item or for everything the caller can manage. Link URLs are not included. **Permission:** READ ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `path` (string) — Only links on this item. - `nodeId` (string) — Only links on this item. ### Response - `links` — "> Each link: `linkId`, `target` (`nodeId` and `path`), `role`, `audience`, `allowDownload`, `allowSearch`, `expiresAt`, `createdBy`, `createdAt`, `accessCount`, and `lastAccessedAt`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/links/list?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "path": "/Shared/Legal/Contracts" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/links/list?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', path: '/Shared/Legal/Contracts' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/links/list?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'path': '/Shared/Legal/Contracts' } ) data = response.json() ``` ```json title="Response" { "links": [ { "linkId": "c6e2d9a4-0f3b-4a71-9e58-7d1b2c4f6a80", "target": { "nodeId": "4170", "path": "/Shared/Legal/Contracts" }, "role": "VIEWER", "audience": "ANYONE", "allowDownload": false, "allowSearch": true, "expiresAt": "2026-12-31T23:59:59Z", "createdBy": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "createdAt": "2026-10-02T14:58:00Z", "accessCount": 14, "lastAccessedAt": "2026-10-02T13:20:44Z" } ] } ``` --- ## Revoke a link **POST** `/links/revoke` Revokes a link. It stops working immediately, including for anyone who already has it open. **Permission:** WRITE, and the same rights as sharing ### Request parameters - `tableId` (string, required) — The Drive's ID. Must match the `resourceId` query parameter. - `linkId` (string, required) — The link to revoke. ### Response - `revoked` (boolean) — `true` when the link was revoked. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb-drive/links/revoke?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "linkId": "c6e2d9a4-0f3b-4a71-9e58-7d1b2c4f6a80" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb-drive/links/revoke?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', linkId: 'c6e2d9a4-0f3b-4a71-9e58-7d1b2c4f6a80' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb-drive/links/revoke?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'linkId': 'c6e2d9a4-0f3b-4a71-9e58-7d1b2c4f6a80' } ) data = response.json() ``` ```json title="Response" { "revoked": true } ``` --- ## Comments Comments live under `/comments/*` and need the **Commenter** role or higher on the item (owners and Drive managers always qualify). All calls take `tableId` and identify the item by `nodeId` or `path`. - `POST /comments/list` with `includeResolved` and `cursor`: threads with their replies, each with the author's display name, time, body, `anchor`, and `resolved`. - `POST /comments/add` with `body`, optional `anchor`, and optional `parentId` to reply. - `POST /comments/edit` with `commentId` and `body` (authors only). - `POST /comments/resolve` with `commentId` and `resolved` (Editors, owners, and managers). - `POST /comments/delete` with `commentId` (authors; Drive managers for any comment). An `anchor` is one of `{ "kind": "page", "page": 3, "quote": "..." }`, `{ "kind": "text", "from": 120, "to": 164, "quote": "..." }` (offsets into the file's extracted text), `{ "kind": "cell", "sheet": "Q1", "range": "B2:D9" }`, `{ "kind": "slide", "slide": 2 }`, or omitted for the whole file. Anchors carry the version they were made on; a text anchor whose quote no longer matches a newer version is returned with `stale: true`. `stat` includes `commentCount` and `openCommentCount`. Live co-editing uses a WebSocket at `/ws/autessadb/drive/collab` with the same session parameters as other sockets; only Editors and above may send changes. ## Errors Failed calls return an error code, a plain-language message and a hint with the next step. Some errors add `details`: ```json title="Error response" { "errorCode": "VERSION_CONFLICT", "errors": ["/Shared/notes/plan.md is now at version 5"], "hint": "Read the file again, then retry with expectedVersion=5.", "error": { "code": "VERSION_CONFLICT", "message": "/Shared/notes/plan.md is now at version 5", "hint": "Read the file again, then retry with expectedVersion=5.", "details": { "path": "/Shared/notes/plan.md", "currentVersion": 5 } } } ``` | Status | Error code | When | | --- | --- | --- | | `400` | `INVALID_PATH` | The path is malformed, contains `.` or `..`, or exceeds the length or depth limits. | | `400` | `INVALID_PATTERN` | A glob or regular expression is invalid, too broad (fewer than 3 literal characters over a large scope), or longer than 1,000 characters. | | `400` | `INVALID_CURSOR` | The cursor is malformed, expired, or was sent with different arguments than the call that produced it. | | `400` | `INVALID_MOVE` | A folder was moved into itself or one of its own subfolders. | | `401` | — | Missing or invalid API key. | | `403` | `FORBIDDEN` | The key lacks the table permission for the operation, the body's `tableId` does not match `resourceId`, the caller's role on the item is too low, or a folder contains items the caller cannot see. | | `404` | `NOT_FOUND` | The item does not exist or the caller cannot open it. | | `409` | `NAME_CONFLICT` | The name is already taken in the folder and the conflict rule is `FAIL`, or a `write` with `mode: create` found the file. | | `409` | `VERSION_CONFLICT` | A `write` or `edit` named an `expectedVersion` the file is no longer at. `error.details.currentVersion` holds the current version. | | `409` | `EDIT_NO_MATCH` | An `edit`'s `oldText` does not occur in the file. | | `409` | `EDIT_AMBIGUOUS` | An `edit`'s `oldText` occurs more than once and `replaceAll` is not set. `error.details.matches` holds the count. | | `413` | `FILE_TOO_LARGE` | A `write` or `edit` would make the file larger than 5 MB. Upload large files instead. | | `413` | `QUOTA_EXCEEDED` | The upload would exceed your storage allowance, the per-file size limit, or the file-count limit. | | `413` | `TOO_MANY_ITEMS` | A move or trash would touch more than 100,000 items at once. | | `415` | `UNSUPPORTED_TYPE` | The file type is not allowed in a Drive. | | `429` | `RATE_LIMITED` | Too many search, grep, or read calls; retry after a short wait. Semantic search also counts toward your company's search limits. | ## Related - [AutessaDB Drive guide](/docs/guide/autessadb/drive.md) - [AutessaDB API](/docs/api/autessadb.md) — row operations for regular tables - [Analytics API](/docs/api/autessadb-analytics.md) - [Drive tool type](/docs/tools/autessadb.md#drive) - [Drive commands in the CLI](/docs/cli/autessadb.md#how-to-work-with-a-drive) - [`autessa.autessadb` SDK](/docs/autessascript/sdk/autessadb.md) --- # AutessaDB Analytics API Source: https://docs.autessa.com/docs/api/autessadb-analytics > Client API for AutessaDB analytics tables: describe tables, run read-only SQL as jobs, page and download results, and load or append data. # AutessaDB Analytics API Query [analytics tables](/docs/guide/autessadb/analytics.md) from your own code: describe what a table holds, run read-only SQL as a job, page through or download the result, and keep the table fed by loading files or appending rows. > Every request is a `POST` with a JSON body to `https://api.autessa.com/clients/autessadb/analytics/`, with the analytics table's ID in the `resourceId` query parameter and your API key in the `Authorization` header (no `Bearer` prefix). For a query that joins several tables, `resourceId` is the primary table, and the key needs READ on every table the query reads. ## Permissions | Permission | Operations | | --- | --- | | READ | describe, preview, estimate, query, job-status, job-results, job-download-url, cancel, jobs | | WRITE | load-from-s3, append-rows | Creating tables, changing columns, and editing the semantic layer are on the [Analytics Poweruser API](/docs/api/poweruser/autessadb-analytics.md) and in the [builder](/docs/guide/autessadb/analytics.md). ## Values in results Rows are JSON objects keyed by column name. Values keep their exact meaning: | Column type | JSON value | | --- | --- | | `STRING`, `BOOLEAN`, `INT`, `DOUBLE` | String, boolean, or number | | `LONG` | Number, or a string when the value is too large to be exact in JSON | | `DECIMAL` | String, e.g. `"1284410.50"` | | `DATE` | `"2026-10-01"` | | `TIMESTAMP`, `TIMESTAMPTZ` | ISO-8601 UTC string, e.g. `"2026-10-01T00:00:00Z"` | | `STRING_LIST` | Array of strings | | `JSON` | The JSON value | | empty | `null` | ## Queries are jobs Every query, load, and append runs as a job. Queries run asynchronously and typically take a few seconds; large scans take longer. Queries are written in ANSI SQL; date and approximate functions use the standard analytical names such as `date_trunc('month', ts)`, `approx_distinct(col)`, and `format_datetime(ts, 'yyyy-MM')`. A typical query: 1. [Run a query](#run-a-query) with `waitMs` up to 20 seconds. If the job finishes in that time, it comes back `SUCCEEDED` with a `preview` of the first 200 rows. 2. If the state is `QUEUED` or `RUNNING`, call [Get job status](#get-job-status) with `waitMs` until it finishes. 3. Page through [Get job results](#get-job-results), or get a [download link](#download-job-results) for the whole result. Results are kept for 7 days. A query can run for at most 15 minutes (less on some plans) before it ends as `TIMED_OUT`. ## Uploading a file to load Files for [Load files](#load-files) are uploaded with the client intake endpoints at `https://api.autessa.com/clients/autessadb-intake`, which send the bytes straight to storage in parts: 1. `POST /multipart-initiate` with `{"fileName": "orders-2026-09.csv"}` returns `uploadId`, `s3Key`, and `s3Uri`. 2. `POST /multipart-part-urls` with `uploadId`, `s3Key`, and `partNumbers` returns a `partUrls` map. `PUT` each part (at least 5 MB except the last) to its link and keep each response's `ETag` header. 3. `POST /multipart-complete` with `uploadId`, `s3Key`, and `partETags` (part number to ETag) returns the final `s3Uri`. The [CLI](/docs/cli/autessadb.md#how-to-load-and-query-analytics-tables) does all of this for you with `autessa autessadb analytics load --localFile`. --- ## Describe tables **POST** `/describe` Returns everything needed to query one or more analytics tables: SQL name, description, grain, row count, partition columns, columns with types and descriptions, measures, joins, and verified queries. Columns the caller cannot see are omitted. Agents should call this before writing SQL. **Permission:** READ on every table described ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `tableIds` — "> Additional tables to describe in the same call. - `includeStats` (boolean) — Include per-column statistics (null count, distinct count, minimum, maximum, and top values). Default `false`. ### Response - `tables` — "> One entry per table: `tableId`, `sqlName`, `name`, `description`, `grain`, `rowCount`, `dataBytes`, `lastLoadAt`, `partitionSpec`, `columns` (`name`, `type`, `nullable`, `description`, `synonyms`, `masked`, and `stats` when requested), `measures` (`name`, `sql`, `description`), `joins` (`sqlName`, `on`, `relationship`), and `verifiedQueries` (`id`, `question`, `sql`). - `semanticsVersion` (integer) — Version of the primary table's semantic layer, for [Update the semantic layer](/docs/api/poweruser/autessadb-analytics.md#update-the-semantic-layer). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/describe?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "includeStats": false }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/describe?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', includeStats: false }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/describe?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'includeStats': False } ) data = response.json() ``` ```json title="Response" { "tables": [ { "tableId": "V1StGXR8Z5", "sqlName": "order_lines", "name": "Order Lines", "description": "Every order line since 2019, loaded nightly from the ERP.", "grain": "one row per order line", "rowCount": 418220931, "dataBytes": 21474836480, "lastLoadAt": "2026-10-02T06:00:12Z", "partitionSpec": [ { "column": "order_date", "transform": "MONTH" } ], "columns": [ { "name": "order_id", "type": "LONG", "nullable": false, "description": "ERP order number." }, { "name": "order_date", "type": "DATE", "nullable": false, "description": "Date the order was placed." }, { "name": "region", "type": "STRING", "nullable": false, "description": "Sales region.", "synonyms": [ "territory" ] }, { "name": "net_amount", "type": "DECIMAL(18,2)", "nullable": false, "description": "Line revenue after discounts, in USD.", "synonyms": [ "net sales" ] } ], "measures": [ { "name": "revenue", "sql": "SUM(net_amount)", "description": "Net revenue in USD." } ], "joins": [ { "sqlName": "customers", "on": "order_lines.customer_id = customers.id", "relationship": "MANY_TO_ONE" } ], "verifiedQueries": [ { "id": "vq_7d2a", "question": "Monthly revenue by region this year", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ] } ], "semanticsVersion": 6 } ``` --- ## Preview rows **POST** `/preview` Returns the first rows of a table without writing SQL. Previews run ahead of regular queries. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `limit` (integer) — Rows to return, up to 100. Default 50. ### Response - `columns` — "> Column `name` and `type`, in order. - `rows` — "> Rows as objects keyed by column name. See [Values in results](#values-in-results). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/preview?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "limit": 2 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/preview?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', limit: 2 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/preview?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'limit': 2 } ) data = response.json() ``` ```json title="Response" { "columns": [ { "name": "order_id", "type": "LONG" }, { "name": "order_date", "type": "DATE" }, { "name": "region", "type": "STRING" }, { "name": "net_amount", "type": "DECIMAL(18,2)" } ], "rows": [ { "order_id": "90210331", "order_date": "2026-10-01", "region": "NA", "net_amount": "129.00" }, { "order_id": "90210332", "order_date": "2026-10-01", "region": "EMEA", "net_amount": "48.50" } ] } ``` --- ## Estimate a query **POST** `/estimate` Checks a query and reports an upper bound on how much data it would scan, without running it. Use it to catch a missing partition filter before running an expensive query. **Permission:** READ on every table the query references ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sql` (string, required) — The query to estimate. ### Response - `estimatedBytesScanned` (integer) — Upper bound on data the query would read, after skipping partitions its filters rule out. - `referencedTables` — "> SQL names of the tables the query reads. - `withinLimits` (boolean) — `false` if the estimate exceeds the per-query or remaining daily scan limit. - `hint` (string) — When `withinLimits` is `false`: what to filter on, naming the partition columns. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/estimate?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "tableId": "V1StGXR8Z5", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } JSON ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/estimate?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sql: "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/estimate?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sql': "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ) data = response.json() ``` ```json title="Response" { "estimatedBytesScanned": 1288490188, "referencedTables": [ "order_lines" ], "withinLimits": true, "hint": null } ``` --- ## Run a query **POST** `/query` Runs a read-only SQL query as a job. If the job finishes within `waitMs`, the response includes the first rows; otherwise poll [Get job status](#get-job-status) and then fetch [results](#get-job-results). The query must be a single `SELECT` statement, optionally with `WITH`, written in ANSI SQL. It can read any analytics tables the caller has READ access to, by SQL name. Row-level and field-level security apply. Statements that change data or settings are rejected with `FORBIDDEN_CONSTRUCT`. SQL is limited to 100 KB. **Permission:** READ on every table the query references ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sql` (string, required) — The query. - `maxRows` (integer) — Maximum rows to keep in the result. Capped at your plan's result-row limit; a larger result is cut off with `truncated: true`. - `maxBytesScanned` (integer) — Refuse to run if the estimate exceeds this many bytes. Cannot exceed your plan's per-query limit. - `waitMs` (integer) — How long to wait for the job before returning, up to 20000. `0` returns as soon as the job is queued. ### Response - `jobId` (string) — The job's ID. - `state` (string) — `QUEUED`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`, or `TIMED_OUT`. - `bytesScanned` (integer) — Data read so far, in bytes. - `elapsedMs` (integer) — Run time so far. - `error` (object) — When the job failed: `errorCode`, `message`, and a `hint` such as the valid column names. See [Errors](#errors). - `preview` (object) — When the job succeeded within `waitMs`: `columns`, the first 200 `rows`, `totalRows`, and `truncated` (`true` if the result hit `maxRows`). **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/query?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "tableId": "V1StGXR8Z5", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", "waitMs": 20000 } JSON ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/query?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sql: "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", waitMs: 20000 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/query?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sql': "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", 'waitMs': 20000 } ) data = response.json() ``` ```json title="Response" { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "state": "SUCCEEDED", "bytesScanned": 402653184, "elapsedMs": 2140, "preview": { "columns": [ { "name": "region", "type": "STRING" }, { "name": "month", "type": "TIMESTAMPTZ" }, { "name": "revenue", "type": "DECIMAL(18,2)" } ], "rows": [ { "region": "EMEA", "month": "2026-01-01T00:00:00Z", "revenue": "1284410.50" }, { "region": "NA", "month": "2026-01-01T00:00:00Z", "revenue": "2390112.75" } ], "totalRows": 24, "truncated": false } } ``` --- ## Get job status **POST** `/job-status` Returns a job's state, optionally waiting for it to finish. Jobs can be read by the caller who started them, or by anyone with READ on every table they reference. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. - `waitMs` (integer) — How long to wait for the job to finish before returning, up to 20000. ### Response - `jobId` (string) — The job's ID. - `state` (string) — `QUEUED`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`, or `TIMED_OUT`. - `bytesScanned` (integer) — Data read so far, in bytes. - `elapsedMs` (integer) — Run time so far. - `error` (object) — When the job failed: `errorCode`, `message`, and a `hint` such as the valid column names. See [Errors](#errors). - `resultRowCount` (integer) — Rows in the result, once succeeded. - `truncated` (boolean) — `true` if the result was cut off at `maxRows`. - `resultColumns` — "> Column `name` and `type` of the result. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/job-status?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "waitMs": 10000 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/job-status?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', waitMs: 10000 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/job-status?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', 'waitMs': 10000 } ) data = response.json() ``` ```json title="Response" { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "state": "SUCCEEDED", "bytesScanned": 402653184, "elapsedMs": 2140, "resultRowCount": 24, "truncated": false, "resultColumns": [ { "name": "region", "type": "STRING" }, { "name": "month", "type": "TIMESTAMPTZ" }, { "name": "revenue", "type": "DECIMAL(18,2)" } ] } ``` --- ## Get job results **POST** `/job-results` Returns one page of a finished job's result. Results are kept for 7 days. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. - `page` (integer) — Page number, starting at 0. - `pageSize` (integer) — Rows per page, up to 1000. Default 1000. ### Response - `columns` — "> Column `name` and `type`. - `rows` — "> Rows as objects keyed by column name. See [Values in results](#values-in-results). - `page` (integer) — This page. - `totalRows` (integer) — Rows in the whole result. - `hasMore` (boolean) — `true` if later pages exist. - `truncated` (boolean) — `true` if the result itself was cut off at `maxRows`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/job-results?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "page": 0, "pageSize": 1000 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/job-results?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', page: 0, pageSize: 1000 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/job-results?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', 'page': 0, 'pageSize': 1000 } ) data = response.json() ``` ```json title="Response" { "columns": [ { "name": "region", "type": "STRING" }, { "name": "month", "type": "TIMESTAMPTZ" }, { "name": "revenue", "type": "DECIMAL(18,2)" } ], "rows": [ { "region": "EMEA", "month": "2026-01-01T00:00:00Z", "revenue": "1284410.50" }, { "region": "NA", "month": "2026-01-01T00:00:00Z", "revenue": "2390112.75" } ], "page": 0, "totalRows": 24, "hasMore": false, "truncated": false } ``` --- ## Download job results **POST** `/job-download-url` Returns a short-lived link to download a finished job's full result. Only the caller who ran the job can download it. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. - `format` (string) — `CSV` (default) or `PARQUET`. ### Response - `url` (string) — The download link. - `expiresAt` (string) — When the link stops working, 5 minutes after it is issued. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/job-download-url?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "format": "CSV" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/job-download-url?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', format: 'CSV' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/job-download-url?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', 'format': 'CSV' } ) data = response.json() ``` ```json title="Response" { "url": "https://files.autessa.com/r/1f8e5c3a.../result.csv", "expiresAt": "2026-10-02T15:05:00Z" } ``` --- ## Cancel a job **POST** `/cancel` Cancels a queued or running job. A running job stops within a few seconds. **Permission:** READ, and the caller must have started the job ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. ### Response - `jobId` (string) — The job. - `state` (string) — `CANCELLED`, or the final state if the job had already finished. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/cancel?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/cancel?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/cancel?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54' } ) data = response.json() ``` ```json title="Response" { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "state": "CANCELLED" } ``` --- ## List jobs **POST** `/jobs` Lists recent jobs on a table, newest first: queries, loads, and appends. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `kind` (string) — Only this kind: `QUERY`, `LOAD`, or `APPEND`. - `state` (string) — Only jobs in this state. - `mine` (boolean) — Only jobs the caller started. Default `false`. - `limit` (integer) — Jobs per page, up to 100. Default 25. - `cursor` (string) — The `nextCursor` from the previous page. ### Response - `jobs` — "> Each job: `jobId`, `kind`, `state`, `sql` (first 500 characters), `createdBy`, `createdAt`, `finishedAt`, `resultRowCount`, `bytesScanned`, and `elapsedMs`. - `nextCursor` (string) — Cursor for the next page, or `null`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/jobs?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "kind": "QUERY", "limit": 1 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/jobs?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', kind: 'QUERY', limit: 1 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/jobs?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'kind': 'QUERY', 'limit': 1 } ) data = response.json() ``` ```json title="Response" { "jobs": [ { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "kind": "QUERY", "state": "SUCCEEDED", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", "createdBy": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "createdAt": "2026-10-02T14:59:51Z", "finishedAt": "2026-10-02T14:59:53Z", "resultRowCount": 24, "bytesScanned": 402653184, "elapsedMs": 2140 } ], "nextCursor": "eyJ0IjoiMjAyNi0xMC0wMlQxNDo1OTo1MVoifQ" } ``` --- ## Load files **POST** `/load-from-s3` Loads one or more CSV or Parquet files into the table as a job. Upload each file first (see [Uploading a file to load](#uploading-a-file-to-load)) and pass the `s3Uri` you get back. Values are converted to the column types; rows that cannot be converted are counted and sampled in the job result rather than silently dropped. A load is all-or-nothing: if it fails, no rows are added. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sourceUris` — " required> The uploaded files' `s3Uri` values. - `format` (string) — `CSV` or `PARQUET`. Inferred from the file extension when omitted. - `mode` (string) — `APPEND` (default) adds the rows; `REPLACE` swaps the table's contents for them. - `csv` (object) — CSV options: `delimiter` (default `,`), `header` (default `true`), `nullString`, `quote`, `dateFormat`, and `timestampFormat`. - `columnMapping` (object) — Map of file column name to table column name, when they differ. Unmapped table columns are left empty. ### Response - `jobId` (string) — The load job. Poll [Get job status](#get-job-status); a finished load reports `rowsLoaded`, `rowsRejected`, and `rejectedSample`. - `state` (string) — The job's state. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/load-from-s3?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "sourceUris": [ "s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-2026-09.csv" ], "format": "CSV", "mode": "APPEND", "csv": { "header": true, "timestampFormat": "%Y-%m-%d %H:%M:%S" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/load-from-s3?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sourceUris: ['s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-2026-09.csv'], format: 'CSV', mode: 'APPEND', csv: { header: true, timestampFormat: '%Y-%m-%d %H:%M:%S' } }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/load-from-s3?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sourceUris': ['s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-2026-09.csv'], 'format': 'CSV', 'mode': 'APPEND', 'csv': { 'header': True, 'timestampFormat': '%Y-%m-%d %H:%M:%S' } } ) data = response.json() ``` ```json title="Response" { "jobId": "6c2b9e14-3f7a-4d81-b0e5-a9d4c2f81b37", "state": "QUEUED" } ``` --- ## Append rows **POST** `/append-rows` Appends a small batch of rows sent in the request body: up to 10,000 rows or 5 MB per call. Appends that arrive within a few seconds of each other are committed together, so rows become visible to queries within about 5 seconds. Use [Load files](#load-files) for bulk data. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `rows` — " required> Rows as objects keyed by column name, with plain JSON values. Send `DECIMAL` and large integers as strings, dates and timestamps as ISO-8601 strings. ### Response - `accepted` (integer) — Rows accepted. - `loadId` (string) — Identifier written to the `_load_id` column of these rows. - `errors` — "> Rows refused: `index` and `message`. Refused rows are not appended; the others are. **cURL:** ```bash curl -X POST 'https://api.autessa.com/clients/autessadb/analytics/append-rows?resourceId=V1StGXR8Z5' \ -H "Authorization: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rows": [ { "order_id": "90210333", "order_date": "2026-10-02", "region": "APAC", "net_amount": "310.00" } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/autessadb/analytics/append-rows?resourceId=V1StGXR8Z5', { method: 'POST', headers: { 'Authorization': process.env.API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', rows: [ { order_id: '90210333', order_date: '2026-10-02', region: 'APAC', net_amount: '310.00' } ] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/autessadb/analytics/append-rows?resourceId=V1StGXR8Z5', headers={'Authorization': os.environ['API_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'rows': [ { 'order_id': '90210333', 'order_date': '2026-10-02', 'region': 'APAC', 'net_amount': '310.00' } ] } ) data = response.json() ``` ```json title="Response" { "accepted": 1, "loadId": "append-2026-10-02T15:00:05Z", "errors": [] } ``` --- ## Masked columns Text columns created with `masked: true` are stored encrypted and appear in results as mask text unless the caller's role allows revealing them. - `create` and `add-columns`: `columns[].masked: true` (STRING only). - `describe`: `columns[].encrypted`, `revealable`, and `maskStrategy`. - `query`, `preview`, `job-results`, and `job-download-url` accept `reveal: true` to return plaintext for callers whose role shows the column; every reveal is recorded in the access log. - `job-results` also accepts `revealRow` (0-based row of the whole result) with `revealColumn` and returns `revealedCell {row, column, value}` for a single cell. - Result `columns[]` carry `masked`, `revealable`, `revealed`, and `maskStrategy`. - In SQL, masked columns support `=`, `<>`, `IN`, `IS NULL`, `GROUP BY`, `DISTINCT`, `COUNT(DISTINCT)`, `MIN`/`MAX`, joins with other masked columns, and `ORDER BY` (by an internal code). `LIKE`, ranges, and other functions fail with `FORBIDDEN_CONSTRUCT` and a hint. - Loads into tables with masked columns run in the app rather than the analytics engine; load responses say so in `notes`. ## Errors Failed calls, and jobs that end in `FAILED` or `TIMED_OUT`, carry an error code, a plain-language message, and where possible a hint: ```json title="Error" { "errorCode": "UNKNOWN_COLUMN", "errors": ["Column \"net_amt\" does not exist in order_lines."], "hint": "Columns in order_lines: order_id, order_date, region, customer_id, net_amount." } ``` | Error code | When | | --- | --- | | `PARSE_ERROR` | The SQL could not be parsed. The message points at the position. | | `FORBIDDEN_CONSTRUCT` | The SQL is not a single read-only query: it changes data or settings, has several statements, or calls a function that reads files or system state. | | `UNKNOWN_TABLE` | The query names a table that does not exist or that the caller cannot read. The two cases are reported the same way. | | `UNKNOWN_COLUMN` | The query names a column that does not exist or is hidden from the caller. The hint lists the available columns. | | `SCAN_LIMIT` | The query would scan more than the per-query limit, `maxBytesScanned`, or the remaining daily allowance. The hint names the partition columns to filter on. | | `TIMEOUT` | The query ran past the time limit. | | `ROW_LIMIT` | Too many rows were requested; lower `maxRows` or `pageSize`. | | `QUOTA` | A plan limit was reached: storage, concurrent queries, data loaded per day, or appended rows per minute. | | `VERSION_CONFLICT` | The semantic layer changed since `expectedVersion`. Describe again and retry. | | `INVALID_SCHEMA` | A column name, type, or partition spec is invalid, or a schema change is not a safe widening. | | `FORBIDDEN` | The caller lacks the table permission for the operation, or the job belongs to another caller. | | `NOT_FOUND` | The table or job does not exist, or its results have expired. | HTTP status codes follow the usual pattern: `400` for invalid requests and SQL errors, `401` for a missing or invalid key, `403` for `FORBIDDEN`, `404` for `NOT_FOUND`, `409` for `VERSION_CONFLICT`, `413` for `SCAN_LIMIT` and `ROW_LIMIT`, and `429` for `QUOTA` and rate limits. ## Related - [AutessaDB Analytics Tables guide](/docs/guide/autessadb/analytics.md) - [SQL Workbench](/docs/guide/autessadb/sql-workbench.md) - [Analytics Poweruser API](/docs/api/poweruser/autessadb-analytics.md) - [Analytics tool types](/docs/tools/autessadb.md#describe-analytics-tables) - [Analytics commands in the CLI](/docs/cli/autessadb.md#how-to-load-and-query-analytics-tables) - [AutessaDB API](/docs/api/autessadb.md) --- # Work Queues API Source: https://docs.autessa.com/docs/api/workqueues > Enqueue and track large batches of work: durable, grouped, staged units of work processed at scale with retries and dedup. # Work Queues API A work queue is a durable backlog of individual units of work — each an invocation of one of your tools with a payload — that the platform processes reliably, with retries, per-item timeouts, grouping, and staged ordering. Use it to fan a large job out into many independent items and track them to completion. > All Work Queues API requests require an `Authorization` header with your API key. These endpoints are company-scoped by the key, so — unlike the AutessaDB API — they take no `resourceId` query parameter. Every item that names a tool to run is checked against your key's permissions for that tool before it is accepted. --- ## Base URL ``` https://api.autessa.com/clients/workqueues ``` --- ## Authentication Pass your API key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: your_api_key_here ``` ```bash title="cURL" curl https://api.autessa.com/clients/workqueues/progress \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "queueId": "b1a7...", "groupKey": "batch-2026-07" }' ``` --- ## Concepts A **queue** is a named, long-lived backlog. Queues are created and configured in the [Builder](https://builder.autessa.com) — where you set their concurrency, default attempt and timeout limits, and stage precedence — and you reference an existing queue by its `queueId` when enqueuing. An **item** is one unit of work: a tool to invoke (`actionToolId`) and the `payload` of per-invocation values it runs with. Each item moves through states — `PENDING`, `RUNNING`, and then a terminal `SUCCEEDED`, `FAILED`, `DEAD` (failed past its `maxAttempts`), or `CANCELLED`. A **group** collects related items under a shared `groupKey` so you can track and act on a batch as a whole. Group counters are maintained as items complete, so [progress](#progress-) is a fast read regardless of how many items the group holds. A **stage** is an optional label on an item. When a queue defines an ordered **stage precedence**, items in earlier-listed stages are leased for processing ahead of later stages — and any staged item is leased ahead of unstaged ones. This drives depth-first completion: a multi-stage pipeline finishes the items already in flight through their later stages before pulling new work in at the first stage. A stage is just a name to the API; the ordering comes from the queue's configured precedence list. --- ## Enqueue Items **POST** `/enqueue` Adds one or more items to a queue in a single call. Every item that sets `actionToolId` is authorized against your API key's permission for that tool before anything is written — if any item fails the check, the whole batch is rejected and nothing is enqueued. ### Request Body - `queueId` (string, required) — ID of the queue to add items to. - `items` — " required> The items to enqueue. Each has the fields below. **EnqueueItem** - `actionToolId` (string, required) — ID of the tool this item invokes when it runs. Your API key must have permission for this tool. - `payload` (object) — Per-invocation values for the tool's `@@{...}` placeholders — the same kind of input you would pass when running the tool directly. - `groupKey` (string) — Groups this item with others under a shared key for batch tracking and group actions. - `stage` (string) — Optional stage label. Its effect on ordering comes from the queue's configured stage precedence. - `priority` (integer) — Explicit lease priority (lower is leased first). When set, it overrides any stage precedence for this item. - `maxAttempts` (integer) — How many times the item may run before it is marked `DEAD`. Defaults to the queue's configured maximum. - `timeoutSeconds` (integer) — Per-run timeout for this item. Defaults to the queue's configured timeout. - `delaySeconds` (integer) — Hold the item invisible for this many seconds before it becomes eligible to run. - `dedupKey` (string) — Optional idempotency key. An item whose `dedupKey` matches a still-live (pending or running) item already on the same queue is skipped rather than enqueued — so a retried or re-delivered enqueue never creates a duplicate. Items with no `dedupKey` are never deduplicated. ### Response - `enqueued` (integer) — Number of items actually added. - `itemIds` — "> IDs of the enqueued items, in request order. - `skipped` (integer) — Number of items silently skipped as `dedupKey` duplicates (0 when none). - `errors` — "> Error messages if the batch was rejected. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/workqueues/enqueue' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "items": [ { "actionToolId": "V1StGXR8Z5", "groupKey": "batch-2026-07", "stage": "extract", "payload": { "s3Uri": "s3://autessa-files/uploads/doc-001.pdf" }, "dedupKey": "doc-001" }, { "actionToolId": "V1StGXR8Z5", "groupKey": "batch-2026-07", "stage": "extract", "payload": { "s3Uri": "s3://autessa-files/uploads/doc-002.pdf" }, "dedupKey": "doc-002" } ] }' ``` ```json title="JSON" { "enqueued": 2, "itemIds": [ "9f1c2b3a-4d5e-6f70-8192-a3b4c5d6e7f8", "0a1b2c3d-4e5f-6071-8293-b4c5d6e7f809" ], "skipped": 0, "errors": [] } ``` --- ## Progress **POST** `/progress` Returns the live counters for one group: how many of its items are in each state, per-stage totals and completions, and an overall status. This is a fast read — it consults the maintained group counters, not a scan of every item. ### Request Body - `queueId` (string, required) — ID of the queue the group belongs to. - `groupKey` (string, required) — The group to report on. ### Response - `group` (WorkGroup) — The group's counters, or `null` if no items have ever been enqueued under this `(queueId, groupKey)`. - `errors` — "> Error messages if any occurred. **WorkGroup** - `nTotal` (integer) — Total items enqueued in the group. - `nPending` (integer) — Items waiting to run. - `nRunning` (integer) — Items currently running. - `nSucceeded` (integer) — Items that completed successfully. - `nFailed` (integer) — Items whose latest attempt failed (and may retry). - `nDead` (integer) — Items that exhausted their attempts. - `nCancelled` (integer) — Items that were cancelled. - `stageCounts` (object) — Per-stage progress: a map of stage name to `{ "total": ..., "done": ... }`. - `status` (string) — The group's overall status. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/workqueues/progress' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "groupKey": "batch-2026-07" }' ``` ```json title="JSON" { "group": { "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "groupKey": "batch-2026-07", "nTotal": 500, "nPending": 120, "nRunning": 8, "nSucceeded": 360, "nFailed": 4, "nDead": 2, "nCancelled": 6, "stageCounts": { "extract": { "total": 500, "done": 498 }, "classify": { "total": 498, "done": 372 } }, "status": "ACTIVE" }, "errors": [] } ``` --- ## List Items **POST** `/items/list` Lists items on a queue, filtered by state, stage, and/or group, with pagination. Use it to inspect individual items — their payloads, results, attempt counts, and last error. ### Request Body - `queueId` (string, required) — ID of the queue to list from. - `state` (string) — Filter to one state: `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `DEAD`, or `CANCELLED`. - `stage` (string) — Filter to one stage. - `groupKey` (string) — Filter to one group. - `page` (integer) — Zero-based page number. Defaults to 0. - `size` (integer) — Items per page. Defaults to 50. ### Response - `items` — "> The matching items. Each carries `id`, `queueId`, `groupKey`, `stage`, `state`, `priority`, `attempt`, `maxAttempts`, `actionToolId`, `payload`, `result`, `lastError`, `timeoutSeconds`, `visibleAt`, `createdAt`, and `completedAt`. - `totalElements` (integer) — Total items matching the filter. - `totalPages` (integer) — Total pages at the requested size. - `page` (integer) — The page returned. - `errors` — "> Error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/workqueues/items/list' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "groupKey": "batch-2026-07", "state": "DEAD", "page": 0, "size": 50 }' ``` ```json title="JSON" { "items": [ { "id": "0a1b2c3d-4e5f-6071-8293-b4c5d6e7f809", "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "groupKey": "batch-2026-07", "stage": "extract", "state": "DEAD", "priority": 10, "attempt": 3, "maxAttempts": 3, "actionToolId": "V1StGXR8Z5", "payload": { "s3Uri": "s3://autessa-files/uploads/doc-002.pdf" }, "result": {}, "lastError": "Extraction timed out", "timeoutSeconds": 300, "visibleAt": "2026-07-15T14:30:00Z", "createdAt": "2026-07-15T14:00:00Z", "completedAt": "2026-07-15T14:35:10Z" } ], "totalElements": 2, "totalPages": 1, "page": 0, "errors": [] } ``` --- ## Retry Dead Items **POST** `/retry-dead` Requeues every `DEAD` item in a group — resetting its attempt count and making it eligible to run again. Use it after fixing whatever caused a batch of items to exhaust their retries. ### Request Body - `queueId` (string, required) — ID of the queue. - `groupKey` (string, required) — The group whose dead items to requeue. ### Response - `count` (integer) — Number of items moved back to `PENDING`. - `errors` — "> Error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/workqueues/retry-dead' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "groupKey": "batch-2026-07" }' ``` ```json title="JSON" { "count": 2, "errors": [] } ``` --- ## Cancel Group **POST** `/cancel-group` Cancels a group's outstanding work: items that are `PENDING` or `FAILED` move to `CANCELLED`. Items already `RUNNING` are left to finish, and items that have already reached a terminal state are unaffected. ### Request Body - `queueId` (string, required) — ID of the queue. - `groupKey` (string, required) — The group to cancel. ### Response - `count` (integer) — Number of items moved to `CANCELLED`. - `errors` — "> Error messages if any occurred. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/clients/workqueues/cancel-group' \ --header 'Content-Type: application/json' \ --header 'Authorization: your_api_key' \ --data '{ "queueId": "b1a7c0de-2f3a-4c5b-9d1e-7a8b9c0d1e2f", "groupKey": "batch-2026-07" }' ``` ```json title="JSON" { "count": 118, "errors": [] } ``` --- ## Related - [Tools API](/docs/api/tools.md) — the tools an item invokes when it runs - [Document tool types](/docs/tools/documents.md) — bulk-intake and per-document processing designed to run as queue items - [AutessaDB API](/docs/api/autessadb.md) — multipart upload for the large archives a bulk-intake queue processes --- # Scheduled Events API Source: https://docs.autessa.com/docs/api/scheduled-events > Complete API reference for creating, managing, and scheduling automated events for agents, tools, and database operations. # Scheduled Events API The Scheduled Events API allows you to create and manage automated recurring or one-time events that execute agents, tools, or database operations on a schedule. All events created via the client API are automatically shared company-wide with ADMIN permissions. > All Scheduled Events API requests require an `Authorization` header with your API key. Events created through this API are automatically set to COMPANY-WIDE access with ADMIN permissions. --- ## Base URL ``` REST API: https://api.autessa.com/clients/scheduled-events ``` --- ## Authentication Pass your API key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: your_api_key_here ``` > The API key must have permissions to access the target resources (agents, tools, or tables) that the scheduled event will interact with. ```bash title="REST" curl https://api.autessa.com/clients/scheduled-events/all \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{}' ``` --- ## Schedule Specs The schedule is described by the `scheduleSpec` object — a tagged union over the `pattern` field. There are exactly three patterns; only the nested object matching the pattern is present: ### EVERY For recurring events at a fixed interval. `every.value` is a positive integer and `every.unit` is one of `MINUTES`, `HOURS`, or `DAYS`. Timezone-independent. ```json { "pattern": "EVERY", "every": { "value": 6, "unit": "HOURS" } } ``` ### CRON For recurring events at specific times, evaluated in an IANA timezone. `cron.expression` is a 6-field AWS cron expression (`minute hour day-of-month month day-of-week year`) without the `cron(...)` wrapper; use `?` in exactly one of day-of-month or day-of-week. `cron.timezone` is required. ```json { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "America/New_York" } } ``` ### ONCE For one-time execution at a local wall-clock time in an IANA timezone. `once.at` is a local ISO-8601 datetime without offset and must be in the future. ```json { "pattern": "ONCE", "once": { "at": "2026-12-31T23:59:59", "timezone": "UTC" } } ``` --- ## Action Types Scheduled events can perform three types of actions: - `INVOKE_AGENT` — Execute an agent with predefined input. The agent runs in single-turn mode with no conversation context. - `INVOKE_TOOL` — Execute a tool with static parameters defined at schedule creation time. - `UPDATE_AUTESSADB` — Perform INSERT or UPDATE operations on AutessaDB tables with predefined values. --- ## REST API Endpoints ## List All Events **POST** `/all` Retrieves all scheduled events for your company. Returns all active (non-deleted) scheduled events that belong to your company. ### Request Body - `(empty)` (object) — Send an empty JSON object `{}`. ### Response - `ownedEvents` — "> All scheduled events owned by your company. - `companySharedEvents` — "> Always empty for client API (all events are in ownedEvents). - `sharedWithMeEvents` — "> Always empty for client API. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/all \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{}' ``` **JavaScript:** ```javascript const response = await fetch('https://api.autessa.com/clients/scheduled-events/all', { method: 'POST', headers: { 'Authorization': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); const data = await response.json(); console.log(data.ownedEvents); ``` ```json title="Response" { "ownedEvents": [ { "eventId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "eventName": "Daily Summary Agent", "description": "Runs every day at 9 AM", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "America/New_York" } }, "enabled": true, "actionType": "INVOKE_AGENT", "sharingLevel": "COMPANY", "companyPermissionLevel": "ADMIN", "userPermission": "ADMIN" } ], "companySharedEvents": [], "sharedWithMeEvents": [] } ``` --- ## Get Single Event **POST** `/get` Retrieves a specific scheduled event by its ID. ### Request Body - `scheduledEventId` (string, required) — The unique ID of the scheduled event. ### Response - `scheduledEvent` (ScheduledEventTriggerDto) — The scheduled event details. - `permission` (EAccessLevel) — Your access level (always ADMIN for client API). - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/get \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{"scheduledEventId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d"}' ``` **JavaScript:** ```javascript const response = await fetch('https://api.autessa.com/clients/scheduled-events/get', { method: 'POST', headers: { 'Authorization': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ scheduledEventId: '8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d' }) }); ``` ```json title="Response" { "scheduledEvent": { "eventId": "8f14e45f-ceea-4e07-8c65-9d2f9a1b3c4d", "eventName": "Daily Summary Agent", "enabled": true, "sharingLevel": "COMPANY", "companyPermissionLevel": "ADMIN" }, "permission": "ADMIN" } ``` --- ## Create Event **POST** `/create` Creates a new scheduled event. The event is automatically set to COMPANY-WIDE access with ADMIN permissions. > Your API key must have permission to access the target resource (agent, tool, or table) that will be executed by this scheduled event. ### Request Body - Common Fields - `eventName` (string, required) — User-friendly name for the scheduled event. - `description` (string) — Optional description of what this event does. - `scheduleSpec` (object, required) — Schedule specification — see [Schedule Specs](#schedule-specs). One of the EVERY, CRON, or ONCE shapes. - `enabled` (boolean) — Whether the event should be enabled. Defaults to true. - `actionType` (EScheduledEventActionType, required) — Type of action: INVOKE_AGENT, INVOKE_TOOL, or UPDATE_AUTESSADB. ### For INVOKE_AGENT - `agentConfig` (object, required) — Configuration for agent invocation. - `agentExecutionRequest.agentId` (string, required): The agent's ID - `agentExecutionRequest.input` (array, required): Multimodal input items, e.g. `[{"inputType": "TEXT", "content": "Generate daily summary"}]` - `agentExecutionRequest.environmentVariables` (object): String key-value pairs - `continueOnError` (boolean): Whether to continue if execution fails. Defaults to false - `description` (string): Optional description of this scheduled execution ### For INVOKE_TOOL - `toolConfig` (object, required) — Configuration for tool execution. - `toolExecutionRequest.toolId` (string, required): The tool's ID - `toolExecutionRequest.variables` (object): Tool input variables. String key-value pairs - `toolExecutionRequest.environmentVariables` (object): String key-value pairs - `continueOnError` (boolean): Whether to continue if execution fails. Defaults to false - `description` (string): Optional description of this scheduled execution ### For UPDATE_AUTESSADB - `autessaDbConfig` (object, required) — Configuration for database operation. - `operation` (DbOperation, required): INSERT or UPDATE - `targetTableId` (string, required): The AutessaDB table's ID - `values` (object, required): Column name to typed column value mappings, e.g. `{"status": {"columnType": "SHORT_TEXT", "textValue": "inactive"}}` - `recordId` (integer): For single-row UPDATE — the specific record ID to update. Mutually exclusive with `whereGroups` - `whereGroups` (array): For batch UPDATE — groups of WHERE conditions. Each group has a `logicalOperator` ("AND"/"OR") and `conditions` (array of `{columnName, operator, value}`). Mutually exclusive with `recordId` - `groupsLogicalOperator` (string): "AND" or "OR" — how to combine multiple WHERE groups - `skipIfTargetTableMissing` (boolean): If true, skip execution when the target table doesn't exist instead of failing. Defaults to false - `description` (string): Optional description of this scheduled operation ### Response - `scheduledEvent` (ScheduledEventTriggerDto) — The created scheduled event details. - `eventId` (string) — The unique ID of the created event. - `scheduleArn` (string) — The schedule ARN. - `errors` — "> Array of error messages if any occurred. **Agent Example:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/create \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "eventName": "Daily Summary Agent", "description": "Runs every day at 9 AM", "cronExpression": "cron(0 9 * * ? *)", "timezone": "America/New_York", "enabled": true, "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": 123, "input": "Generate daily summary", "environmentVariables": {} }, "continueOnError": false } }' ``` **Tool Example:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/create \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "eventName": "Hourly Data Sync", "cronExpression": "rate(1 hour)", "timezone": "UTC", "enabled": true, "actionType": "INVOKE_TOOL", "toolConfig": { "toolExecutionRequest": { "toolId": 456, "variables": { "source": "production", "destination": "warehouse" } } } }' ``` **Database Example:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/create \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "eventName": "Daily Cleanup", "cronExpression": "cron(0 0 * * ? *)", "enabled": true, "actionType": "UPDATE_AUTESSADB", "autessaDbConfig": { "operation": "UPDATE", "targetTableId": 789, "values": { "status": "inactive" }, "whereConditions": "{...}" } }' ``` ```json title="Response" { "scheduledEvent": { "eventId": "evt_abc123", "eventName": "Daily Summary Agent", "enabled": true, "sharingLevel": "COMPANY", "companyPermissionLevel": "ADMIN" }, "eventId": "evt_abc123", "scheduleArn": "arn:aws:scheduler:..." } ``` --- ## Update Event **POST** `/update` Updates an existing scheduled event. You can modify the schedule, configuration, or any other field. ### Request Body - `scheduledEventId` (string, required) — The ID of the event to update. - `eventName` (string) — Updated event name. - `description` (string) — Updated description. - `cronExpression` (string) — Updated schedule expression. - `timezone` (string) — Updated timezone. - `actionType` (EScheduledEventActionType) — Updated action type (requires corresponding config). - `agentConfig / toolConfig / autessaDbConfig` (object) — Updated configuration (based on action type). > If you change the action type or target resource, your API key must have permission to access the new target resource. ### Response - `scheduledEvent` (ScheduledEventTriggerDto) — The updated scheduled event details. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/update \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "evt_abc123", "eventName": "Updated Event Name", "cronExpression": "rate(2 hours)" }' ``` **JavaScript:** ```javascript const response = await fetch('https://api.autessa.com/clients/scheduled-events/update', { method: 'POST', headers: { 'Authorization': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ scheduledEventId: 'evt_abc123', cronExpression: 'rate(2 hours)' }) }); ``` ```json title="Response" { "scheduledEvent": { "eventId": "evt_abc123", "eventName": "Updated Event Name", "cronExpression": "rate(2 hours)" } } ``` --- ## Toggle Event **POST** `/toggle` Enables or disables a scheduled event. Disabled events will not execute until re-enabled. ### Request Body - `scheduledEventId` (string, required) — The ID of the event to toggle. - `enabled` (boolean, required) — true to enable, false to disable. ### Response - `eventId` (string) — The ID of the toggled event. - `enabled` (boolean) — The new enabled state. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/toggle \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "evt_abc123", "enabled": false }' ``` **JavaScript:** ```javascript const response = await fetch('https://api.autessa.com/clients/scheduled-events/toggle', { method: 'POST', headers: { 'Authorization': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ scheduledEventId: 'evt_abc123', enabled: false }) }); ``` ```json title="Response" { "eventId": "evt_abc123", "enabled": false } ``` --- ## Delete Event **POST** `/delete` Soft-deletes a scheduled event. The event will be marked as deleted and will no longer execute. ### Request Body - `scheduledEventId` (string, required) — The ID of the event to delete. ### Response - `eventId` (string) — The ID of the deleted event. - `deletedAt` (string) — ISO timestamp of when the event was deleted. - `errors` — "> Array of error messages if any occurred. **cURL:** ```bash curl -X POST https://api.autessa.com/clients/scheduled-events/delete \ -H "Authorization: your_api_key" \ -H "Content-Type: application/json" \ -d '{"scheduledEventId": "evt_abc123"}' ``` **JavaScript:** ```javascript const response = await fetch('https://api.autessa.com/clients/scheduled-events/delete', { method: 'POST', headers: { 'Authorization': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ scheduledEventId: 'evt_abc123' }) }); ``` ```json title="Response" { "eventId": "evt_abc123", "deletedAt": "2025-11-04T12:00:00Z" } ``` --- ## Error Handling All endpoints return errors in a consistent format: ```json { "errors": [ "API key does not have permission to access the target agent (ID: 123)", "Event name is required" ] } ``` ### Common Error Messages - `Event name is required` (required) — The `eventName` field is missing or empty. - `API key does not have permission to access the target X (ID: Y)` — Your API key doesn't have permission to the specified agent, tool, or table. - `Target resource does not belong to your company` — The specified resource doesn't exist or belongs to a different company. - `Agent/Tool/Table ID is required for X action` (required) — The configuration for the specified action type is missing required fields. - `Scheduled event not found` — The specified event ID doesn't exist or doesn't belong to your company. - `Scheduled event does not belong to your company` — You're trying to access an event from a different company. --- ## Important Notes > **Company-Wide Access**: All scheduled events created via the client API are automatically set to COMPANY-WIDE sharing with ADMIN permissions. This means all members of your company can view, edit, and manage these scheduled events. > **Permission Validation**: Your API key must have explicit permission to access the target resources (agents, tools, or tables) that the scheduled event will interact with. The API will reject creation/updates if you don't have access. > **Owner Assignment**: Scheduled events created via the client API are owned by your company's primary admin user, not by the API key itself. --- # Custom Webhooks API Source: https://docs.autessa.com/docs/api/custom-webhooks > Receive events from external systems over a single public endpoint and trigger tools, agents, managers, or AutessaDB operations asynchronously. # Custom Webhooks API Custom webhooks give every webhook handler you configure in Autessa a public HTTPS trigger URL. External systems (payment processors, CRMs, CI pipelines, form builders) call the URL when something happens; Autessa validates the request, transforms the payload into variables, and asynchronously executes the handler you attached — a tool, an agent, a manager, or an AutessaDB operation. > Webhook execution is **asynchronous**. A successful call returns `202 Accepted` immediately with an `executionId` — the handler runs in the background. Use the execution history in the builder to inspect the outcome of each delivery. --- ## Base URL ``` https://api.autessa.com/clients/custom-webhooks ``` All webhooks share this single endpoint. The `resourceId` query parameter selects which webhook handler receives the request, and the same URL accepts **GET, POST, PUT, DELETE, and PATCH** — so it fits whatever method the external system insists on sending. There are no sub-paths; the full trigger URL for a webhook is: ``` https://api.autessa.com/clients/custom-webhooks?resourceId={webhookId} ``` --- ## Authentication Webhook endpoints use a dedicated **webhook API key**, generated when you create the webhook handler. Webhook keys have the shape: ``` wh:{keyId}:{keySecret} ``` Pass the full key (including the `wh:` prefix) in the `Authorization` header — no `Bearer` prefix: ``` Authorization: wh:your_key_id:your_key_secret ``` If the calling system cannot set headers, you can instead pass the same value as an `authorization` query parameter (matched case-insensitively). The header takes precedence when both are present. Each webhook key is bound to exactly one webhook: the `{keyId}` segment must match the key registered for the webhook selected by `resourceId`, the secret is verified against its stored hash, and both the webhook and the key must be enabled. Regular client API keys and poweruser keys are rejected on this endpoint. > If the webhook has a **validation script** configured (for example, to verify an HMAC signature from the sender), it runs on every request after key validation. The script receives the raw body, all headers, and all query parameters, and the request is rejected with `401` if it fails. See [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md). **Header:** ```bash curl 'https://api.autessa.com/clients/custom-webhooks?resourceId=123' \ -H "Authorization: wh:your_key_id:your_key_secret" \ -H "Content-Type: application/json" \ -d '{"event": "ping"}' ``` **Query parameter:** ```bash curl 'https://api.autessa.com/clients/custom-webhooks?resourceId=123&authorization=wh:your_key_id:your_key_secret' \ -H "Content-Type: application/json" \ -d '{"event": "ping"}' ``` --- ## How execution works Every request to the trigger URL goes through the same pipeline: 1. **Authenticate** — the webhook is looked up by `resourceId`, the webhook key is verified, and the optional validation script runs. Failures return `401` immediately. 2. **Transform** — the raw body, headers, and query parameters are transformed into a flat map of variables using the webhook's transform configuration (JSONPath mappings or an AutessaScript transform). This happens synchronously; a transform failure returns `500`. 3. **Log and queue** — an execution record is written with status `QUEUED` and the execution message is placed on the processing queue. 4. **Acknowledge** — you receive `202 Accepted` with the `executionId`. 5. **Execute asynchronously** — a background worker runs the configured handler resource (tool, agent, manager, or AutessaDB operation), substituting the transformed variables into the handler's configured inputs via `@@{variableName}` references. The execution record is updated with the result. The immediate response only tells you the request was accepted and queued — it never contains handler output. Correlate deliveries with the `executionId` when reviewing execution history. (Every response also carries a separate per-request `X-Request-Id` header.) --- ## Trigger a webhook **POST** `/clients/custom-webhooks` Delivers an event to a webhook handler. `POST` with a JSON body is the most common shape, but `PUT`, `PATCH`, and `DELETE` behave identically — the method is recorded in execution history and otherwise does not change processing. ### Query Parameters - `resourceId` (integer, required) — The ID of the webhook to trigger. This is baked into the trigger URL shown for the webhook in the builder. - `authorization` (string) — The webhook API key, as an alternative to the `Authorization` header (see [Authentication](#authentication)). - `*` (string) — Any additional query parameters are passed through to the validation script and payload transform as `queryParams`. ### Headers - `Authorization` (string, required) — The webhook API key (`wh:{keyId}:{keySecret}`). Required unless passed as the `authorization` query parameter. - `Content-Type` (string) — The body content type. JSON is recommended — JSONPath transforms expect a JSON body. - `*` (string) — All request headers are passed through to the validation script and payload transform as `headers`. ### Request Body The body is **passed through as-is** — there is no required schema. It is delivered untouched (up to 10 MB) to the validation script and payload transform as `payload`. Send whatever the external system produces; the webhook's transform configuration maps it to the variables your handler needs. ### Response - `success` (boolean) — `true` if the request was validated, transformed, and queued for execution. - `message` (string) — Human-readable status, e.g. `"Webhook execution queued successfully"`. - `executionId` (string) — Unique ID for this delivery. Use it to find the delivery in the webhook's execution history. - `webhookId` (string) — ID of the webhook that was triggered. - `errors` — "> Array of error messages if any occurred. The `X-Request-Id` response header carries a per-request correlation ID. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/clients/custom-webhooks?resourceId=123' \ --header 'Content-Type: application/json' \ --header 'Authorization: wh:your_key_id:your_key_secret' \ --data '{ "event": "payment.succeeded", "customer": { "email": "jane@example.com", "name": "Jane Doe" }, "amountCents": 4900 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/clients/custom-webhooks?resourceId=123', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'wh:your_key_id:your_key_secret' }, body: JSON.stringify({ event: 'payment.succeeded', customer: { email: 'jane@example.com', name: 'Jane Doe' }, amountCents: 4900 }) } ); const data = await response.json(); // 202 Accepted console.log(data.executionId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/clients/custom-webhooks', params={'resourceId': 123}, headers={ 'Content-Type': 'application/json', 'Authorization': 'wh:your_key_id:your_key_secret' }, json={ 'event': 'payment.succeeded', 'customer': { 'email': 'jane@example.com', 'name': 'Jane Doe' }, 'amountCents': 4900 } ) data = response.json() # 202 Accepted print(data['executionId']) ``` ```json title="202 Accepted" { "success": true, "message": "Webhook execution queued successfully", "executionId": "3f1c9a52-8f0e-4a7d-9b6f-2d4c1e8a7b31", "webhookId": "V1StGXR8Z5" } ``` --- ## Trigger with GET **GET** `/clients/custom-webhooks` Some senders (email link trackers, simple pingers, legacy systems) can only issue `GET` requests. The endpoint accepts them at the same URL — carry your event data in query parameters instead of a body. Every query parameter (including `resourceId` and `authorization` themselves) is available to the validation script and payload transform as `queryParams`, so an AutessaScript transform can map them to handler variables. ### Query Parameters - `resourceId` (integer, required) — The ID of the webhook to trigger. - `authorization` (string) — The webhook API key, if not sent as a header. - `*` (string) — Your event data — arbitrary key-value pairs, passed through to the transform. ### Response Identical to the `POST` response: `202 Accepted` with `success`, `message`, `executionId`, and `webhookId`. **cURL:** ```bash curl --request GET \ --url 'https://api.autessa.com/clients/custom-webhooks?resourceId=123&event=user.signup&email=jane%40example.com' \ --header 'Authorization: wh:your_key_id:your_key_secret' ``` **JavaScript:** ```javascript const params = new URLSearchParams({ resourceId: '123', event: 'user.signup', email: 'jane@example.com' }); const response = await fetch( `https://api.autessa.com/clients/custom-webhooks?${params}`, { headers: { 'Authorization': 'wh:your_key_id:your_key_secret' } } ); const data = await response.json(); // 202 Accepted ``` **Python:** ```python response = requests.get( 'https://api.autessa.com/clients/custom-webhooks', params={ 'resourceId': 123, 'event': 'user.signup', 'email': 'jane@example.com' }, headers={'Authorization': 'wh:your_key_id:your_key_secret'} ) data = response.json() # 202 Accepted ``` ```json title="202 Accepted" { "success": true, "message": "Webhook execution queued successfully", "executionId": "9d2b7e10-4c3a-4f58-a1e2-6b8f0d5c9e44", "webhookId": "V1StGXR8Z5" } ``` --- ## Configuring handlers The trigger endpoint is only half of a webhook — the other half is the handler configuration you create in the builder, which controls: - **Handler resource** — the tool, agent, manager, or AutessaDB operation to execute for each delivery, and the inputs it receives (with `@@{variableName}` references to transformed variables). - **Payload transform** — JSONPath mappings over the JSON body, or an AutessaScript transform with access to `payload`, `headers`, and `queryParams`, producing the variable map. - **Validation script** — optional request-authenticity check (signature verification, allow-listing) that runs before anything is queued. - **API key lifecycle** — the webhook key is shown once at creation and can be rotated at any time; rotating it invalidates the old key immediately. See [Webhook Handlers](/docs/guide/api-management/webhook-handlers.md) for the full configuration walkthrough. --- ## Errors Authentication and lookup failures are rejected by the webhook filter **before** anything is queued, with a `401` and a minimal error body: ```json title="401 Unauthorized" { "error": "Invalid API key for this webhook." } ``` | Status | Body | When | | --- | --- | --- | | `401` | `{"error": "No webhook ID provided."}` | The `resourceId` query parameter is missing. | | `401` | `{"error": "Invalid webhook ID."}` | `resourceId` is not a valid webhook ID. | | `401` | `{"error": "Webhook not found."}` | No webhook exists with that ID. | | `401` | `{"error": "Webhook is disabled."}` | The webhook exists but is switched off. | | `401` | `{"error": "No API key provided."}` | No `Authorization` header or `authorization` query parameter. | | `401` | `{"error": "Invalid API key format."}` | The key is not a `wh:{keyId}:{keySecret}` webhook key (regular client keys are rejected here). | | `401` | `{"error": "Invalid API key for this webhook."}` | The key belongs to a different webhook. | | `401` | `{"error": "Invalid API key."}` | The key ID is unknown or the secret does not match. | | `401` | `{"error": "API key is disabled."}` | The key exists but has been disabled. | | `401` | Validation script message | The webhook's custom validation script rejected the request. | | `500` | `ExecuteWebhookResponse` with `success: false` | The payload transform failed, or an internal error occurred while queuing. | A `500` uses the same response shape as a success, so you can still log the `executionId`: ```json title="500 Internal Server Error" { "success": false, "message": "Failed to transform webhook payload", "executionId": "3f1c9a52-8f0e-4a7d-9b6f-2d4c1e8a7b31", "webhookId": "V1StGXR8Z5" } ``` > `202 Accepted` means the delivery was queued — not that the handler succeeded. Handler failures (a tool erroring, an agent timing out) are recorded in the webhook's execution history, not in the HTTP response. --- ## Related - [API Overview](/docs/api.md) - [Webhook Handlers guide](/docs/guide/api-management/webhook-handlers.md) - [API Keys](/docs/guide/api-management/api-keys.md) - [Authentication](/docs/guide/authentication.md) - [Agent API](/docs/api/agents.md) - [Manager API](/docs/api/managers.md) - [Tool API](/docs/api/tools.md) - [AutessaDB API](/docs/api/autessadb.md) - [Scheduled Events API](/docs/api/scheduled-events.md) --- # Poweruser API Source: https://docs.autessa.com/docs/api/poweruser > Full-platform management API authenticated with poweruser keys — manage agents, tools, managers, AutessaDB, connectors, and every other company resource programmatically. # Poweruser API The Poweruser API gives you programmatic access to everything you can do in the Autessa Builder — create and publish agents, configure tools, manage AutessaDB tables and triggers, provision phone numbers, send e-signature envelopes, and more. It is the management surface of the platform, as opposed to the Client API, which executes published resources. --- ## Base URL ``` https://api.autessa.com/autessa_poweruser ``` Every endpoint is **POST** and takes a JSON request body. Endpoints with no parameters still expect a body — send an empty object `{}`. --- ## Authentication Pass a poweruser key in the `Authorization` header. Two formats are accepted: **Permanent keys** ``` Authorization: pwrusr:{key_id}:{secret} ``` **Temporary keys** (JWT-based, expiring credentials — issued for scoped integrations): ``` Authorization: pwrusr_temp:{jwt} ``` Both formats are validated by the same filter and behave identically once authenticated; the only difference is lifetime and how the credential is issued. ### Creating a key Poweruser keys are created in the Builder under API management. See [Poweruser Keys](/docs/guide/api-management/poweruser-keys.md) for a step-by-step walkthrough, including how to create endpoint-scoped keys. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/user/me \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/user/me', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({}) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/user/me', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={} ) data = response.json() ``` ```json title="Response" { "email": "jane@acme.com", "name": "Jane Doe", "companyName": "Acme Corp" } ``` --- ## The acts-as-user model Every poweruser key is linked to a **user** and a **company**. When a request authenticates, the platform builds the exact same session context that user would have in the Builder for that company, and the request executes *as that user*: - Resource visibility follows the linked user's ownership and sharing — "list all" endpoints return the same owned / company-shared / shared-with-me groupings the user sees in the Builder. - Access-level checks (READ / WRITE / PUBLISH / ADMIN on agents, tools, managers, and so on) are evaluated against the linked user. - Anything the key creates is owned by the linked user in the linked company. - All activity is logged to the transaction log as the linked user. If you need the API to own resources on behalf of a team, link the key to a service user with the right memberships rather than a personal account. --- ## Scoped keys and endpoint discovery A poweruser key can be **scoped** to an explicit allow-list of endpoints when it is created. A scoped key that calls any endpoint outside its allow-list is rejected with HTTP `403` before the request reaches the endpoint: ```json { "error": "Endpoint not permitted for this API key" } ``` Scopes are defined in terms of endpoint keys (for example `AGENTS_ALL`, `TOOLS_CREATE`). To discover every endpoint key, its path, and its group, call [`/user/available-endpoints`](/docs/api/poweruser/user.md#list-available-endpoints) — it returns the full endpoint registry, which is also the set of values you can select when creating a scoped key. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/user/available-endpoints \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "endpoints": [ { "key": "AGENTS_ALL", "path": "/autessa_poweruser/agents/all", "group": "Agents" }, { "key": "TOOLS_CREATE", "path": "/autessa_poweruser/tools/create", "group": "Tools" } ], "errors": [] } ``` --- ## Conventions ### IDs are opaque strings Every resource ID that appears in a request or response — `agentId`, `toolId`, `managerId`, `newOwnerId`, `mcpServerId`, and so on — is a short opaque string like `"V1StGXR8Z5"`. Treat IDs as stable identifiers: pass them back exactly as you received them, and never construct or increment them. ### Requests - Method is always `POST`, body is always JSON (`Content-Type: application/json`). - Endpoints without parameters take an empty object `{}`. - Timestamps are ISO-8601 date-times without a zone suffix (for example `"2026-06-28T17:42:10"`), interpreted as UTC. ### Error shape Every response body extends a common envelope. On success the HTTP status is `200` and `errors` is empty or absent. On failure the body carries human-readable messages: ```json { "errors": ["User does not have permission to delete agent."] } ``` HTTP status codes: | Status | Meaning | |--------|---------| | `200` | Success. | | `401` | Authentication or authorization failed (bad key, or the linked user lacks access to the resource). | | `403` | A scoped key called an endpoint outside its allow-list (body is `{"error": "..."}`, sent by the auth filter). | | `408` | A server-side error was caught while executing the request; `errors` contains the message. | Responses may also include a `deprecationWarnings` array of strings when you call something slated for change — treat entries as a prompt to migrate. --- ## Endpoint directory | Group | Page | What it covers | |-------|------|----------------| | User | [User](/docs/api/poweruser/user.md) | Key introspection: linked user, company, endpoint registry. | | Agents | [Agents](/docs/api/poweruser/agents.md) | Create, configure, version, publish, and analyze agents. | | Tools | [Tools](/docs/api/poweruser/tools.md) | Create and configure tools of any tool type, including from MCP servers. | | Managers | [Managers](/docs/api/poweruser/managers.md) | Multi-agent managers: configuration, versioning, conversations. | | AutessaDB Tables | [AutessaDB Tables](/docs/api/poweruser/autessadb-tables.md) | Table and schema management, knowledge bases, sharing. | | AutessaDB Queries | [AutessaDB Query](/docs/api/poweruser/autessadb-query.md) | Row-level insert, update, delete, query, search, and uploads. | | AutessaDB Triggers | [AutessaDB Triggers](/docs/api/poweruser/autessadb-triggers.md) | Trigger creation, enable/disable, and execution history. | | AutessaDB Analytics | [AutessaDB Analytics](/docs/api/poweruser/autessadb-analytics.md) | Analytics table creation, columns, semantic layer, loads, and SQL query jobs. | | Connectors | [Connectors](/docs/api/poweruser/connectors.md) | Connect, manage, share, and operate external connectors, including per-family actions. | | Scheduled Events | [Scheduled Events](/docs/api/poweruser/scheduled-events.md) | Cron and one-off scheduled event management. | | Custom Webhooks | [Custom Webhooks](/docs/api/poweruser/custom-webhooks.md) | Inbound webhook handlers and execution history. | | API Keys | [API Keys](/docs/api/poweruser/keys.md) | Provision, scope, rotate and audit client API keys (so a hosted app can mint its own). | | MCP Servers | [MCP](/docs/api/poweruser/mcp.md) | MCP server registration, connection testing, discovery. | | Custom Apps | [Custom Apps](/docs/api/poweruser/custom-apps.md) | Custom app projects, files, builds, and vibecode sessions. | | Phone Numbers | [Phone Numbers](/docs/api/poweruser/phone-numbers.md) | Phone number purchase, routing, and release. | | SMS Consent | [SMS Consent](/docs/api/poweruser/sms-consent.md) | SMS consent record creation and search. | | Meeting Recorder | [Meeting Recorder](/docs/api/poweruser/meeting-recorder.md) | Meeting bots and recordings. | | E-Signature | [E-Signature Envelopes](/docs/api/poweruser/esignature-envelopes.md) and [E-Signature Templates](/docs/api/poweruser/esignature-templates.md) | Envelope send/track and template management. | | Voice Cloning | [Voice Cloning](/docs/api/poweruser/voice-cloning.md) | Instant and deep voice clones. | | Conversation Sync | [Conversation Sync](/docs/api/poweruser/conversation-sync.md) | Conversation data export and sync. | | Company | [Company](/docs/api/poweruser/company.md) | Company settings, team, and limits. | --- ## Related - [Poweruser Keys guide](/docs/guide/api-management/poweruser-keys.md) — creating and scoping keys - [Authentication guide](/docs/guide/authentication.md) - [API overview](/docs/api.md) --- # User Source: https://docs.autessa.com/docs/api/poweruser/user > Poweruser endpoints for inspecting the authenticated key's linked user, company, and the full endpoint registry. # User Introspection endpoints for a poweruser key. Use them to confirm which user and company a key acts as, and to discover every endpoint available on the Poweruser API surface. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/user`. Every request is a POST with a JSON body — send `{}` when there are no parameters. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## Get the current user **POST** `/user/me` Returns the identity behind the key: the linked user and the company scope every request executes in. Call this first when wiring up an integration to verify you are acting as the account you expect. ### Request parameters None — send an empty object `{}`. ### Response - `email` (string) — Email address of the linked user. - `name` (string) — Display name of the linked user. - `companyName` (string) — Name of the company the key is scoped to. All resources you create or read belong to this company. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/user/me \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/user/me', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({}) } ); const { email, name, companyName } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/user/me', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={} ) data = response.json() print(data['email'], data['companyName']) ``` ```json title="Response" { "email": "jane@acme.com", "name": "Jane Doe", "companyName": "Acme Corp" } ``` --- ## List available endpoints **POST** `/user/available-endpoints` Returns the complete registry of Poweruser API endpoints — every endpoint key, its request path, and the group it belongs to. This is the same list of endpoint keys you choose from when creating a **scoped key**, so use it to build key-provisioning UIs or to check what a given `key` value maps to. > The registry is static: it always returns every endpoint on the platform, > regardless of the calling key's own scope. To find out whether *your* key can > call an endpoint, call it — scoped keys receive HTTP `403` for endpoints > outside their allow-list. ### Request parameters None — send an empty object `{}`. ### Response - `endpoints` (array) — One entry per endpoint on the Poweruser API. - `endpoints[].key` (string) — Stable enum key for the endpoint (for example `AGENTS_CREATE`). Use these values when defining a scoped key's allow-list. - `endpoints[].path` (string) — Full request path, including the `/autessa_poweruser` prefix. - `endpoints[].group` (string) — Human-readable group the endpoint belongs to (for example `"Agents"`, `"AutessaDB Queries"`). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/user/available-endpoints \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/user/available-endpoints', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({}) } ); const { endpoints } = await response.json(); const agentEndpoints = endpoints.filter(e => e.group === 'Agents'); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/user/available-endpoints', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={} ) endpoints = response.json()['endpoints'] groups = sorted({e['group'] for e in endpoints}) ``` ```json title="Response (truncated)" { "endpoints": [ { "key": "USER_ME", "path": "/autessa_poweruser/user/me", "group": "User" }, { "key": "AGENTS_CREATE", "path": "/autessa_poweruser/agents/create", "group": "Agents" }, { "key": "AUTESSADB_QUERY_QUERY", "path": "/autessa_poweruser/autessadb/query/query", "group": "AutessaDB Queries" } ], "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success. | | `401` | Invalid or expired key. | | `403` | Scoped key without `USER_ME` / `USER_AVAILABLE_ENDPOINTS` in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Poweruser Keys guide](/docs/guide/api-management/poweruser-keys.md) - [Agents](/docs/api/poweruser/agents.md) --- # Agents Source: https://docs.autessa.com/docs/api/poweruser/agents > Poweruser endpoints for creating, configuring, versioning, publishing, sharing, and analyzing agents. # Agents Manage the full lifecycle of agents programmatically: create and configure them, publish and roll back versions, inspect conversation history, submit evaluations, and control sharing and ownership. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/agents`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## The agent object Agents are returned as a polymorphic object keyed on `agentType`. All types share the base fields; each type adds its own configuration on top. ### Base fields - `agentId` (string) — Unique agent identifier. - `agentType` (string) — One of `SIMPLE`, `MCP`, `CRITIC`, `SEARCH`. Immutable after creation. - `versionNumber` (integer) — Version number when reading a specific published or archived version; absent on the draft working copy. - `name` (string) — Display name. - `description` (string) — What the agent does. Used by managers when deciding which agent to invoke. - `agentInstructions` (string) — The system prompt. May contain `@@{variableName}@@` placeholders that are filled from `promptTemplateVariables` at execution time. - `model` (string) — Model alias: `BIG_INSTRUCTION_FOLLOWING_REASONING`, `BIG_INSTRUCTION_FOLLOWING`, `SMALL_INSTRUCTION_FOLLOWING`, `VIBE_CODING`, or `AUTESSA_120B`. - `voiceConfig` (object) — Voice settings for audio output: `voiceModel` (`SMALL`, `MEDIUM`, `LARGE`) and `voiceName`. - `promptTemplateVariables` (object) — Map of variable name to argument definition (`description`, `isRequired`, `argumentSchema` — a JSON Schema object). - `evaluationNotes` (string) — Grading rubric used by automated evaluation. - `lastPublishedTime` (string) — ISO-8601 timestamp of the last publish, or absent if never published. - `tags` (array) — Free-form organizational tags. - `enableBrowserTools` (boolean) — Whether the agent may use built-in browser tools. ### Type-specific fields - `maxIterations` (integer) — `SIMPLE`, `MCP`, `CRITIC`, `SEARCH` — maximum reasoning/tool loop iterations per turn (default `10`). - `tools` (array) — `SIMPLE`, `CRITIC`, `SEARCH` — tools attached to the agent. See [Tools](/docs/api/poweruser/tools.md). - `mcpServerId` (string) — `MCP` — the MCP server whose tools the agent uses. - `autessaDbTableId` (string) — `SEARCH` — the AutessaDB table to search. - `searchColumnName` (string) — `SEARCH` — the column that is semantically indexed. - `topK` (integer) — `SEARCH` — number of results to retrieve (default `5`). - `scoreThreshold` (number) — `SEARCH` — minimum similarity score (default `0.7`). - `goodCriteria` (array) — `CRITIC` — criteria a response must satisfy. - `badCriteria` (array) — `CRITIC` — criteria a response must avoid. - `maxCriticIterations` (integer) — `CRITIC` — max revise-and-recheck rounds (default `3`). - `criticModel` (string) — `CRITIC` — model alias used by the critic pass. - `streamCriticFeedback` (boolean) — `CRITIC` — whether critic feedback is streamed to the caller. - `criticContextExchanges` (integer) — `CRITIC` — how many prior exchanges the critic sees (default `3`). ```json title="Agent object (SIMPLE)" { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "description": "Classifies inbound support tickets and drafts replies.", "agentInstructions": "You are a support triage specialist for @@{companyName}@@ ...", "model": "BIG_INSTRUCTION_FOLLOWING", "voiceConfig": { "voiceModel": "SMALL", "voiceName": "FEMALE" }, "promptTemplateVariables": { "companyName": { "description": "Company the agent represents", "isRequired": true, "argumentSchema": { "type": "string" } } }, "evaluationNotes": "A good response classifies the ticket and drafts a reply.", "lastPublishedTime": "2026-06-28T17:42:10", "tags": ["support"], "enableBrowserTools": false, "maxIterations": 10, "tools": [ { "toolId": "kZ3mQ9pW2x", "name": "LookupOrder", "description": "Looks up an order by ID in AutessaDB." } ] } ``` --- ## Versioning and access levels Agents follow the platform-wide draft/published model (see the [Versions guide](/docs/guide/agentic/versions.md)): - **Draft** — [`/update`](#update-an-agent) always edits the draft working copy. Draft changes are never visible to executions that use the published version. - **Published** — [`/publish`](#publish-an-agent) snapshots the draft as a new published version; the previously published version becomes **archived**. - **Rollback** — [`/rollback`](#roll-back-an-agent) republishes an archived version. The draft is untouched. Endpoints enforce the caller's access level on the agent (the poweruser key acts as its linked user): | Operation | Required access | |-----------|-----------------| | `get`, `all`, `versions`, `conversations/*`, `statistics` | Any access (READ or higher) | | `update` | WRITE, PUBLISH, or ADMIN | | `publish`, `rollback` | PUBLISH or ADMIN | | `delete`, `share` | ADMIN | | `transfer-ownership` | Owner only | --- ## Create an agent **POST** `/agents/create` Creates a new agent as a draft. `agentName` and `agentDescription` are both required — creation fails with an error message if either is blank. Configure instructions, model, and tools afterwards with [`/update`](#update-an-agent), then [`/publish`](#publish-an-agent) to make it executable. ### Request parameters - `agentType` (string, required) — `SIMPLE`, `MCP`, `CRITIC`, or `SEARCH`. - `agentName` (string, required) — Display name. Must be non-blank. - `agentDescription` (string, required) — Description of the agent's purpose. Must be non-blank. - `mcpServerId` (string) — `MCP` agents only — the MCP server to bind. - `autessaDbTableId` (string) — `SEARCH` agents only — the AutessaDB table to search. - `searchColumnName` (string) — `SEARCH` agents only — the indexed column to search over. - `topK` (integer) — `SEARCH` agents only — number of results to retrieve. - `scoreThreshold` (number) — `SEARCH` agents only — minimum similarity score. - `enableAutoContext` (boolean) — `SEARCH` agents only — automatically inject retrieved context. ### Response - `agent` (Agent) — The newly created agent object. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/agents/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentType": "SIMPLE", "agentName": "Support Triage Agent", "agentDescription": "Classifies inbound support tickets and drafts replies." }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/agents/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentType: 'SIMPLE', agentName: 'Support Triage Agent', agentDescription: 'Classifies inbound support tickets and drafts replies.' }) } ); const { agent } = await response.json(); console.log(agent.agentId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/agents/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'agentType': 'SIMPLE', 'agentName': 'Support Triage Agent', 'agentDescription': 'Classifies inbound support tickets and drafts replies.' } ) agent = response.json()['agent'] ``` ```json title="Response" { "agent": { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "description": "Classifies inbound support tickets and drafts replies.", "agentInstructions": "You are a professional agent who does as asked.", "model": "SMALL_INSTRUCTION_FOLLOWING", "maxIterations": 10, "tools": [] }, "errors": [] } ``` --- ## Get an agent **POST** `/agents/get` Retrieves an agent's full configuration. Without a `version` parameter you get the current draft working copy; pass `version` to read a specific published or archived snapshot. ### Request parameters - `agentId` (string, required) — The agent to fetch. - `version` (object) — Optional version selector: `versionState` (`PUBLISHED`, `DRAFT`, or `ARCHIVED`; defaults to `PUBLISHED` when the object is present) and an optional `versionNumber`. ### Response - `agent` (Agent) — The agent object. - `permission` (string) — The calling user's access level on this agent: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `voiceOptionsList` (array) — Voice models available to the company, each with `voiceModel` and a `voices` object listing `builtInVoiceNames` and `customVoiceNames` (custom voice clones, on supported models). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/agents/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "version": { "versionState": "PUBLISHED" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/agents/get', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5' }) } ); const { agent, permission } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/agents/get', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'agentId': 'V1StGXR8Z5'} ) data = response.json() ``` ```json title="Response (truncated)" { "agent": { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "model": "BIG_INSTRUCTION_FOLLOWING", "maxIterations": 10, "tools": [{ "toolId": "kZ3mQ9pW2x", "name": "LookupOrder" }] }, "permission": "ADMIN", "voiceOptionsList": [ { "voiceModel": "SMALL", "voices": { "builtInVoiceNames": ["FEMALE", "MALE"], "customVoiceNames": [] } } ], "errors": [] } ``` --- ## List all agents **POST** `/agents/all` Lists every agent visible to the linked user, grouped by how they have access. Entries are lightweight summaries — use [`/get`](#get-an-agent) for full configuration. ### Request parameters None — send an empty object `{}`. ### Response - `ownedAgents` (array) — Agents the linked user owns. - `companySharedAgents` (array) — Agents shared with the whole company. - `sharedWithMeAgents` (array) — Agents shared specifically with the linked user. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "ownedAgents": [ { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "description": "Classifies inbound support tickets and drafts replies.", "tags": ["support"], "lastPublishedTime": "2026-06-28T17:42:10" } ], "companySharedAgents": [], "sharedWithMeAgents": [], "errors": [] } ``` --- ## Update an agent **POST** `/agents/update` Replaces the agent's draft configuration with `updatedAgent`. This is a whole-object update: fetch the draft with [`/get`](#get-an-agent), modify it, and send the full object back. Published versions are unaffected until you [`/publish`](#publish-an-agent). Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `agentId` (string, required) — The agent to update. - `updatedAgent` (Agent, required) — The complete agent object to store as the new draft. Uses the [agent object](#the-agent-object) shape; `agentType` must match the existing agent. ### Response - `updatedAgent` (Agent) — The stored draft. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/agents/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "updatedAgent": { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "description": "Classifies inbound support tickets and drafts replies.", "agentInstructions": "You are a support triage specialist. Classify each ticket ...", "model": "BIG_INSTRUCTION_FOLLOWING", "maxIterations": 10, "tools": [{ "toolId": "kZ3mQ9pW2x" }] } }' ``` **JavaScript:** ```javascript // Read-modify-write the draft const getResp = await fetch( 'https://api.autessa.com/autessa_poweruser/agents/get', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5' }) } ); const { agent } = await getResp.json(); agent.model = 'BIG_INSTRUCTION_FOLLOWING'; agent.agentInstructions = 'You are a support triage specialist ...'; await fetch( 'https://api.autessa.com/autessa_poweruser/agents/update', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: agent.agentId, updatedAgent: agent }) } ); ``` **Python:** ```python headers = {'Authorization': os.environ['POWERUSER_KEY']} base = 'https://api.autessa.com/autessa_poweruser/agents' agent = requests.post(f'{base}/get', headers=headers, json={'agentId': 'V1StGXR8Z5'}).json()['agent'] agent['model'] = 'BIG_INSTRUCTION_FOLLOWING' requests.post(f'{base}/update', headers=headers, json={'agentId': agent['agentId'], 'updatedAgent': agent}) ``` ```json title="Response (truncated)" { "updatedAgent": { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "model": "BIG_INSTRUCTION_FOLLOWING" }, "errors": [] } ``` --- ## Publish an agent **POST** `/agents/publish` Snapshots the current draft as a new published version. Client API executions that don't pin a version always use the latest published version. The previously published version becomes archived and remains available for [`/rollback`](#roll-back-an-agent). Requires PUBLISH or ADMIN access. ### Request parameters - `agentId` (string, required) — The agent to publish. ### Response - `publishedAgentVersion` (integer) — The new published version number. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/publish \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5" }' ``` ```json title="Response" { "publishedAgentVersion": 4, "errors": [] } ``` --- ## Roll back an agent **POST** `/agents/rollback` Republishes an archived version, making it the live published version again. The draft working copy is not changed. Use [`/versions`](#list-versions) to find rollback candidates. Requires PUBLISH or ADMIN access. ### Request parameters - `agentId` (string, required) — The agent to roll back. - `versionToRollback` (integer, required) — The archived version number to republish. ### Response - `newPublishedVersion` (integer) — The version number now published. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/rollback \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "versionToRollback": 3 }' ``` ```json title="Response" { "newPublishedVersion": 5, "errors": [] } ``` --- ## List versions **POST** `/agents/versions` Returns the agent's published version and all archived version numbers. ### Request parameters - `agentId` (string, required) — The agent to inspect. ### Response - `currentPublishedVersion` (integer) — The live published version, or absent if the agent was never published. - `archivedVersions` (array) — Previously published version numbers available for rollback. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/versions \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5" }' ``` ```json title="Response" { "currentPublishedVersion": 4, "archivedVersions": [1, 2, 3], "errors": [] } ``` --- ## List conversations **POST** `/agents/conversations/history` Returns the agent's conversation history, newest first, paginated. ### Request parameters - `agentId` (string, required) — The agent whose conversations to list. - `page` (integer) — Zero-based page index. Defaults to `0`. - `includeMySandboxConversations` (boolean) — Also include the calling user's sandbox (Builder test) conversations. Defaults to `false`. ### Response - `conversations` (array) — Conversation records. Each has `resourceId` (the agent's ID), `version`, `conversationId`, `apiKeyId`, `closeDate`, `logs` (see [conversation logs](#conversation-logs)), and an optional `evaluationContext`. - `totalElements` (integer) — Total conversations across all pages. - `totalPages` (integer) — Total number of pages. - `currentPage` (integer) — The page returned. - `pageSize` (integer) — Conversations per page. - `hasNext` (boolean) — Whether another page exists. - `hasPrevious` (boolean) — Whether a previous page exists. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/agents/conversations/history \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "page": 0 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/agents/conversations/history', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ agentId: 'V1StGXR8Z5', page: 0 }) } ); const { conversations, hasNext } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/agents/conversations/history', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'agentId': 'V1StGXR8Z5', 'page': 0} ) data = response.json() ``` ```json title="Response (truncated)" { "conversations": [ { "resourceId": "V1StGXR8Z5", "version": "4", "conversationId": "conv_a1b2c3d4e5", "apiKeyId": "key_xyz", "closeDate": "2026-06-30T09:15:22", "logs": [ { "logType": "USER", "userMultimodalInputs": [ { "inputType": "TEXT", "content": "My order is late." } ] }, { "logType": "MESSAGE_TO_USER", "multimodalOutput": { "outputType": "TEXT", "content": "I'm sorry to hear that — let me look it up." } } ], "evaluationContext": null } ], "totalElements": 128, "totalPages": 13, "currentPage": 0, "pageSize": 10, "hasNext": true, "hasPrevious": false, "errors": [] } ``` ### Conversation logs Each entry in `logs` has a `logType` that determines which other fields are present: | logType | Payload | |---------|---------| | `USER` | `userMultimodalInputs` — array of text/audio inputs from the user. | | `MESSAGE_TO_USER` | `multimodalOutput` — the agent's text or audio response. | | `TOOL_REQUEST` | `executeToolRequest` — `toolCallId`, tool `name`, and `variables`. | | `TOOL_RESPONSE` | `executeToolResponse` — `toolCallId`, `toolName`, and the tool's `result`. | Audio entries additionally carry playback metadata (`playbackState`, `playbackStartedAt`, `playbackCompletedAt`, `spokenContent` when a response was interrupted), and CRITIC agents can emit rejected-message entries with `criticEvaluation` and `criticAttemptNumber`. --- ## Get a conversation **POST** `/agents/conversations/conversation` Fetches a single conversation by ID — unlike [`/conversations/history`](#list-conversations) this is not paginated, so deep links to older conversations resolve directly. ### Request parameters - `agentId` (string, required) — The agent the conversation belongs to. - `conversationId` (string, required) — The conversation to fetch. ### Response - `conversation` (Conversation) — The conversation record, same shape as entries in [`/conversations/history`](#list-conversations). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/conversations/conversation \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5" }' ``` ```json title="Response (truncated)" { "conversation": { "resourceId": "V1StGXR8Z5", "version": "4", "conversationId": "conv_a1b2c3d4e5", "logs": [ { "logType": "USER", "userMultimodalInputs": [] } ], "evaluationContext": { "humanEvaluation": { "status": "SUCCESSFUL", "notes": "Resolved on first contact.", "evaluationTime": "2026-06-30T10:02:44" } } }, "errors": [] } ``` --- ## Submit a human evaluation **POST** `/agents/submit-evaluation` Attaches a human evaluation to a conversation. Human evaluations take precedence over automated ones in [statistics](#get-statistics). The evaluation timestamp and evaluator are set server-side from the calling user — you only supply the verdict and notes. ### Request parameters - `agentId` (string, required) — The agent the conversation belongs to. - `conversationId` (string, required) — The conversation being evaluated. - `humanEvaluation` (object, required) — The evaluation payload. - `humanEvaluation.status` (string, required) — One of `SUCCESSFUL`, `SUCCESSFUL_NOT_EFFICIENT`, `SUCCESSFUL_NOT_ALL_STEPS`, `UNSUCCESSFUL`. - `humanEvaluation.notes` (string) — Free-form reviewer notes. ### Response - `errors` (array) — Error messages, if any occurred (for example `"Conversation not found"`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/submit-evaluation \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "conversationId": "conv_a1b2c3d4e5", "humanEvaluation": { "status": "SUCCESSFUL", "notes": "Resolved on first contact." } }' ``` ```json title="Response" { "errors": [] } ``` --- ## Get statistics **POST** `/agents/statistics` Aggregated evaluation statistics for an agent, optionally windowed by date, with a per-version breakdown. ### Request parameters - `agentId` (string, required) — The agent to analyze. - `startDate` (string) — ISO-8601 date-time lower bound (for example `"2026-06-01T00:00:00"`). - `endDate` (string) — ISO-8601 date-time upper bound. ### Response - `statistics` (object) — The statistics envelope. - `statistics.totalConversations` (integer) — Conversations in the window. - `statistics.humanEvaluation` (EvaluationStats) — Counts from human evaluations: `totalEvaluated`, `successful`, `successfulNotEfficient`, `successfulNotAllSteps`, `unsuccessful`. - `statistics.automatedEvaluation` (EvaluationStats) — Same counts from automated evaluation. - `statistics.humanPrecedenceEvaluation` (EvaluationStats) — Merged view where a human verdict overrides the automated one for the same conversation. - `statistics.versionBreakdown` (object) — Map of version number to a per-version stats object with the same four fields. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/statistics \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "startDate": "2026-06-01T00:00:00", "endDate": "2026-07-01T00:00:00" }' ``` ```json title="Response (truncated)" { "statistics": { "totalConversations": 128, "humanEvaluation": { "totalEvaluated": 40, "successful": 31, "successfulNotEfficient": 4, "successfulNotAllSteps": 2, "unsuccessful": 3 }, "automatedEvaluation": { "totalEvaluated": 128, "successful": 97, "successfulNotEfficient": 12, "successfulNotAllSteps": 8, "unsuccessful": 11 }, "humanPrecedenceEvaluation": { "totalEvaluated": 128, "successful": 99, "successfulNotEfficient": 11, "successfulNotAllSteps": 8, "unsuccessful": 10 }, "versionBreakdown": { "4": { "totalConversations": 74 } } }, "errors": [] } ``` --- ## Delete an agent **POST** `/agents/delete` Permanently deletes an agent, including its draft and version history. This is destructive and cannot be undone. Requires ADMIN access on the agent. ### Request parameters - `agentId` (string, required) — The agent to delete. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Share an agent **POST** `/agents/share` Sets the agent's sharing level and per-user grants in one call. Requires ADMIN access on the agent. ### Request parameters - `agentId` (string, required) — The agent to share. - `level` (string) — Sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `accessLevel` (string) — When `level` is `COMPANY`, the access level every company member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` (array) — Users to grant access. Each entry: `userEmail` or `userId` (string), plus `accessLevel`. - `usersToRemove` (array) — Users whose access to revoke, same entry shape. ### Response - `level` (string) — The resulting sharing level. - `accessLevel` (string) — The resulting company-wide access level, when shared with the company. - `usersWithAccess` (array) — The resulting per-user grants. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userEmail": "sam@acme.com", "accessLevel": "WRITE" } ], "usersToRemove": [] }' ``` ```json title="Response" { "level": "SPECIFIC_MEMBER", "usersWithAccess": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "errors": [] } ``` --- ## Get sharing details **POST** `/agents/get-sharing-details` Returns the agent's current sharing configuration and the calling user's own access level. ### Request parameters - `agentId` (string, required) — The agent to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId`, `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/get-sharing-details \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5" }' ``` ```json title="Response" { "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": null, "sharedWith": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "userAccessLevel": "ADMIN", "errors": [] } ``` --- ## Transfer ownership **POST** `/agents/transfer-ownership` Transfers the agent to another member of the company. Only the **current owner** may call this, and the new owner must already have some access to the agent (share it with them first). ### Request parameters - `agentId` (string, required) — The agent to transfer. - `newOwnerId` (string, required) — User ID of the new owner. Must be a member of the company with existing access to the agent. ### Response - `errors` (array) — Error messages, if any occurred (for example `"New owner does not have access to agent yet."`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/transfer-ownership \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "agentId": "V1StGXR8Z5", "newOwnerId": "uB7nT2wQ9k" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Generate an audio upload link **POST** `/agents/generate-audio-upload-link` Returns a pre-signed S3 URL for uploading a WAV audio file. Upload the file with an HTTP `PUT` to `uploadUrl`, then reference the returned `s3Uri` wherever an audio input is accepted. ### Request parameters None — send an empty object `{}` (WAV format is currently fixed). ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here with `Content-Type: audio/wav`. - `s3Uri` (string) — S3 URI to reference the uploaded file. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/generate-audio-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' # Then upload the file: curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: audio/wav" \ --data-binary '@recording.wav' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-audio/...", "s3Uri": "s3://autessa-audio/company-cP2mK7dX4v/audio-8f3a.wav", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Generate an image upload link **POST** `/agents/generate-image-upload-link` Returns a pre-signed S3 URL for uploading an image. ### Request parameters - `fileExtension` (string) — `"png"`, `"jpg"`, or `"jpeg"`. Defaults to `"png"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to reference the uploaded file. - `mimeType` (string) — MIME type to send as `Content-Type` on the upload. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/generate-image-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileExtension": "png" }' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-images/...", "s3Uri": "s3://autessa-images/company-cP2mK7dX4v/image-2b7c.png", "mimeType": "image/png", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Generate a file upload link **POST** `/agents/generate-file-upload-link` Returns a pre-signed S3 URL for uploading an arbitrary document. ### Request parameters - `fileExtension` (string) — File extension, for example `"pdf"`, `"docx"`, `"txt"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to reference the uploaded file. - `mimeType` (string) — MIME type to send as `Content-Type` on the upload. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/agents/generate-file-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileExtension": "pdf" }' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/file-9d1e.pdf", "mimeType": "application/pdf", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (blank name/description on create, missing `versionToRollback`, conversation not found). | | `401` | The linked user lacks the required access level (see the [access table](#versioning-and-access-levels)), or is not the owner for `transfer-ownership`. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Tools (Poweruser API)](/docs/api/poweruser/tools.md) - [Managers (Poweruser API)](/docs/api/poweruser/managers.md) - [Agents guide](/docs/guide/agentic/agents.md) - [Versions guide](/docs/guide/agentic/versions.md) --- # Tools Source: https://docs.autessa.com/docs/api/poweruser/tools > Poweruser endpoints for creating, configuring, versioning, publishing, sharing, and deleting tools, including bulk creation from MCP servers. # Tools Manage the full lifecycle of tools programmatically: create them, configure their type-specific behavior and arguments, publish and roll back versions, import entire MCP servers as tool sets, and control sharing and ownership. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/tools`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## The tool object Tools are self-contained actions that agents can call. What a tool actually does is determined by its `toolConfig` — the `toolType` selects one of the platform's tool implementations, and the `config` map carries that type's fields. - `toolId` (string) — Unique tool identifier. - `companyId` (string) — The owning company. - `name` (string) — Display name. This is also the function name the model sees, so keep it descriptive. - `description` (string) — What the tool does. The model uses this when deciding whether to call the tool. - `toolConfig` (object) — The tool's behavior: `toolType` (string — which implementation runs, e.g. `AUTESSA_SCRIPT`, `API_CALL`, `LLM_CALL`, `SEND_EMAIL`, `AUTESSADB_QUERY_TABLE`, `MCP`, and the rest of the tool-type catalog) and `config` (object — that type's configuration fields). The per-type fields are documented in the [tool-type reference](/docs/tools.md). Config values may contain `{{argumentName}}` placeholders that are filled from the tool's arguments at execution time. - `parsedToolConfig` (object) — Server-populated typed view of `toolConfig` for the tool's type. Read-only — send changes through `toolConfig`. - `arguments` (object) — Map of argument name to definition (`description`, `isRequired`, `argumentSchema` — a JSON Schema object). These are the parameters the model fills when calling the tool. - `privateArguments` (object) — Map of argument name to private-argument definition. Same fields as `arguments` plus `messageToUser` (string — the prompt shown when the value is collected from the end user), `regexValidation` (string), and `allowedValues` (array). Private argument values are never seen or filled by the model. - `lastPublishedTime` (string) — ISO-8601 timestamp of the last publish, or absent if never published. - `tags` (array) — Free-form organizational tags. Tools created from MCP servers are tagged with `mcp` and the server's hostname. ```json title="Tool object" { "toolId": "kZ3mQ9pW2x", "companyId": "cP2mK7dX4v", "name": "SendOrderUpdate", "description": "Emails the customer an update about their order.", "toolConfig": { "toolType": "SEND_EMAIL", "config": { "recipientEmail": "{{recipientEmail}}", "subject": "Update on your order", "messageBody": "{{updateText}}" } }, "arguments": { "recipientEmail": { "description": "Email address of the customer", "isRequired": true, "argumentSchema": { "type": "string" } }, "updateText": { "description": "The update to send", "isRequired": true, "argumentSchema": { "type": "string" } } }, "privateArguments": {}, "lastPublishedTime": "2026-06-28T17:42:10", "tags": ["support"] } ``` --- ## Versioning and access levels Tools follow the platform-wide draft/published model (see the [Versions guide](/docs/guide/agentic/versions.md)): - **Draft** — [`/update`](#update-a-tool) always edits the draft working copy. - **Published** — [`/publish`](#publish-a-tool) snapshots the draft as a new published version; the previously published version becomes **archived**. - **Rollback** — [`/rollback`](#roll-back-a-tool) republishes an archived version without touching the draft. Endpoints enforce the caller's access level on the tool (the poweruser key acts as its linked user): | Operation | Required access | |-----------|-----------------| | `get`, `all`, `versions`, `get-sharing-details` | Any access (READ or higher) | | `update` | WRITE, PUBLISH, or ADMIN | | `publish`, `rollback` | PUBLISH or ADMIN | | `delete`, `share` | ADMIN | | `transfer-ownership` | Owner only | --- ## Create a tool **POST** `/tools/create` Creates a new tool as a private draft. `toolName` and `toolDescription` are both required — creation fails with an error message if either is blank. The new tool starts with a placeholder `AUTESSA_SCRIPT` config; set the real `toolType`, `toolConfig`, and arguments with [`/update`](#update-a-tool), then [`/publish`](#publish-a-tool) to make it usable by published agents. See the [tool-type reference](/docs/tools.md) for the configuration fields each `toolType` accepts. ### Request parameters - `toolName` (string, required) — Display name. Must be non-blank. - `toolDescription` (string, required) — Description of what the tool does. Must be non-blank — the model relies on it to pick the right tool. ### Response - `tool` (Tool) — The newly created tool object. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/tools/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolName": "SendOrderUpdate", "toolDescription": "Emails the customer an update about their order." }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/tools/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ toolName: 'SendOrderUpdate', toolDescription: 'Emails the customer an update about their order.' }) } ); const { tool } = await response.json(); console.log(tool.toolId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/tools/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'toolName': 'SendOrderUpdate', 'toolDescription': 'Emails the customer an update about their order.' } ) tool = response.json()['tool'] ``` ```json title="Response (truncated)" { "tool": { "toolId": "kZ3mQ9pW2x", "companyId": "cP2mK7dX4v", "name": "SendOrderUpdate", "description": "Emails the customer an update about their order.", "toolConfig": { "toolType": "AUTESSA_SCRIPT", "config": { "script": "..." } }, "arguments": {}, "privateArguments": {} }, "errors": [] } ``` --- ## Get a tool **POST** `/tools/get` Retrieves a tool's full configuration. Without a `version` parameter you get the current draft working copy; pass `version` to read a specific published or archived snapshot. ### Request parameters - `toolId` (string, required) — The tool to fetch. - `version` (object) — Optional version selector: `versionState` (`PUBLISHED`, `DRAFT`, or `ARCHIVED`; defaults to `PUBLISHED` when the object is present) and an optional `versionNumber`. ### Response - `tool` (Tool) — The tool object. - `permission` (string) — The calling user's access level on this tool: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/tools/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x", "version": { "versionState": "PUBLISHED" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/tools/get', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ toolId: 'kZ3mQ9pW2x' }) } ); const { tool, permission } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/tools/get', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'toolId': 'kZ3mQ9pW2x'} ) data = response.json() ``` ```json title="Response (truncated)" { "tool": { "toolId": "kZ3mQ9pW2x", "name": "SendOrderUpdate", "toolConfig": { "toolType": "SEND_EMAIL", "config": { "subject": "Update on your order" } } }, "permission": "ADMIN", "errors": [] } ``` --- ## List all tools **POST** `/tools/all` Lists every tool visible to the linked user, grouped by how they have access. Entries are lightweight summaries — use [`/get`](#get-a-tool) for the full configuration. ### Request parameters None — send an empty object `{}`. ### Response - `ownedTools` (array) — Tools the linked user owns. - `companySharedTools` (array) — Tools shared with the whole company. - `sharedWithMeTools` (array) — Tools shared specifically with the linked user. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "ownedTools": [ { "toolId": "kZ3mQ9pW2x", "name": "SendOrderUpdate", "description": "Emails the customer an update about their order.", "tags": ["support"], "lastPublishedTime": "2026-06-28T17:42:10" } ], "companySharedTools": [], "sharedWithMeTools": [], "errors": [] } ``` --- ## Update a tool **POST** `/tools/update` Replaces the tool's draft configuration with `updatedTool`. This is a whole-object update: fetch the draft with [`/get`](#get-a-tool), modify it, and send the full object back. Published versions are unaffected until you [`/publish`](#publish-a-tool). This is where you set the tool's real behavior — `toolConfig.toolType` selects the implementation and `toolConfig.config` carries its fields. The required and optional config fields for every type are listed in the [tool-type reference](/docs/tools.md). Define the model-facing parameters in `arguments` and reference them from config values as `{{argumentName}}`. Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `toolId` (string, required) — The tool to update. - `updatedTool` (Tool, required) — The complete tool object to store as the new draft, using the [tool object](#the-tool-object) shape. ### Response - `updatedTool` (Tool) — The stored draft. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/tools/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x", "updatedTool": { "toolId": "kZ3mQ9pW2x", "name": "SendOrderUpdate", "description": "Emails the customer an update about their order.", "toolConfig": { "toolType": "SEND_EMAIL", "config": { "recipientEmail": "{{recipientEmail}}", "subject": "Update on your order", "messageBody": "{{updateText}}" } }, "arguments": { "recipientEmail": { "description": "Email address of the customer", "isRequired": true, "argumentSchema": { "type": "string" } }, "updateText": { "description": "The update to send", "isRequired": true, "argumentSchema": { "type": "string" } } }, "privateArguments": {} } }' ``` **JavaScript:** ```javascript // Read-modify-write the draft const base = 'https://api.autessa.com/autessa_poweruser/tools'; const headers = { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }; const getResp = await fetch(`${base}/get`, { method: 'POST', headers, body: JSON.stringify({ toolId: 'kZ3mQ9pW2x' }) }); const { tool } = await getResp.json(); tool.toolConfig = { toolType: 'SEND_EMAIL', config: { recipientEmail: '{{recipientEmail}}', subject: 'Update on your order', messageBody: '{{updateText}}' } }; await fetch(`${base}/update`, { method: 'POST', headers, body: JSON.stringify({ toolId: tool.toolId, updatedTool: tool }) }); ``` **Python:** ```python headers = {'Authorization': os.environ['POWERUSER_KEY']} base = 'https://api.autessa.com/autessa_poweruser/tools' tool = requests.post(f'{base}/get', headers=headers, json={'toolId': 'kZ3mQ9pW2x'}).json()['tool'] tool['toolConfig'] = { 'toolType': 'SEND_EMAIL', 'config': { 'recipientEmail': '{{recipientEmail}}', 'subject': 'Update on your order', 'messageBody': '{{updateText}}' } } requests.post(f'{base}/update', headers=headers, json={'toolId': tool['toolId'], 'updatedTool': tool}) ``` ```json title="Response (truncated)" { "updatedTool": { "toolId": "kZ3mQ9pW2x", "name": "SendOrderUpdate", "toolConfig": { "toolType": "SEND_EMAIL", "config": { "subject": "Update on your order" } } }, "errors": [] } ``` --- ## Publish a tool **POST** `/tools/publish` Snapshots the current draft as a new published version. Agents that use the tool's published version pick up the change; the previously published version becomes archived and remains available for [`/rollback`](#roll-back-a-tool). Requires PUBLISH or ADMIN access. ### Request parameters - `toolId` (string, required) — The tool to publish. ### Response - `publishedToolVersion` (integer) — The new published version number. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/publish \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x" }' ``` ```json title="Response" { "publishedToolVersion": 3, "errors": [] } ``` --- ## Roll back a tool **POST** `/tools/rollback` Republishes an archived version, making it the live published version again. The draft working copy is not changed. Use [`/versions`](#list-versions) to find rollback candidates. `versionToRollback` is required. Requires PUBLISH or ADMIN access. ### Request parameters - `toolId` (string, required) — The tool to roll back. - `versionToRollback` (integer, required) — The archived version number to republish. ### Response - `newPublishedVersion` (integer) — The version number now published. - `errors` (array) — Error messages, if any occurred (for example `"Failed to rollback tool to specified version"`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/rollback \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x", "versionToRollback": 2 }' ``` ```json title="Response" { "newPublishedVersion": 4, "errors": [] } ``` --- ## List versions **POST** `/tools/versions` Returns the tool's published version and all archived version numbers. ### Request parameters - `toolId` (string, required) — The tool to inspect. ### Response - `currentPublishedVersion` (integer) — The live published version, or absent if the tool was never published. - `archivedVersions` (array) — Previously published version numbers available for rollback. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/versions \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x" }' ``` ```json title="Response" { "currentPublishedVersion": 3, "archivedVersions": [1, 2], "errors": [] } ``` --- ## Create tools from an MCP server **POST** `/tools/create-from-mcp` Connects to a registered MCP server, lists its tools, and creates one Autessa tool per MCP tool. Each created tool: - has `toolType: MCP` bound to the server and the MCP tool's name, - gets its `arguments` parsed from the MCP tool's JSON Schema input schema, - is tagged with `mcp` and the server's hostname. Creation is best-effort per tool: if one MCP tool fails to convert, the rest are still created. An error is returned only when the server can't be reached, exposes no tools, or every conversion fails. Register servers first through the [MCP API](/docs/api/poweruser/mcp.md). ### Request parameters - `mcpServerId` (string, required) — The registered MCP server to import tools from. ### Response - `createdTools` (array) — The tools that were created, one per MCP tool. - `errors` (array) — Error messages, if any occurred (for example `"MCP server not found or access denied"` or `"No tools found on MCP server"`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/create-from-mcp \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "mcpServerId": "mQ4xT8rY2b" }' ``` ```json title="Response (truncated)" { "createdTools": [ { "toolId": "pR7vN2sK9j", "name": "search_issues", "description": "Search issues in the tracker.", "toolConfig": { "toolType": "MCP", "config": { "mcpServerId": "mQ4xT8rY2b", "mcpToolName": "search_issues" } }, "tags": ["mcp", "mcp_example_com"] } ], "errors": [] } ``` --- ## Share a tool **POST** `/tools/share` Sets the tool's sharing level and per-user grants in one call. Requires ADMIN access on the tool. ### Request parameters - `toolId` (string, required) — The tool to share. - `level` (string) — Sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `accessLevel` (string) — When `level` is `COMPANY`, the access level every company member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` (array) — Users to grant access. Each entry: `userEmail` or `userId` (string), plus `accessLevel`. - `usersToRemove` (array) — Users whose access to revoke, same entry shape. ### Response - `level` (string) — The resulting sharing level. - `accessLevel` (string) — The resulting company-wide access level, when shared with the company. - `usersWithAccess` (array) — The resulting per-user grants (populated when the tool is shared with specific members). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userEmail": "sam@acme.com", "accessLevel": "WRITE" } ], "usersToRemove": [] }' ``` ```json title="Response" { "level": "SPECIFIC_MEMBER", "usersWithAccess": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "errors": [] } ``` --- ## Get sharing details **POST** `/tools/get-sharing-details` Returns the tool's current sharing configuration and the calling user's own access level. ### Request parameters - `toolId` (string, required) — The tool to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId`, `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/get-sharing-details \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x" }' ``` ```json title="Response" { "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": null, "sharedWith": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "userAccessLevel": "ADMIN", "errors": [] } ``` --- ## Delete a tool **POST** `/tools/delete` Permanently deletes a tool, including its draft and version history. The delete is refused with a `422` status while any agent still uses the tool — the `errors` array names each blocking agent, for example `"Support Triage Agent is currently using tool. Remove all usages before deleting"`. Detach the tool from those agents first. Requires ADMIN access on the tool. ### Request parameters - `toolId` (string, required) — The tool to delete. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x" }' ``` ```json title="Response" { "errors": [] } ``` ```json title="Response (tool still in use, HTTP 422)" { "errors": [ "Support Triage Agent is currently using tool. Remove all usages before deleting" ] } ``` --- ## Transfer ownership **POST** `/tools/transfer-ownership` Transfers the tool to another member of the company. Only the **current owner** may call this, and the new owner must already have some access to the tool (share it with them first). ### Request parameters - `toolId` (string, required) — The tool to transfer. - `newOwnerId` (string, required) — User ID of the new owner. Must be a member of the company with existing access to the tool. ### Response - `errors` (array) — Error messages, if any occurred (for example `"New owner does not have access to tool yet."`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/tools/transfer-ownership \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "toolId": "kZ3mQ9pW2x", "newOwnerId": "uB7nT2wQ9k" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (blank name/description on create, missing `versionToRollback`, MCP server unreachable). | | `401` | The linked user lacks the required access level (see the [access table](#versioning-and-access-levels)), or is not the owner for `transfer-ownership`. | | `403` | Scoped key without the endpoint in its allow-list. | | `422` | `delete` refused because agents still use the tool; `errors` names them. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) - [Managers (Poweruser API)](/docs/api/poweruser/managers.md) - [MCP servers (Poweruser API)](/docs/api/poweruser/mcp.md) - [Tool-type reference](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Versions guide](/docs/guide/agentic/versions.md) --- # Managers Source: https://docs.autessa.com/docs/api/poweruser/managers > Poweruser endpoints for creating, configuring, versioning, publishing, sharing, and analyzing managers and their sub-agent wiring. # Managers Manage multi-agent managers programmatically: create and configure them, wire up sub-agents, publish and roll back versions, inspect conversation history and statistics, and control sharing and ownership. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/managers`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## The manager object A manager coordinates a team of agents: it reasons about the user's request, delegates tasks to its sub-agents, and composes their results into a response. Managers are returned as an object keyed on `managerType`; the only type today is `SIMPLE`. - `managerId` (string) — Unique manager identifier. - `managerType` (string) — Currently always `SIMPLE`. Immutable after creation. - `versionNumber` (integer) — Version number when reading a specific published or archived version; absent on the draft working copy. - `name` (string) — Display name. - `description` (string) — What the manager does. - `instructions` (string) — The system prompt. Defaults to `"Coordinate and manage the agents to accomplish the task"`. May contain `@@{variableName}@@` placeholders filled from `promptTemplateVariables` at execution time. - `model` (string) — Model alias: `BIG_INSTRUCTION_FOLLOWING_REASONING`, `BIG_INSTRUCTION_FOLLOWING`, `SMALL_INSTRUCTION_FOLLOWING`, `VIBE_CODING`, or `AUTESSA_120B`. Defaults to `SMALL_INSTRUCTION_FOLLOWING`. - `voiceConfig` (object) — Voice settings for audio output: `voiceModel` (`SMALL`, `MEDIUM`, `LARGE`) and `voiceName`. - `promptTemplateVariables` (object) — Map of variable name to argument definition (`description`, `isRequired`, `argumentSchema` — a JSON Schema object). - `evaluationNotes` (string) — Grading rubric used by automated evaluation. - `lastPublishedTime` (string) — ISO-8601 timestamp of the last publish, or absent if never published. - `tags` (array) — Free-form organizational tags. - `maxIterations` (integer) — `SIMPLE` — maximum coordination-loop iterations per turn (default `10`). Each sub-agent delegation consumes an iteration. - `agents` (array) — `SIMPLE` — the sub-agents the manager can delegate to. Reads return agent summaries; when updating, each entry only needs its `agentId` — the manager stores references and resolves the agents' published versions at execution time. See [Agents](/docs/api/poweruser/agents.md). ```json title="Manager object (SIMPLE)" { "managerId": "mZ8kW3qN5d", "managerType": "SIMPLE", "name": "Customer Success Manager", "description": "Coordinates triage, research, and reply agents for support requests.", "instructions": "Coordinate and manage the agents to accomplish the task", "model": "BIG_INSTRUCTION_FOLLOWING", "voiceConfig": { "voiceModel": "SMALL", "voiceName": "FEMALE" }, "promptTemplateVariables": {}, "evaluationNotes": "A good conversation resolves the request end to end.", "lastPublishedTime": "2026-06-28T17:42:10", "tags": ["support"], "maxIterations": 10, "agents": [ { "agentId": "V1StGXR8Z5", "agentType": "SIMPLE", "name": "Support Triage Agent", "description": "Classifies inbound support tickets and drafts replies." } ] } ``` --- ## Versioning and access levels Managers follow the platform-wide draft/published model (see the [Versions guide](/docs/guide/agentic/versions.md)): - **Draft** — [`/update`](#update-a-manager) always edits the draft working copy. - **Published** — [`/publish`](#publish-a-manager) snapshots the draft as a new published version; the previously published version becomes **archived**. - **Rollback** — [`/rollback`](#roll-back-a-manager) republishes an archived version. The draft is untouched. Endpoints enforce the caller's access level on the manager (the poweruser key acts as its linked user): | Operation | Required access | |-----------|-----------------| | `get`, `get-all`, `versions`, `conversations`, `conversation`, `statistics`, `get-sharing-details` | Any access (READ or higher) | | `update` | WRITE, PUBLISH, or ADMIN | | `publish`, `rollback` | PUBLISH or ADMIN | | `delete`, `share` | ADMIN | | `transfer-ownership` | Owner only | --- ## Create a manager **POST** `/managers/create` Creates a new manager as a draft. `managerName` and `managerDescription` are both required — creation fails with an error message if either is blank. Wire up instructions, model, and sub-agents afterwards with [`/update`](#update-a-manager), then [`/publish`](#publish-a-manager) to make it executable. ### Request parameters - `managerType` (string) — Currently only `SIMPLE`. - `managerName` (string, required) — Display name. Must be non-blank. - `managerDescription` (string, required) — Description of the manager's purpose. Must be non-blank. ### Response - `manager` (Manager) — The newly created manager object. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/managers/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerType": "SIMPLE", "managerName": "Customer Success Manager", "managerDescription": "Coordinates triage, research, and reply agents for support requests." }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/managers/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ managerType: 'SIMPLE', managerName: 'Customer Success Manager', managerDescription: 'Coordinates triage, research, and reply agents for support requests.' }) } ); const { manager } = await response.json(); console.log(manager.managerId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/managers/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'managerType': 'SIMPLE', 'managerName': 'Customer Success Manager', 'managerDescription': 'Coordinates triage, research, and reply agents for support requests.' } ) manager = response.json()['manager'] ``` ```json title="Response (truncated)" { "manager": { "managerId": "mZ8kW3qN5d", "managerType": "SIMPLE", "name": "Customer Success Manager", "description": "Coordinates triage, research, and reply agents for support requests.", "instructions": "Coordinate and manage the agents to accomplish the task", "model": "SMALL_INSTRUCTION_FOLLOWING", "maxIterations": 10, "agents": [] }, "errors": [] } ``` --- ## Get a manager **POST** `/managers/get` Retrieves a manager's full configuration, including its sub-agent list. ### Request parameters - `managerId` (string, required) — The manager to fetch. ### Response - `manager` (Manager) — The manager object. - `permission` (string) — The calling user's access level on this manager: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `voiceOptionsList` (array) — Voice models available to the company, each with `voiceModel` and a `voices` object listing `builtInVoiceNames` and `customVoiceNames` (custom voice clones, on supported models). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/managers/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/managers/get', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ managerId: 'mZ8kW3qN5d' }) } ); const { manager, permission } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/managers/get', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'managerId': 'mZ8kW3qN5d'} ) data = response.json() ``` ```json title="Response (truncated)" { "manager": { "managerId": "mZ8kW3qN5d", "managerType": "SIMPLE", "name": "Customer Success Manager", "model": "BIG_INSTRUCTION_FOLLOWING", "maxIterations": 10, "agents": [ { "agentId": "V1StGXR8Z5", "name": "Support Triage Agent" } ] }, "permission": "ADMIN", "voiceOptionsList": [ { "voiceModel": "SMALL", "voices": { "builtInVoiceNames": ["FEMALE", "MALE"], "customVoiceNames": [] } } ], "errors": [] } ``` --- ## List all managers **POST** `/managers/get-all` Lists every manager visible to the linked user, grouped by how they have access. Entries are lightweight summaries — use [`/get`](#get-a-manager) for full configuration. ### Request parameters None — send an empty object `{}`. ### Response - `ownedManagers` (array) — Managers the linked user owns. - `companySharedManagers` (array) — Managers shared with the whole company. - `sharedWithMeManagers` (array) — Managers shared specifically with the linked user. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/get-all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "ownedManagers": [ { "managerId": "mZ8kW3qN5d", "managerType": "SIMPLE", "name": "Customer Success Manager", "description": "Coordinates triage, research, and reply agents for support requests.", "tags": ["support"], "lastPublishedTime": "2026-06-28T17:42:10" } ], "companySharedManagers": [], "sharedWithMeManagers": [], "errors": [] } ``` --- ## Update a manager **POST** `/managers/update` Replaces the manager's draft configuration with `updatedManager`. This is a whole-object update: fetch the draft with [`/get`](#get-a-manager), modify it, and send the full object back. To change the sub-agent team, set `agents` to the desired list — each entry only needs its `agentId`. Published versions are unaffected until you [`/publish`](#publish-a-manager). Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `managerId` (string, required) — The manager to update. - `updatedManager` (Manager, required) — The complete manager object to store as the new draft, using the [manager object](#the-manager-object) shape; `managerType` must match the existing manager. ### Response - `updatedManager` (Manager) — The stored draft, with sub-agent summaries hydrated. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/managers/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "updatedManager": { "managerId": "mZ8kW3qN5d", "managerType": "SIMPLE", "name": "Customer Success Manager", "description": "Coordinates triage, research, and reply agents for support requests.", "instructions": "Delegate each request to the best agent. Escalate billing issues.", "model": "BIG_INSTRUCTION_FOLLOWING", "maxIterations": 10, "agents": [ { "agentId": "V1StGXR8Z5" }, { "agentId": "aQ6rD4xL8c" } ] } }' ``` **JavaScript:** ```javascript // Read-modify-write the draft const base = 'https://api.autessa.com/autessa_poweruser/managers'; const headers = { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }; const getResp = await fetch(`${base}/get`, { method: 'POST', headers, body: JSON.stringify({ managerId: 'mZ8kW3qN5d' }) }); const { manager } = await getResp.json(); manager.agents = [{ agentId: 'V1StGXR8Z5' }, { agentId: 'aQ6rD4xL8c' }]; manager.instructions = 'Delegate each request to the best agent.'; await fetch(`${base}/update`, { method: 'POST', headers, body: JSON.stringify({ managerId: manager.managerId, updatedManager: manager }) }); ``` **Python:** ```python headers = {'Authorization': os.environ['POWERUSER_KEY']} base = 'https://api.autessa.com/autessa_poweruser/managers' manager = requests.post(f'{base}/get', headers=headers, json={'managerId': 'mZ8kW3qN5d'}).json()['manager'] manager['agents'] = [{'agentId': 'V1StGXR8Z5'}, {'agentId': 'aQ6rD4xL8c'}] requests.post(f'{base}/update', headers=headers, json={'managerId': manager['managerId'], 'updatedManager': manager}) ``` ```json title="Response (truncated)" { "updatedManager": { "managerId": "mZ8kW3qN5d", "managerType": "SIMPLE", "name": "Customer Success Manager", "agents": [ { "agentId": "V1StGXR8Z5", "name": "Support Triage Agent" }, { "agentId": "aQ6rD4xL8c", "name": "Billing Research Agent" } ] }, "errors": [] } ``` --- ## Publish a manager **POST** `/managers/publish` Snapshots the current draft as a new published version. Client API executions that don't pin a version always use the latest published version. The previously published version becomes archived and remains available for [`/rollback`](#roll-back-a-manager). Requires PUBLISH or ADMIN access. ### Request parameters - `managerId` (string, required) — The manager to publish. ### Response - `publishedManagerVersion` (integer) — The new published version number. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/publish \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d" }' ``` ```json title="Response" { "publishedManagerVersion": 2, "errors": [] } ``` --- ## Roll back a manager **POST** `/managers/rollback` Republishes an archived version, making it the live published version again. The draft working copy is not changed. Use [`/versions`](#list-versions) to find rollback candidates. Requires PUBLISH or ADMIN access. ### Request parameters - `managerId` (string, required) — The manager to roll back. - `versionToRollback` (integer, required) — The archived version number to republish. ### Response - `newPublishedVersion` (integer) — The version number now published. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/rollback \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "versionToRollback": 1 }' ``` ```json title="Response" { "newPublishedVersion": 3, "errors": [] } ``` --- ## List versions **POST** `/managers/versions` Returns the manager's published version and all archived version numbers. ### Request parameters - `managerId` (string, required) — The manager to inspect. ### Response - `currentPublishedVersion` (integer) — The live published version, or absent if the manager was never published. - `archivedVersions` (array) — Previously published version numbers available for rollback. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/versions \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d" }' ``` ```json title="Response" { "currentPublishedVersion": 2, "archivedVersions": [1], "errors": [] } ``` --- ## List conversations **POST** `/managers/conversations` Returns the manager's conversation history, newest first, paginated. By default sub-agent conversations are referenced by ID inside the logs; pass `expandAgents: true` to inline the full agent conversations. ### Request parameters - `managerId` (string, required) — The manager whose conversations to list. - `page` (integer) — Zero-based page index. Defaults to `0`. - `includeMySandboxConversations` (boolean) — Also include the calling user's sandbox (Builder test) conversations. Defaults to `false`. - `expandAgents` (boolean) — Inline each delegated agent's full conversation inside its `INVOKE_AGENT_RESPONSE` log entry instead of returning only the agent conversation ID. Defaults to `false`. ### Response - `conversations` (array) — Conversation records. Each has `resourceId` (the manager's ID), `version`, `conversationId`, `apiKeyId` (or `"my-sandbox"`), `closeDate`, `logs` (see [conversation logs](#conversation-logs)), `currentIteration` (coordination iterations used), and an optional `evaluationContext`. - `totalElements` (integer) — Total conversations across all pages. - `totalPages` (integer) — Total number of pages. - `currentPage` (integer) — The page returned. - `pageSize` (integer) — Conversations per page. - `hasNext` (boolean) — Whether another page exists. - `hasPrevious` (boolean) — Whether a previous page exists. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/managers/conversations \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "page": 0, "expandAgents": false }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/managers/conversations', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ managerId: 'mZ8kW3qN5d', page: 0 }) } ); const { conversations, hasNext } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/managers/conversations', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'managerId': 'mZ8kW3qN5d', 'page': 0} ) data = response.json() ``` ```json title="Response (truncated)" { "conversations": [ { "resourceId": "mZ8kW3qN5d", "version": "2", "conversationId": "conv_m7f2e9c1a4", "apiKeyId": "key_xyz", "closeDate": "2026-06-30T09:15:22", "currentIteration": 3, "logs": [ { "logType": "USER_INPUT", "combinedMultimodalInput": [ { "inputType": "TEXT", "content": "My last invoice looks wrong." } ] }, { "logType": "INVOKE_AGENT_REQUEST", "invokeAgentRequest": { "invocationId": "8f3a1c2e-...", "agentId": "aQ6rD4xL8c", "agentName": "Billing Research Agent", "taskDescription": "Look up the customer's latest invoice and check the line items." } }, { "logType": "INVOKE_AGENT_RESPONSE", "invokeAgentResponse": { "invocationId": "8f3a1c2e-...", "agentId": "aQ6rD4xL8c", "responseType": "COMPLETE", "summary": "Invoice INV-1042 double-billed the June commitment." } }, { "logType": "MESSAGE_TO_USER", "multimodalOutput": { "outputType": "TEXT", "content": "You're right — the June commitment was billed twice. I've flagged it." } } ], "evaluationContext": null } ], "totalElements": 42, "totalPages": 5, "currentPage": 0, "pageSize": 10, "hasNext": true, "hasPrevious": false, "errors": [] } ``` ### Conversation logs Each entry in `logs` has a `logType` that determines which other fields are present: | logType | Payload | |---------|---------| | `USER_INPUT` | `combinedMultimodalInput` — array of text/audio inputs from the user. | | `MANAGER_REASONING` | `multimodalOutput` — the manager's internal reasoning. | | `MESSAGE_TO_USER` | `multimodalOutput` — the manager's text or audio response. | | `INVOKE_AGENT_REQUEST` | `invokeAgentRequest` — `invocationId`, `agentId`, `agentName`, `taskDescription`, `context`, `variables`, and any forwarded multimodal inputs. | | `INVOKE_AGENT_RESPONSE` | `invokeAgentResponse` — matching `invocationId`, `agentId`, `agentName`, `responseType` (`COMPLETE` or `HELP`), `summary` (for COMPLETE), `helpRequest` (for HELP), and the sub-agent's conversation ID (expanded inline when `expandAgents` is true). | | `USER_REQUEST` | `userRequest` — the manager asking the user for clarification; the reply appears as `userResponse`. | Audio entries additionally carry playback metadata (`playbackState`, `playbackStartedAt`, `playbackCompletedAt`, `spokenContent` when a response was interrupted). --- ## Get a conversation **POST** `/managers/conversation` Fetches a single conversation by ID — unlike [`/conversations`](#list-conversations) this is not paginated, so deep links to older conversations resolve directly. ### Request parameters - `managerId` (string, required) — The manager the conversation belongs to. - `conversationId` (string, required) — The conversation to fetch. - `expandAgents` (boolean) — Inline delegated agents' full conversations. Defaults to `false`. ### Response - `conversation` (Conversation) — The conversation record, same shape as entries in [`/conversations`](#list-conversations). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/conversation \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "conversationId": "conv_m7f2e9c1a4", "expandAgents": true }' ``` ```json title="Response (truncated)" { "conversation": { "resourceId": "mZ8kW3qN5d", "version": "2", "conversationId": "conv_m7f2e9c1a4", "currentIteration": 3, "logs": [ { "logType": "USER_INPUT", "combinedMultimodalInput": [] } ], "evaluationContext": { "humanEvaluation": { "status": "SUCCESSFUL", "notes": "Resolved on first contact.", "evaluationTime": "2026-06-30T10:02:44" } } }, "errors": [] } ``` --- ## Get statistics **POST** `/managers/statistics` Aggregated evaluation statistics for a manager, optionally windowed by date, with a per-version breakdown. ### Request parameters - `managerId` (string, required) — The manager to analyze. - `startDate` (string) — ISO-8601 date-time lower bound (for example `"2026-06-01T00:00:00"`). - `endDate` (string) — ISO-8601 date-time upper bound. ### Response - `statistics` (object) — The statistics envelope. - `statistics.totalConversations` (integer) — Conversations in the window. - `statistics.humanEvaluation` (EvaluationStats) — Counts from human evaluations: `totalEvaluated`, `successful`, `successfulNotEfficient`, `successfulNotAllSteps`, `unsuccessful`. - `statistics.automatedEvaluation` (EvaluationStats) — Same counts from automated evaluation. - `statistics.humanPrecedenceEvaluation` (EvaluationStats) — Merged view where a human verdict overrides the automated one for the same conversation. - `statistics.versionBreakdown` (object) — Map of version number to a per-version stats object with the same fields. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/statistics \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "startDate": "2026-06-01T00:00:00", "endDate": "2026-07-01T00:00:00" }' ``` ```json title="Response (truncated)" { "statistics": { "totalConversations": 42, "humanEvaluation": { "totalEvaluated": 12, "successful": 9, "successfulNotEfficient": 1, "successfulNotAllSteps": 1, "unsuccessful": 1 }, "automatedEvaluation": { "totalEvaluated": 42, "successful": 33, "successfulNotEfficient": 4, "successfulNotAllSteps": 2, "unsuccessful": 3 }, "humanPrecedenceEvaluation": { "totalEvaluated": 42, "successful": 34, "successfulNotEfficient": 3, "successfulNotAllSteps": 2, "unsuccessful": 3 }, "versionBreakdown": { "2": { "totalConversations": 28 } } }, "errors": [] } ``` --- ## Submit a human evaluation **POST** `/managers/submit-evaluation` Attaches a human evaluation to a manager conversation. Human evaluations take precedence over automated ones in [statistics](#get-statistics). The evaluation timestamp and evaluator are set server-side from the calling user — you only supply the verdict and notes. ### Request parameters - `managerId` (string) — The manager the conversation belongs to. - `conversationId` (string, required) — The conversation being evaluated. - `humanEvaluation` (object, required) — The evaluation payload. - `humanEvaluation.status` (string, required) — One of `SUCCESSFUL`, `SUCCESSFUL_NOT_EFFICIENT`, `SUCCESSFUL_NOT_ALL_STEPS`, `UNSUCCESSFUL`. - `humanEvaluation.notes` (string) — Free-form reviewer notes. ### Response - `errors` (array) — Error messages, if any occurred (for example `"Conversation not found"`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/submit-evaluation \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "conversationId": "conv_a1b2c3d4e5", "humanEvaluation": { "status": "SUCCESSFUL", "notes": "Routed to the right sub-agent and resolved on first pass." } }' ``` ```json title="Response" { "errors": [] } ``` --- ## Delete a manager **POST** `/managers/delete` Permanently deletes a manager, including its draft and version history. The sub-agents themselves are not deleted. This is destructive and cannot be undone. Requires ADMIN access on the manager. ### Request parameters - `managerId` (string, required) — The manager to delete. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Share a manager **POST** `/managers/share` Sets the manager's sharing level and per-user grants in one call. Requires ADMIN access on the manager. ### Request parameters - `managerId` (string, required) — The manager to share. - `level` (string) — Sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `accessLevel` (string) — When `level` is `COMPANY`, the access level every company member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` (array) — Users to grant access. Each entry: `userEmail` or `userId` (string), plus `accessLevel`. - `usersToRemove` (array) — Users whose access to revoke, same entry shape. ### Response - `level` (string) — The resulting sharing level. - `accessLevel` (string) — The resulting company-wide access level, when shared with the company. - `usersWithAccess` (array) — The resulting per-user grants. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userEmail": "sam@acme.com", "accessLevel": "WRITE" } ], "usersToRemove": [] }' ``` ```json title="Response" { "level": "SPECIFIC_MEMBER", "usersWithAccess": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "errors": [] } ``` --- ## Get sharing details **POST** `/managers/get-sharing-details` Returns the manager's current sharing configuration and the calling user's own access level. ### Request parameters - `managerId` (string, required) — The manager to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId`, `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/get-sharing-details \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d" }' ``` ```json title="Response" { "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": null, "sharedWith": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "userAccessLevel": "ADMIN", "errors": [] } ``` --- ## Transfer ownership **POST** `/managers/transfer-ownership` Transfers the manager to another member of the company. Only the **current owner** may call this, and the new owner must already have some access to the manager (share it with them first). ### Request parameters - `managerId` (string, required) — The manager to transfer. - `newOwnerId` (string, required) — User ID of the new owner. Must be a member of the company with existing access to the manager. ### Response - `errors` (array) — Error messages, if any occurred (for example `"New owner does not have access to manager yet."`). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/transfer-ownership \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "managerId": "mZ8kW3qN5d", "newOwnerId": "uB7nT2wQ9k" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Generate an audio upload link **POST** `/managers/generate-audio-upload-link` Returns a pre-signed S3 URL for uploading a WAV audio file. Upload the file with an HTTP `PUT` to `uploadUrl`, then reference the returned `s3Uri` wherever an audio input is accepted. ### Request parameters None — send an empty object `{}` (WAV format is currently fixed). ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here with `Content-Type: audio/wav`. - `s3Uri` (string) — S3 URI to reference the uploaded file. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/generate-audio-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' # Then upload the file: curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: audio/wav" \ --data-binary '@recording.wav' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-audio/...", "s3Uri": "s3://autessa-audio/company-cP2mK7dX4v/audio-8f3a.wav", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Generate an image upload link **POST** `/managers/generate-image-upload-link` Returns a pre-signed S3 URL for uploading an image. ### Request parameters - `fileExtension` (string) — `"png"`, `"jpg"`, or `"jpeg"`. Defaults to `"png"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to reference the uploaded file. - `mimeType` (string) — MIME type to send as `Content-Type` on the upload. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/generate-image-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileExtension": "png" }' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-images/...", "s3Uri": "s3://autessa-images/company-cP2mK7dX4v/image-2b7c.png", "mimeType": "image/png", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Generate a file upload link **POST** `/managers/generate-file-upload-link` Returns a pre-signed S3 URL for uploading an arbitrary document. ### Request parameters - `fileExtension` (string) — File extension, for example `"pdf"`, `"docx"`, `"txt"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to reference the uploaded file. - `mimeType` (string) — MIME type to send as `Content-Type` on the upload. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/managers/generate-file-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileExtension": "pdf" }' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/file-9d1e.pdf", "mimeType": "application/pdf", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (blank name/description on create, unknown conversation ID). | | `401` | The linked user lacks the required access level (see the [access table](#versioning-and-access-levels)), or is not the owner for `transfer-ownership`. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) - [Tools (Poweruser API)](/docs/api/poweruser/tools.md) - [Managers guide](/docs/guide/agentic/managers.md) - [Versions guide](/docs/guide/agentic/versions.md) --- # AutessaDB Tables Source: https://docs.autessa.com/docs/api/poweruser/autessadb-tables > Poweruser endpoints for creating, sharing, updating, and deleting AutessaDB tables and knowledge bases. # AutessaDB Tables The AutessaDB Tables API manages the lifecycle of AutessaDB tables: create regular tables and knowledge bases, inspect and update schemas, control sharing and ownership, analyze trigger cascades, and safely delete tables with a two-step confirmation flow. > All endpoints are `POST` and accept a JSON body. Requests are scoped to the company that owns the poweruser key. --- ## Base URL ``` https://api.autessa.com/autessa_poweruser/autessadb/tables ``` ## Authentication Pass your poweruser key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: pwrusr:{key_id}:{secret} ``` The examples below use `$POWERUSER_KEY` as a stand-in for the full `pwrusr:...` value. --- ## Column types Every column in an AutessaDB table has one of twelve types. Text-like types can additionally be **vectorized** (`vectorizeField: true`) to make their content available for semantic search. | Type | Vectorizable | Character limit | Notes | | --- | --- | --- | --- | | `SHORT_TEXT` | Yes | 32,000 | Plain text; limit matches embedding model constraints. | | `LONG_TEXT` | Yes | 128,000 | Long-form text; limit matches long-context embedding constraints. | | `FILE` | Yes | — | File stored in S3; content is extracted and analyzed for vectorization. | | `AUDIO` | Yes | — | Audio file; transcribed before vectorization. | | `IMAGE` | No | — | Image file reference. | | `VIDEO` | No | — | Video file reference. | | `INTEGER` | No | — | Whole numbers; supports `numberConfiguration` bounds. | | `FLOAT` | No | — | Decimal numbers; supports `numberConfiguration` bounds. | | `BOOLEAN` | No | — | True/false. | | `SINGLE_SELECT` | No | — | One value from `allowedValues`. | | `MULTI_SELECT` | No | — | Multiple values from `allowedValues`. | Store timestamps as `SHORT_TEXT` holding UTC ISO-8601 strings (e.g. `2026-07-15T14:30:00Z`). `DATETIME` is a legacy, read-only type: existing `DATETIME` columns still read back their stored value, but it is rejected when creating a table or adding a column. Only `SHORT_TEXT`, `LONG_TEXT`, `FILE`, and `AUDIO` support `vectorizeField` — the same list returned by [`/table-options`](#get-table-options-). Setting `vectorizeField: true` on any other type is rejected. --- ## Schema reference A table's structure is described by a schema object. You send one when creating a table, and every read endpoint returns the current schema. ### Schema object - `tableName` (string) — The table's name. On `/create` the server sets this from the request's top-level `tableName`. - `columnDefinitions` — "> The user-defined columns. See [Column definition](#column-definition) below. - `builtInColumns` — "> Server-populated on reads: the automatic `id` (INTEGER), `created_at` (DATETIME), and `updated_at` (DATETIME) columns every table has. Do not send these when creating a table. - `dbTriggers` — "> Triggers attached to the table. Managed through the [AutessaDB Triggers API](/docs/api/poweruser/autessadb-triggers.md), not this endpoint group. - `triggerSettings` (object) — Table-level trigger settings. Also managed through the triggers API. - `dbType` (string) — One of `DEFAULT`, `KNOWLEDGE_BASE`, or `DEPENDENT`. Defaults to `DEFAULT` when omitted. `KNOWLEDGE_BASE` tables are created by `/create-knowledge-base`; `DEPENDENT` tables are created by connectors and cannot be shared, transferred, or deleted directly. - `tags` — "> Free-form tags on the table. - `appendOnly` (boolean) — Whether the table is append-only (immutable / audit-grade). When `true`, rows can be inserted and read but never updated or deleted — every update and delete is rejected for all callers. Additional restrictions apply: computed columns cannot be defined on an append-only table, and column removal is blocked (the schema can only grow). This flag is a **one-way latch** — it can be turned on but never off. Set it on [Create Table](#create-table-); dropping the whole table remains a separate admin operation. Defaults to `false`. ### Column definition - `columnType` (string, required) — One of the twelve [column types](#column-types). - `columnName` (string, required) — The column's name. Validated on create; must not collide with built-in columns. - `columnComment` (string) — Human-readable description of the column. - `required` (boolean, required) — Whether a value is required on insert. - `numberConfiguration` (object) — Bounds for `INTEGER`/`FLOAT` columns. Fields: `minValue` (number), `maxValue` (number). - `textConfiguration` (object) — Length constraints for text columns. Fields: `maxLength` (integer), `minLength` (integer). `maxLength` may not exceed the type's character limit (32,000 for `SHORT_TEXT`, 128,000 for `LONG_TEXT`). - `vectorizeField` (boolean) — Index this column's content for semantic search. Only valid on `SHORT_TEXT`, `LONG_TEXT`, `FILE`, and `AUDIO` columns. - `indexed` (boolean) — Add an equality index on this column for faster filtering. `INTEGER`, `FLOAT`, `BOOLEAN`, `SINGLE_SELECT`, and foreign-key columns are always indexed, so the flag is accepted as a no-op for them. `SHORT_TEXT` can opt in — but once a `SHORT_TEXT` column is indexed its values are length-capped to 2000 characters. `LONG_TEXT`, `MULTI_SELECT`, and media columns cannot be indexed and are rejected. - `defaultValue` (object) — Hardcoded default as a column-value object. Set `columnType` plus the matching value field: `textValue` (string), `filePath` (string), `integerValue` (integer), `floatValue` (float), `booleanValue` (boolean), or `multiSelectValue` (array). - `computationValue` (object) — Computed-column definition. Fields: `script` (string — AutessaScript source), `dependsOn` (array — column names the computation reads), `type` (string — `SCRIPT`, `AGENTIC_FLOW`, `LLM_CALL`, or `CUSTOM`; defaults to `SCRIPT`), `timeoutMs` (integer — computation timeout in milliseconds, default 600000). - `allowedValues` — "> The permitted values for `SINGLE_SELECT` and `MULTI_SELECT` columns. - `valueToDisplayNameMapping` (object) — Optional map of allowed value to display name for select columns. - `isForeignKey` (boolean) — Marks this column as a foreign key. Foreign keys can only reference the `id` field of another AutessaDB table. - `foreignKeyTableId` (string) — The referenced table's ID. Required when `isForeignKey` is true; the table must exist in your company or the create fails. ```json title="Example column definition" { "columnType": "SHORT_TEXT", "columnName": "email", "columnComment": "Customer email address", "required": true, "textConfiguration": { "minLength": 3, "maxLength": 320 }, "vectorizeField": false, "isForeignKey": false } ``` ```json title="Foreign key column" { "columnType": "INTEGER", "columnName": "account_id", "columnComment": "Owning account", "required": true, "isForeignKey": true, "foreignKeyTableId": "kTn4vB2p9Q" } ``` ```json title="Computed column" { "columnType": "SHORT_TEXT", "columnName": "file_name", "columnComment": "File name extracted from full_path", "required": true, "computationValue": { "script": "// AutessaScript...", "dependsOn": ["full_path"], "type": "SCRIPT", "timeoutMs": 5000 } } ``` --- ## Get Table Options **POST** `/table-options` Returns which column types support vectorization. Useful for building schema editors without hardcoding the type list. ### Request Body No request body fields. Send an empty JSON object. ### Response - `vectorizableColumns` — "> Column types that allow `vectorizeField: true`: `SHORT_TEXT`, `LONG_TEXT`, `FILE`, `AUDIO`. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/table-options' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{}' ``` ```json title="JSON" { "vectorizableColumns": ["SHORT_TEXT", "LONG_TEXT", "FILE", "AUDIO"] } ``` --- ## Create Table **POST** `/create` Creates a new AutessaDB table from a schema. The server validates the table name (it must be unique within your company), every column name, text length constraints against the type's character limit, and any foreign key references before creating the table. Two convenience behaviors happen automatically: - For every vectorizable `FILE` column, a companion notes column is auto-generated to hold extracted file content. - For every vectorizable `AUDIO` column, a companion transcription column is auto-generated. Every table also gets built-in `id`, `created_at`, and `updated_at` columns — do not define these yourself. Set `schema.appendOnly` to `true` to create an immutable, audit-grade table — rows can be inserted and read but never updated or deleted (every update/delete is rejected for all callers), computed columns cannot be defined, and later column removal is blocked. The flag is a one-way latch (see the [Schema object](#schema-object)). Set `indexed: true` on a column definition to add an equality index for faster filtering (see [Column definition](#column-definition) for which types support it and the 2000-character cap on indexed `SHORT_TEXT`). ### Request Body - `tableName` (string, required) — Name for the new table. Must be unique within your company. - `schema` (object, required) — The table schema. See the [Schema reference](#schema-reference). `columnDefinitions` is the important part; `dbType` defaults to `DEFAULT` when omitted. Set `appendOnly: true` for an immutable table, and `indexed: true` on individual columns to index them. ### Response - `tableId` (string) — ID of the newly created table. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/create' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableName": "customers", "schema": { "columnDefinitions": [ { "columnType": "SHORT_TEXT", "columnName": "email", "columnComment": "Customer email address", "required": true }, { "columnType": "LONG_TEXT", "columnName": "notes", "columnComment": "Free-form account notes", "vectorizeField": true }, { "columnType": "SINGLE_SELECT", "columnName": "tier", "allowedValues": ["free", "pro", "enterprise"] } ], "tags": ["crm"] } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/create', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableName: 'customers', schema: { columnDefinitions: [ { columnType: 'SHORT_TEXT', columnName: 'email', columnComment: 'Customer email address', required: true }, { columnType: 'LONG_TEXT', columnName: 'notes', vectorizeField: true } ], tags: ['crm'] } }) } ); const data = await response.json(); console.log(data.tableId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableName': 'customers', 'schema': { 'columnDefinitions': [ { 'columnType': 'SHORT_TEXT', 'columnName': 'email', 'columnComment': 'Customer email address', 'required': True }, { 'columnType': 'LONG_TEXT', 'columnName': 'notes', 'vectorizeField': True } ], 'tags': ['crm'] } } ) print(response.json()['tableId']) ``` ```json title="JSON" { "tableId": "V1StGXR8Z5" } ``` --- ## Create Knowledge Base **POST** `/create-knowledge-base` Creates a **knowledge base**: an AutessaDB table (`dbType: KNOWLEDGE_BASE`) purpose-built for semantic search over uploaded files. The full workflow is: 1. Call [`/generate-knowledge-base-upload-links`](#generate-knowledge-base-upload-links-) to get presigned S3 upload URLs. 2. `PUT` each file's bytes to its `uploadUrl`. 3. Call this endpoint with each file's logical `fullPath` and the `s3Uri` returned alongside its upload link. The table is created with three columns: - `full_path` (`SHORT_TEXT`, required) — the file's path including folders. - `file_name` (`SHORT_TEXT`, required, computed) — extracted from `full_path`. - `file` (`FILE`, required, `vectorizeField: true`) — the file content, vectorized for semantic search. Files are moved from their temporary upload location to permanent storage, then inserted as rows. Row inserts that fail are reported in `errors` while successful inserts still count toward `filesInserted`. ### Request Body - `tableName` (string, required) — Name for the knowledge base table. Must be unique within your company. - `files` — " required> At least one file. Each entry has `fullPath` (string, required — logical path such as `docs/handbook.pdf`; duplicates are rejected) and `s3Uri` (string, required — must start with `s3://`; use the value returned by the upload-links endpoint). ### Response - `tableId` (string) — ID of the created knowledge base table. - `tableName` (string) — Name of the created table. - `filesInserted` (integer) — How many of the requested files were successfully inserted as rows. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/create-knowledge-base' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableName": "product_docs", "files": [ { "fullPath": "docs/getting-started.pdf", "s3Uri": "s3://autessa-uploads/temp/knowledge_base/1024/3f9c2a7e" }, { "fullPath": "docs/api/reference.md", "s3Uri": "s3://autessa-uploads/temp/knowledge_base/1024/8b1d4c5f" } ] }' ``` ```json title="JSON" { "tableId": "aB3xK9mQ2w", "tableName": "product_docs", "filesInserted": 2 } ``` --- ## Generate Knowledge Base Upload Links **POST** `/generate-knowledge-base-upload-links` Generates presigned S3 `PUT` URLs for uploading knowledge base files. Each link comes with the `s3Uri` you later pass to [`/create-knowledge-base`](#create-knowledge-base-). Links point at a temporary location and expire after 5 minutes — upload promptly, then create the knowledge base (which moves files to permanent storage). ### Request Body - `count` (integer, required) — Number of upload links to generate. Must be between 1 and 50. ### Response - `uploadLinks` — "> One entry per requested link. Each has `uploadUrl` (string — presigned S3 `PUT` URL) and `s3Uri` (string — the S3 URI to reference in `/create-knowledge-base`). - `expiresInSeconds` (integer) — Lifetime of the presigned URLs in seconds (300). ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/generate-knowledge-base-upload-links' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "count": 2 }' ``` ```json title="JSON" { "uploadLinks": [ { "uploadUrl": "https://autessa-uploads.s3.amazonaws.com/temp/knowledge_base/1024/3f9c2a7e?X-Amz-Signature=...", "s3Uri": "s3://autessa-uploads/temp/knowledge_base/1024/3f9c2a7e" }, { "uploadUrl": "https://autessa-uploads.s3.amazonaws.com/temp/knowledge_base/1024/8b1d4c5f?X-Amz-Signature=...", "s3Uri": "s3://autessa-uploads/temp/knowledge_base/1024/8b1d4c5f" } ], "expiresInSeconds": 300 } ``` --- ## Get Table **POST** `/get` Fetches a single table, including its full schema (with server-populated `builtInColumns`) and your effective access level. Requires at least read access to the table. ### Request Body - `tableId` (string, required) — ID of the table to fetch. ### Response - `permission` (string) — Your access level on the table: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `table` (object) — The table. Fields: `tableId` (string), `tableName` (string), `tableSchema` (object — see [Schema reference](#schema-reference)). ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/get' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "permission": "ADMIN", "table": { "tableId": "V1StGXR8Z5", "tableName": "customers", "tableSchema": { "tableName": "customers", "dbType": "DEFAULT", "tags": ["crm"], "columnDefinitions": [ { "columnType": "SHORT_TEXT", "columnName": "email", "columnComment": "Customer email address", "required": true, "isForeignKey": false } ], "builtInColumns": [ { "columnType": "INTEGER", "columnName": "id", "columnComment": "Unique identifier for each record", "required": true, "isForeignKey": false }, { "columnType": "DATETIME", "columnName": "created_at", "columnComment": "Timestamp when record was created", "required": true, "isForeignKey": false }, { "columnType": "DATETIME", "columnName": "updated_at", "columnComment": "Timestamp when record was last updated", "required": true, "isForeignKey": false } ], "dbTriggers": [] } } } ``` --- ## List All Tables **POST** `/all` Lists every table visible to the authenticated user in the current company scope, split into three buckets: tables you own, tables shared company-wide, and tables shared with you individually. ### Request Body No request body fields. Send an empty JSON object. ### Response - `ownedTables` — "> Tables owned by the authenticated user. Each entry has `tableId` (string), `tableName` (string), and `tableSchema` (object). - `companySharedTables` — "> Tables shared at the company level. Same shape as `ownedTables`. - `sharedWithMeTables` — "> Tables shared directly with the user. Same shape as `ownedTables`. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/all' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{}' ``` ```json title="JSON" { "ownedTables": [ { "tableId": "V1StGXR8Z5", "tableName": "customers", "tableSchema": { "tableName": "customers", "dbType": "DEFAULT" } } ], "companySharedTables": [ { "tableId": "kTn4vB2p9Q", "tableName": "orders", "tableSchema": { "tableName": "orders", "dbType": "DEFAULT" } } ], "sharedWithMeTables": [] } ``` --- ## Share Table **POST** `/share` Updates a table's sharing configuration. Requires `ADMIN` access on the table. `DEPENDENT` tables (created by connectors) cannot be re-shared — update the linked connector instead. Sharing levels: - `PRIVATE` — only the owner has access. - `SPECIFIC_MEMBER` — shared with the users in `usersToAdd`, each at their own access level. - `COMPANY` — everyone in the company gets `accessLevel`. ### Request Body - `tableId` (string, required) — ID of the table to share. - `level` (string, required) — Target sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `accessLevel` (string) — Company-wide access level when `level` is `COMPANY`: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` — "> Users to grant access when `level` is `SPECIFIC_MEMBER`. Each entry has `userId` (string), `userEmail` (string), and `accessLevel` (string). - `usersToRemove` — "> Users whose access should be revoked. Same shape as `usersToAdd`. ### Response - `accessLevel` (string) — The access level applied by the request. - `level` (string) — The sharing level applied by the request. - `usersWithAccess` — "> When the table is shared at `SPECIFIC_MEMBER` level, the users who now have access, each with `userId` (string) and `accessLevel` (string). Empty for other levels. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/share' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userId": "kTn4vB2p9Q", "userEmail": "teammate@example.com", "accessLevel": "WRITE" } ] }' ``` ```json title="JSON" { "level": "SPECIFIC_MEMBER", "usersWithAccess": [ { "userId": "kTn4vB2p9Q", "accessLevel": "WRITE" } ] } ``` --- ## Get Sharing Details **POST** `/get-sharing-details` Returns a table's current sharing configuration plus the caller's own access level. Requires at least read access to the table. ### Request Body - `tableId` (string, required) — ID of the table. ### Response - `sharingLevel` (string) — The table's sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when shared at `COMPANY` level. - `sharedWith` — "> Users the table is shared with, each with `userId` (string), `userEmail` (string), and `accessLevel` (string). - `userAccessLevel` (string) — The caller's effective access level on the table. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/get-sharing-details' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "sharingLevel": "SPECIFIC_MEMBER", "sharedWith": [ { "userEmail": "teammate@example.com", "userId": "kTn4vB2p9Q", "accessLevel": "WRITE" } ], "userAccessLevel": "ADMIN" } ``` --- ## Transfer Ownership **POST** `/transfer-ownership` Transfers table ownership to another user. Only the current owner can transfer, and the new owner must be a member of the company **and** already have some access to the table (via sharing) before the transfer. `DEPENDENT` tables cannot be transferred — transfer the linked connector instead. ### Request Body - `tableId` (string, required) — ID of the table. - `newOwnerId` (string, required) — User ID of the new owner. ### Response No fields on success. Failures (unknown user, user without prior access, caller not the owner) are reported in `errors`. ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/transfer-ownership' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5", "newOwnerId": "kTn4vB2p9Q" }' ``` ```json title="JSON" {} ``` --- ## Delete Table **POST** `/delete` Starts the **two-step delete flow**. Deleting a table is destructive — it drops the underlying SQL table, all stored files, and all vector indexes — so this endpoint does **not** delete anything. It validates that you have `ADMIN` access, then issues a single-use delete token that is valid for **5 minutes**. Pass that token to [`/confirm-delete`](#confirm-delete-table-) to actually delete the table. > The token is bound to the specific `tableId` it was issued for. If it expires before you confirm, call `/delete` again to get a fresh token. `DEPENDENT` tables cannot be deleted — delete the linked connector instead. ### Request Body - `tableId` (string, required) — ID of the table to delete. ### Response - `deleteToken` (string) — Single-use confirmation token (a UUID). Required by `/confirm-delete`. - `expiresIn` (integer) — Token lifetime in seconds (300). **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/delete' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/delete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableId: 'V1StGXR8Z5' }) } ); const { deleteToken } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/delete', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'tableId': 'V1StGXR8Z5'} ) delete_token = response.json()['deleteToken'] ``` ```json title="JSON" { "deleteToken": "1e8a9c2f-4b7d-4f3a-9c6e-2d5b8a7f1c3e", "expiresIn": 300 } ``` --- ## Confirm Delete Table **POST** `/confirm-delete` Completes the two-step delete flow. Requires `ADMIN` access and the `deleteToken` issued by [`/delete`](#delete-table-) for the **same** `tableId`. If the token has expired (or was never issued) you get an error telling you to initiate the delete again; a token mismatch is also rejected. On a valid confirmation the server: 1. Deletes all S3 files stored for the table. 2. Deletes all vector-index entries for the table. 3. Drops the underlying SQL table. 4. Soft-deletes the table reference, preserving metadata and trigger event history for audit purposes. S3 and vector cleanup failures are logged but do not stop the deletion; a failure to drop the SQL table aborts and is returned in `errors`. ### Request Body - `tableId` (string, required) — ID of the table to delete. Must match the table `/delete` was called for. - `deleteToken` (string, required) — The token returned by `/delete`, still within its 5-minute lifetime. ### Response No fields on success. Token and deletion failures are reported in `errors`. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/confirm-delete' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5", "deleteToken": "1e8a9c2f-4b7d-4f3a-9c6e-2d5b8a7f1c3e" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/confirm-delete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', deleteToken: '1e8a9c2f-4b7d-4f3a-9c6e-2d5b8a7f1c3e' }) } ); const data = await response.json(); if (data.errors) { console.error('Delete failed:', data.errors); } ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/confirm-delete', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'deleteToken': '1e8a9c2f-4b7d-4f3a-9c6e-2d5b8a7f1c3e' } ) data = response.json() if data.get('errors'): raise RuntimeError(data['errors']) ``` ```json title="JSON" {} ``` --- ## Update Table Schema **POST** `/update-schema` Modifies an existing table's schema. Requires `ADMIN` access. Supports adding columns, removing columns, adding new options to existing select columns, toggling column indexes, and replacing the table's tags — all in one request. On an [append-only table](#schema-object), column removal is rejected (rows are immutable, so the schema can only grow). ### Request Body - `tableId` (string, required) — ID of the table to modify. - `columnsToAdd` — "> New columns to add, as [column definitions](#column-definition). - `columnNamesToRemove` — "> Names of existing columns to drop. - `selectOptionsToAdd` (object) — Map of existing `SINGLE_SELECT`/`MULTI_SELECT` column name to an array of new allowed values to add. - `indexChanges` (object) — Map of existing column name to the desired `indexed` state (`true` adds the equality index, `false` drops it). Columns not listed are unchanged. Only `SHORT_TEXT` columns gain or lose a physical index; the flag is a no-op for the always-indexed types (see [Column definition](#column-definition)). Enabling an index on a `SHORT_TEXT` column length-caps its values to 2000 characters going forward. - `tags` — "> Tags to set on the table. ### Response - `updatedSchema` (object) — The table's schema after the update. See the [Schema reference](#schema-reference). - `indexCreationInProgress` — "> Column names whose index is still building in the background. On a large table an index requested via `indexChanges` is built asynchronously — the schema update itself has already committed, and each listed column's index becomes usable once its build finishes. Empty when every requested index was created inline. **cURL:** ```bash curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/update-schema' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5", "columnsToAdd": [ { "columnType": "BOOLEAN", "columnName": "is_active", "columnComment": "Whether the customer is active", "required": false } ], "columnNamesToRemove": ["legacy_notes"], "selectOptionsToAdd": { "tier": ["enterprise_plus"] }, "tags": ["crm", "v2"] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/update-schema', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', columnsToAdd: [ { columnType: 'BOOLEAN', columnName: 'is_active', columnComment: 'Whether the customer is active' } ], columnNamesToRemove: ['legacy_notes'], selectOptionsToAdd: { tier: ['enterprise_plus'] }, tags: ['crm', 'v2'] }) } ); const { updatedSchema } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/tables/update-schema', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'columnsToAdd': [ { 'columnType': 'BOOLEAN', 'columnName': 'is_active', 'columnComment': 'Whether the customer is active' } ], 'columnNamesToRemove': ['legacy_notes'], 'selectOptionsToAdd': {'tier': ['enterprise_plus']}, 'tags': ['crm', 'v2'] } ) updated_schema = response.json()['updatedSchema'] ``` ```json title="JSON" { "updatedSchema": { "tableName": "customers", "dbType": "DEFAULT", "tags": ["crm", "v2"], "columnDefinitions": [ { "columnType": "SHORT_TEXT", "columnName": "email", "required": true, "isForeignKey": false }, { "columnType": "BOOLEAN", "columnName": "is_active", "columnComment": "Whether the customer is active", "isForeignKey": false } ] } } ``` --- ## Analyze Trigger Impact **POST** `/analyze-trigger-impact` Analyzes what would happen if a database event fired on a table: which triggers match, which tables, tools, and agents the resulting cascade could touch, and whether the trigger graph contains cycles. Use this before schema or trigger changes to understand blast radius. Nothing is executed — this is a static analysis. The result is a flow graph: `flowNodes` is a map of node ID to node, starting at `rootNodeId`. Each node's `branches` describe the possible next steps (always / conditional / fallback), and `affectedResources` summarizes everything reachable. ### Request Body - `tableId` (string, required) — The source table for the hypothetical event. - `eventType` (string, required) — The event to analyze: `INSERT`, `UPDATE`, `DELETE`, or `ANY`. - `maxDepth` (integer) — Maximum cascade depth to analyze. Defaults to 10. - `includeDisabledTriggers` (boolean) — Include triggers that are currently disabled. Defaults to false. - `stopAtCycles` (boolean) — Stop traversal when a cycle is detected. Defaults to true. - `includeSecondaryImpacts` (boolean) — Recurse through database operations performed by tools and agents that triggers invoke. Defaults to true. ### Response - `sourceTableId` (string) — The analyzed source table. - `sourceTableName` (string) — Name of the source table. - `eventType` (string) — The analyzed event type. - `flowNodes` (object) — Map of node ID to flow node. Each node has `nodeId` (string), `depth` (integer — distance from the source event), `nodeType` (string — `TABLE_EVENT`, `TRIGGER_CHECK`, `ACTION_EXECUTE`, `TOOL_CALL`, `AGENT_CALL`, or `TERMINAL`), `encodedResourceId` (string — the ID of the resource this node refers to, nullable), `resourceKind` (string — `TABLE`, `TOOL`, or `AGENT`, nullable), `resourceName` (string), `description` (string), `branches` (array — see below), and `metadata` (object — additional context, nullable). - `rootNodeId` (string) — ID of the starting node in `flowNodes`. - `totalPossiblePaths` (integer) — Number of distinct execution paths through the graph. - `maxDepth` (integer) — Deepest cascade level reached in the analysis. - `affectedResources` (object) — Summary of everything reachable. Fields: `tables`, `agents`, and `tools` (each array with `encodedResourceId` (string), `resourceName` (string), `resourceType` (string — `TABLE`, `AGENT`, or `TOOL`), `possibleOperations` (array), and `appearsInPaths` (integer)), plus `totalResources` (integer). - `detectedCycles` — "> Cycles found in the trigger graph. Each has `cycleNodeIds` (array), `cycleDescription` (string), and `severity` (string — `WARNING` or `ERROR`). - `warnings` — "> Non-fatal analysis warnings. Each entry in a node's `branches` array has `branchId` (string), `condition` (string — human-readable, e.g. an IF expression, `ELSE`, or `ALWAYS`), `conditionType` (string — `ALWAYS`, `CONDITIONAL`, or `FALLBACK`), `triggerId` (string — UUID of the trigger creating the branch, nullable), `triggerName` (string, nullable), `priority` (integer, nullable), `nextNodeId` (string — the next flow node), and `detailedCondition` (object — full condition details for programmatic use, nullable). ```bash title="cURL" curl --request POST \ --url 'https://api.autessa.com/autessa_poweruser/autessadb/tables/analyze-trigger-impact' \ -H "Authorization: $POWERUSER_KEY" \ -H 'Content-Type: application/json' \ -d '{ "tableId": "V1StGXR8Z5", "eventType": "INSERT", "maxDepth": 10, "includeDisabledTriggers": false, "stopAtCycles": true, "includeSecondaryImpacts": true }' ``` ```json title="JSON" { "sourceTableId": "V1StGXR8Z5", "sourceTableName": "customers", "eventType": "INSERT", "rootNodeId": "node-0", "flowNodes": { "node-0": { "nodeId": "node-0", "depth": 0, "nodeType": "TABLE_EVENT", "encodedResourceId": "V1StGXR8Z5", "resourceKind": "TABLE", "resourceName": "customers", "description": "INSERT into customers", "branches": [ { "branchId": "branch-0", "condition": "IF tier = 'enterprise'", "conditionType": "CONDITIONAL", "triggerId": "1e8a9c2f-4b7d-4f3a-9c6e-2d5b8a7f1c3e", "triggerName": "Notify sales agent", "priority": 1, "nextNodeId": "node-1" } ] }, "node-1": { "nodeId": "node-1", "depth": 1, "nodeType": "AGENT_CALL", "encodedResourceId": "kTn4vB2p9Q", "resourceKind": "AGENT", "resourceName": "Sales Assistant", "description": "Invoke agent Sales Assistant", "branches": [] } }, "totalPossiblePaths": 1, "maxDepth": 1, "affectedResources": { "tables": [], "agents": [ { "encodedResourceId": "kTn4vB2p9Q", "resourceName": "Sales Assistant", "resourceType": "AGENT", "possibleOperations": [], "appearsInPaths": 1 } ], "tools": [], "totalResources": 1 }, "detectedCycles": [], "warnings": [] } ``` --- ## Errors Every response can carry an `errors` field of type array. It is absent on success and populated on failure — server-side exceptions are caught and folded into `errors` rather than surfacing as raw 5xx bodies. Whenever `errors` is present the HTTP status is non-2xx. Responses may also include a `deprecationWarnings` array (array) on successful calls that use deprecated behavior. Common failure modes: - **401 Unauthorized** — missing or invalid poweruser key, or the authenticated user lacks the required access level on the table (for example, non-`ADMIN` callers hitting `/share`, `/delete`, `/confirm-delete`, or `/update-schema`). - **Validation errors** — duplicate table name, invalid column names, text constraints exceeding the type's character limit, invalid foreign key references, upload-link `count` outside 1–50, or a missing `eventType` on `/analyze-trigger-impact`. - **Delete-token errors** — `/confirm-delete` with an expired, missing, or mismatched `deleteToken`. - **Dependent-table errors** — `/share`, `/transfer-ownership`, `/delete`, and `/confirm-delete` reject tables with `dbType: DEPENDENT`; manage the linked connector instead. ```json title="Error response" { "errors": ["A table with name customers already exists"] } ``` --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [AutessaDB Query API](/docs/api/poweruser/autessadb-query.md) — insert, query, update, and delete rows - [AutessaDB Triggers API](/docs/api/poweruser/autessadb-triggers.md) — manage triggers on tables - [Client AutessaDB API](/docs/api/autessadb.md) - [AutessaDB tables guide](/docs/guide/autessadb/tables.md) - [AutessaDB triggers guide](/docs/guide/autessadb/triggers.md) - [AutessaDB tools](/docs/tools/autessadb.md) --- # AutessaDB Query Source: https://docs.autessa.com/docs/api/poweruser/autessadb-query > Poweruser endpoints for inserting, updating, deleting, querying, joining, semantically searching, and indexing rows in AutessaDB tables. # AutessaDB Query Row-level operations on AutessaDB tables: insert and update rows, run filtered and joined queries, delete in bulk with WHERE conditions, upload files into FILE/IMAGE/AUDIO/VIDEO columns, and run semantic vector search over indexed text. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/autessadb/query`. Every request is a POST with a JSON body, and the target table is selected by the `tableId` field (a string) in the body — unlike the [client AutessaDB API](/docs/api/autessadb.md), there is no `resourceId` query parameter. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. The poweruser key acts as its linked user. Mutating endpoints (`insert`, `update`, `batch-update`, `bulk-insert`, `bulk-update-by-ids`, `bulk-delete-by-ids`, `delete-row`, `batch-delete`, `generate-upload-link`) require **WRITE** or **ADMIN** access on the table; read endpoints (including `aggregate`) work with any access to the table. Table lifecycle (create, schema changes, delete) lives in the [AutessaDB Tables API](/docs/api/poweruser/autessadb-tables.md). Read and aggregation endpoints (`get-data`, `query`, `join-query`, `aggregate`) are rate-limited per company on a rolling per-minute and per-day basis; the ceiling is generous for normal use. Bulk writes are governed by a per-call row cap instead. --- ## Column values Every cell travels as an `AutessaDbColumnValue` object: the column's type plus the matching value field. You send these in `values` maps on writes, and reads return them in `columnData` / `rowData` maps keyed by column name. - `columnType` (string, required) — One of: `SHORT_TEXT`, `LONG_TEXT`, `FILE`, `AUDIO`, `IMAGE`, `VIDEO`, `INTEGER`, `FLOAT`, `BOOLEAN`, `SINGLE_SELECT`, `MULTI_SELECT`. `DATETIME` is legacy and read-only — it still appears on reads of pre-existing `DATETIME` columns and on the built-in `created_at`/`updated_at`, but new columns cannot be created with it; store timestamps as `SHORT_TEXT` holding UTC ISO-8601 strings instead. - `textValue` (string) — Value for `SHORT_TEXT`, `LONG_TEXT`, `SINGLE_SELECT`, and `DATETIME` (ISO-8601, e.g. `"2026-06-30T15:30:00"`). - `filePath` (string) — S3 URI for `FILE`, `AUDIO`, `IMAGE`, and `VIDEO` columns. Obtain it from [`/generate-upload-link`](#generate-an-upload-link). - `presignedUrl` (string) — Read-only: pre-signed GET URL for `FILE`/`IMAGE` values (10-minute expiry), populated on [`/get-single-row`](#get-a-single-row). - `integerValue` (integer) — Value for `INTEGER` columns. - `floatValue` (number) — Value for `FLOAT` columns. - `booleanValue` (boolean) — Value for `BOOLEAN` columns. - `multiSelectValue` (array) — Values for `MULTI_SELECT` columns. Each must be in the column's `allowedValues`. - `variableValue` (string) — Placeholder used by tool configs for type-aware variable replacement; not needed for direct API calls. Rows returned by reads also include the built-in `id`, `created_at`, and `updated_at` fields. ```json title="Text, number, boolean" { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 }, "rating": { "columnType": "FLOAT", "floatValue": 4.5 }, "is_active": { "columnType": "BOOLEAN", "booleanValue": true } } ``` ```json title="Selects and datetime" { "tier": { "columnType": "SINGLE_SELECT", "textValue": "pro" }, "channels": { "columnType": "MULTI_SELECT", "multiSelectValue": ["email", "sms"] }, "signup": { "columnType": "DATETIME", "textValue": "2026-06-30T15:30:00" } } ``` ```json title="Files (after generate-upload-link)" { "resume": { "columnType": "FILE", "filePath": "s3://autessa-files/company-cP2mK7dX4v/customers/resume/3f9c2a7e.pdf" } } ``` --- ## WHERE conditions [`/query`](#query-table-data), [`/join-query`](#join-query), [`/batch-update`](#batch-update-rows), and [`/batch-delete`](#batch-delete-rows) filter rows with the same structure: a list of **groups**, each holding a list of **conditions**. - Conditions inside a group are combined with the group's `logicalOperator` (`"AND"` or `"OR"`). - Groups are combined with the request's `groupsLogicalOperator` (`"AND"` or `"OR"`, default `"OR"`). ### WhereGroup - `logicalOperator` (string) — `"AND"` or `"OR"` — how to combine the conditions within this group. - `conditions` (array) — The conditions in this group. ### WhereCondition - `columnName` (string, required) — The column to filter on. Built-in columns (`id`, `created_at`, `updated_at`) can be used too. - `operator` (string) — One of the operators below (case-insensitive). Defaults to `EQUALS` when omitted. - `value` (any) — The value to compare against. Scalar for most operators, an array for `IN`/`NOT_IN`, omitted for `IS_NULL`/`IS_NOT_NULL`. ### Operators | Operator | Meaning | |----------|---------| | `EQUALS` / `NOT_EQUALS` | Exact match / not equal | | `GREATER_THAN` / `LESS_THAN` | Greater than / less than | | `GREATER_THAN_OR_EQUAL` / `LESS_THAN_OR_EQUAL` | Greater than or equal / less than or equal | | `IN` / `NOT_IN` | Value in / not in a list (value must be an array) | | `IS_NULL` / `IS_NOT_NULL` | Null check (no value needed) | | `CONTAINS` | Substring match | | `STARTS_WITH` / `ENDS_WITH` | Prefix / suffix match | Not every operator applies to every column type — invalid combinations are rejected with a descriptive error: | Column type | Supported operators | |-------------|---------------------| | `SHORT_TEXT`, `LONG_TEXT` | All operators | | `INTEGER`, `FLOAT` | All except `CONTAINS`, `STARTS_WITH`, `ENDS_WITH` | | `BOOLEAN` | `EQUALS`, `NOT_EQUALS`, `IN`, `NOT_IN`, `IS_NULL`, `IS_NOT_NULL` | | `SINGLE_SELECT`, `MULTI_SELECT` | `EQUALS`, `NOT_EQUALS`, `IN`, `NOT_IN`, `IS_NULL`, `IS_NOT_NULL` | | `FILE`, `IMAGE`, `AUDIO`, `VIDEO` | `IS_NULL`, `IS_NOT_NULL` only | ```json title="Active adults (AND within a group)" { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "age", "operator": "GREATER_THAN_OR_EQUAL", "value": 18 }, { "columnName": "status", "operator": "EQUALS", "value": "active" } ] } ] } ``` ```json title="Admins or moderators (OR between groups)" { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "role", "operator": "EQUALS", "value": "admin" } ] }, { "logicalOperator": "AND", "conditions": [ { "columnName": "role", "operator": "EQUALS", "value": "moderator" } ] } ], "groupsLogicalOperator": "OR" } ``` ```json title="IN, IS_NULL, and text search" { "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "IN", "value": ["active", "pending"] }, { "columnName": "deleted_reason", "operator": "IS_NULL" }, { "columnName": "email", "operator": "ENDS_WITH", "value": "@example.com" } ] } ] } ``` --- ## Insert a row **POST** `/query/insert` Inserts a new row. Required columns must be present, select values must be in the column's `allowedValues`, and text/number constraints from the schema are enforced. If the table has computed columns, they are calculated in the background — the response carries `computationStatus: "COMPUTING"` until they land. Inserts fire the table's `INSERT` triggers. Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table to insert into. - `values` (object, required) — Map of column name to [column value](#column-values). ### Response - `insertedId` (integer) — Row ID of the new row. Row IDs are plain integers. - `newRow` (object) — The complete inserted row as a map of column name to [column value](#column-values), including built-in fields. - `computationStatus` (string) — `"COMPUTING"` while computed columns are being calculated in the background; absent otherwise. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/autessadb/query/insert \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "values": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "email": { "columnType": "SHORT_TEXT", "textValue": "john@example.com" }, "age": { "columnType": "INTEGER", "integerValue": 30 } } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/query/insert', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', values: { name: { columnType: 'SHORT_TEXT', textValue: 'John Doe' }, email: { columnType: 'SHORT_TEXT', textValue: 'john@example.com' }, age: { columnType: 'INTEGER', integerValue: 30 } } }) } ); const { insertedId } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/query/insert', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'values': { 'name': {'columnType': 'SHORT_TEXT', 'textValue': 'John Doe'}, 'email': {'columnType': 'SHORT_TEXT', 'textValue': 'john@example.com'}, 'age': {'columnType': 'INTEGER', 'integerValue': 30} } } ) inserted_id = response.json()['insertedId'] ``` ```json title="Response" { "insertedId": 12345, "newRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "email": { "columnType": "SHORT_TEXT", "textValue": "john@example.com" }, "age": { "columnType": "INTEGER", "integerValue": 30 } }, "errors": [] } ``` --- ## Update a row **POST** `/query/update` Updates a single row by ID. Only the columns present in `values` are modified; everything else is untouched. Computed columns that depend on the changed values are recomputed in the background. Updates fire the table's `UPDATE` triggers. Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table containing the row. - `rowId` (integer, required) — The row to update. - `values` (object, required) — Map of column name to [column value](#column-values) for the columns to change. ### Response - `updatedId` (integer) — Row ID of the updated row. - `oldRow` (object) — The row before the update, as a column-value map. - `newRow` (object) — The row after the update. - `computationStatus` (string) — `"COMPUTING"` while computed columns are recalculating; absent otherwise. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rowId": 12345, "values": { "age": { "columnType": "INTEGER", "integerValue": 31 } } }' ``` ```json title="Response" { "updatedId": 12345, "oldRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 30 } }, "newRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 31 } }, "errors": [] } ``` --- ## Batch update rows **POST** `/query/batch-update` Applies the same `values` to every row matching the [WHERE conditions](#where-conditions). `whereGroups` is required — to update every row, send a condition that explicitly matches all rows. If the match would exceed the server's batch cap (500 rows by default) the whole operation aborts and rolls back with an error telling you to narrow the conditions. Fires the table's `UPDATE` triggers for each changed row. Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table to update. - `values` (object, required) — Map of column name to [column value](#column-values) applied to every matching row. - `whereGroups` (array, required) — Condition groups selecting the rows to update. - `groupsLogicalOperator` (string) — `"AND"` or `"OR"` — how to combine the groups. Defaults to `"OR"`. ### Response - `updatedCount` (integer) — Number of rows updated. - `results` (array) — Per-row detail: `rowId` (integer), `oldRow` (object), `newRow` (object). - `computationStatus` (string) — `"COMPUTING"` while computed columns are recalculating; absent otherwise. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/batch-update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "archived" } }, "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "inactive" }, { "columnName": "last_login", "operator": "IS_NULL" } ] } ] }' ``` ```json title="Response (truncated)" { "updatedCount": 5, "results": [ { "rowId": 12345, "oldRow": { "status": { "columnType": "SINGLE_SELECT", "textValue": "inactive" } }, "newRow": { "status": { "columnType": "SINGLE_SELECT", "textValue": "archived" } } } ], "errors": [] } ``` --- ## Delete a row **POST** `/query/delete-row` Deletes a single row by ID and returns the deleted data. Fires the table's `DELETE` triggers. Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table containing the row. - `rowId` (integer, required) — The row to delete. ### Response - `deletedId` (integer) — Row ID of the deleted row. - `oldRow` (object) — The deleted row as a column-value map. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/delete-row \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rowId": 12345 }' ``` ```json title="Response" { "deletedId": 12345, "oldRow": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 31 } }, "errors": [] } ``` --- ## Batch delete rows **POST** `/query/batch-delete` Deletes every row matching the [WHERE conditions](#where-conditions). `whereGroups` is required — to delete every row, send a condition that explicitly matches all rows. If the match would exceed the server's batch cap (500 rows by default) the whole operation aborts and rolls back. Fires the table's `DELETE` triggers for each removed row. Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table to delete from. - `whereGroups` (array, required) — Condition groups selecting the rows to delete. - `groupsLogicalOperator` (string) — `"AND"` or `"OR"` — how to combine the groups. Defaults to `"OR"`. ### Response - `deletedCount` (integer) — Number of rows deleted. - `results` (array) — Per-row detail: `rowId` (integer) and `oldRow` (object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/batch-delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "archived" } ] } ] }' ``` ```json title="Response (truncated)" { "deletedCount": 3, "results": [ { "rowId": 12345, "oldRow": { "status": { "columnType": "SINGLE_SELECT", "textValue": "archived" } } } ], "errors": [] } ``` --- ## Bulk insert rows **POST** `/query/bulk-insert` Inserts many rows into a table in a single call. Returns a per-row result list so you can correlate individual outcomes. Bulk writes **suppress the table's triggers by default** — a deliberate divergence from single-row `insert`, whose triggers always fire. Set `triggerMode` to `"PER_ROW"` or `"BATCH_CONTEXT"` to opt in. The row count per call is capped (a generous first-party limit); requests above the cap are rejected. Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table to insert into. - `rows` (array, required) — List of rows to insert. Each is a column-name → [column value](#column-values) map (same shape as a single `insert`'s `values`). - `atomicity` (string) — `"ALL_OR_NOTHING"` (default) writes the whole call as one transaction; `"BEST_EFFORT_PER_CHUNK"` writes in chunks and reports failures per row while earlier chunks stay committed. - `indexingMode` (string) — `"IMMEDIATE"` (default), `"DEFERRED"`, or `"SKIP"` — when semantic-search indexing of the written rows happens. - `computeMode` (string) — `"IMMEDIATE"` (default), `"DEFERRED"`, or `"SKIP"` — when computed fields are calculated. - `triggerMode` (string) — `"SUPPRESS"` (default), `"PER_ROW"`, or `"BATCH_CONTEXT"`. ### Response - `insertedCount` (integer) — Number of rows actually written. - `results` (array) — One entry per input row: `inputIndex` (its position in the request), `insertedId`, `newRow`, and `errors`. - `computationStatus` (string) — `"COMPUTING"` if computed fields are being calculated in the background. - `errors` (array) — Top-level errors when the whole call was rejected. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/bulk-insert \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rows": [ { "name": { "columnType": "SHORT_TEXT", "textValue": "Ada" } }, { "name": { "columnType": "SHORT_TEXT", "textValue": "Grace" } } ] }' ``` ```json title="Response (truncated)" { "insertedCount": 2, "results": [ { "inputIndex": 0, "insertedId": 12345, "newRow": { }, "errors": [] }, { "inputIndex": 1, "insertedId": 12346, "newRow": { }, "errors": [] } ], "errors": [] } ``` --- ## Bulk update rows by IDs **POST** `/query/bulk-update-by-ids` Updates many rows by id in a single call. Each update is a partial patch — only the columns you supply change. Same mode fields and defaults as bulk insert (triggers suppressed by default). Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table to update. - `updates` (array, required) — List of per-row updates. Each is `{ "id": , "values": { ... } }`, where `values` is a partial column-name → [column value](#column-values) map. - `atomicity` (string) — `"ALL_OR_NOTHING"` (default) or `"BEST_EFFORT_PER_CHUNK"`. - `indexingMode` (string) — `"IMMEDIATE"` (default), `"DEFERRED"`, or `"SKIP"`. - `computeMode` (string) — `"IMMEDIATE"` (default), `"DEFERRED"`, or `"SKIP"`. - `triggerMode` (string) — `"SUPPRESS"` (default), `"PER_ROW"`, or `"BATCH_CONTEXT"`. ### Response - `updatedCount` (integer) — Number of rows actually written. - `results` (array) — One entry per input row: `inputIndex`, `updatedId`, `oldRow`, `newRow`, and `errors`. An id not found or outside your access is reported per-row. - `computationStatus` (string) — `"COMPUTING"` if computed fields are being recalculated. - `errors` (array) — Top-level errors when the whole call was rejected. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/bulk-update-by-ids \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "updates": [ { "id": 12345, "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "done" } } }, { "id": 12346, "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "open" } } } ] }' ``` ```json title="Response (truncated)" { "updatedCount": 2, "results": [ { "inputIndex": 0, "updatedId": 12345, "oldRow": { }, "newRow": { }, "errors": [] }, { "inputIndex": 1, "updatedId": 12346, "oldRow": { }, "newRow": { }, "errors": [] } ], "errors": [] } ``` --- ## Bulk delete rows by IDs **POST** `/query/bulk-delete-by-ids` Deletes many rows by id in a single call. Only `atomicity` and `triggerMode` apply (there is nothing to index or compute on a delete). Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table to delete from. - `ids` (array, required) — List of row ids to delete. - `atomicity` (string) — `"ALL_OR_NOTHING"` (default) or `"BEST_EFFORT_PER_CHUNK"`. - `triggerMode` (string) — `"SUPPRESS"` (default), `"PER_ROW"`, or `"BATCH_CONTEXT"`. ### Response - `deletedCount` (integer) — Number of rows actually removed. - `results` (array) — One entry per input id: `inputIndex`, `deletedId`, and `errors`. An id not found or outside your access is reported per-row. - `errors` (array) — Top-level errors when the whole call was rejected. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/bulk-delete-by-ids \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "ids": [12345, 12346, 12347] }' ``` ```json title="Response (truncated)" { "deletedCount": 3, "results": [ { "inputIndex": 0, "deletedId": 12345, "errors": [] }, { "inputIndex": 1, "deletedId": 12346, "errors": [] }, { "inputIndex": 2, "deletedId": 12347, "errors": [] } ], "errors": [] } ``` --- ## Get table data **POST** `/query/get-data` Returns paginated rows from a table without filtering, ordered by `updated_at` (newest first). Use [`/query`](#query-table-data) to filter. ### Request parameters - `tableId` (string, required) — The table to read. - `orderBy` (array) — Sort terms in priority order. Each is `{ "columnName": "...", "direction": "ASC" | "DESC" }` (direction defaults to `DESC`). - `page` (integer) — Page number (1-based). Defaults to `1`. Ignored when `cursor` is set. - `pageSize` (integer) — Rows per page. Defaults to `50`; values above `100` are clamped to `100`. - `cursor` (string) — Opaque cursor from a prior response's `nextCursor`, for efficient seek pagination through large tables. When set, `page` is ignored and the same `orderBy` must be reused. - `includeTotal` (boolean) — Whether to compute `totalRows`/`totalPages`. Offset requests default to `true`; cursor requests default to `false`. ### Response - `schema` (object) — The table schema (see the [schema reference](/docs/api/poweruser/autessadb-tables.md#schema-reference)). - `rows` (array) — Rows on this page. Each has `id` (integer), `columnData` (map of column name to [column value](#column-values)), `columnMetadata`, `createdAt`, and `updatedAt`. - `currentPage` (integer) — The page returned. - `pageSize` (integer) — Rows per page. - `totalRows` (integer) — Total rows matching the request. `null` on a cursor request unless `includeTotal` was set. - `totalPages` (integer) — Total number of pages. `null` on a cursor request unless `includeTotal` was set. - `nextCursor` (string) — Cursor to fetch the next page; `null` when there are no more rows. - `hasMore` (boolean) — Whether another page exists beyond this one. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/get-data \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "page": 1, "pageSize": 50 }' ``` ```json title="Response (truncated)" { "schema": { "tableName": "customers", "dbType": "DEFAULT", "columnDefinitions": [ { "columnName": "name", "columnType": "SHORT_TEXT", "required": true }, { "columnName": "age", "columnType": "INTEGER" } ] }, "rows": [ { "id": 12345, "columnData": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "age": { "columnType": "INTEGER", "integerValue": 31 } }, "createdAt": "2026-06-29T10:00:00", "updatedAt": "2026-06-30T15:30:00" } ], "currentPage": 1, "pageSize": 50, "totalRows": 150, "totalPages": 3, "errors": [] } ``` --- ## Query table data **POST** `/query/query` Returns paginated rows matching the [WHERE conditions](#where-conditions). The response has the same shape as [`/get-data`](#get-table-data). ### Request parameters - `tableId` (string, required) — The table to query. - `whereGroups` (array, required) — Condition groups to filter by. - `groupsLogicalOperator` (string) — `"AND"` or `"OR"` — how to combine the groups. Defaults to `"OR"`. - `orderBy` (array) — Sort terms in priority order. Each is `{ "columnName": "...", "direction": "ASC" | "DESC" }` (direction defaults to `DESC`). - `page` (integer) — Page number (1-based). Defaults to `1`. Ignored when `cursor` is set. - `pageSize` (integer) — Rows per page. Defaults to `50`, maximum `100`. - `cursor` (string) — Opaque cursor from a prior response's `nextCursor` for seek pagination. When set, `page` is ignored and the same `orderBy` must be reused. The response also carries `nextCursor` / `hasMore`, and `totalRows` / `totalPages` are `null` unless `includeTotal` is set. - `includeTotal` (boolean) — Whether to compute `totalRows`/`totalPages`. Offset requests default to `true`; cursor requests default to `false`. ### Response Same fields as [`/get-data`](#get-table-data): `schema`, `rows`, `currentPage`, `pageSize`, `totalRows` (matching rows), `totalPages`, and `errors`. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/autessadb/query/query \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "age", "operator": "GREATER_THAN_OR_EQUAL", "value": 18 }, { "columnName": "status", "operator": "EQUALS", "value": "active" } ] } ], "page": 1, "pageSize": 50 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/query/query', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', whereGroups: [ { logicalOperator: 'AND', conditions: [ { columnName: 'age', operator: 'GREATER_THAN_OR_EQUAL', value: 18 }, { columnName: 'status', operator: 'EQUALS', value: 'active' } ] } ] }) } ); const { rows, totalRows } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/query/query', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'whereGroups': [ { 'logicalOperator': 'AND', 'conditions': [ {'columnName': 'age', 'operator': 'GREATER_THAN_OR_EQUAL', 'value': 18}, {'columnName': 'status', 'operator': 'EQUALS', 'value': 'active'} ] } ] } ) rows = response.json()['rows'] ``` ```json title="Response (truncated)" { "schema": { "tableName": "customers" }, "rows": [ { "id": 12345, "columnData": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "status": { "columnType": "SINGLE_SELECT", "textValue": "active" } }, "createdAt": "2026-06-29T10:00:00", "updatedAt": "2026-06-30T15:30:00" } ], "currentPage": 1, "pageSize": 50, "totalRows": 25, "totalPages": 1, "errors": [] } ``` --- ## Join query **POST** `/query/join-query` Joins the main table with one or more other tables and returns the combined rows, optionally filtered by [WHERE conditions](#where-conditions) applied after the join. Joined column names are prefixed with their table name (`orders.user_id`, `users.name`). ### Request parameters - `tableId` (string, required) — The main (left) table. - `joins` (array, required) — The tables to join. Each entry has `rightTableId` (string — the ID of the table to join with), `joinType` (`"INNER"`, `"LEFT"`, or `"RIGHT"`; defaults to `"INNER"`), `leftColumnName` (column on the main table), and `rightColumnName` (column on the right table). - `whereGroups` (array) — Optional condition groups applied to the joined result. - `groupsLogicalOperator` (string) — `"AND"` or `"OR"` — how to combine the groups. Defaults to `"OR"`. - `orderBy` (array) — Sort terms (main-table columns) in priority order. Each is `{ "columnName": "...", "direction": "ASC" | "DESC" }`. - `page` (integer) — Page number (1-based). Defaults to `1`. Ignored when `cursor` is set. - `pageSize` (integer) — Rows per page. Defaults to `50`, maximum `100`. - `cursor` (string) — Opaque cursor from a prior response's `nextCursor` for seek pagination. When set, `page` is ignored and the same `orderBy` must be reused. The response carries `nextCursor` / `hasMore`; `totalRows` / `totalPages` are `null` unless `includeTotal` is set. - `includeTotal` (boolean) — Whether to compute `totalRows`/`totalPages`. Offset requests default to `true`; cursor requests default to `false`. ### Response - `mainSchema` (object) — Schema of the main table. - `joinedSchemas` (object) — Map of joined table ID to that table's schema. - `rows` (array) — Joined rows. Each has `sourceTableId` (ID of the main table), `sourceRowId` (integer), `columnData` (all joined columns, prefixed by table name), `createdAt`, and `updatedAt`. - `currentPage` (integer) — The page returned. - `pageSize` (integer) — Rows per page. - `totalRows` (integer) — Total joined rows. - `totalPages` (integer) — Total number of pages. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/join-query \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "joins": [ { "rightTableId": "kTn4vB2p9Q", "joinType": "INNER", "leftColumnName": "user_id", "rightColumnName": "id" } ], "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "active" } ] } ] }' ``` ```json title="Response (truncated)" { "mainSchema": { "tableName": "orders" }, "joinedSchemas": { "790": { "tableName": "users" } }, "rows": [ { "sourceTableId": "V1StGXR8Z5", "sourceRowId": 12345, "columnData": { "orders.order_id": { "columnType": "SHORT_TEXT", "textValue": "ORD-1042" }, "orders.user_id": { "columnType": "INTEGER", "integerValue": 88 }, "users.name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" } }, "createdAt": "2026-06-29T10:00:00", "updatedAt": "2026-06-30T15:30:00" } ], "currentPage": 1, "pageSize": 50, "totalRows": 42, "totalPages": 1, "errors": [] } ``` --- ## Aggregate **POST** `/query/aggregate` Runs a grouped aggregation over a table: filter, group by up to a few columns, compute aggregates (`COUNT` / `SUM` / `AVG` / `MIN` / `MAX`), optionally filter groups and sort, and return a capped set of groups. Works with any access to the table. Columns hidden from your access are treated as not existing, and columns you may only see in masked form cannot be grouped or aggregated (the request is rejected). ### Request parameters - `tableId` (string, required) — The table to aggregate. - `whereGroups` (array) — Optional pre-aggregation filter. - `groupsLogicalOperator` (string) — `"AND"` or `"OR"` — how to combine the groups. Defaults to `"OR"`. - `groupBy` (array) — Columns to group by (a small maximum applies). Omit to aggregate the filtered set into a single group. - `aggregates` (array, required) — The aggregates to compute. Each is `{ "function": "COUNT" | "SUM" | "AVG" | "MIN" | "MAX", "column": "...", "alias": "..." }`. `column` may be omitted for `COUNT`. `alias` is required, unique, and is how the value is returned and referenced by `having` / `orderBy`. - `having` (array) — Post-aggregation filters on aggregate aliases. Each is `{ "alias": "...", "operator": "=" | "!=" | ">" | "<" | ">=" | "<=", "value": }`. Multiple conditions are ANDed. - `orderBy` (array) — Sort by a `groupBy` column or an aggregate alias. - `limit` (integer) — Maximum number of groups to return (clamped to the configured maximum). ### Response - `groups` (array) — One entry per group: `key` (groupBy column → value; empty when there is no grouping) and `values` (aggregate alias → numeric value). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/aggregate \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "groupBy": ["status"], "aggregates": [ { "function": "COUNT", "alias": "count" }, { "function": "AVG", "column": "age", "alias": "avg_age" } ], "orderBy": [{ "columnName": "count", "direction": "DESC" }] }' ``` ```json title="Response (truncated)" { "groups": [ { "key": { "status": "active" }, "values": { "count": 120, "avg_age": 34.2 } }, { "key": { "status": "inactive" }, "values": { "count": 30, "avg_age": 41.7 } } ], "errors": [] } ``` --- ## Get a single row **POST** `/query/get-single-row` Fetches one row by ID. `FILE` and `IMAGE` values come back with a `presignedUrl` (10-minute expiry) so the content can be downloaded directly. ### Request parameters - `tableId` (string, required) — The table containing the row. - `rowId` (integer, required) — The row to fetch. ### Response - `schema` (object) — The table schema. - `row` (Row) — The row: `id`, `columnData`, `columnMetadata`, `createdAt`, `updatedAt`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/get-single-row \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rowId": 12345 }' ``` ```json title="Response (truncated)" { "schema": { "tableName": "customers" }, "row": { "id": 12345, "columnData": { "name": { "columnType": "SHORT_TEXT", "textValue": "John Doe" }, "avatar": { "columnType": "IMAGE", "filePath": "s3://autessa-files/company-cP2mK7dX4v/customers/avatar/2b7c.png", "presignedUrl": "https://s3.amazonaws.com/..." } }, "createdAt": "2026-06-29T10:00:00", "updatedAt": "2026-06-30T15:30:00" }, "errors": [] } ``` --- ## Generate an upload link **POST** `/query/generate-upload-link` Generates a pre-signed S3 `PUT` URL for a `FILE`, `IMAGE`, `AUDIO`, or `VIDEO` column. Upload the bytes to `uploadUrl` (with the matching `Content-Type`), then use the returned `s3Uri` as the column's `filePath` in [`/insert`](#insert-a-row) or [`/update`](#update-a-row). Links expire after **5 minutes**. The `fileType` must match the column's type: | Column type | Allowed file types | |-------------|--------------------| | `FILE` | `DOCX`, `DOC`, `PDF`, `TXT`, `XLSX`, `XLS`, `EML` | | `IMAGE` | `PNG`, `JPG`, `JPEG`, `GIF`, `WEBP`, `AVIF`, `BMP`, `ICO`, `TIFF`, `TIF`, `HEIC`, `HEIF` | | `AUDIO` | `MP3`, `M4A`, `WAV` | | `VIDEO` | `MP4`, `AVI`, `MOV`, `WMV`, `FLV`, `MKV`, `WEBM`, `MPEG`, `MPG` | Requires WRITE or ADMIN access on the table. ### Request parameters - `tableId` (string, required) — The table the file belongs to. - `columnName` (string, required) — The `FILE`/`IMAGE`/`AUDIO`/`VIDEO` column the file will be stored in. - `fileType` (string, required) — File extension from the table above, e.g. `"PDF"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to use as `filePath` in insert/update requests. - `expiresInSeconds` (integer) — Lifetime of the upload URL in seconds (300). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/generate-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "columnName": "resume", "fileType": "PDF" }' # Then upload the file: curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: application/pdf" \ --data-binary '@resume.pdf' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/customers/resume/3f9c2a7e.pdf", "expiresInSeconds": 300, "errors": [] } ``` --- ## Semantic search **POST** `/query/search` Runs semantic vector search over one column. The column must have `vectorizeField: true` in the table schema, and rows must be indexed (indexing happens automatically on insert/update; use [`/index-record`](#index-a-record) to reindex manually). Results are ordered by similarity score, highest first, with the full row data attached. Search strategy adapts to the column type — `SHORT_TEXT` columns are embedded whole, `LONG_TEXT` (and extracted `FILE`/`AUDIO` content) is also chunked, so matches can point into a specific part of a long document. Semantic searches are metered and count against the company's semantic search limits; a request over the limit is rejected. ### Request parameters - `tableId` (string, required) — The table to search. - `query` (string, required) — The natural-language search query. - `columnName` (string, required) — The vectorized column to search within. - `limit` (integer) — Maximum results. Defaults to `10`, maximum `100`. - `scoreThreshold` (number) — Minimum similarity score, `0.0`–`1.0`. Defaults to `0.0`. - `page` (integer) — Page number (1-based). Defaults to `1`. - `includeDetails` (boolean) — Include per-result match details (which embedding matched, character positions for chunked matches, all matching embeddings with scores). Defaults to `false`. ### Response - `schema` (object) — The table schema. - `results` (array) — Matches ordered by relevance. Each has `recordId` (integer), `score` (0.0–1.0), `rank` (1-based), `contentHash`, `rowData` (full row as a column-value map), `columnMetadata`, `createdAt`, `updatedAt`, and — when `includeDetails` is true — `details` (`embedType`, `embedIndex`, `charPosition`, `allMatches`). - `totalResults` (integer) — Total matches before pagination. - `currentPage` (integer) — The page returned. - `totalPages` (integer) — Total number of pages. - `metadata` (object) — Search metadata: `query`, `columnName`, `columnType`, `searchStrategy`, `targetEmbeddingTypes`, `executionTimeMs`. - `errors` (array) — Error messages, if any occurred (unknown column, column not vectorized). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/autessadb/query/search \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "query": "machine learning engineer", "columnName": "job_description", "limit": 10, "scoreThreshold": 0.7 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/query/search', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', query: 'machine learning engineer', columnName: 'job_description', limit: 10, scoreThreshold: 0.7 }) } ); const { results } = await response.json(); results.forEach(r => console.log(r.score, r.recordId)); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/query/search', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'query': 'machine learning engineer', 'columnName': 'job_description', 'limit': 10, 'scoreThreshold': 0.7 } ) for result in response.json()['results']: print(result['score'], result['recordId']) ``` ```json title="Response (truncated)" { "schema": { "tableName": "jobs" }, "results": [ { "recordId": 12345, "score": 0.92, "rank": 1, "contentHash": "8f3a1c2e", "rowData": { "job_title": { "columnType": "SHORT_TEXT", "textValue": "Senior ML Engineer" }, "job_description": { "columnType": "LONG_TEXT", "textValue": "We are looking for..." } }, "createdAt": "2026-06-29T10:00:00", "updatedAt": "2026-06-30T15:30:00" } ], "totalResults": 25, "currentPage": 1, "totalPages": 3, "metadata": { "query": "machine learning engineer", "columnName": "job_description", "columnType": "LONG_TEXT", "searchStrategy": "HYBRID", "targetEmbeddingTypes": ["FULL", "CHUNK"], "executionTimeMs": 45 }, "errors": [] } ``` --- ## Index a record **POST** `/query/index-record` Rebuilds the vector index for one row: every vectorizable column (`SHORT_TEXT`, `LONG_TEXT`, `FILE`, `AUDIO` with `vectorizeField: true`) with non-blank content is re-embedded. Indexing normally happens automatically on insert/update — use this to backfill rows written before a column was vectorized or to repair an index. If the table has no vectorizable columns the call succeeds with `columnsIndexed: 0`. ### Request parameters - `tableId` (string, required) — The table containing the row. - `rowId` (integer, required) — The row to index. ### Response - `recordId` (integer) — The indexed row ID. - `columnsIndexed` (integer) — Number of columns that were indexed. - `vectorsCreated` (integer) — Total vectors created (long content produces multiple chunk vectors). - `indexingDurationMs` (integer) — Time taken, in milliseconds. - `indexedColumns` (array) — Names of the columns that were indexed. - `errors` (array) — Error messages, if any occurred (for example row not found). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/query/index-record \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rowId": 12345 }' ``` ```json title="Response" { "recordId": 12345, "columnsIndexed": 2, "vectorsCreated": 15, "indexingDurationMs": 234, "indexedColumns": ["job_title", "job_description"], "errors": [] } ``` --- ## Errors Every response can carry an `errors` array of strings; it is empty or absent on success. | Status | When | |--------|------| | `200` | Success — still check `errors` for row-level validation messages. | | `401` | Missing or invalid poweruser key, or the linked user lacks WRITE/ADMIN access for a mutating endpoint. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | Common validation failures (returned in `errors`): - **Schema violations** — missing required column, value not in `allowedValues`, text/number constraint breaches, unknown column names. - **WHERE errors** — unknown column in a condition, an operator the column's type doesn't support, `IN`/`NOT_IN` without a list value, an invalid operator name (the message lists the valid operators), or a missing `whereGroups` on batch endpoints. - **Batch caps** — a batch update/delete matching more rows than the server's cap (500 by default) aborts and rolls back. - **Upload-link errors** — unknown column, non-file column, or a `fileType` that doesn't match the column type. - **Search errors** — column not found, column not vectorized, or the company's semantic search limit exceeded. ```json title="Error response" { "errors": [ "Operator 'CONTAINS' is not supported for column 'age' of type INTEGER. Use comparison operators (=, <, >, etc.) instead." ] } ``` --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [AutessaDB Tables API](/docs/api/poweruser/autessadb-tables.md) — create tables, manage schemas, sharing, and deletion - [AutessaDB Triggers API](/docs/api/poweruser/autessadb-triggers.md) — react to inserts, updates, and deletes - [Client AutessaDB API](/docs/api/autessadb.md) — the same row operations with client API keys - [AutessaDB tables guide](/docs/guide/autessadb/tables.md) --- # AutessaDB Triggers Source: https://docs.autessa.com/docs/api/poweruser/autessadb-triggers > Poweruser API reference for creating, managing, and auditing AutessaDB triggers that fire agents, tools, managers, or database updates on row changes. # AutessaDB Triggers A trigger is attached to an AutessaDB table and fires an action — invoke an agent, invoke a tool, invoke a manager, or update an AutessaDB table — whenever rows are inserted, updated, or deleted. A trigger's condition names the database event it listens for and, optionally, column filters that narrow it down to specific column changes or values. Triggers execute asynchronously after the row operation commits. > All endpoints are **POST** with a JSON body and require a poweruser key in the `Authorization` header, in the form `pwrusr:{id}:{secret}`. Every request identifies the table the trigger lives on via its `tableId`. --- ## Base URL ``` https://api.autessa.com/autessa_poweruser/autessadb/trigger ``` --- ## Trigger anatomy A trigger definition is a single JSON object. The same shape is sent when creating or updating a trigger and returned by every endpoint that responds with a trigger. Exactly **one** of the four action config fields must be set, and it must match `triggerType` — otherwise the request is rejected with an error. - `triggerId` (string) — Unique identifier for the trigger — a UUID string. Assigned by the server on `/add` (any value you send is overwritten) and immutable afterwards. - `triggerName` (string) — Human-readable name for the trigger. - `description` (string) — Optional description of what the trigger does. - `triggerType` (string, required) — The kind of action this trigger performs. One of `INVOKE_AGENT`, `INVOKE_TOOL`, `INVOKE_MANAGER`, `UPDATE_AUTESSADB`. - `condition` (object, required) — When the trigger should fire. See [Trigger condition](#trigger-condition). - `invokeAgentActionConfig` (object) — Action config for `INVOKE_AGENT` triggers. See [Invoke an agent](#invoke-an-agent). - `invokeToolActionConfig` (object) — Action config for `INVOKE_TOOL` triggers. See [Invoke a tool](#invoke-a-tool). - `invokeManagerActionConfig` (object) — Action config for `INVOKE_MANAGER` triggers. See [Invoke a manager](#invoke-a-manager). - `updateDbActionConfig` (object) — Action config for `UPDATE_AUTESSADB` triggers. See [Update AutessaDB](#update-autessadb). - `enabled` (boolean) — Whether the trigger is active. Defaults to `true`. Disabled triggers never fire. - `priority` (integer) — Execution ordering when multiple triggers match the same operation — lower numbers run first. Defaults to `100`. Each table also has table-level trigger settings (configured on the table itself, not through these endpoints): a master `enabled` switch that silences all triggers on the table, and `maxTriggersPerOperation` (default `10`) that caps how many matching triggers fire for a single row operation. When more triggers match than the cap allows, the highest-priority (lowest number) triggers win. ```json title="Complete trigger definition" { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "description": "Qualify every new lead and store the summary on the row.", "triggerType": "INVOKE_AGENT", "condition": { "event": "INSERT", "filterMode": "SPECIFIED_COLUMNS", "logicalOperator": "AND", "columnFilters": [ { "columnName": "status", "operator": "EQUALS", "compareValue": { "textValue": "new" }, "caseSensitive": false } ] }, "invokeAgentActionConfig": { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "version": { "versionState": "PUBLISHED" }, "input": [ { "inputType": "TEXT", "content": "Qualify this lead: @@{name} from @@{company}. Message: @@{message}" } ], "environmentVariables": { "LEAD_EMAIL": "@@{email}" }, "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "qualification_summary", "continueOnError": true }, "enabled": true, "priority": 100 } ``` ### Trigger condition The `condition` object decides when the trigger fires. - `event` (string, required) — The database event to listen for. One of `INSERT`, `UPDATE`, `DELETE`, or `ANY` (fires on all three). - `filterMode` (string) — How column filters are applied: `ANY_COLUMN`, `SPECIFIED_COLUMNS`, or `ALL_EXCEPT_SPECIFIED`. See [Column filters](#column-filters). If omitted, the server treats a condition **with** `columnFilters` as `SPECIFIED_COLUMNS` and a condition **without** them as `ANY_COLUMN`. - `columnFilters` — "> The column filters to evaluate. See [Column filters](#column-filters). - `logicalOperator` (string) — How multiple column filters combine in `SPECIFIED_COLUMNS` mode: `AND` (all must match — the default), `OR` (any must match), or `XOR` (exactly one must match). ```json title="Condition: high-value deal moved to won" { "event": "UPDATE", "filterMode": "SPECIFIED_COLUMNS", "logicalOperator": "AND", "columnFilters": [ { "columnName": "stage", "operator": "EQUALS", "compareValue": { "textValue": "won" } }, { "columnName": "deal_value", "operator": "GREATER_THAN", "compareValue": { "integerValue": 10000 } } ] } ``` ### Variable substitution Action configs can reference the triggering row's data with the `@@{columnName}` placeholder syntax. Placeholders inside input content, tool variables, environment variables, and prompt template variables are replaced with the actual column values at execution time. On `UPDATE` events, `@@{old.columnName}` references the value before the change. ### Invoke an agent `invokeAgentActionConfig` — for `INVOKE_AGENT` triggers. Each firing creates a fresh, single-turn, autonomous agent conversation; do not set a `conversationId` inside the execution request. - `agentExecutionRequest` (object, required) — The agent execution request, with the same shape as the Agent API execute call. Fields: `agentId` (string, required), `version` (object with `versionState` — `PUBLISHED`, and optionally `versionNumber`; defaults to the published version), `input` (array of multimodal inputs; each has `inputType` — `TEXT`, `AUDIO`, `IMAGE`, `FILE`, `IMAGE_URL`, `FILE_URL` — and its content fields, e.g. `content` for `TEXT`), `environmentVariables` (object of string key-value pairs), `promptTemplateVariables` (object of string key-value pairs), and `executionOutputMode` (`TEXT` or `AUDIO`). Input content and variable values support `@@{columnName}` substitution. - `storeOutputInColumn` (boolean) — If `true`, the agent's text output is written back to a column of the triggering record. Defaults to `false`. - `outputColumnName` (string) — Name of the column that receives the agent output. Only used when `storeOutputInColumn` is `true`. - `continueOnError` (boolean) — If `true` (the default), the trigger completes even when the agent invocation fails. If `false`, an agent failure fails the whole trigger. ```json title="invokeAgentActionConfig" { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "version": { "versionState": "PUBLISHED" }, "input": [ { "inputType": "TEXT", "content": "Analyze this feedback: @@{customer_feedback}" } ], "environmentVariables": { "CUSTOMER_ID": "@@{customer_id}" }, "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "analysis", "continueOnError": true } ``` ### Invoke a tool `invokeToolActionConfig` — for `INVOKE_TOOL` triggers. - `toolExecutionRequest` (object, required) — The tool execution request. Fields: `toolId` (string, required), `variables` (object of string key-value pairs — the tool's arguments), `environmentVariables` (object of string key-value pairs), and `privateVariables` (object of string key-value pairs that are not logged). All three maps support `@@{columnName}` substitution in their values. - `storeResultInColumn` (boolean) — If `true`, the tool execution result is written back to a column of the triggering record. Defaults to `false`. - `resultColumnName` (string) — Name of the column that receives the tool result. Only used when `storeResultInColumn` is `true`. - `continueOnError` (boolean) — If `true` (the default), the trigger completes even when the tool invocation fails. If `false`, a tool failure fails the whole trigger. ```json title="invokeToolActionConfig" { "toolExecutionRequest": { "toolId": "pQ7wE2rT9y", "variables": { "email": "@@{user_email}", "priority": "high" }, "environmentVariables": { "REGION": "us-west-2" } }, "storeResultInColumn": false, "continueOnError": true } ``` ### Invoke a manager `invokeManagerActionConfig` — for `INVOKE_MANAGER` triggers. Like agent triggers, each firing creates a fresh, single-turn conversation; do not set a `conversationId`. - `managerExecutionRequest` (object, required) — The manager execution request. Fields: `managerId` (string, required), `input` (array of multimodal inputs, same shape as agent input), `environmentVariables` (object of string key-value pairs), `promptTemplateVariables` (object of string key-value pairs), and `executionOutputMode` (`TEXT` or `AUDIO`). Input content and variable values support `@@{columnName}` substitution. - `storeOutputInColumn` (boolean) — If `true`, the manager's text output is written back to a column of the triggering record. Defaults to `false`. - `outputColumnName` (string) — Name of the column that receives the manager output. Only used when `storeOutputInColumn` is `true`. - `continueOnError` (boolean) — If `true` (the default), the trigger completes even when the manager invocation fails. If `false`, a manager failure fails the whole trigger. ```json title="invokeManagerActionConfig" { "managerExecutionRequest": { "managerId": "mR5tY8uI3o", "input": [ { "inputType": "TEXT", "content": "Handle escalation for ticket @@{ticket_id}: @@{summary}" } ], "executionOutputMode": "TEXT" }, "storeOutputInColumn": false, "continueOnError": true } ``` ### Update AutessaDB `updateDbActionConfig` — for `UPDATE_AUTESSADB` triggers. Performs a database operation against another (or the same) AutessaDB table when the trigger fires. - `targetTableId` (string, required) — The table the operation is performed on. - `operation` (string, required) — The operation to perform: `INSERT` (create a record from `values`), `UPDATE` (modify records selected by `recordId` or `whereGroups`), `UPSERT` (insert, or update on conflict per `conflictResolution`, using `values` and `whereGroups` for conflict detection), or `DELETE` (remove records selected by `recordId` or `whereGroups`). - `values` (object) — Column values for `INSERT`, `UPDATE`, and `UPSERT` — a map from column name to a column value object. Each value object carries `columnType` (one of the AutessaDB column types) plus the matching typed field: `textValue` (string), `integerValue` (integer), `floatValue` (float), `booleanValue` (boolean), `multiSelectValue` (array), `filePath` (string, for file-backed types), or `variableValue` (string with `@@{columnName}` placeholders, resolved and parsed to the column's type at execution time). String values also support `@@{columnName}` substitution. - `recordId` (integer) — Record ID for single-row `UPDATE` or `DELETE`. When set, `whereGroups` is ignored. - `whereGroups` — "> WHERE conditions for batch `UPDATE`/`DELETE`, or conflict detection for `UPSERT`. Each group has `logicalOperator` (`"AND"` or `"OR"`, combining the conditions inside the group) and `conditions` — an array of objects with `columnName` (string), `operator` (string, e.g. `EQUALS`, `NOT_EQUALS`, `GREATER_THAN`, `LESS_THAN`, `IN`, `NOT_IN`, `IS_NULL`, `IS_NOT_NULL`), and `value`. - `groupsLogicalOperator` (string) — How multiple WHERE groups combine: `"AND"` or `"OR"`. Defaults to `"OR"`. - `conflictResolution` (string) — UPSERT conflict strategy: `REPLACE` (replace the entire record — the default), `MERGE` (merge fields, new values win), `SKIP` (leave the existing record untouched), or `ERROR` (fail the trigger on conflict). - `skipIfTargetTableMissing` (boolean) — If `true`, the trigger does not fail when the target table is missing or inaccessible. Defaults to `false`. ```json title="updateDbActionConfig" { "targetTableId": "zX8cV3bN6m", "operation": "UPSERT", "values": { "customer_email": { "columnType": "SHORT_TEXT", "variableValue": "@@{email}" }, "total_orders": { "columnType": "INTEGER", "integerValue": 1 }, "last_seen": { "columnType": "SHORT_TEXT", "variableValue": "@@{updated_at}" } }, "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "customer_email", "operator": "EQUALS", "value": "@@{email}" } ] } ], "groupsLogicalOperator": "OR", "conflictResolution": "MERGE", "skipIfTargetTableMissing": false } ``` --- ## Column filters Column filters narrow a condition down from "any row event" to specific column changes or values. How they are applied depends on the condition's `filterMode`: - **`ANY_COLUMN`** — the trigger fires whenever anything about the row changed. Every column is compared between the old and new record (inserts and deletes always count as a change), including columns added to the table after the trigger was created. `columnFilters` are not evaluated in this mode. - **`SPECIFIED_COLUMNS`** — only the listed `columnFilters` are evaluated, each with its `operator` and `compareValue`, combined with the condition's `logicalOperator` (`AND`, `OR`, or `XOR`). Columns added later are ignored. An empty filter list in this mode means the trigger never fires. - **`ALL_EXCEPT_SPECIFIED`** — the trigger fires when any column **not** named in `columnFilters` changed. Filters are used only for their `columnName` here (operators and compare values are ignored). Columns added later are included. A filter that names a column that does not exist in the table schema never matches. ### ColumnFilter - `columnName` (string, required) — The column the filter applies to. - `operator` (string, required) — The comparison operator. See [Filter operators](#filter-operators). - `compareValue` (object) — Typed comparison value, required by value-comparison operators. See below. - `caseSensitive` (boolean) — Whether text comparisons (`EQUALS`, `NOT_EQUALS`, `CONTAINS`, `STARTS_WITH`, `ENDS_WITH`) are case-sensitive. Defaults to `true`. ### ColumnFilterValue A type-safe wrapper — populate the one field that matches the column's type, or use `variableValue` for dynamic comparison. - `textValue` (string) — For `SHORT_TEXT`, `LONG_TEXT`, `SINGLE_SELECT`, and file-backed column types. For text columns this may itself contain `@@{columnName}` placeholders, resolved at evaluation time. Also used as the single value checked by the multi-select operators. - `integerValue` (integer) — For `INTEGER` columns. - `floatValue` (float) — For `FLOAT` columns. - `booleanValue` (boolean) — For `BOOLEAN` columns. - `multiSelectValue` — "> For `MULTI_SELECT` columns. - `variableValue` (string) — A variable reference using `@@{columnName}` or `@@{old.columnName}` notation. When set, it takes precedence over the typed fields: the placeholder is replaced with the row's actual data at evaluation time, then parsed to the compared column's type (for `MULTI_SELECT`, as a comma-separated list). For example, comparing `unit_price` with `variableValue` `"@@{old.unit_price}"` fires on price changes relative to the previous value. ```json title="Filter: status changed to a closed state" { "columnName": "status", "operator": "CHANGED" } ``` ```json title="Filter: tag added to a multi-select" { "columnName": "tags", "operator": "VALUE_ADDED", "compareValue": { "textValue": "vip" } } ``` ```json title="Filter: dynamic comparison against another column" { "columnName": "quantity", "operator": "GREATER_THAN", "compareValue": { "variableValue": "@@{reorder_threshold}" } } ``` ### Filter operators Value-comparison operators are evaluated against the **new** (post-operation) value of the column; the change-detection and multi-select add/remove operators compare the old and new values. On `INSERT` the old record is empty; on `DELETE` the new record is empty. | Operator | Meaning | Column types | | --- | --- | --- | | `CHANGED` | The column's value differs between old and new record. | All | | `BECAME_NULL` | The column had a value and is now empty. | All | | `BECAME_NON_NULL` | The column was empty and now has a value. | All | | `EQUALS` | New value equals `compareValue` (honors `caseSensitive` for text). | All | | `NOT_EQUALS` | New value does not equal `compareValue`. | All | | `NULL` | New value is empty. | All | | `NOT_NULL` | New value is present. | All | | `GREATER_THAN` | New value is greater than `compareValue`. | `INTEGER`, `FLOAT` | | `LESS_THAN` | New value is less than `compareValue`. | `INTEGER`, `FLOAT` | | `GREATER_OR_EQUAL` | New value is greater than or equal to `compareValue`. | `INTEGER`, `FLOAT` | | `LESS_OR_EQUAL` | New value is less than or equal to `compareValue`. | `INTEGER`, `FLOAT` | | `CONTAINS` | New text contains `compareValue.textValue` as a substring. | `SHORT_TEXT`, `LONG_TEXT` | | `STARTS_WITH` | New text starts with `compareValue.textValue`. | `SHORT_TEXT`, `LONG_TEXT` | | `ENDS_WITH` | New text ends with `compareValue.textValue`. | `SHORT_TEXT`, `LONG_TEXT` | | `VALUE_ADDED` | `compareValue.textValue` is in the new selection but was not in the old one. | `MULTI_SELECT` | | `VALUE_REMOVED` | `compareValue.textValue` was in the old selection but is not in the new one. | `MULTI_SELECT` | | `CONTAINS_VALUE` | The new selection contains `compareValue.textValue`. | `MULTI_SELECT` | | `IS_TRUE` | New value is `true`. | `BOOLEAN` | | `IS_FALSE` | New value is `false`. | `BOOLEAN` | For file-backed column types (`FILE`, `IMAGE`, `AUDIO`, `VIDEO`), value comparisons operate on the stored file path. --- ## Add Trigger **POST** `/add` Creates a trigger on a table. The server assigns the `triggerId` (a UUID string) — any `triggerId` in the submitted trigger is ignored. The trigger must have exactly one action config set, matching its `triggerType`. Requires **publish** or **admin** access to the table. ### Request Body - `tableId` (string, required) — The table to attach the trigger to. - `trigger` (object, required) — The full trigger definition. See [Trigger anatomy](#trigger-anatomy). ### Response - `trigger` (object) — The stored trigger, including its server-assigned `triggerId`. - `errors` — "> Error messages if the request failed. Absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/add \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "trigger": { "triggerName": "New lead follow-up", "description": "Qualify every new lead and store the summary on the row.", "triggerType": "INVOKE_AGENT", "condition": { "event": "INSERT", "filterMode": "SPECIFIED_COLUMNS", "logicalOperator": "AND", "columnFilters": [ { "columnName": "status", "operator": "EQUALS", "compareValue": { "textValue": "new" }, "caseSensitive": false } ] }, "invokeAgentActionConfig": { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "version": { "versionState": "PUBLISHED" }, "input": [ { "inputType": "TEXT", "content": "Qualify this lead: @@{name} from @@{company}. Message: @@{message}" } ], "environmentVariables": { "LEAD_EMAIL": "@@{email}" }, "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "qualification_summary", "continueOnError": true }, "enabled": true, "priority": 100 } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/trigger/add', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableId: 'aB3xK9mQ2w', trigger: { triggerName: 'New lead follow-up', triggerType: 'INVOKE_AGENT', condition: { event: 'INSERT', filterMode: 'SPECIFIED_COLUMNS', columnFilters: [ { columnName: 'status', operator: 'EQUALS', compareValue: { textValue: 'new' }, caseSensitive: false } ] }, invokeAgentActionConfig: { agentExecutionRequest: { agentId: 'kTn4vB2p9Q', input: [ { inputType: 'TEXT', content: 'Qualify this lead: @@{name} from @@{company}.' } ], executionOutputMode: 'TEXT' }, storeOutputInColumn: true, outputColumnName: 'qualification_summary' } } }) } ); const data = await response.json(); console.log(data.trigger.triggerId); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/trigger/add', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'aB3xK9mQ2w', 'trigger': { 'triggerName': 'New lead follow-up', 'triggerType': 'INVOKE_AGENT', 'condition': { 'event': 'INSERT', 'filterMode': 'SPECIFIED_COLUMNS', 'columnFilters': [ { 'columnName': 'status', 'operator': 'EQUALS', 'compareValue': {'textValue': 'new'}, 'caseSensitive': False } ] }, 'invokeAgentActionConfig': { 'agentExecutionRequest': { 'agentId': 'kTn4vB2p9Q', 'input': [ { 'inputType': 'TEXT', 'content': 'Qualify this lead: @@{name} from @@{company}.' } ], 'executionOutputMode': 'TEXT' }, 'storeOutputInColumn': True, 'outputColumnName': 'qualification_summary' } } } ) print(response.json()['trigger']['triggerId']) ``` ```json title="JSON" { "trigger": { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "description": "Qualify every new lead and store the summary on the row.", "triggerType": "INVOKE_AGENT", "condition": { "event": "INSERT", "filterMode": "SPECIFIED_COLUMNS", "logicalOperator": "AND", "columnFilters": [ { "columnName": "status", "operator": "EQUALS", "compareValue": { "textValue": "new" }, "caseSensitive": false } ] }, "invokeAgentActionConfig": { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "version": { "versionState": "PUBLISHED" }, "input": [ { "inputType": "TEXT", "content": "Qualify this lead: @@{name} from @@{company}. Message: @@{message}" } ], "environmentVariables": { "LEAD_EMAIL": "@@{email}" }, "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "qualification_summary", "continueOnError": true }, "enabled": true, "priority": 100 } } ``` --- ## Update Trigger **POST** `/update` Replaces an existing trigger's definition. This is a **full replacement**, not a patch — send the complete trigger object. The top-level `triggerId` identifies the trigger and always wins over any `triggerId` inside the `trigger` object. The replacement must pass the same validation as `/add` (exactly one action config, matching the `triggerType`). Requires **admin** access to the table. ### Request Body - `tableId` (string, required) — The table the trigger belongs to. - `triggerId` (string, required) — UUID of the trigger to update. - `trigger` (object, required) — The complete replacement definition. See [Trigger anatomy](#trigger-anatomy). ### Response - `trigger` (object) — The updated trigger. - `errors` — "> Error messages if the request failed. Absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/update \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "trigger": { "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "condition": { "event": "INSERT", "filterMode": "SPECIFIED_COLUMNS", "columnFilters": [ { "columnName": "status", "operator": "EQUALS", "compareValue": { "textValue": "new" }, "caseSensitive": false } ] }, "invokeAgentActionConfig": { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "input": [ { "inputType": "TEXT", "content": "Qualify this lead: @@{name} from @@{company}." } ], "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "qualification_summary" }, "enabled": true, "priority": 50 } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/trigger/update', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableId: 'aB3xK9mQ2w', triggerId: '3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d', trigger: updatedTriggerDefinition // full trigger object }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/trigger/update', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'aB3xK9mQ2w', 'triggerId': '3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d', 'trigger': updated_trigger_definition, # full trigger object }, ) data = response.json() ``` ```json title="JSON" { "trigger": { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "condition": { "event": "INSERT", "filterMode": "SPECIFIED_COLUMNS", "columnFilters": [ { "columnName": "status", "operator": "EQUALS", "compareValue": { "textValue": "new" }, "caseSensitive": false } ] }, "invokeAgentActionConfig": { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "input": [ { "inputType": "TEXT", "content": "Qualify this lead: @@{name} from @@{company}." } ], "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "qualification_summary", "continueOnError": true }, "enabled": true, "priority": 50 } } ``` --- ## Delete Trigger **POST** `/delete` Permanently removes a trigger from a table. Requires **admin** access to the table. ### Request Body - `tableId` (string, required) — The table the trigger belongs to. - `triggerId` (string, required) — UUID of the trigger to delete. ### Response - `errors` — "> Error messages if the request failed. Absent on success — a successful delete returns an empty JSON object. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/delete \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d" }' ``` ```json title="JSON" {} ``` --- ## Enable Trigger **POST** `/enable` Sets a trigger's `enabled` flag to `true` so it resumes firing. Requires **admin** access to the table. ### Request Body - `tableId` (string, required) — The table the trigger belongs to. - `triggerId` (string, required) — UUID of the trigger to enable. ### Response - `trigger` (object) — The trigger with `enabled` set to `true`. - `errors` — "> Error messages if the request failed. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/enable \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d" }' ``` ```json title="JSON" { "trigger": { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "enabled": true, "priority": 100 } } ``` --- ## Disable Trigger **POST** `/disable` Sets a trigger's `enabled` flag to `false`. The trigger definition is kept but it stops firing until re-enabled. Requires **admin** access to the table. ### Request Body - `tableId` (string, required) — The table the trigger belongs to. - `triggerId` (string, required) — UUID of the trigger to disable. ### Response - `trigger` (object) — The trigger with `enabled` set to `false`. - `errors` — "> Error messages if the request failed. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/disable \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d" }' ``` ```json title="JSON" { "trigger": { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "enabled": false, "priority": 100 } } ``` --- ## Get Trigger **POST** `/get` Fetches a single trigger's definition. Requires any access level to the table. ### Request Body - `tableId` (string, required) — The table the trigger belongs to. - `triggerId` (string, required) — UUID of the trigger to fetch. ### Response - `trigger` (object) — The trigger definition. See [Trigger anatomy](#trigger-anatomy). - `errors` — "> Error messages if the request failed. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/get \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d" }' ``` ```json title="JSON" { "trigger": { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "condition": { "event": "INSERT", "filterMode": "SPECIFIED_COLUMNS", "columnFilters": [ { "columnName": "status", "operator": "EQUALS", "compareValue": { "textValue": "new" }, "caseSensitive": false } ] }, "invokeAgentActionConfig": { "agentExecutionRequest": { "agentId": "kTn4vB2p9Q", "input": [ { "inputType": "TEXT", "content": "Qualify this lead: @@{name} from @@{company}." } ], "executionOutputMode": "TEXT" }, "storeOutputInColumn": true, "outputColumnName": "qualification_summary", "continueOnError": true }, "enabled": true, "priority": 100 } } ``` --- ## List Triggers **POST** `/all` Lists every trigger configured on a table. Requires any access level to the table. ### Request Body - `tableId` (string, required) — The table to list triggers for. ### Response - `triggers` — "> All triggers on the table. Each element is a full trigger definition — see [Trigger anatomy](#trigger-anatomy). - `errors` — "> Error messages if the request failed. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/all \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w" }' ``` ```json title="JSON" { "triggers": [ { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "enabled": true, "priority": 100 }, { "triggerId": "7a1d4e9b-2c5f-4b8a-9d3e-6f0c8b2a4e7d", "triggerName": "Sync won deals to customers", "triggerType": "UPDATE_AUTESSADB", "enabled": true, "priority": 200 } ] } ``` --- ## Execution History **POST** `/history` Returns the trigger execution log for a table, one calendar month at a time, newest first. Results are paginated with a server-defined page size (50 by default). Requires any access level to the table. ### Request Body - `tableId` (string, required) — The table whose trigger history to fetch. - `year` (integer, required) — Calendar year to filter by (e.g. `2026`). Must be between 2000 and next year. - `month` (integer, required) — Calendar month to filter by, `1`–`12`. - `page` (integer) — Zero-based page number. Defaults to `0`. Negative values are treated as `0`. ### Response - `triggerEvents` — "> The trigger events for the requested page. Each event has the following fields: - `eventId` (string) — unique identifier of the execution event, a UUID string. - `tableId` (string) — the table the trigger ran on. - `triggerId` (string) — UUID of the trigger that fired. - `triggerDefinition` (object) — the trigger's current full definition (see [Trigger anatomy](#trigger-anatomy)), looked up at read time. `null` if the trigger has since been deleted. - `triggerEvent` (string) — JSON-encoded payload describing what fired the trigger, including the before/after records of the row operation. - `triggerResponse` (string) — JSON-encoded result of the trigger execution. - `createdAt` (string) — when the trigger fired, as an ISO-8601 UTC timestamp. - `totalElements` (integer) — Total number of trigger events in the requested month. - `totalPages` (integer) — Total number of pages. - `currentPage` (integer) — The zero-based page number returned. - `pageSize` (integer) — Number of events per page. - `hasNext` (boolean) — Whether a next page exists. - `hasPrevious` (boolean) — Whether a previous page exists. - `errors` — "> Error messages if the request failed. Absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/autessadb/trigger/history \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "aB3xK9mQ2w", "year": 2026, "month": 6, "page": 0 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/trigger/history', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ tableId: 'aB3xK9mQ2w', year: 2026, month: 6, page: 0 }) } ); const data = await response.json(); for (const event of data.triggerEvents) { console.log(event.createdAt, event.triggerDefinition?.triggerName); } ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/trigger/history', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'aB3xK9mQ2w', 'year': 2026, 'month': 6, 'page': 0, }, ) data = response.json() for event in data['triggerEvents']: print(event['createdAt'], event['eventId']) ``` ```json title="JSON" { "triggerEvents": [ { "eventId": "b6e2f8a4-1c3d-4e5f-9a7b-8c0d2e4f6a1b", "tableId": "aB3xK9mQ2w", "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerDefinition": { "triggerId": "3f8c2a1e-9b4d-4c6a-8e2f-1d5a7b9c0e3d", "triggerName": "New lead follow-up", "triggerType": "INVOKE_AGENT", "enabled": true, "priority": 100 }, "triggerEvent": "{\"event\":\"INSERT\",\"newRecord\":{\"name\":{\"columnType\":\"SHORT_TEXT\",\"textValue\":\"Ada Lovelace\"},\"status\":{\"columnType\":\"SHORT_TEXT\",\"textValue\":\"new\"}}}", "triggerResponse": "{\"status\":\"SUCCESS\",\"output\":\"Qualified: strong fit, recommend follow-up call.\"}", "createdAt": "2026-06-15T09:21:07" } ], "totalElements": 42, "totalPages": 1, "currentPage": 0, "pageSize": 50, "hasNext": false, "hasPrevious": true } ``` --- ## Errors All responses share the same envelope. On failure the response contains an `errors` array of human-readable messages — server-side exceptions are caught and folded into `errors` rather than surfaced as raw stack traces — and the HTTP status is non-2xx whenever `errors` is present. The envelope may also carry `deprecationWarnings` (array) on otherwise-successful calls. Common failure cases: - **401** — missing or invalid poweruser key, or insufficient table access: creating a trigger requires publish or admin access; updating, deleting, enabling, or disabling requires admin access; reads require any access to the table. - **Validation errors** — missing `trigger` or `triggerId`, a trigger whose action config does not match its `triggerType` (or with zero / more than one action config set), a missing `year`/`month` on `/history`, a month outside `1`–`12`, or a year outside the accepted range. - **Not found** — unknown `tableId` or a `triggerId` that does not exist on the table. --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [AutessaDB Tables (Poweruser API)](/docs/api/poweruser/autessadb-tables.md) - [AutessaDB Query (Poweruser API)](/docs/api/poweruser/autessadb-query.md) - [AutessaDB client API](/docs/api/autessadb.md) - [Triggers guide](/docs/guide/autessadb/triggers.md) - [Tables guide](/docs/guide/autessadb/tables.md) --- # AutessaDB Analytics Source: https://docs.autessa.com/docs/api/poweruser/autessadb-analytics > Poweruser endpoints for AutessaDB analytics tables: create tables, evolve columns, edit the semantic layer, load data, and run read-only SQL as jobs. # AutessaDB Analytics The full lifecycle of [analytics tables](/docs/guide/autessadb/analytics.md): create a table from a schema or a sample file, add columns, describe it for agents with a semantic layer, load and append data, run read-only SQL as jobs, and provision the agent tools that query it. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/autessadb/analytics`. Every request is a POST with a JSON body, and the target table is selected by the `tableId` field in the body. There is no `resourceId` query parameter. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. The poweruser key acts as its linked user, so table access levels apply as they do in the builder: READ to describe and query, WRITE to load and append, ADMIN to change columns and the semantic layer. A query that joins several tables needs READ on every one of them. ## Values in results Rows are JSON objects keyed by column name. Values keep their exact meaning: | Column type | JSON value | | --- | --- | | `STRING`, `BOOLEAN`, `INT`, `DOUBLE` | String, boolean, or number | | `LONG` | Number, or a string when the value is too large to be exact in JSON | | `DECIMAL` | String, e.g. `"1284410.50"` | | `DATE` | `"2026-10-01"` | | `TIMESTAMP`, `TIMESTAMPTZ` | ISO-8601 UTC string, e.g. `"2026-10-01T00:00:00Z"` | | `STRING_LIST` | Array of strings | | `JSON` | The JSON value | | empty | `null` | ## Queries are jobs Every query, load, and append runs as a job. Queries run asynchronously and typically take a few seconds; large scans take longer. Queries are written in ANSI SQL; date and approximate functions use the standard analytical names such as `date_trunc('month', ts)`, `approx_distinct(col)`, and `format_datetime(ts, 'yyyy-MM')`. A typical query: 1. [Run a query](#run-a-query) with `waitMs` up to 20 seconds. If the job finishes in that time, it comes back `SUCCEEDED` with a `preview` of the first 200 rows. 2. If the state is `QUEUED` or `RUNNING`, call [Get job status](#get-job-status) with `waitMs` until it finishes. 3. Page through [Get job results](#get-job-results), or get a [download link](#download-job-results) for the whole result. Results are kept for 7 days. A query can run for at most 15 minutes (less on some plans) before it ends as `TIMED_OUT`. ## Uploading a file to load Files for [Load files](#load-files) and [Infer a schema](#infer-a-schema-from-a-file) are uploaded with the client intake endpoints at `https://api.autessa.com/clients/autessadb-intake`, which send the bytes straight to storage in parts: 1. `POST /multipart-initiate` with `{"fileName": "orders-2026-09.csv"}` returns `uploadId`, `s3Key`, and `s3Uri`. 2. `POST /multipart-part-urls` with `uploadId`, `s3Key`, and `partNumbers` returns a `partUrls` map. `PUT` each part (at least 5 MB except the last) to its link and keep each response's `ETag` header. 3. `POST /multipart-complete` with `uploadId`, `s3Key`, and `partETags` (part number to ETag) returns the final `s3Uri`. The [CLI](/docs/cli/autessadb.md#how-to-load-and-query-analytics-tables) does all of this for you with `autessa autessadb analytics load --localFile`. --- ## Create an analytics table **POST** `/create` Creates an analytics table. Choose the columns, an optional partition spec of up to two fields, and the SQL name queries will use. The table counts toward your AutessaDB table limit. **Permission:** Company-level; no table permission is needed ### Request parameters - `tableName` (string, required) — Display name. - `description` (string) — What the table holds. - `schema` (object, required) — The table's columns in `columnDefinitions`. Each has `columnName` (lowercase `snake_case`), `columnType` or `analytics.type` (`STRING`, `INT`, `LONG`, `DOUBLE`, `DECIMAL` with `precision` and `scale`, `BOOLEAN`, `DATE`, `TIMESTAMP`, `TIMESTAMPTZ`, `STRING_LIST`, or `JSON`), `required`, `columnComment`, and optionally `masked` and `analytics.synonyms`. - `analytics` (object) — `partitionSpec`: up to two `{column, transform, param}` entries, where `transform` is `IDENTITY`, `YEAR`, `MONTH`, `DAY`, `HOUR`, `BUCKET` (`param` = bucket count), or `TRUNCATE` (`param` = width). `sortOrder`: columns to sort data by within partitions. `sqlName`: unique within your company; defaults to the table name in `snake_case`. ### Response - `tableId` (string) — The new table's ID. - `sqlName` (string) — The SQL name to use in queries. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/create' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableName": "Order Lines", "description": "Every order line since 2019.", "schema": { "columnDefinitions": [ { "columnName": "order_id", "columnType": "INTEGER", "required": true }, { "columnName": "order_date", "analytics": { "type": "DATE" }, "required": true }, { "columnName": "region", "columnType": "SHORT_TEXT", "required": true }, { "columnName": "net_amount", "analytics": { "type": "DECIMAL", "precision": 18, "scale": 2 }, "columnComment": "Line revenue after discounts, in USD." } ] }, "analytics": { "partitionSpec": [ { "column": "order_date", "transform": "MONTH" } ], "sqlName": "order_lines" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableName: 'Order Lines', description: 'Every order line since 2019.', schema: { columnDefinitions: [ { columnName: 'order_id', columnType: 'INTEGER', required: true }, { columnName: 'order_date', analytics: { type: 'DATE' }, required: true }, { columnName: 'region', columnType: 'SHORT_TEXT', required: true }, { columnName: 'net_amount', analytics: { type: 'DECIMAL', precision: 18, scale: 2 }, columnComment: 'Line revenue after discounts, in USD.' } ] }, analytics: { partitionSpec: [ { column: 'order_date', transform: 'MONTH' } ], sqlName: 'order_lines' } }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableName': 'Order Lines', 'description': 'Every order line since 2019.', 'schema': { 'columnDefinitions': [ { 'columnName': 'order_id', 'columnType': 'INTEGER', 'required': True }, { 'columnName': 'order_date', 'analytics': { 'type': 'DATE' }, 'required': True }, { 'columnName': 'region', 'columnType': 'SHORT_TEXT', 'required': True }, { 'columnName': 'net_amount', 'analytics': { 'type': 'DECIMAL', 'precision': 18, 'scale': 2 }, 'columnComment': 'Line revenue after discounts, in USD.' } ] }, 'analytics': { 'partitionSpec': [ { 'column': 'order_date', 'transform': 'MONTH' } ], 'sqlName': 'order_lines' } } ) data = response.json() ``` ```json title="Response" { "tableId": "V1StGXR8Z5", "sqlName": "order_lines" } ``` --- ## Infer a schema from a file **POST** `/infer-schema` Reads a sample CSV or Parquet file and proposes columns and types, with sample rows and warnings such as mixed types or unparseable dates. Use the result as the `schema` for [Create an analytics table](#create-an-analytics-table). **Permission:** Company-level ### Request parameters - `s3Uri` (string, required) — The uploaded sample file (see [Uploading a file to load](#uploading-a-file-to-load)). - `format` (string) — `CSV` or `PARQUET`. Inferred from the extension when omitted. ### Response - `columns` — "> Proposed column definitions. - `sampleRows` — "> Up to 20 parsed rows. - `warnings` — "> Problems found in the sample. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/infer-schema' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "s3Uri": "s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-sample.csv" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/infer-schema', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ s3Uri: 's3://autessa-intake/cP2mK7dX4v/9a1e.../orders-sample.csv' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/infer-schema', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 's3Uri': 's3://autessa-intake/cP2mK7dX4v/9a1e.../orders-sample.csv' } ) data = response.json() ``` ```json title="Response" { "columns": [ { "columnName": "order_id", "analytics": { "type": "LONG" }, "required": true }, { "columnName": "order_date", "analytics": { "type": "DATE" }, "required": true }, { "columnName": "region", "analytics": { "type": "STRING" }, "required": false } ], "sampleRows": [ { "order_id": "90210331", "order_date": "2026-10-01", "region": "NA" } ], "warnings": [ "region: 3 of 1000 sampled rows are empty." ] } ``` --- ## Describe tables **POST** `/describe` Returns everything needed to query one or more analytics tables: SQL name, description, grain, row count, partition columns, columns with types and descriptions, measures, joins, and verified queries. Columns the caller cannot see are omitted. Agents should call this before writing SQL. **Permission:** READ on every table described ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `tableIds` — "> Additional tables to describe in the same call. - `includeStats` (boolean) — Include per-column statistics (null count, distinct count, minimum, maximum, and top values). Default `false`. ### Response - `tables` — "> One entry per table: `tableId`, `sqlName`, `name`, `description`, `grain`, `rowCount`, `dataBytes`, `lastLoadAt`, `partitionSpec`, `columns` (`name`, `type`, `nullable`, `description`, `synonyms`, `masked`, and `stats` when requested), `measures` (`name`, `sql`, `description`), `joins` (`sqlName`, `on`, `relationship`), and `verifiedQueries` (`id`, `question`, `sql`). - `semanticsVersion` (integer) — Version of the primary table's semantic layer, for [Update the semantic layer](/docs/api/poweruser/autessadb-analytics.md#update-the-semantic-layer). **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/describe' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "includeStats": false }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/describe', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', includeStats: false }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/describe', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'includeStats': False } ) data = response.json() ``` ```json title="Response" { "tables": [ { "tableId": "V1StGXR8Z5", "sqlName": "order_lines", "name": "Order Lines", "description": "Every order line since 2019, loaded nightly from the ERP.", "grain": "one row per order line", "rowCount": 418220931, "dataBytes": 21474836480, "lastLoadAt": "2026-10-02T06:00:12Z", "partitionSpec": [ { "column": "order_date", "transform": "MONTH" } ], "columns": [ { "name": "order_id", "type": "LONG", "nullable": false, "description": "ERP order number." }, { "name": "order_date", "type": "DATE", "nullable": false, "description": "Date the order was placed." }, { "name": "region", "type": "STRING", "nullable": false, "description": "Sales region.", "synonyms": [ "territory" ] }, { "name": "net_amount", "type": "DECIMAL(18,2)", "nullable": false, "description": "Line revenue after discounts, in USD.", "synonyms": [ "net sales" ] } ], "measures": [ { "name": "revenue", "sql": "SUM(net_amount)", "description": "Net revenue in USD." } ], "joins": [ { "sqlName": "customers", "on": "order_lines.customer_id = customers.id", "relationship": "MANY_TO_ONE" } ], "verifiedQueries": [ { "id": "vq_7d2a", "question": "Monthly revenue by region this year", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ] } ], "semanticsVersion": 6 } ``` --- ## Preview rows **POST** `/preview` Returns the first rows of a table without writing SQL. Previews run ahead of regular queries. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `limit` (integer) — Rows to return, up to 100. Default 50. ### Response - `columns` — "> Column `name` and `type`, in order. - `rows` — "> Rows as objects keyed by column name. See [Values in results](#values-in-results). **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/preview' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "limit": 2 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/preview', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', limit: 2 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/preview', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'limit': 2 } ) data = response.json() ``` ```json title="Response" { "columns": [ { "name": "order_id", "type": "LONG" }, { "name": "order_date", "type": "DATE" }, { "name": "region", "type": "STRING" }, { "name": "net_amount", "type": "DECIMAL(18,2)" } ], "rows": [ { "order_id": "90210331", "order_date": "2026-10-01", "region": "NA", "net_amount": "129.00" }, { "order_id": "90210332", "order_date": "2026-10-01", "region": "EMEA", "net_amount": "48.50" } ] } ``` --- ## Estimate a query **POST** `/estimate` Checks a query and reports an upper bound on how much data it would scan, without running it. Use it to catch a missing partition filter before running an expensive query. **Permission:** READ on every table the query references ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sql` (string, required) — The query to estimate. ### Response - `estimatedBytesScanned` (integer) — Upper bound on data the query would read, after skipping partitions its filters rule out. - `referencedTables` — "> SQL names of the tables the query reads. - `withinLimits` (boolean) — `false` if the estimate exceeds the per-query or remaining daily scan limit. - `hint` (string) — When `withinLimits` is `false`: what to filter on, naming the partition columns. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/estimate' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "tableId": "V1StGXR8Z5", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } JSON ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/estimate', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sql: "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/estimate', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sql': "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ) data = response.json() ``` ```json title="Response" { "estimatedBytesScanned": 1288490188, "referencedTables": [ "order_lines" ], "withinLimits": true, "hint": null } ``` --- ## Run a query **POST** `/query` Runs a read-only SQL query as a job. If the job finishes within `waitMs`, the response includes the first rows; otherwise poll [Get job status](#get-job-status) and then fetch [results](#get-job-results). The query must be a single `SELECT` statement, optionally with `WITH`, written in ANSI SQL. It can read any analytics tables the caller has READ access to, by SQL name. Row-level and field-level security apply. Statements that change data or settings are rejected with `FORBIDDEN_CONSTRUCT`. SQL is limited to 100 KB. **Permission:** READ on every table the query references ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sql` (string, required) — The query. - `maxRows` (integer) — Maximum rows to keep in the result. Capped at your plan's result-row limit; a larger result is cut off with `truncated: true`. - `maxBytesScanned` (integer) — Refuse to run if the estimate exceeds this many bytes. Cannot exceed your plan's per-query limit. - `waitMs` (integer) — How long to wait for the job before returning, up to 20000. `0` returns as soon as the job is queued. ### Response - `jobId` (string) — The job's ID. - `state` (string) — `QUEUED`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`, or `TIMED_OUT`. - `bytesScanned` (integer) — Data read so far, in bytes. - `elapsedMs` (integer) — Run time so far. - `error` (object) — When the job failed: `errorCode`, `message`, and a `hint` such as the valid column names. See [Errors](#errors). - `preview` (object) — When the job succeeded within `waitMs`: `columns`, the first 200 `rows`, `totalRows`, and `truncated` (`true` if the result hit `maxRows`). **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/query' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "tableId": "V1StGXR8Z5", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", "waitMs": 20000 } JSON ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/query', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sql: "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", waitMs: 20000 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/query', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sql': "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", 'waitMs': 20000 } ) data = response.json() ``` ```json title="Response" { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "state": "SUCCEEDED", "bytesScanned": 402653184, "elapsedMs": 2140, "preview": { "columns": [ { "name": "region", "type": "STRING" }, { "name": "month", "type": "TIMESTAMPTZ" }, { "name": "revenue", "type": "DECIMAL(18,2)" } ], "rows": [ { "region": "EMEA", "month": "2026-01-01T00:00:00Z", "revenue": "1284410.50" }, { "region": "NA", "month": "2026-01-01T00:00:00Z", "revenue": "2390112.75" } ], "totalRows": 24, "truncated": false } } ``` --- ## Get job status **POST** `/job-status` Returns a job's state, optionally waiting for it to finish. Jobs can be read by the caller who started them, or by anyone with READ on every table they reference. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. - `waitMs` (integer) — How long to wait for the job to finish before returning, up to 20000. ### Response - `jobId` (string) — The job's ID. - `state` (string) — `QUEUED`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`, or `TIMED_OUT`. - `bytesScanned` (integer) — Data read so far, in bytes. - `elapsedMs` (integer) — Run time so far. - `error` (object) — When the job failed: `errorCode`, `message`, and a `hint` such as the valid column names. See [Errors](#errors). - `resultRowCount` (integer) — Rows in the result, once succeeded. - `truncated` (boolean) — `true` if the result was cut off at `maxRows`. - `resultColumns` — "> Column `name` and `type` of the result. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-status' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "waitMs": 10000 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-status', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', waitMs: 10000 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-status', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', 'waitMs': 10000 } ) data = response.json() ``` ```json title="Response" { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "state": "SUCCEEDED", "bytesScanned": 402653184, "elapsedMs": 2140, "resultRowCount": 24, "truncated": false, "resultColumns": [ { "name": "region", "type": "STRING" }, { "name": "month", "type": "TIMESTAMPTZ" }, { "name": "revenue", "type": "DECIMAL(18,2)" } ] } ``` --- ## Get job results **POST** `/job-results` Returns one page of a finished job's result. Results are kept for 7 days. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. - `page` (integer) — Page number, starting at 0. - `pageSize` (integer) — Rows per page, up to 1000. Default 1000. ### Response - `columns` — "> Column `name` and `type`. - `rows` — "> Rows as objects keyed by column name. See [Values in results](#values-in-results). - `page` (integer) — This page. - `totalRows` (integer) — Rows in the whole result. - `hasMore` (boolean) — `true` if later pages exist. - `truncated` (boolean) — `true` if the result itself was cut off at `maxRows`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-results' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "page": 0, "pageSize": 1000 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-results', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', page: 0, pageSize: 1000 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-results', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', 'page': 0, 'pageSize': 1000 } ) data = response.json() ``` ```json title="Response" { "columns": [ { "name": "region", "type": "STRING" }, { "name": "month", "type": "TIMESTAMPTZ" }, { "name": "revenue", "type": "DECIMAL(18,2)" } ], "rows": [ { "region": "EMEA", "month": "2026-01-01T00:00:00Z", "revenue": "1284410.50" }, { "region": "NA", "month": "2026-01-01T00:00:00Z", "revenue": "2390112.75" } ], "page": 0, "totalRows": 24, "hasMore": false, "truncated": false } ``` --- ## Download job results **POST** `/job-download-url` Returns a short-lived link to download a finished job's full result. Only the caller who ran the job can download it. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. - `format` (string) — `CSV` (default) or `PARQUET`. ### Response - `url` (string) — The download link. - `expiresAt` (string) — When the link stops working, 5 minutes after it is issued. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-download-url' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "format": "CSV" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-download-url', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', format: 'CSV' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/job-download-url', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54', 'format': 'CSV' } ) data = response.json() ``` ```json title="Response" { "url": "https://files.autessa.com/r/1f8e5c3a.../result.csv", "expiresAt": "2026-10-02T15:05:00Z" } ``` --- ## Cancel a job **POST** `/cancel` Cancels a queued or running job. A running job stops within a few seconds. **Permission:** READ, and the caller must have started the job ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `jobId` (string, required) — The job. ### Response - `jobId` (string) — The job. - `state` (string) — `CANCELLED`, or the final state if the job had already finished. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/cancel' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/cancel', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', jobId: '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/cancel', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'jobId': '1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54' } ) data = response.json() ``` ```json title="Response" { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "state": "CANCELLED" } ``` --- ## List jobs **POST** `/jobs` Lists recent jobs on a table, newest first: queries, loads, and appends. **Permission:** READ ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `kind` (string) — Only this kind: `QUERY`, `LOAD`, or `APPEND`. - `state` (string) — Only jobs in this state. - `mine` (boolean) — Only jobs the caller started. Default `false`. - `limit` (integer) — Jobs per page, up to 100. Default 25. - `cursor` (string) — The `nextCursor` from the previous page. ### Response - `jobs` — "> Each job: `jobId`, `kind`, `state`, `sql` (first 500 characters), `createdBy`, `createdAt`, `finishedAt`, `resultRowCount`, `bytesScanned`, and `elapsedMs`. - `nextCursor` (string) — Cursor for the next page, or `null`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/jobs' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "kind": "QUERY", "limit": 1 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/jobs', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', kind: 'QUERY', limit: 1 }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/jobs', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'kind': 'QUERY', 'limit': 1 } ) data = response.json() ``` ```json title="Response" { "jobs": [ { "jobId": "1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54", "kind": "QUERY", "state": "SUCCEEDED", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1", "createdBy": { "userId": "u8Kd2LmQ4x", "name": "Dana Ortiz" }, "createdAt": "2026-10-02T14:59:51Z", "finishedAt": "2026-10-02T14:59:53Z", "resultRowCount": 24, "bytesScanned": 402653184, "elapsedMs": 2140 } ], "nextCursor": "eyJ0IjoiMjAyNi0xMC0wMlQxNDo1OTo1MVoifQ" } ``` --- ## Load files **POST** `/load-from-s3` Loads one or more CSV or Parquet files into the table as a job. Upload each file first (see [Uploading a file to load](#uploading-a-file-to-load)) and pass the `s3Uri` you get back. Values are converted to the column types; rows that cannot be converted are counted and sampled in the job result rather than silently dropped. A load is all-or-nothing: if it fails, no rows are added. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sourceUris` — " required> The uploaded files' `s3Uri` values. - `format` (string) — `CSV` or `PARQUET`. Inferred from the file extension when omitted. - `mode` (string) — `APPEND` (default) adds the rows; `REPLACE` swaps the table's contents for them. - `csv` (object) — CSV options: `delimiter` (default `,`), `header` (default `true`), `nullString`, `quote`, `dateFormat`, and `timestampFormat`. - `columnMapping` (object) — Map of file column name to table column name, when they differ. Unmapped table columns are left empty. ### Response - `jobId` (string) — The load job. Poll [Get job status](#get-job-status); a finished load reports `rowsLoaded`, `rowsRejected`, and `rejectedSample`. - `state` (string) — The job's state. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/load-from-s3' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "sourceUris": [ "s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-2026-09.csv" ], "format": "CSV", "mode": "APPEND", "csv": { "header": true, "timestampFormat": "%Y-%m-%d %H:%M:%S" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/load-from-s3', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sourceUris: ['s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-2026-09.csv'], format: 'CSV', mode: 'APPEND', csv: { header: true, timestampFormat: '%Y-%m-%d %H:%M:%S' } }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/load-from-s3', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sourceUris': ['s3://autessa-intake/cP2mK7dX4v/9a1e.../orders-2026-09.csv'], 'format': 'CSV', 'mode': 'APPEND', 'csv': { 'header': True, 'timestampFormat': '%Y-%m-%d %H:%M:%S' } } ) data = response.json() ``` ```json title="Response" { "jobId": "6c2b9e14-3f7a-4d81-b0e5-a9d4c2f81b37", "state": "QUEUED" } ``` --- ## Append rows **POST** `/append-rows` Appends a small batch of rows sent in the request body: up to 10,000 rows or 5 MB per call. Appends that arrive within a few seconds of each other are committed together, so rows become visible to queries within about 5 seconds. Use [Load files](#load-files) for bulk data. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `rows` — " required> Rows as objects keyed by column name, with plain JSON values. Send `DECIMAL` and large integers as strings, dates and timestamps as ISO-8601 strings. ### Response - `accepted` (integer) — Rows accepted. - `loadId` (string) — Identifier written to the `_load_id` column of these rows. - `errors` — "> Rows refused: `index` and `message`. Refused rows are not appended; the others are. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/append-rows' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "rows": [ { "order_id": "90210333", "order_date": "2026-10-02", "region": "APAC", "net_amount": "310.00" } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/append-rows', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', rows: [ { order_id: '90210333', order_date: '2026-10-02', region: 'APAC', net_amount: '310.00' } ] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/append-rows', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'rows': [ { 'order_id': '90210333', 'order_date': '2026-10-02', 'region': 'APAC', 'net_amount': '310.00' } ] } ) data = response.json() ``` ```json title="Response" { "accepted": 1, "loadId": "append-2026-10-02T15:00:05Z", "errors": [] } ``` --- ## Add columns **POST** `/add-columns` Adds columns to an analytics table, or widens existing column types safely (for example `INT` to `LONG`, or a `DECIMAL` to a larger precision). New columns are empty for existing rows. Renaming, removing, and narrowing columns are not supported. **Permission:** ADMIN ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `columns` — "> New columns, in the same form as `schema.columnDefinitions`. New columns cannot be required. - `widen` — "> Type changes: `columnName` and the new `type` (with `precision` and `scale` for `DECIMAL`). ### Response - `columns` — "> The table's full column list after the change. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/add-columns' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "columns": [ { "columnName": "channel", "analytics": { "type": "STRING" }, "columnComment": "Sales channel." } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/add-columns', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', columns: [ { columnName: 'channel', analytics: { type: 'STRING' }, columnComment: 'Sales channel.' } ] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/add-columns', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'columns': [ { 'columnName': 'channel', 'analytics': { 'type': 'STRING' }, 'columnComment': 'Sales channel.' } ] } ) data = response.json() ``` ```json title="Response" { "columns": [ { "name": "order_id", "type": "LONG" }, { "name": "order_date", "type": "DATE" }, { "name": "region", "type": "STRING" }, { "name": "net_amount", "type": "DECIMAL(18,2)" }, { "name": "channel", "type": "STRING" } ] } ``` --- ## Suggest a semantic layer with AI **POST** `/suggest-semantics` Drafts the semantic layer from the table's columns, statistics, and a 50-row sample, as the caller is allowed to see them: description, grain, column descriptions and synonyms, measures, example verified queries, and joins to other analytics tables the caller can read. Nothing is saved. Suggested queries and measures are checked against the SQL rules and the caller's access; items that fail are dropped with a note, and items the engine cannot validate are returned flagged. Pass the result (or the `merged` layer) to [Update the semantic layer](#update-the-semantic-layer) to save it. Suggestions do not count toward AI usage. **Permission:** WRITE ### Request parameters - `tableId` (string, required) — The analytics table. - `focus` (array) — Limit the draft to some parts: any of `description`, `columns`, `measures`, `synonyms`, `verifiedQueries`, `joins`. Default: all. - `existing` (boolean) — `true` (default) keeps the current values and only fills gaps; `false` drafts everything again. ### Response - `suggestion` (object) — The draft: `description`, `grain`, `columns` (map of column name to `description` and `synonyms`), `measures`, `verifiedQueries` (`question`, `sql`), `joins`. - `merged` (object) — The current semantic layer with the passing suggestions applied; never removes anything. Send it to `/update-semantics` to save. - `validated` (object) — Per suggested query and measure: `ok`, and `error` when the check failed. - `notes` (array) — Plain-language notes, for example which items were dropped and why. - `semanticsVersion` (integer) — The version the draft was made from; pass it as `expectedVersion` when saving. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/suggest-semantics' -H 'Authorization: Bearer $AUTESSA_POWERUSER_KEY' -H 'Content-Type: application/json' -d '{"tableId": "V1StGXR8Z5", "focus": ["description", "columns", "measures"]}' ``` ## Update the semantic layer **POST** `/update-semantics` Replaces the table's semantic layer: description, grain, column descriptions and synonyms, measures, joins, and verified queries. Send the `semanticsVersion` you last read; if someone else saved in between, the call fails with `VERSION_CONFLICT`. Measures and verified queries are checked in the background, and any that no longer run are flagged. **Permission:** ADMIN ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `expectedVersion` (integer, required) — The `semanticsVersion` from [Describe tables](#describe-tables). - `semantics` (object, required) — `description`, `grain`, `columns` (map of column name to `description` and `synonyms`), `measures` (`name`, `sql`, `description`, `format`), `joins` (`tableId`, `on`, `relationship` of `MANY_TO_ONE`, `ONE_TO_MANY`, or `ONE_TO_ONE`), and `verifiedQueries` (`question`, `sql`). ### Response - `semanticsVersion` (integer) — The new version. - `validationJobId` (string) — The background check of measures and verified queries. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/update-semantics' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ --data-binary @- <<'JSON' { "tableId": "V1StGXR8Z5", "expectedVersion": 6, "semantics": { "description": "Every order line since 2019, loaded nightly from the ERP.", "grain": "one row per order line", "columns": { "net_amount": { "description": "Line revenue after discounts, in USD.", "synonyms": [ "net sales" ] } }, "measures": [ { "name": "revenue", "sql": "SUM(net_amount)", "description": "Net revenue in USD.", "format": "currency" } ], "joins": [ { "tableId": "Hq4ZpN8sXe", "on": "order_lines.customer_id = customers.id", "relationship": "MANY_TO_ONE" } ], "verifiedQueries": [ { "question": "Monthly revenue by region this year", "sql": "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ] } } JSON ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/update-semantics', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', expectedVersion: 6, semantics: { description: 'Every order line since 2019, loaded nightly from the ERP.', grain: 'one row per order line', columns: { net_amount: { description: 'Line revenue after discounts, in USD.', synonyms: ['net sales'] } }, measures: [ { name: 'revenue', sql: 'SUM(net_amount)', description: 'Net revenue in USD.', format: 'currency' } ], joins: [ { tableId: 'Hq4ZpN8sXe', on: 'order_lines.customer_id = customers.id', relationship: 'MANY_TO_ONE' } ], verifiedQueries: [ { question: 'Monthly revenue by region this year', sql: "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ] } }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/update-semantics', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'expectedVersion': 6, 'semantics': { 'description': 'Every order line since 2019, loaded nightly from the ERP.', 'grain': 'one row per order line', 'columns': { 'net_amount': { 'description': 'Line revenue after discounts, in USD.', 'synonyms': ['net sales'] } }, 'measures': [ { 'name': 'revenue', 'sql': 'SUM(net_amount)', 'description': 'Net revenue in USD.', 'format': 'currency' } ], 'joins': [ { 'tableId': 'Hq4ZpN8sXe', 'on': 'order_lines.customer_id = customers.id', 'relationship': 'MANY_TO_ONE' } ], 'verifiedQueries': [ { 'question': 'Monthly revenue by region this year', 'sql': "SELECT region, date_trunc('month', order_date) AS month, SUM(net_amount) AS revenue\nFROM order_lines\nWHERE order_date >= DATE '2026-01-01'\nGROUP BY 1, 2\nORDER BY 2, 1" } ] } } ) data = response.json() ``` ```json title="Response" { "semanticsVersion": 7, "validationJobId": "0b7d4f9e-2c1a-4e63-8d5f-71a9c3e0b2d8" } ``` --- ## Load from an AutessaDB table **POST** `/load-from-table` Copies rows from a regular AutessaDB table into the analytics table as a job, mapping source columns to target columns. **Permission:** WRITE on the analytics table and READ on the source table, with access to all of its rows ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `sourceTableId` (string, required) — The regular table to copy from. - `columnMapping` (object, required) — Map of source column name to analytics column name. - `mode` (string) — `APPEND` (default) or `REPLACE`. ### Response - `jobId` (string) — The load job. - `state` (string) — The job's state. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/load-from-table' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "sourceTableId": "Hq4ZpN8sXe", "columnMapping": { "order_number": "order_id", "placed_on": "order_date", "region": "region", "total": "net_amount" }, "mode": "APPEND" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/load-from-table', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', sourceTableId: 'Hq4ZpN8sXe', columnMapping: { order_number: 'order_id', placed_on: 'order_date', region: 'region', total: 'net_amount' }, mode: 'APPEND' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/load-from-table', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'sourceTableId': 'Hq4ZpN8sXe', 'columnMapping': { 'order_number': 'order_id', 'placed_on': 'order_date', 'region': 'region', 'total': 'net_amount' }, 'mode': 'APPEND' } ) data = response.json() ``` ```json title="Response" { "jobId": "9d3a1c7e-5b20-4f84-a6e9-2c8f0b1d4e73", "state": "QUEUED" } ``` --- ## Create agent tools **POST** `/create-agent-tools` Creates the three analytics tools for a set of tables in one call: [`ANALYTICS_DESCRIBE`](/docs/tools/autessadb.md#describe-analytics-tables), [`ANALYTICS_QUERY`](/docs/tools/autessadb.md#query-analytics-tables), and [`ANALYTICS_GET_RESULT`](/docs/tools/autessadb.md#get-analytics-result). Attach them to an agent to let it answer questions from the data. **Permission:** READ on every table ### Request parameters - `tableId` (string, required) — The analytics table. For multi-table queries, the primary table; on the client API it must match the `resourceId` query parameter. - `tableIds` — "> Additional tables the tools may query. - `namePrefix` (string) — Prefix for the tool names. Defaults to the primary table's name. ### Response - `toolIds` (object) — The new tools' IDs keyed by `describe`, `query`, and `getResult`. **cURL:** ```bash curl -X POST 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/create-agent-tools' \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "tableId": "V1StGXR8Z5", "tableIds": [ "Hq4ZpN8sXe" ], "namePrefix": "Sales data" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/create-agent-tools', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ tableId: 'V1StGXR8Z5', tableIds: ['Hq4ZpN8sXe'], namePrefix: 'Sales data' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/autessadb/analytics/create-agent-tools', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'tableId': 'V1StGXR8Z5', 'tableIds': ['Hq4ZpN8sXe'], 'namePrefix': 'Sales data' } ) data = response.json() ``` ```json title="Response" { "toolIds": { "describe": "Zt5mW8qR1k", "query": "Bn3xL6vD9p", "getResult": "Cf7yH2jS4u" } } ``` --- ## Masked columns Text columns created with `masked: true` are stored encrypted and appear in results as mask text unless the caller's role allows revealing them. - `create` and `add-columns`: `columns[].masked: true` (STRING only). - `describe`: `columns[].encrypted`, `revealable`, and `maskStrategy`. - `query`, `preview`, `job-results`, and `job-download-url` accept `reveal: true` to return plaintext for callers whose role shows the column; every reveal is recorded in the access log. - `job-results` also accepts `revealRow` (0-based row of the whole result) with `revealColumn` and returns `revealedCell {row, column, value}` for a single cell. - Result `columns[]` carry `masked`, `revealable`, `revealed`, and `maskStrategy`. - In SQL, masked columns support `=`, `<>`, `IN`, `IS NULL`, `GROUP BY`, `DISTINCT`, `COUNT(DISTINCT)`, `MIN`/`MAX`, joins with other masked columns, and `ORDER BY` (by an internal code). `LIKE`, ranges, and other functions fail with `FORBIDDEN_CONSTRUCT` and a hint. - Loads into tables with masked columns run in the app rather than the analytics engine; load responses say so in `notes`. ## Errors Failed calls, and jobs that end in `FAILED` or `TIMED_OUT`, carry an error code, a plain-language message, and where possible a hint: ```json title="Error" { "errorCode": "UNKNOWN_COLUMN", "errors": ["Column \"net_amt\" does not exist in order_lines."], "hint": "Columns in order_lines: order_id, order_date, region, customer_id, net_amount." } ``` | Error code | When | | --- | --- | | `PARSE_ERROR` | The SQL could not be parsed. The message points at the position. | | `FORBIDDEN_CONSTRUCT` | The SQL is not a single read-only query: it changes data or settings, has several statements, or calls a function that reads files or system state. | | `UNKNOWN_TABLE` | The query names a table that does not exist or that the caller cannot read. The two cases are reported the same way. | | `UNKNOWN_COLUMN` | The query names a column that does not exist or is hidden from the caller. The hint lists the available columns. | | `SCAN_LIMIT` | The query would scan more than the per-query limit, `maxBytesScanned`, or the remaining daily allowance. The hint names the partition columns to filter on. | | `TIMEOUT` | The query ran past the time limit. | | `ROW_LIMIT` | Too many rows were requested; lower `maxRows` or `pageSize`. | | `QUOTA` | A plan limit was reached: storage, concurrent queries, data loaded per day, or appended rows per minute. | | `VERSION_CONFLICT` | The semantic layer changed since `expectedVersion`. Describe again and retry. | | `INVALID_SCHEMA` | A column name, type, or partition spec is invalid, or a schema change is not a safe widening. | | `FORBIDDEN` | The caller lacks the table permission for the operation, or the job belongs to another caller. | | `NOT_FOUND` | The table or job does not exist, or its results have expired. | HTTP status codes follow the usual pattern: `400` for invalid requests and SQL errors, `401` for a missing or invalid key, `403` for `FORBIDDEN`, `404` for `NOT_FOUND`, `409` for `VERSION_CONFLICT`, `413` for `SCAN_LIMIT` and `ROW_LIMIT`, and `429` for `QUOTA` and rate limits. ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [AutessaDB Tables API](/docs/api/poweruser/autessadb-tables.md) — sharing, ownership, and deletion work the same for analytics tables - [Client Analytics API](/docs/api/autessadb-analytics.md) - [AutessaDB Analytics Tables guide](/docs/guide/autessadb/analytics.md) - [Analytics tool types](/docs/tools/autessadb.md#describe-analytics-tables) --- # Connectors Source: https://docs.autessa.com/docs/api/poweruser/connectors > Poweruser API endpoints to list, inspect, create, share, connect, and manage your company's external connectors, plus family-specific connector actions. # Connectors The Connectors API manages your company's external connectors — Gmail, Outlook, HubSpot, QuickBooks, Acumatica, and more — through a single set of endpoints. Instead of a separate path per integration, you select the connector type in the request body and the platform routes the call to the matching connector utility. Beyond listing and inspection, you can delete, share, and transfer connectors, obtain OAuth authorization URLs to connect or reconnect accounts, create direct-credential connectors, and call family-specific connector actions. All endpoints are `POST` requests under the base path `https://api.autessa.com/autessa_poweruser/connectors` and authenticate with a poweruser key in the `Authorization` header, formatted as `pwrusr:{key_id}:{secret}`. --- ## Connector types Every request selects a connector type with the `connectorType` field. The value is one of the enum names below, which also determine how the underlying connector authenticates with the external service: | `connectorType` | Auth type | | --- | --- | | `QUICKBOOKS` | `OAUTH` | | `GMAIL` | `OAUTH` | | `GOOGLE_CALENDAR` | `OAUTH` | | `GOOGLE_ANALYTICS` | `OAUTH` | | `BOX` | `OAUTH` | | `LINKEDIN` | `OAUTH` | | `TWITTER` | `OAUTH` | | `FACEBOOK` | `OAUTH` | | `INSTAGRAM` | `OAUTH` | | `REDDIT` | `OAUTH` | | `THREADS` | `OAUTH` | | `ZENDESK` | `OAUTH` | | `YOUTUBE` | `OAUTH` | | `TIKTOK` | `OAUTH` | | `ACUMATICA` | `OAUTH` | | `HUBSPOT` | `OAUTH` | | `ZOHO_CRM` | `OAUTH` | | `OUTLOOK` | `OAUTH` | | `OUTLOOK_CALENDAR` | `OAUTH` | | `NOTION` | `OAUTH` | | `MAILCHIMP` | `OAUTH_NO_REFRESH` | | `GHOST` | `API_KEY` | | `BEEHIIV` | `API_KEY` | | `BLUESKY` | `USERNAME_PASSWORD` | | `MEETING_BOT` | `OAUTH` | | `AUTESSA_PAYMENTS` | `OAUTH_NO_REFRESH` | Auth types: `OAUTH` is standard OAuth2 with access and refresh tokens, `OAUTH_NO_REFRESH` is OAuth with an access token only (e.g. Mailchimp), `API_KEY` is a single API key (e.g. Ghost), and `USERNAME_PASSWORD` is a handle plus app password that authenticates at runtime (e.g. Bluesky). A connector type is served here as soon as its utility registers with the platform's connector registry. `MEETING_BOT` and `AUTESSA_PAYMENTS` do not yet have a registered utility: `/all` and `/get` requests for those two types currently fail with a server error, and every other endpoint returns a `422` with `errors: ["Connector type MEETING_BOT is not supported via the poweruser API"]`. --- ## Capabilities by connector family Every registered connector type supports `/all` and `/get`. The management, OAuth, and direct-credential endpoints are opt-in per family: | `connectorType` | `/delete` | `/share` | `/get-sharing-details` | `/transfer-ownership` | `/connect-url`, `/reconnect-url` | `/create` | `/test-connection` | | --- | --- | --- | --- | --- | --- | --- | --- | | `QUICKBOOKS` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `GMAIL` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `GOOGLE_CALENDAR` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `GOOGLE_ANALYTICS` | ✓ | ✓ | — | — | ✓ | — | — | | `BOX` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `LINKEDIN` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `TWITTER` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `FACEBOOK` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `INSTAGRAM` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `REDDIT` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `THREADS` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `ZENDESK` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `YOUTUBE` | ✓ | — | — | — | ✓ | — | — | | `TIKTOK` | ✓ | — | — | — | ✓ | — | — | | `ACUMATICA` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `HUBSPOT` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `ZOHO_CRM` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `OUTLOOK` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `OUTLOOK_CALENDAR` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `NOTION` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `MAILCHIMP` | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | | `GHOST` | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | | `BEEHIIV` | ✓ | — | — | — | — | ✓ | ✓ | | `BLUESKY` | ✓ | ✓ | ✓ | ✓ | — | ✓ | — | Calling an operation a family does not support returns HTTP `422` with an explanatory message in the body: ```json title="422 Unprocessable Entity" { "errors": [ "Operation 'share' is not supported for connector type TIKTOK." ] } ``` > Updating an existing connector's configuration (rename, activate/deactivate, rotate an API key) is not available through the Poweruser API — update payloads differ per family, so connector updates remain UI-only in the builder. --- ## List Connectors **POST** `/all` Lists all active connectors of the given type in your company that the calling user can see. Connectors the caller has no access level on are filtered out of the result. ### Request Body - `connectorType` (string, required) — The connector type to list, e.g. `GMAIL`. One of the enum values in [Connector types](#connector-types). Omitting it returns `errors: ["connectorType is required"]`. - `connectorId` (string) — Ignored by this endpoint. Only used by `/get`. ### Response - `connectors` (array) — The visible connectors of the requested type. Each element is a connector object (see below). - `errors` (array) — Error messages, if any occurred. Absent on success. #### Connector object Every connector type shares this common set of fields: - `connectorId` (string) — Unique ID of the connector. - `companyId` (string) — The company the connector belongs to. - `ownerId` (string) — The user who owns the connector. - `connectorName` (string) — Display name of the connector. - `ownerType` (string) — The owner type of the connector. - `sharingLevel` (string) — How the connector is shared: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — The access level granted company-wide when shared at the `COMPANY` level: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `isActive` (boolean) — Whether the connector is active. - `createdAt` (string) — When the connector was created (ISO-8601, e.g. `2026-05-04T18:22:10`). - `updatedAt` (string) — When the connector was last updated (ISO-8601). - `usersWithAccess` (array) — Users the connector is shared with. Each entry has `userEmail` (string), `userId` (string), and `accessLevel` (string). - `currentUserAccessLevel` (string) — The calling user's effective access level on the connector: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. On top of these, each connector type adds its own fields. For example, `GMAIL` connectors include the connected `gmailAccountEmail` and the `availableLabels` on the mailbox, while `ACUMATICA` connectors include the instance `hostUrl`, `databaseIdentifier`, `accountEmail`, `accountName`, and `tokenExpiresAt`. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/all \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "GMAIL" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/all', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'GMAIL' }) } ); const data = await response.json(); console.log(data.connectors); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/all", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={"connectorType": "GMAIL"}, ) data = response.json() print(data["connectors"]) ``` ```json title="JSON" { "connectors": [ { "connectorId": "V1StGXR8Z5", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xQ9LmZ2", "connectorName": "Support Inbox", "ownerType": "USER", "gmailAccountEmail": "support@example.com", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "isActive": true, "createdAt": "2026-05-04T18:22:10", "updatedAt": "2026-06-12T09:41:23", "usersWithAccess": [ { "userEmail": "ops@example.com", "userId": "pQ7wY4RdX1", "accessLevel": "WRITE" } ], "currentUserAccessLevel": "ADMIN", "availableLabels": [ { "labelId": "Label_12", "labelName": "Tickets", "labelType": "user", "isEnabled": true, "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ] } ``` --- ## Get Connector **POST** `/get` Fetches a single connector by ID. The connector must belong to your company and the calling user must have at least `READ` access to it; otherwise the response carries an error instead of the connector. ### Request Body - `connectorType` (string, required) — The connector's type, e.g. `ACUMATICA`. One of the enum values in [Connector types](#connector-types). Omitting it returns `errors: ["connectorType is required"]`. - `connectorId` (string, required) — The ID of the connector to fetch. Omitting it returns `errors: ["connectorId is required"]`. ### Response - `connector` (object) — The connector object — the same shape as the elements of `connectors` in [List Connectors](#connector-object): the common fields (`connectorId`, `companyId`, `ownerId`, `connectorName`, `ownerType`, `sharingLevel`, `companyPermissionLevel`, `isActive`, `createdAt`, `updatedAt`, `usersWithAccess`, `currentUserAccessLevel`) plus the type-specific fields for the requested `connectorType`. - `errors` (array) — Error messages, if any occurred (e.g. `"Connector not found"`). Absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/get \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "ACUMATICA", "connectorId": "mN8cE5TvW3" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/get', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'ACUMATICA', connectorId: 'mN8cE5TvW3' }) } ); const data = await response.json(); console.log(data.connector); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/get", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "ACUMATICA", "connectorId": "mN8cE5TvW3", }, ) data = response.json() print(data["connector"]) ``` ```json title="JSON" { "connector": { "connectorId": "mN8cE5TvW3", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xQ9LmZ2", "connectorName": "Production ERP", "ownerType": "USER", "hostUrl": "https://erp.example.com", "databaseIdentifier": "CompanyDB", "accountEmail": "erp-admin@example.com", "accountName": "ERP Admin", "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": "READ", "isActive": true, "tokenExpiresAt": "2026-07-01T12:00:00", "createdAt": "2026-04-18T14:03:55", "updatedAt": "2026-06-28T08:17:42", "usersWithAccess": [ { "userEmail": "finance@example.com", "userId": "pQ7wY4RdX1", "accessLevel": "READ" } ], "currentUserAccessLevel": "WRITE" } } ``` --- ## Delete Connector **POST** `/delete` Permanently deletes a connector. Supported by every registered connector family. Deletion is enforced server-side: deleting a connector you don't own returns a permission error such as `"Access denied: Only the owner can delete this connector"`. ### Request Body - `connectorType` (string, required) — The connector's type. Omitting it returns a `422` with `errors: ["connectorType is required"]`. - `connectorId` (string, required) — The connector to delete. Omitting it returns a `422` with `errors: ["connectorId is required"]`. ### Response - `message` (string) — Confirmation message, e.g. `"Connector deleted successfully"`. `GOOGLE_ANALYTICS` returns a boolean `deleted` and `ACUMATICA` a boolean `success` instead of `message`. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/delete \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/delete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'GMAIL', connectorId: 'V1StGXR8Z5' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/delete", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5", }, ) data = response.json() ``` ```json title="JSON" { "message": "Connector deleted successfully" } ``` --- ## Share Connector **POST** `/share` Sets the connector's sharing level and per-user grants in one call. Requires `ADMIN` access on the connector. Not supported for `BEEHIIV`, `TIKTOK`, and `YOUTUBE` (returns a `422` — see [Capabilities by connector family](#capabilities-by-connector-family)). ### Request Body - `connectorType` (string, required) — The connector's type. - `connectorId` (string, required) — The connector to share. - `level` (string) — Sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `accessLevel` (string) — When `level` is `COMPANY`, the access level every company member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` (array) — Users to grant access. Each entry: `userEmail` or `userId` (string), plus `accessLevel`. - `usersToRemove` (array) — Users whose access to revoke, same entry shape. ### Response - `level` (string) — The resulting sharing level. - `accessLevel` (string) — The resulting company-wide access level, when shared with the company. - `usersWithAccess` (array) — The resulting per-user grants (`userEmail`, `userId`, `accessLevel`). - `errors` (array) — Error messages, if any occurred. `GOOGLE_ANALYTICS` and `ACUMATICA` return the updated `connector` object instead of the `level`/`accessLevel`/`usersWithAccess` triple. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/share \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userEmail": "ops@example.com", "accessLevel": "WRITE" } ], "usersToRemove": [] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/share', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'GMAIL', connectorId: 'V1StGXR8Z5', level: 'SPECIFIC_MEMBER', usersToAdd: [ { userEmail: 'ops@example.com', accessLevel: 'WRITE' } ], usersToRemove: [] }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/share", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5", "level": "SPECIFIC_MEMBER", "usersToAdd": [ {"userEmail": "ops@example.com", "accessLevel": "WRITE"} ], "usersToRemove": [], }, ) data = response.json() ``` ```json title="JSON" { "level": "SPECIFIC_MEMBER", "usersWithAccess": [ { "userEmail": "ops@example.com", "userId": "pQ7wY4RdX1", "accessLevel": "WRITE" } ] } ``` --- ## Get Sharing Details **POST** `/get-sharing-details` Returns the connector's current sharing configuration and the calling user's own access level. Not supported for `BEEHIIV`, `GOOGLE_ANALYTICS`, `TIKTOK`, and `YOUTUBE` (returns a `422`). ### Request Body - `connectorType` (string, required) — The connector's type. - `connectorId` (string, required) — The connector to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId`, `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/get-sharing-details \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/get-sharing-details', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'GMAIL', connectorId: 'V1StGXR8Z5' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/get-sharing-details", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5", }, ) data = response.json() ``` ```json title="JSON" { "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "sharedWith": [], "userAccessLevel": "ADMIN" } ``` --- ## Transfer Ownership **POST** `/transfer-ownership` Transfers the connector to another member of the company. Only the **current owner** may call this, and the new owner must be a member of the company who already has some access to the connector (share it with them first). Not supported for `BEEHIIV`, `GOOGLE_ANALYTICS`, `TIKTOK`, and `YOUTUBE` (returns a `422`). ### Request Body - `connectorType` (string, required) — The connector's type. - `connectorId` (string, required) — The connector to transfer. - `newOwnerId` (string, required) — User ID of the new owner. Omitting it returns a `422` with `errors: ["newOwnerId is required"]`. A user outside the company returns `"New owner does not exist"`, and one without access returns `"New owner does not have access to connector yet."`. ### Response - `message` (string) — Confirmation message. - `newOwnerId` (string) — The user who now owns the connector. `ACUMATICA` returns the updated `connector` object instead of `message`/`newOwnerId`. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/transfer-ownership \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5", "newOwnerId": "uB7nT2wQ9k" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/transfer-ownership', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'GMAIL', connectorId: 'V1StGXR8Z5', newOwnerId: 'uB7nT2wQ9k' }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/transfer-ownership", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "GMAIL", "connectorId": "V1StGXR8Z5", "newOwnerId": "uB7nT2wQ9k", }, ) data = response.json() ``` ```json title="JSON" { "message": "Ownership transferred successfully", "newOwnerId": "uB7nT2wQ9k" } ``` --- ## Get Connect URL **POST** `/connect-url` Returns the external provider's authorization URL to connect a **new** OAuth connector. Only the URL discovery happens through the API — the OAuth dance itself stays in the browser: 1. Call `/connect-url` with the `connectorType` (and any family-specific fields below). 2. Open the returned `authorizationUrl` in a browser — typically by sending it to the user whose account should be connected. 3. The user signs in and grants consent. The provider redirects to Autessa's OAuth callback, which stores the tokens server-side — there is nothing further for you to call. 4. Poll `/all` for the connector type until the new connector appears with `isActive: true`. The OAuth state embeds the poweruser key's linked user and company, so the resulting connector is owned by the key's linked user. Supported by every OAuth family. The direct-credential families (`BEEHIIV`, `GHOST`, `BLUESKY`) return a `422` — use [`/create`](#create-connector-) for those instead. ### Request Body - `connectorType` (string, required) — The connector type to connect, e.g. `GMAIL`. - `connectorName` (string) — Display name for the new connector. Applies to all families. - `subdomain` (string) — `ZENDESK` only (required) — the Zendesk subdomain to authorize against. - `hostUrl` (string) — `ACUMATICA` only — the Acumatica instance URL. - `databaseIdentifier` (string) — `ACUMATICA` only — the tenant/database identifier. - `clientId` (string) — `ACUMATICA` only — the OAuth client ID of the connected application registered on the instance. - `clientSecret` (string) — `ACUMATICA` only — the OAuth client secret of the connected application. - `loginMethod` (string) — `INSTAGRAM` only — `FACEBOOK_LOGIN` (default) or `INSTAGRAM_LOGIN`. ### Response - `authorizationUrl` (string) — The provider authorization URL to open in a browser. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/connect-url \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "GMAIL", "connectorName": "Support Inbox" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/connect-url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'GMAIL', connectorName: 'Support Inbox' }) } ); const { authorizationUrl } = await response.json(); // Send authorizationUrl to the user, then poll /all // until the new connector appears with isActive: true. ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/connect-url", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "GMAIL", "connectorName": "Support Inbox", }, ) authorization_url = response.json()["authorizationUrl"] # Send authorization_url to the user, then poll /all # until the new connector appears with isActive: true. ``` ```json title="JSON" { "authorizationUrl": "https://accounts.google.com/o/oauth2/v2/auth?client_id=...&state=..." } ``` --- ## Get Reconnect URL **POST** `/reconnect-url` Returns the provider authorization URL to re-authenticate an **existing** OAuth connector — for example after the user revoked access, the refresh token expired, or QuickBooks access was revoked with [`/quickbooks/revoke-access`](#revoke-quickbooks-access-). The flow is the same as [`/connect-url`](#get-connect-url-): open the URL in a browser, let the user re-consent, and the callback completes server-side. Afterwards poll `/get` with the same `connectorId` until the connector reports `isActive: true`. Supported by every OAuth family; direct-credential families return a `422`. ### Request Body - `connectorType` (string, required) — The connector's type. - `connectorId` (string, required) — The existing connector to re-authenticate. Omitting it returns a `422` with `errors: ["connectorId is required"]`. ### Response - `authorizationUrl` (string) — The provider authorization URL to open in a browser. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/reconnect-url \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "QUICKBOOKS", "connectorId": "mN8cE5TvW3" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/reconnect-url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'QUICKBOOKS', connectorId: 'mN8cE5TvW3' }) } ); const { authorizationUrl } = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/reconnect-url", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "QUICKBOOKS", "connectorId": "mN8cE5TvW3", }, ) authorization_url = response.json()["authorizationUrl"] ``` ```json title="JSON" { "authorizationUrl": "https://appcenter.intuit.com/connect/oauth2?client_id=...&state=..." } ``` --- ## Create Connector **POST** `/create` Creates a connector directly from credentials — no OAuth dance. Only the direct-credential families support this endpoint; OAuth families return a `422` such as `"Operation 'create' is not supported for connector type GMAIL."` (use [`/connect-url`](#get-connect-url-) for those). Credentials are validated against the external service before the connector is saved — for example, `BEEHIIV` tests the API key and fetches its publications first. Per family: - `BEEHIIV` — `connectorName` + `apiKey`. - `GHOST` — `connectorName` + `adminApiKey` + `apiUrl`. - `BLUESKY` — `connectorName` + `handle` + `appPassword`. ### Request Body - `connectorType` (string, required) — `BEEHIIV`, `GHOST`, or `BLUESKY`. - `connectorName` (string, required) — Display name for the new connector. - `apiKey` (string) — `BEEHIIV` — the Beehiiv API key. - `adminApiKey` (string) — `GHOST` — the Ghost Admin API key. - `apiUrl` (string) — `GHOST` — the Ghost site's API URL. - `handle` (string) — `BLUESKY` — the Bluesky handle. - `appPassword` (string) — `BLUESKY` — a Bluesky app password (not the account password). ### Response - `connector` (object) — The newly created connector object, in the family's shape (see [Connector object](#connector-object)). - `message` (string) — Confirmation message. - `errors` (array) — Error messages, if any occurred (e.g. invalid credentials). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/create \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "BEEHIIV", "connectorName": "Company Newsletter", "apiKey": "'"$BEEHIIV_API_KEY"'" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/create', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'BEEHIIV', connectorName: 'Company Newsletter', apiKey: process.env.BEEHIIV_API_KEY }) } ); const { connector } = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/create", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "BEEHIIV", "connectorName": "Company Newsletter", "apiKey": os.environ["BEEHIIV_API_KEY"], }, ) connector = response.json()["connector"] ``` ```json title="JSON" { "connector": { "connectorId": "kZ3mQ9pW2x", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xQ9LmZ2", "connectorName": "Company Newsletter", "ownerType": "USER", "publications": [ { "id": "pub_00000000-0000-0000-0000-000000000000", "name": "Weekly Digest" } ], "sharingLevel": "PRIVATE", "isActive": true, "createdAt": "2026-07-01T10:15:00", "updatedAt": "2026-07-01T10:15:00", "currentUserAccessLevel": "ADMIN" }, "message": "Beehiiv connector created successfully" } ``` --- ## Test Connection **POST** `/test-connection` Tests connectivity with the external service for the direct-credential families. What is tested depends on the family: - `BEEHIIV` — validates a **pre-create** `apiKey` and returns the publications it can access, so you can test before calling [`/create`](#create-connector-). - `GHOST` — tests an **existing** connector identified by `connectorId`. - `BLUESKY` — not supported (`422`); credentials are validated at create time instead. OAuth families return a `422` unsupported-operation error. ### Request Body - `connectorType` (string, required) — `BEEHIIV` or `GHOST`. - `apiKey` (string) — `BEEHIIV` — the API key to validate. - `connectorId` (string) — `GHOST` — the existing connector to test. ### Response - `connectionSuccessful` (boolean) — Whether the connection test passed. - `message` (string) — Human-readable result, e.g. `"Connection successful. Found 2 publication(s)."`. - `publications` (array) — `BEEHIIV` only — the publications visible to the API key. Each entry has `id` and `name`. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/connectors/test-connection \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorType": "BEEHIIV", "apiKey": "'"$BEEHIIV_API_KEY"'" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/connectors/test-connection', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ connectorType: 'BEEHIIV', apiKey: process.env.BEEHIIV_API_KEY }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/connectors/test-connection", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "connectorType": "BEEHIIV", "apiKey": os.environ["BEEHIIV_API_KEY"], }, ) data = response.json() ``` ```json title="JSON" { "connectionSuccessful": true, "message": "Connection successful. Found 2 publication(s).", "publications": [ { "id": "pub_00000000-0000-0000-0000-000000000000", "name": "Weekly Digest" }, { "id": "pub_11111111-1111-1111-1111-111111111111", "name": "Product Updates" } ] } ``` --- ## Connector actions Beyond the uniform surface above, some families expose family-specific actions under `https://api.autessa.com/autessa_poweruser/connectors/{family}/...`. These are the same operations the builder UI uses, with the same request and response shapes. All are `POST` requests with a JSON body and the same poweruser authentication; the `connectorId` must reference a connector of the matching family. No `connectorType` field is needed — the family is in the path. Families without an action listed here (LinkedIn, Twitter, HubSpot, and so on) have no family-specific actions; they are fully covered by the endpoints above. ### View available Gmail labels **POST** `/gmail/view-available-labels` Lists the labels on the connected Gmail mailbox and whether each one is enabled for use on the platform. Requires at least `READ` access to the connector. #### Request Body - `connectorId` (string, required) — The Gmail connector to inspect. #### Response - `labels` (array) — The mailbox's labels. Each entry has `labelId`, `labelName`, `labelType` (`system` or `user`), `isEnabled`, `currentUserAccessLevel`, `sharingLevel`, and `companyPermissionLevel`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/gmail/view-available-labels \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "labels": [ { "labelId": "Label_12", "labelName": "Tickets", "labelType": "user", "isEnabled": true, "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ``` --- ### Update available Gmail labels **POST** `/gmail/update-available-labels` Enables or disables mailbox labels for use on the platform. Requires `ADMIN` access to the connector. #### Request Body - `connectorId` (string, required) — The Gmail connector to update. - `labelsToEnable` (array) — Label IDs to enable. - `labelsToDisable` (array) — Label IDs to disable. #### Response - `message` (string) — Confirmation message, e.g. `"Label settings updated successfully"`. - `enabledLabels` (array) — The resulting label list, same entry shape as [view-available-labels](#view-available-gmail-labels-). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/gmail/update-available-labels \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "labelsToEnable": ["Label_12"], "labelsToDisable": ["Label_7"] }' ``` ```json title="JSON" { "message": "Label settings updated successfully", "enabledLabels": [ { "labelId": "Label_12", "labelName": "Tickets", "labelType": "user", "isEnabled": true, "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ``` --- ### View available Box folders **POST** `/box/view-available-folders` Lists the Box folders known to the connector and whether each one is enabled for use on the platform. #### Request Body - `connectorId` (string, required) — The Box connector to inspect. #### Response - `folders` (array) — The known folders. Each entry has `folderId`, `folderName`, `folderPath`, `isEnabled`, `currentUserAccessLevel`, `sharingLevel`, and `companyPermissionLevel`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/box/view-available-folders \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "folders": [ { "folderId": "184029571230", "folderName": "Contracts", "folderPath": "/Legal/Contracts", "isEnabled": true, "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ``` --- ### Update available Box folders **POST** `/box/update-available-folders` Enables or disables Box folders for use on the platform. Requires `ADMIN` access to the connector. #### Request Body - `connectorId` (string, required) — The Box connector to update. - `foldersToEnable` (array) — Folder IDs to enable. - `foldersToDisable` (array) — Folder IDs to disable. #### Response - `folders` (array) — The resulting folder list, same entry shape as [view-available-folders](#view-available-box-folders-). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/box/update-available-folders \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "foldersToEnable": ["184029571230"], "foldersToDisable": [] }' ``` ```json title="JSON" { "folders": [ { "folderId": "184029571230", "folderName": "Contracts", "folderPath": "/Legal/Contracts", "isEnabled": true, "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ``` --- ### Sync Box folders **POST** `/box/sync-folders` Refreshes the connector's folder list from Box, adding newly discovered folders. Requires `ADMIN` access to the connector. #### Request Body - `connectorId` (string, required) — The Box connector to sync. #### Response - `message` (string) — Confirmation message. - `foldersAdded` (integer) — How many new folders the sync discovered. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/box/sync-folders \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "message": "Folders synced successfully", "foldersAdded": 3 } ``` --- ### View available Google calendars **POST** `/google-calendar/view-available-calendars` Lists the calendars on the connected Google account and whether each one is enabled for use on the platform. #### Request Body - `connectorId` (string, required) — The Google Calendar connector to inspect. #### Response - `calendars` (array) — The account's calendars. Each entry has `calendarId`, `calendarName`, `isEnabled`, `timezone`, `currentUserAccessLevel`, `sharingLevel`, and `companyPermissionLevel`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/google-calendar/view-available-calendars \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "calendars": [ { "calendarId": "team@example.com", "calendarName": "Team Calendar", "isEnabled": true, "timezone": "America/Los_Angeles", "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ``` --- ### Update available Google calendars **POST** `/google-calendar/update-available-calendars` Enables or disables calendars for use on the platform. Requires `ADMIN` access to the connector. #### Request Body - `connectorId` (string, required) — The Google Calendar connector to update. - `calendarsToEnable` (array) — Calendar IDs to enable. - `calendarsToDisable` (array) — Calendar IDs to disable. #### Response - `message` (string) — Confirmation message. - `enabledCalendars` (array) — The resulting calendar list, same entry shape as [view-available-calendars](#view-available-google-calendars-). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/google-calendar/update-available-calendars \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "calendarsToEnable": ["team@example.com"], "calendarsToDisable": [] }' ``` ```json title="JSON" { "message": "Calendar settings updated successfully", "enabledCalendars": [ { "calendarId": "team@example.com", "calendarName": "Team Calendar", "isEnabled": true, "timezone": "America/Los_Angeles", "currentUserAccessLevel": "ADMIN", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ" } ] } ``` --- ### Sync Google calendars **POST** `/google-calendar/sync-calendars` Forces a re-sync of the calendar list from Google, bypassing the platform's sync throttling. Requires `WRITE`, `PUBLISH`, or `ADMIN` access to the connector. #### Request Body - `connectorId` (string, required) — The Google Calendar connector to sync. #### Response - `message` (string) — Confirmation message. - `syncPerformed` (boolean) — Whether a sync was actually performed. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/google-calendar/sync-calendars \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "message": "Calendars synced successfully", "syncPerformed": true } ``` --- ### List Outlook folders **POST** `/outlook/list-folders` Lists the mail folders on the connected Outlook mailbox. #### Request Body - `connectorId` (string, required) — The Outlook connector to inspect. #### Response - `folders` (array) — The mailbox's folders. Each entry has `folderId` and `displayName`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/outlook/list-folders \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "folders": [ { "folderId": "AAMkAGI2T...", "displayName": "Inbox" }, { "folderId": "AAMkAGI2U...", "displayName": "Invoices" } ] } ``` --- ### List Outlook calendars **POST** `/outlook-calendar/list-calendars` Lists the calendars on the connected Outlook account. #### Request Body - `connectorId` (string, required) — The Outlook Calendar connector to inspect. #### Response - `calendars` (array) — The account's calendars. Each entry has `calendarId`, `name`, `isDefaultCalendar`, and `canEdit`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/outlook-calendar/list-calendars \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "calendars": [ { "calendarId": "AQMkAGI2T...", "name": "Calendar", "isDefaultCalendar": true, "canEdit": true } ] } ``` --- ### List BigQuery projects **POST** `/google-analytics/bigquery/projects` Lists the BigQuery projects visible to the connected Google Analytics account. Requires at least `READ` access to the connector. Use this with [datasets](#list-bigquery-datasets-) and [tables](#list-bigquery-tables-) to discover where GA4 exports live. #### Request Body - `connectorId` (string, required) — The Google Analytics connector to query. #### Response - `projects` (array) — The visible projects. Each entry has `projectId`, `friendlyName`, and `numericId`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/google-analytics/bigquery/projects \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5" }' ``` ```json title="JSON" { "projects": [ { "projectId": "acme-analytics", "friendlyName": "Acme Analytics", "numericId": "123456789012" } ] } ``` --- ### List BigQuery datasets **POST** `/google-analytics/bigquery/datasets` Lists the datasets in a BigQuery project. #### Request Body - `connectorId` (string, required) — The Google Analytics connector to query. - `projectId` (string, required) — The BigQuery project ID, from [projects](#list-bigquery-projects-). #### Response - `datasets` (array) — The project's datasets. Each entry has `datasetId`, `friendlyName`, and `location`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/google-analytics/bigquery/datasets \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "projectId": "acme-analytics" }' ``` ```json title="JSON" { "datasets": [ { "datasetId": "analytics_987654321", "friendlyName": "GA4 Export", "location": "US" } ] } ``` --- ### List BigQuery tables **POST** `/google-analytics/bigquery/tables` Lists the tables in a BigQuery dataset. #### Request Body - `connectorId` (string, required) — The Google Analytics connector to query. - `projectId` (string, required) — The BigQuery project ID. - `datasetId` (string, required) — The dataset ID, from [datasets](#list-bigquery-datasets-). #### Response - `tables` (array) — The dataset's tables. Each entry has `tableId`, `friendlyName`, and `type`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/google-analytics/bigquery/tables \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "projectId": "acme-analytics", "datasetId": "analytics_987654321" }' ``` ```json title="JSON" { "tables": [ { "tableId": "events_20260630", "friendlyName": "events_20260630", "type": "TABLE" } ] } ``` --- ### Look up Notion pages **POST** `/notion/lookup/pages` Searches the connected Notion workspace for pages matching a query. Requires at least `READ` access to the connector. #### Request Body - `connectorId` (string, required) — The Notion connector to search with. - `query` (string) — Search text. Matches page titles in the workspace. #### Response - `pages` (array) — Matching pages. Each entry has `id`, `title`, `parentType`, and `lastEditedTime`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/notion/lookup/pages \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "query": "onboarding" }' ``` ```json title="JSON" { "pages": [ { "id": "a1b2c3d4-0000-0000-0000-000000000000", "title": "Customer Onboarding Checklist", "parentType": "workspace", "lastEditedTime": "2026-06-29T14:12:00.000Z" } ] } ``` --- ### Look up Notion databases **POST** `/notion/lookup/databases` Searches the connected Notion workspace for databases matching a query. #### Request Body - `connectorId` (string, required) — The Notion connector to search with. - `query` (string) — Search text. Matches database titles in the workspace. #### Response - `databases` (array) — Matching databases. Each entry has `id`, `title`, and `lastEditedTime`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/notion/lookup/databases \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "query": "tasks" }' ``` ```json title="JSON" { "databases": [ { "id": "b2c3d4e5-0000-0000-0000-000000000000", "title": "Engineering Tasks", "lastEditedTime": "2026-06-30T09:03:00.000Z" } ] } ``` --- ### Look up a Notion database schema **POST** `/notion/lookup/database-schema` Returns the property schema of a Notion database, so you can construct valid property payloads for tools that write to it. #### Request Body - `connectorId` (string, required) — The Notion connector to use. - `databaseId` (string, required) — The Notion database ID, from [lookup/databases](#look-up-notion-databases-). #### Response - `databaseId` (string) — The database's ID. - `title` (string) — The database's title. - `properties` (array) — The database's properties. Each entry has `name`, `type`, and — for select-like types — the available `options`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/notion/lookup/database-schema \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "V1StGXR8Z5", "databaseId": "b2c3d4e5-0000-0000-0000-000000000000" }' ``` ```json title="JSON" { "databaseId": "b2c3d4e5-0000-0000-0000-000000000000", "title": "Engineering Tasks", "properties": [ { "name": "Status", "type": "select", "options": ["Todo", "In Progress", "Done"] }, { "name": "Title", "type": "title" } ] } ``` --- ### List Acumatica API versions **POST** `/acumatica/api-versions` Lists the contract-based REST API versions available on the connected Acumatica instance. #### Request Body - `connectorId` (string, required) — The Acumatica connector to query. #### Response - `apiVersions` (array) — The available API version identifiers. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/acumatica/api-versions \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "mN8cE5TvW3" }' ``` ```json title="JSON" { "apiVersions": ["24.200.001", "23.200.001"] } ``` --- ### Revoke QuickBooks access **POST** `/quickbooks/revoke-access` Revokes the connector's OAuth grant with Intuit and clears the stored tokens. The connector record remains, but it cannot reach QuickBooks again until it is re-authenticated via [`/reconnect-url`](#get-reconnect-url-). Requires `ADMIN` access to the connector. #### Request Body - `connectorId` (string, required) — The QuickBooks connector whose access to revoke. #### Response - `message` (string) — Confirmation message, e.g. `"QuickBooks access revoked successfully"`. - `connector` (object) — The updated QuickBooks connector object. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/connectors/quickbooks/revoke-access \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "connectorId": "mN8cE5TvW3" }' ``` ```json title="JSON" { "message": "QuickBooks access revoked successfully", "connector": { "connectorId": "mN8cE5TvW3", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xQ9LmZ2", "connectorName": "Main QuickBooks", "realmId": "9341453907xxxx", "qbCompanyName": "Acme Corp", "accountEmail": "finance@example.com", "isSandbox": false, "paymentsEnabled": true, "isActive": true, "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "userAccessLevel": "ADMIN", "isOwner": true, "createdAt": "2026-03-11T16:40:02", "updatedAt": "2026-07-01T08:12:45", "sharedWith": [] } } ``` --- ## Errors Failed requests return an `errors` array of human-readable messages in the response body: - A missing or invalid poweruser key returns `401 Unauthorized`. - The management endpoints (`/delete`, `/share`, `/get-sharing-details`, `/transfer-ownership`, `/connect-url`, `/reconnect-url`, `/create`, `/test-connection`) validate the request with HTTP `422`: omitting `connectorType` returns `errors: ["connectorType is required"]`, omitting a required `connectorId` returns `errors: ["connectorId is required"]`, and omitting `newOwnerId` on `/transfer-ownership` returns `errors: ["newOwnerId is required"]`. On `/all` and `/get` the same missing-field messages are returned without the `422` status. - Requesting a `connectorType` with no registered utility (currently `MEETING_BOT` and `AUTESSA_PAYMENTS`) returns a `422` with `errors: ["Connector type MEETING_BOT is not supported via the poweruser API"]` on the management endpoints, and fails with a server error on `/all` and `/get`. - Calling an operation the connector family does not support (see [Capabilities by connector family](#capabilities-by-connector-family)) returns a `422` with a message such as `"Operation 'share' is not supported for connector type TIKTOK."`. - Requesting a connector that doesn't exist in your company returns `errors: ["Connector not found"]`. - Operating on a connector without the required access returns a permission error: reads require `READ` (e.g. `"User does not have permission to view this connector."`), `/share`, label/folder/calendar updates, `/box/sync-folders`, and `/quickbooks/revoke-access` require `ADMIN` (e.g. `"User does not have permission to share connector."`), `/google-calendar/sync-calendars` requires `WRITE` or higher, and `/delete` and `/transfer-ownership` are restricted to the connector's owner (e.g. `"Access denied: Only the owner can delete this connector"`). - `/transfer-ownership` also validates the new owner: `"New owner does not exist"` if they aren't a member of the company, and `"New owner does not have access to connector yet."` if the connector hasn't been shared with them. --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Connectors guide](/docs/guide/connectors.md) - [Tools API](/docs/api/poweruser/tools.md) - [Agents API](/docs/api/poweruser/agents.md) --- # Scheduled Events Source: https://docs.autessa.com/docs/api/poweruser/scheduled-events > Poweruser endpoints for creating, managing, sharing, and auditing scheduled events that invoke agents, invoke tools, or update AutessaDB tables. # Scheduled Events Scheduled events run an action on a schedule — invoke an agent, invoke a tool, or update an AutessaDB table. Events fire on a fixed interval, a cron expression, or once at a specific time. These poweruser endpoints cover the full lifecycle: create, update, enable/disable, execution history, sharing, ownership transfer, and deletion. > All endpoints are `POST` with a JSON body and require an `Authorization` header carrying your poweruser key. The acting user (for ownership, permissions, and sharing) is the user the poweruser key belongs to. --- ## Base URL ``` https://api.autessa.com/autessa_poweruser/scheduled-events ``` --- ## Authentication Pass your poweruser key in the `Authorization` header (no "Bearer" prefix): ``` Authorization: pwrusr:{id}:{secret} ``` Never embed a real key in code — read it from the environment. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/all \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` --- ## Schedule expressions Every event's timing is defined by a `scheduleSpec` object — a discriminated union over the `pattern` field. There are exactly three patterns; only the nested object matching the pattern is present. The spec is compiled into a schedule expression (`rate(...)`, `cron(...)`, or `at(...)`), which is echoed back in the read-only `cronExpression` response field. ### EVERY — fixed interval Recurring at a fixed interval; maps to `rate(value unit)` (e.g. `rate(30 minutes)`, `rate(1 day)`). Timezone-independent. ```json title="Every 30 minutes" { "pattern": "EVERY", "every": { "value": 30, "unit": "MINUTES" } } ``` - `every.value` (integer, required) — Positive integer (>= 1). E.g. `5` for "every 5 minutes". - `every.unit` (string, required) — One of `MINUTES`, `HOURS`, `DAYS`. ### CRON — recurring at specific times Recurring on a cron expression evaluated in an IANA timezone; maps to `cron(...)`. The expression is the 6-field AWS cron format: ``` minute hour day-of-month month day-of-week year ``` Rules the backend validates and enforces: - Exactly 6 space-separated fields — do NOT include the `cron(...)` wrapper. - Use `?` in exactly one of day-of-month or day-of-week; `*` means "any". - Day-of-week is `1`=Sunday through `7`=Saturday. - `timezone` is required and must be a valid IANA name (e.g. `UTC`, `America/Los_Angeles`). ```json title="Daily at 9:00 AM UTC — compiles to cron(0 9 * * ? *)" { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "UTC" } } ``` ```json title="Every Monday at 7:00 AM Pacific" { "pattern": "CRON", "cron": { "expression": "0 7 ? * 2 *", "timezone": "America/Los_Angeles" } } ``` ```json title="First of every month at midnight UTC" { "pattern": "CRON", "cron": { "expression": "0 0 1 * ? *", "timezone": "UTC" } } ``` - `cron.expression` (string, required) — 6-field AWS cron expression (minute hour day-of-month month day-of-week year), without the `cron(...)` wrapper. - `cron.timezone` (string, required) — IANA timezone the expression is evaluated in, e.g. `UTC`, `America/Los_Angeles`. ### ONCE — one-time execution Fires exactly once at a local wall-clock time in a timezone; maps to `at(yyyy-mm-ddThh:mm:ss)` (the backend converts the local time to UTC before writing the AWS schedule). The time must be in the future. ```json title="One-time: Dec 31 2026, 11:59 PM UTC" { "pattern": "ONCE", "once": { "at": "2026-12-31T23:59:00", "timezone": "UTC" } } ``` - `once.at` (string, required) — Local wall-clock ISO-8601 datetime without offset, e.g. `2026-12-31T23:59:00`. Interpreted in `once.timezone`; must be in the future. - `once.timezone` (string, required) — IANA timezone the wall-clock time is interpreted in. --- ## Action types The `actionType` field selects what the event does when it fires. Set exactly ONE matching config object (`agentConfig`, `toolConfig`, or `autessaDbConfig`) — the backend rejects requests with zero or multiple configs, and validates the config against the action type. - `INVOKE_AGENT` (agentConfig) — Execute an agent with predefined input. Runs in single-turn mode (no conversation context) using a system key for your company. - `INVOKE_TOOL` (toolConfig) — Execute a tool with static parameters defined at schedule creation time. - `UPDATE_AUTESSADB` (autessaDbConfig) — Perform an INSERT or UPDATE operation on an AutessaDB table with predefined values. ### agentConfig (INVOKE_AGENT) - `agentExecutionRequest` (object, required) — The agent execution request, executed single-turn when the event fires. - `agentExecutionRequest.agentId` (string, required) — The agent to execute, e.g. `"V1StGXR8Z5"`. - `agentExecutionRequest.input` (array, required) — Multimodal input array; must be non-empty. Text input: `{ "inputType": "TEXT", "content": "..." }`. - `agentExecutionRequest.environmentVariables` (object) — Key-value environment variables passed to the execution. - `agentExecutionRequest.promptTemplateVariables` (object) — Key-value pairs injected into the agent's prompt template (`@@{variableName}@@` syntax). - `continueOnError` (boolean) — If `true`, an agent failure is logged but the execution is marked successful. Default `false` (execution marked FAILED on error). - `description` (string) — Optional description of what this scheduled agent run does. ### toolConfig (INVOKE_TOOL) - `toolExecutionRequest` (object, required) — The tool execution request, executed as-is when the event fires. No variable substitution from record data — parameters are static. - `toolExecutionRequest.toolId` (string, required) — The tool to execute, e.g. `"kTn4vB2p9Q"`. - `toolExecutionRequest.variables` (object) — Key-value tool input variables. - `toolExecutionRequest.environmentVariables` (object) — Key-value environment variables for the tool run. - `toolExecutionRequest.privateVariables` (object) — Key-value private variables (not logged). - `continueOnError` (boolean) — If `true`, a tool failure is logged but the execution is marked successful. Default `false`. - `description` (string) — Optional description of what this scheduled tool run does. ### autessaDbConfig (UPDATE_AUTESSADB) - `operation` (string, required) — `INSERT` (add a record with predefined values) or `UPDATE` (modify existing records). - `targetTableId` (string, required) — The AutessaDB table to write to, e.g. `"aB3xK9mQ2w"`. - `values` (object, required) — Column values to insert/update. Map of column name to a typed value object, e.g. `{ "status": { "columnType": "SHORT_TEXT", "textValue": "archived" } }`. Value objects carry `columnType` plus the matching field: `textValue`, `integerValue`, `floatValue`, `booleanValue`, `multiSelectValue` (array of strings), or `filePath`. - `recordId` (integer) — For single-row UPDATE: the specific record ID to update. Mutually exclusive with `whereGroups`. Not allowed for INSERT. - `whereGroups` (array) — For batch UPDATE: WHERE condition groups matching the records to update. Each group has a `logicalOperator` (`AND`/`OR`) and a `conditions` array. Mutually exclusive with `recordId`. Not allowed for INSERT. UPDATE requires either `recordId` or `whereGroups`. - `groupsLogicalOperator` (string) — Logical operator between WHERE groups (`AND`/`OR`). Only used with `whereGroups`. - `skipIfTargetTableMissing` (boolean) — If `true`, skip the execution with a warning when the target table no longer exists. Default `false` (fail). - `description` (string) — Optional description of what this scheduled DB operation does. --- ## The scheduled event object Endpoints that return an event (`/all`, `/get`, `/create`, `/update`) use this shape. - `eventId` (string) — Unique event identifier (UUID string). - `eventName` (string) — Display name of the event. - `description` (string) — Description of what the event does. - `resourceType` (string) — Target resource type derived from the action: `AGENT`, `TOOL`, or `TABLE`. - `resourceId` (string) — ID of the target resource (the agent, tool, or table the action operates on). - `isRecurring` (boolean) — `true` for EVERY/CRON schedules, `false` for one-time (ONCE) events. - `enabled` (boolean) — Whether the schedule is currently active. - `completed` (boolean) — `true` once a one-time (ONCE) event has fired. Completed events cannot be updated. - `cronExpression` (string) — Read-only compiled schedule expression, e.g. `cron(0 9 * * ? *)`, `rate(30 minutes)`, or `at(2026-12-31T23:59:00)`. - `scheduleSpec` (object) — The schedule as submitted — see [Schedule expressions](#schedule-expressions). - `timezone` (string) — IANA timezone of the schedule (`null` for EVERY schedules, which are timezone-independent). - `nextExecutionAt` (string) — ISO-8601 timestamp of the next planned execution. - `lastExecutedAt` (string) — ISO-8601 timestamp of the most recent execution, or `null` if never fired. - `executionCount` (integer) — Number of times the event has executed. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. New events start `PRIVATE`. - `companyPermissionLevel` (string) — Access level granted company-wide when `sharingLevel` is `COMPANY` (`READ`, `WRITE`, `PUBLISH`, or `ADMIN`); otherwise `null`. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 last-modified timestamp. - `userPermission` (string) — Your access level on this event: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `actionType` (string) — `INVOKE_AGENT`, `INVOKE_TOOL`, or `UPDATE_AUTESSADB` — see [Action types](#action-types). - `agentConfig` (object) — Present when `actionType` is `INVOKE_AGENT`. - `toolConfig` (object) — Present when `actionType` is `INVOKE_TOOL`. - `autessaDbConfig` (object) — Present when `actionType` is `UPDATE_AUTESSADB`. ```json title="JSON" { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "Daily summary", "description": "Runs the reporting agent every morning", "resourceType": "AGENT", "resourceId": "V1StGXR8Z5", "isRecurring": true, "enabled": true, "completed": false, "cronExpression": "cron(0 9 * * ? *)", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "UTC" } }, "timezone": "UTC", "nextExecutionAt": "2026-07-02T09:00:00", "lastExecutedAt": "2026-07-01T09:00:00", "executionCount": 42, "sharingLevel": "PRIVATE", "companyPermissionLevel": null, "createdAt": "2026-05-14T18:22:10", "updatedAt": "2026-06-30T08:15:44", "userPermission": "ADMIN", "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": [ { "inputType": "TEXT", "content": "Generate the daily summary report." } ] }, "continueOnError": false, "description": "Daily summary agent run" } } ``` --- ## List All Events **POST** `/all` Returns every scheduled event visible to you in the current company scope, split into three buckets: events you own, events shared company-wide, and events shared with you individually. ### Request Body - `(none)` (object) — Send an empty JSON object `{}`. ### Response - `ownedEvents` (array) — Events you own. Each entry is a [scheduled event object](#the-scheduled-event-object); `userPermission` is `ADMIN`. - `companySharedEvents` (array) — Events shared with the whole company; `userPermission` reflects the company-wide access level. - `sharedWithMeEvents` (array) — Events shared with you specifically; `userPermission` reflects your individual grant. - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/all \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="JSON" { "ownedEvents": [ { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "Daily summary", "resourceType": "AGENT", "resourceId": "V1StGXR8Z5", "isRecurring": true, "enabled": true, "completed": false, "cronExpression": "cron(0 9 * * ? *)", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "UTC" } }, "timezone": "UTC", "nextExecutionAt": "2026-07-02T09:00:00", "executionCount": 42, "sharingLevel": "PRIVATE", "userPermission": "ADMIN", "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": [ { "inputType": "TEXT", "content": "Generate the daily summary report." } ] }, "continueOnError": false } } ], "companySharedEvents": [], "sharedWithMeEvents": [] } ``` --- ## Get Event **POST** `/get` Fetches a single scheduled event by ID. You must have at least READ access (owner, individual share, or company share); otherwise the call returns `401` with an error. ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID) to fetch. ### Response - `permission` (string) — Your access level on the event: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `scheduledEvent` (object) — The [scheduled event object](#the-scheduled-event-object). - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/get \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c" }' ``` ```json title="JSON" { "permission": "ADMIN", "scheduledEvent": { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "Daily summary", "description": "Runs the reporting agent every morning", "resourceType": "AGENT", "resourceId": "V1StGXR8Z5", "isRecurring": true, "enabled": true, "completed": false, "cronExpression": "cron(0 9 * * ? *)", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "UTC" } }, "timezone": "UTC", "nextExecutionAt": "2026-07-02T09:00:00", "lastExecutedAt": "2026-07-01T09:00:00", "executionCount": 42, "sharingLevel": "PRIVATE", "companyPermissionLevel": null, "createdAt": "2026-05-14T18:22:10", "updatedAt": "2026-06-30T08:15:44", "userPermission": "ADMIN", "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": [ { "inputType": "TEXT", "content": "Generate the daily summary report." } ] }, "continueOnError": false } } } ``` --- ## Create Event **POST** `/create` Creates a scheduled event. Creation is all-or-nothing — if any part of it fails, nothing is created and the error is returned. You become the event's owner with `ADMIN` permission; new events start with `PRIVATE` sharing. Set `actionType` and exactly ONE matching config object — see [Action types](#action-types) for each config's fields. ### Request Body - `eventName` (string, required) — Display name for the event. Must be non-blank. - `description` (string) — Description of what the event does. - `scheduleSpec` (object, required) — When the event fires — see [Schedule expressions](#schedule-expressions). - `enabled` (boolean) — Whether the event is active immediately. Default `true`. - `actionType` (string, required) — `INVOKE_AGENT`, `INVOKE_TOOL`, or `UPDATE_AUTESSADB`. - `agentConfig` (object) — Required when `actionType` is `INVOKE_AGENT` — see [agentConfig](#agentconfig-invoke_agent). - `toolConfig` (object) — Required when `actionType` is `INVOKE_TOOL` — see [toolConfig](#toolconfig-invoke_tool). - `autessaDbConfig` (object) — Required when `actionType` is `UPDATE_AUTESSADB` — see [autessaDbConfig](#autessadbconfig-update_autessadb). ### Response - `scheduledEvent` (object) — The created [scheduled event object](#the-scheduled-event-object). - `eventId` (string) — The new event's ID (UUID). - `scheduleArn` (string) — ARN of the underlying schedule. - `errors` (array) — Error messages; absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/scheduled-events/create \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "eventName": "Daily summary", "description": "Runs the reporting agent every morning", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "UTC" } }, "enabled": true, "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": [ { "inputType": "TEXT", "content": "Generate the daily summary report." } ], "promptTemplateVariables": { "team": "Sales" } }, "continueOnError": false, "description": "Daily summary agent run" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/scheduled-events/create', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ eventName: 'Daily summary', description: 'Runs the reporting agent every morning', scheduleSpec: { pattern: 'CRON', cron: { expression: '0 9 * * ? *', timezone: 'UTC' } }, enabled: true, actionType: 'INVOKE_AGENT', agentConfig: { agentExecutionRequest: { agentId: 'V1StGXR8Z5', input: [ { inputType: 'TEXT', content: 'Generate the daily summary report.' } ], promptTemplateVariables: { team: 'Sales' } }, continueOnError: false, description: 'Daily summary agent run' } }) } ); const data = await response.json(); console.log(data.eventId); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/scheduled-events/create", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "eventName": "Daily summary", "description": "Runs the reporting agent every morning", "scheduleSpec": { "pattern": "CRON", "cron": {"expression": "0 9 * * ? *", "timezone": "UTC"}, }, "enabled": True, "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": [ {"inputType": "TEXT", "content": "Generate the daily summary report."} ], "promptTemplateVariables": {"team": "Sales"}, }, "continueOnError": False, "description": "Daily summary agent run", }, }, ) data = response.json() print(data["eventId"]) ``` ```json title="JSON" { "scheduledEvent": { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "Daily summary", "description": "Runs the reporting agent every morning", "resourceType": "AGENT", "resourceId": "V1StGXR8Z5", "isRecurring": true, "enabled": true, "completed": false, "cronExpression": "cron(0 9 * * ? *)", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "UTC" } }, "timezone": "UTC", "nextExecutionAt": "2026-07-02T09:00:00", "executionCount": 0, "sharingLevel": "PRIVATE", "userPermission": "ADMIN", "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": [ { "inputType": "TEXT", "content": "Generate the daily summary report." } ], "promptTemplateVariables": { "team": "Sales" } }, "continueOnError": false, "description": "Daily summary agent run" } }, "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "scheduleArn": "arn:aws:scheduler:us-east-1:123456789012:schedule/default/3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c" } ``` --- ## Update Event **POST** `/update` Replaces an event's definition. This is a full replacement, not a patch: `scheduleSpec`, `actionType`, and the matching action config are re-validated and must all be supplied. Requires `WRITE` or `ADMIN` access. Completed one-time events and deleted events cannot be updated. ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID) to update. - `eventName` (string) — New display name. - `description` (string) — New description. - `scheduleSpec` (object, required) — The new schedule — see [Schedule expressions](#schedule-expressions). The next execution time is recalculated when the schedule changes. - `actionType` (string, required) — `INVOKE_AGENT`, `INVOKE_TOOL`, or `UPDATE_AUTESSADB`. - `agentConfig` (object) — Required when `actionType` is `INVOKE_AGENT` — see [agentConfig](#agentconfig-invoke_agent). - `toolConfig` (object) — Required when `actionType` is `INVOKE_TOOL` — see [toolConfig](#toolconfig-invoke_tool). - `autessaDbConfig` (object) — Required when `actionType` is `UPDATE_AUTESSADB` — see [autessaDbConfig](#autessadbconfig-update_autessadb). ### Response - `scheduledEvent` (object) — The updated [scheduled event object](#the-scheduled-event-object). - `errors` (array) — Error messages; absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/scheduled-events/update \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "CRM sync", "description": "Sync CRM records every 30 minutes", "scheduleSpec": { "pattern": "EVERY", "every": { "value": 30, "unit": "MINUTES" } }, "actionType": "INVOKE_TOOL", "toolConfig": { "toolExecutionRequest": { "toolId": "kTn4vB2p9Q", "variables": { "mode": "incremental" } }, "continueOnError": true } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/scheduled-events/update', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ scheduledEventId: '3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c', eventName: 'CRM sync', description: 'Sync CRM records every 30 minutes', scheduleSpec: { pattern: 'EVERY', every: { value: 30, unit: 'MINUTES' } }, actionType: 'INVOKE_TOOL', toolConfig: { toolExecutionRequest: { toolId: 'kTn4vB2p9Q', variables: { mode: 'incremental' } }, continueOnError: true } }) } ); const data = await response.json(); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/scheduled-events/update", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "CRM sync", "description": "Sync CRM records every 30 minutes", "scheduleSpec": { "pattern": "EVERY", "every": {"value": 30, "unit": "MINUTES"}, }, "actionType": "INVOKE_TOOL", "toolConfig": { "toolExecutionRequest": { "toolId": "kTn4vB2p9Q", "variables": {"mode": "incremental"}, }, "continueOnError": True, }, }, ) data = response.json() ``` ```json title="JSON" { "scheduledEvent": { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "CRM sync", "description": "Sync CRM records every 30 minutes", "resourceType": "TOOL", "resourceId": "kTn4vB2p9Q", "isRecurring": true, "enabled": true, "completed": false, "cronExpression": "rate(30 minutes)", "scheduleSpec": { "pattern": "EVERY", "every": { "value": 30, "unit": "MINUTES" } }, "timezone": null, "nextExecutionAt": "2026-07-01T12:30:00", "executionCount": 42, "sharingLevel": "PRIVATE", "userPermission": "ADMIN", "actionType": "INVOKE_TOOL", "toolConfig": { "toolExecutionRequest": { "toolId": "kTn4vB2p9Q", "variables": { "mode": "incremental" } }, "continueOnError": true } } } ``` --- ## Toggle Event **POST** `/toggle` Enables or disables an event without changing its definition. Disabled events stay in place (schedule, config, history) but do not fire. Requires `WRITE` or `ADMIN` access. ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID) to toggle. - `enabled` (boolean, required) — `true` to enable, `false` to disable. ### Response - `eventId` (string) — The toggled event's ID. - `enabled` (boolean) — The new enabled state. - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/toggle \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "enabled": false }' ``` ```json title="JSON" { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "enabled": false } ``` --- ## Get Execution History **POST** `/history` Returns paginated execution history plus monthly statistics. Pass `scheduledEventId` to scope to one event (requires at least READ access to it), or omit it for history across all of your company's events. Results cover one calendar month (defaults to the current month), sorted newest first, 50 per page. ### Request Body - `scheduledEventId` (string) — Optional event ID (UUID). When omitted, history for all company events is returned. - `page` (integer) — Zero-based page number. Default `0`. Page size is fixed at 50. - `month` (integer) — Calendar month 1-12. Both `month` and `year` must be set to select a month; otherwise the current month is used. - `year` (integer) — Calendar year, e.g. `2026`. ### Response - `executions` (array) — Execution records for the page — fields below. - `totalElements` (integer) — Number of executions returned after filtering. - `totalPages` (integer) — Total pages available for the month. - `currentPage` (integer) — Zero-based index of this page. - `pageSize` (integer) — Page size (50). - `hasNext` (boolean) — Whether a next page exists. - `hasPrevious` (boolean) — Whether a previous page exists. - `successCount` (integer) — Number of `SUCCESS` executions in the month (for the selected scope). - `failedCount` (integer) — Number of `FAILED` executions in the month. - `successRate` (float) — Fraction of month executions that succeeded, `0.0`-`1.0`. - `averageDurationMs` (float) — Average execution duration in milliseconds. Only populated when `scheduledEventId` is provided; `null` for company-wide queries. - `errors` (array) — Error messages; absent on success. #### Execution record fields - `id` (string) — Unique execution record ID (UUID). - `scheduledEventId` (string) — ID of the scheduled event that fired. - `eventName` (string) — Event name at execution time. - `resourceType` (string) — Target resource type: `AGENT`, `TOOL`, or `TABLE`. - `resourceId` (string) — ID of the target resource. - `startedAt` (string) — ISO-8601 timestamp when execution started. - `endedAt` (string) — ISO-8601 timestamp when execution finished. - `durationMs` (integer) — Execution duration in milliseconds. - `status` (string) — `SUCCESS` or `FAILED`. - `errorMessage` (string) — Failure reason when `status` is `FAILED`; `null` otherwise. - `scheduledFireTime` (string) — ISO-8601 timestamp the event was scheduled to fire. - `actualFireTime` (string) — ISO-8601 timestamp the event actually fired. - `attemptNumber` (integer) — Retry attempt number (1 for the first attempt). - `payloadJson` (string) — JSON string of the action payload that was executed. - `responseJson` (string) — JSON string of the action's response. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/scheduled-events/history \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "page": 0, "month": 6, "year": 2026 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/scheduled-events/history', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ scheduledEventId: '3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c', page: 0, month: 6, year: 2026 }) } ); const data = await response.json(); console.log(`${data.successCount} succeeded, ${data.failedCount} failed`); ``` **Python:** ```python response = requests.post( "https://api.autessa.com/autessa_poweruser/scheduled-events/history", headers={"Authorization": os.environ["POWERUSER_KEY"]}, json={ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "page": 0, "month": 6, "year": 2026, }, ) data = response.json() print(f"{data['successCount']} succeeded, {data['failedCount']} failed") ``` ```json title="JSON" { "executions": [ { "id": "9c4d1e7f-2a3b-4c5d-8e9f-0a1b2c3d4e5f", "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "eventName": "Daily summary", "resourceType": "AGENT", "resourceId": "V1StGXR8Z5", "startedAt": "2026-06-30T09:00:01", "endedAt": "2026-06-30T09:00:14", "durationMs": 13204, "status": "SUCCESS", "errorMessage": null, "scheduledFireTime": "2026-06-30T09:00:00", "actualFireTime": "2026-06-30T09:00:01", "attemptNumber": 1, "payloadJson": "{\"agentExecutionRequest\":{\"agentId\":\"V1StGXR8Z5\"}}", "responseJson": "{\"output\":[{\"outputType\":\"TEXT\",\"content\":\"Summary sent.\"}]}" } ], "totalElements": 30, "totalPages": 1, "currentPage": 0, "pageSize": 50, "hasNext": false, "hasPrevious": false, "successCount": 29, "failedCount": 1, "successRate": 0.9666666666666667, "averageDurationMs": 12841.5 } ``` --- ## Delete Event **POST** `/delete` Deletes a scheduled event. The AWS schedule is removed first, then the event is soft-deleted (execution history is retained). Requires `ADMIN` access. The operation is idempotent with respect to the AWS schedule. ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID) to delete. ### Response - `eventId` (string) — The deleted event's ID. - `deletedAt` (string) — ISO-8601 timestamp of the deletion. - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/delete \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c" }' ``` ```json title="JSON" { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "deletedAt": "2026-07-01T15:42:07" } ``` --- ## Share Event **POST** `/share` Updates an event's sharing settings. Requires `ADMIN` access. Set `level` to `COMPANY` with an `accessLevel` to share company-wide, or `SPECIFIC_MEMBER` with `usersToAdd`/`usersToRemove` for individual grants, or `PRIVATE` to unshare. ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID) to share. - `level` (string, required) — Target sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `accessLevel` (string) — Company-wide access level when `level` is `COMPANY`: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` (array) — Users to grant access to. Each entry: `userEmail` (string), `userId` (string, e.g. `"uK4pT7vB1z"`), `accessLevel` (string). - `usersToRemove` (array) — Users whose access to revoke; same entry shape as `usersToAdd`. ### Response - `eventId` (string) — The event's ID. - `sharingLevel` (string) — The event's sharing level after the update. - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/share \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userEmail": "teammate@example.com", "userId": "uK4pT7vB1z", "accessLevel": "WRITE" } ], "usersToRemove": [] }' ``` ```json title="JSON" { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "sharingLevel": "SPECIFIC_MEMBER" } ``` --- ## Get Sharing Details **POST** `/get-sharing-details` Returns the current sharing configuration for an event. You must have at least READ access. ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID). ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Company-wide access level when shared with the company; `null` otherwise. - `sharedWith` (array) — Individual grants. Each entry: `userEmail` (string), `userId` (string), `accessLevel` (string). - `userAccessLevel` (string) — Your access level on the event. - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/get-sharing-details \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c" }' ``` ```json title="JSON" { "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": null, "sharedWith": [ { "userEmail": "teammate@example.com", "userId": "uK4pT7vB1z", "accessLevel": "WRITE" } ], "userAccessLevel": "ADMIN" } ``` --- ## Transfer Ownership **POST** `/transfer-ownership` Transfers ownership of an event to another user. Only the current owner can transfer, and the new owner must already have access to the event (share it with them first via [/share](#share-event-)). ### Request Body - `scheduledEventId` (string, required) — The event ID (UUID). - `newOwnerId` (string, required) — ID of the user receiving ownership, e.g. `"uK4pT7vB1z"`. ### Response - `eventId` (string) — The event's ID. - `newOwnerId` (string) — ID of the new owner. - `errors` (array) — Error messages; absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/scheduled-events/transfer-ownership \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "scheduledEventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "newOwnerId": "uK4pT7vB1z" }' ``` ```json title="JSON" { "eventId": "3f8a2c1e-9b4d-4a7e-b2c5-6d1e8f0a9b3c", "newOwnerId": "uK4pT7vB1z" } ``` --- ## Errors Failures return the `errors` array (`array` of human-readable messages); it is absent on success. Server-side exceptions are caught and folded into `errors` rather than surfacing as raw stack traces. When `errors` is present the HTTP status is non-2xx. - `401` — invalid or missing poweruser key, or insufficient access to the event (e.g. updating without WRITE, deleting or sharing without ADMIN, transferring when you are not the owner). - Validation failures (blank `eventName`, malformed `scheduleSpec`, missing/mismatched action config, cron expression not 6 fields, `once.at` in the past, invalid IANA timezone, UPDATE without `recordId` or `whereGroups`) return the specific message in `errors`. - Update/delete of a missing, deleted, or completed one-time event returns `errors` with the reason. ```json title="Error response" { "errors": [ "User does not have write access to this scheduled event" ] } ``` --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Scheduled Events client API](/docs/api/scheduled-events.md) - [Scheduled jobs guide](/docs/guide/scheduled-jobs.md) - [Poweruser Agents API](/docs/api/poweruser/agents.md) - [Poweruser Tools API](/docs/api/poweruser/tools.md) - [Poweruser AutessaDB Tables API](/docs/api/poweruser/autessadb-tables.md) --- # Custom Webhooks Source: https://docs.autessa.com/docs/api/poweruser/custom-webhooks > Poweruser endpoints for creating, configuring, sharing, and auditing custom webhook handlers. # Custom Webhooks Custom webhooks give you a public HTTPS endpoint that external systems (payment processors, CRMs, CI pipelines, form builders) can call to trigger a tool, agent, manager, or AutessaDB operation inside Autessa. This page covers **managing webhook definitions** — creating them, updating their configuration, rotating keys, sharing, and auditing deliveries — via the poweruser API. > This page manages webhook **definitions**. External systems **call** the webhook through the client-side execution endpoint (`/clients/custom-webhooks?resourceId={webhookId}`), which accepts any HTTP method and authenticates with the webhook's own `wh:` key. See the [Custom Webhooks execution API](/docs/api/custom-webhooks.md) for the trigger URL, payload format, and execution pipeline. --- ## Base URL ``` https://api.autessa.com/autessa_poweruser/custom-webhooks ``` All management endpoints are `POST` with a JSON body. Authenticate with your poweruser key in the `Authorization` header (no `Bearer` prefix): ``` Authorization: pwrusr:{id}:{secret} ``` --- ## The webhook object Every webhook returned by these endpoints has the same shape. - `id` (string) — Unique ID of the webhook. This is the `resourceId` external systems use in the trigger URL: `https://api.autessa.com/clients/custom-webhooks?resourceId={id}`. - `companyId` (string) — The company that owns the webhook. - `ownerId` (string) — The user who created the webhook. Only the owner can delete it. - `webhookConfig` (object) — The full webhook configuration. See [The webhookConfig object](#the-webhookconfig-object) below. - `apiKeyId` (string) — ID of the dedicated webhook API key. This matches the `{keyId}` segment of the webhook's `wh:{keyId}:{keySecret}` key. The secret itself is never returned here — only from [`/create`](#create-webhook-) and [`/refresh-api-key`](#refresh-api-key-). - `isEnabled` (boolean) — Whether the webhook accepts deliveries. Disabled webhooks reject trigger requests. - `sharingLevel` (string) — Visibility of the webhook: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted to everyone in the company when `sharingLevel` is `COMPANY`: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `createdAt` (string) — When the webhook was created, as an ISO-8601 UTC timestamp (e.g. `2026-06-30T18:22:41`). - `updatedAt` (string) — When the webhook was last updated, as an ISO-8601 UTC timestamp. - `userAccessLevel` (string) — The calling user's effective access to this webhook: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. ### The webhookConfig object - `name` (string, required) — Display name of the webhook. Required on create. - `description` (string) — What the webhook is for. - `validationConfig` (object) — Optional custom request validation. See [validationConfig](#validationconfig). - `payloadTransformConfig` (object) — How the incoming payload is turned into variables. See [payloadTransformConfig](#payloadtransformconfig). - `resourceExecutionConfig` (object) — Which resource runs when the webhook fires, and with what inputs. See [resourceExecutionConfig](#resourceexecutionconfig). - `executionSettings` (object) — Timeout and retry behavior. See [executionSettings](#executionsettings). ### validationConfig - `enableValidation` (boolean) — Whether to run the validation script on every delivery. Defaults to `false`. - `validationScript` (string) — AutessaScript that runs after key authentication. It receives `headers`, `body`, and `queryParams` as variables and must return a boolean — `false` rejects the delivery with `401`. Use this to verify sender signatures (e.g. HMAC). ### payloadTransformConfig - `transformType` (string) — `JSONPATH` (default) or `AUTESSA_SCRIPT`. - `jsonPathMappings` (object) — Map of variable name to JSONPath expression evaluated against the delivery body, e.g. `{"orderId": "$.order.id"}`. Used when `transformType` is `JSONPATH`. - `transformScript` (string) — AutessaScript for complex transformations. It receives `payload` (raw body string), `headers`, and `queryParams` as variables and must return a JSON object of variable mappings. Used when `transformType` is `AUTESSA_SCRIPT`. The extracted variables are substituted into the handler's configured inputs wherever a `@@{variableName}` reference appears. ### resourceExecutionConfig - `resourceType` (string) — Type of resource to execute: `TOOL`, `AGENT`, `MANAGER`, or `AUTESSADB`. - `resourceId` (string) — ID of the target tool, agent, or manager. Not used when `resourceType` is `AUTESSADB` — the target table is set in `autessaDbConfig.targetTableId` instead. - `variables` (object) — Tool input variables (`TOOL` only). Values support `@@{variableName}` references. - `environmentVariables` (object) — Environment variables for the execution (`TOOL`, `AGENT`, `MANAGER`). Values support `@@{variableName}` references. - `promptTemplateVariables` (object) — Prompt template variables (`AGENT`, `MANAGER`). Values support `@@{variableName}` references. - `privateVariables` (object) — Secret variables (`TOOL`). Not written to execution logs. Values support `@@{variableName}` references. - `userMessage` (string) — The user message sent to the agent or manager (`AGENT`, `MANAGER`). Supports `@@{variableName}` references. - `autessaDbConfig` (object) — Direct database operation configuration (`AUTESSADB` only). See [autessaDbConfig](#autessadbconfig). ### autessaDbConfig - `operation` (string) — The database operation: `INSERT`, `UPDATE`, `UPSERT`, or `DELETE`. - `targetTableId` (string) — ID of the target AutessaDB table. - `values` (object) — Column values to insert or update. Map of column name to a typed column-value object (`columnType` plus the matching value field, e.g. `textValue`, `integerValue`, `floatValue`, `booleanValue`, `multiSelectValue`, `filePath`, or `variableValue`). Values support `@@{variableName}` references. - `recordId` (string) — For single-row updates: the specific record ID to update. Mutually exclusive with `whereGroups`. Supports `@@{variableName}` references. - `whereGroups` (array) — For batch updates: WHERE condition groups matching the records to modify. Mutually exclusive with `recordId`. - `groupsLogicalOperator` (string) — Logical operator between WHERE groups: `AND` or `OR`. Only used with `whereGroups`. - `description` (string) — Optional description of what the operation does. ### executionSettings - `timeoutSeconds` (integer) — Execution timeout in seconds. Defaults to `300`. - `maxRetries` (integer) — Retry attempts on failure. Defaults to `3`. - `retryDelaySeconds` (integer) — Delay between retries in seconds. Defaults to `5`. ```json title="The webhook object" { "id": "V1StGXR8Z5", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xK9mQ2w", "webhookConfig": { "name": "Order events", "description": "Handles order.created events from the storefront", "validationConfig": { "enableValidation": false }, "payloadTransformConfig": { "transformType": "JSONPATH", "jsonPathMappings": { "orderId": "$.order.id", "eventType": "$.event.type" } }, "resourceExecutionConfig": { "resourceType": "AGENT", "resourceId": "pQ7wXr2LmN", "userMessage": "Process order @@{orderId} for event @@{eventType}", "environmentVariables": { "SOURCE": "storefront" }, "promptTemplateVariables": {} }, "executionSettings": { "timeoutSeconds": 300, "maxRetries": 3, "retryDelaySeconds": 5 } }, "apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678", "isEnabled": true, "sharingLevel": "PRIVATE", "companyPermissionLevel": null, "createdAt": "2026-06-30T18:22:41", "updatedAt": "2026-06-30T18:22:41", "userAccessLevel": "ADMIN" } ``` --- ## Create Webhook **POST** `/create` Creates a new webhook handler. The webhook is created **enabled**, and a dedicated webhook API key (named after the webhook) is generated for it. The response returns the full key value — `wh:{keyId}:{keySecret}` — **once**; only the key ID is stored retrievably, so save the key now. If you lose it, rotate it with [`/refresh-api-key`](#refresh-api-key-). The webhook's public trigger URL is derived from the returned `id`: ``` https://api.autessa.com/clients/custom-webhooks?resourceId={id} ``` The `webhookConfig.name` field is required; everything else in the configuration is optional and can be filled in later with [`/update`](#update-webhook-). ### Request Body - `webhookConfig` (object, required) — The webhook configuration. Must include `name`. See [The webhookConfig object](#the-webhookconfig-object). ### Response - `webhook` (object) — The created webhook. See [The webhook object](#the-webhook-object). - `apiKey` (string) — The full webhook API key (`wh:{keyId}:{keySecret}`). **Returned only in this response** — store it securely. - `errors` (array) — Array of error messages. Absent on success. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-webhooks/create \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "webhookConfig": { "name": "Order events", "description": "Handles order.created events from the storefront", "payloadTransformConfig": { "transformType": "JSONPATH", "jsonPathMappings": { "orderId": "$.order.id", "eventType": "$.event.type" } }, "resourceExecutionConfig": { "resourceType": "AGENT", "resourceId": "pQ7wXr2LmN", "userMessage": "Process order @@{orderId} for event @@{eventType}" } } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-webhooks/create', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ webhookConfig: { name: 'Order events', description: 'Handles order.created events from the storefront', payloadTransformConfig: { transformType: 'JSONPATH', jsonPathMappings: { orderId: '$.order.id', eventType: '$.event.type' } }, resourceExecutionConfig: { resourceType: 'AGENT', resourceId: 'pQ7wXr2LmN', userMessage: 'Process order @@{orderId} for event @@{eventType}' } } }) } ); const data = await response.json(); console.log(data.webhook.id, data.apiKey); // save the key! ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-webhooks/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'webhookConfig': { 'name': 'Order events', 'description': 'Handles order.created events from the storefront', 'payloadTransformConfig': { 'transformType': 'JSONPATH', 'jsonPathMappings': { 'orderId': '$.order.id', 'eventType': '$.event.type' } }, 'resourceExecutionConfig': { 'resourceType': 'AGENT', 'resourceId': 'pQ7wXr2LmN', 'userMessage': 'Process order @@{orderId} for event @@{eventType}' } } } ) data = response.json() print(data['webhook']['id'], data['apiKey']) # save the key! ``` ```json title="JSON" { "webhook": { "id": "V1StGXR8Z5", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xK9mQ2w", "webhookConfig": { "name": "Order events", "description": "Handles order.created events from the storefront", "payloadTransformConfig": { "transformType": "JSONPATH", "jsonPathMappings": { "orderId": "$.order.id", "eventType": "$.event.type" } }, "resourceExecutionConfig": { "resourceType": "AGENT", "resourceId": "pQ7wXr2LmN", "userMessage": "Process order @@{orderId} for event @@{eventType}" } }, "apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678", "isEnabled": true, "sharingLevel": "PRIVATE", "createdAt": "2026-06-30T18:22:41", "updatedAt": "2026-06-30T18:22:41", "userAccessLevel": "ADMIN" }, "apiKey": "wh:c1f2a3b4-5d6e-4789-9abc-0def12345678:sk_9f8e7d6c5b4a" } ``` --- ## Get Webhook **POST** `/get` Fetches a single webhook by ID. Requires at least `READ` access to the webhook. ### Request Body - `webhookId` (string, required) — ID of the webhook to fetch. ### Response - `webhook` (object) — The webhook. See [The webhook object](#the-webhook-object). - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-webhooks/get \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"webhookId": "V1StGXR8Z5"}' ``` ```json title="JSON" { "webhook": { "id": "V1StGXR8Z5", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xK9mQ2w", "webhookConfig": { "name": "Order events", "resourceExecutionConfig": { "resourceType": "AGENT", "resourceId": "pQ7wXr2LmN" } }, "apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678", "isEnabled": true, "sharingLevel": "PRIVATE", "createdAt": "2026-06-30T18:22:41", "updatedAt": "2026-06-30T18:22:41", "userAccessLevel": "ADMIN" } } ``` --- ## List Webhooks **POST** `/get-all` Lists every webhook the calling user can access in the current company — webhooks they own, webhooks shared with them directly, and company-shared webhooks. ### Request Body No fields — send an empty JSON object (`{}`). ### Response - `webhooks` (array) — All accessible webhooks. See [The webhook object](#the-webhook-object). - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-webhooks/get-all \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="JSON" { "webhooks": [ { "id": "V1StGXR8Z5", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xK9mQ2w", "webhookConfig": { "name": "Order events" }, "apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678", "isEnabled": true, "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "createdAt": "2026-06-30T18:22:41", "updatedAt": "2026-06-30T18:22:41", "userAccessLevel": "ADMIN" } ] } ``` --- ## Update Webhook **POST** `/update` Updates a webhook's configuration and/or toggles it on or off. Requires `WRITE` access or higher — `READ`-only users are rejected. The `webhookConfig` you send **replaces** the stored configuration, so send the complete config (fetch it with [`/get`](#get-webhook-), modify, and send it back). If `isEnabled` is omitted, the current enabled state is kept. ### Request Body - `webhookId` (string, required) — ID of the webhook to update. - `webhookConfig` (object) — The full replacement configuration. See [The webhookConfig object](#the-webhookconfig-object). - `isEnabled` (boolean) — Set `false` to pause deliveries, `true` to resume. Unchanged if omitted. ### Response - `webhook` (object) — The updated webhook. See [The webhook object](#the-webhook-object). - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-webhooks/update \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "webhookId": "V1StGXR8Z5", "webhookConfig": { "name": "Order events", "description": "Now also handles refund events", "payloadTransformConfig": { "transformType": "JSONPATH", "jsonPathMappings": { "orderId": "$.order.id", "eventType": "$.event.type" } }, "resourceExecutionConfig": { "resourceType": "AGENT", "resourceId": "pQ7wXr2LmN", "userMessage": "Process order @@{orderId} for event @@{eventType}" } }, "isEnabled": true }' ``` ```json title="JSON" { "webhook": { "id": "V1StGXR8Z5", "companyId": "kTn4vB2p9Q", "ownerId": "aB3xK9mQ2w", "webhookConfig": { "name": "Order events", "description": "Now also handles refund events" }, "apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678", "isEnabled": true, "sharingLevel": "PRIVATE", "createdAt": "2026-06-30T18:22:41", "updatedAt": "2026-07-01T09:15:03", "userAccessLevel": "ADMIN" } } ``` --- ## Delete Webhook **POST** `/delete` Permanently deletes a webhook. Only the webhook's **owner** can delete it — even `ADMIN`-level collaborators are rejected. External calls to the webhook's trigger URL stop working immediately. ### Request Body - `webhookId` (string, required) — ID of the webhook to delete. ### Response - `deleted` (boolean) — `true` if the webhook was deleted. - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-webhooks/delete \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"webhookId": "V1StGXR8Z5"}' ``` ```json title="JSON" { "deleted": true } ``` --- ## Refresh API Key **POST** `/refresh-api-key` Rotates the webhook's API key. The **old key is invalidated immediately** — any external system still sending it will start receiving `401` on the trigger URL — and a new full key value is generated and **returned once** in this response. Update the calling systems with the new key right away. Requires `ADMIN` access to the webhook. ### Request Body - `webhookId` (string, required) — ID of the webhook whose key to rotate. ### Response - `apiKey` (string) — The new webhook API key (`wh:{keyId}:{keySecret}`). **Returned only in this response** — store it securely. - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-webhooks/refresh-api-key \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"webhookId": "V1StGXR8Z5"}' ``` ```json title="JSON" { "apiKey": "wh:c1f2a3b4-5d6e-4789-9abc-0def12345678:sk_2b3c4d5e6f7a" } ``` --- ## Get Execution History **POST** `/get-execution-history` Returns the paginated delivery log for a webhook — every trigger request that passed authentication, with its status, timing, and full execution details (request payload, transformed variables, and the handler's request/response). Requires at least `READ` access to the webhook. Each entry's `executionId` matches the `executionId` returned to the caller by the [execution endpoint](/docs/api/custom-webhooks.md), so you can correlate a specific delivery end to end. ### Request Body - `webhookId` (string, required) — ID of the webhook whose history to fetch. - `status` (string) — Optional status filter: `QUEUED`, `PROCESSING`, `SUCCESS`, `FAILED`, or `TRANSFORM_FAILED`. Omit to return all statuses. - `page` (integer) — Zero-based page number. Defaults to `0`. - `size` (integer) — Page size. Defaults to `20`. ### Response - `history` (array) — Execution history entries, one per delivery attempt. See fields below. - `totalPages` (integer) — Total number of pages available. - `totalElements` (integer) — Total number of history entries matching the query. - `currentPage` (integer) — The zero-based page number returned. - `errors` (array) — Array of error messages. Absent on success. ### History entry fields - `id` (string) — Unique ID of the history record (a UUID string). - `executionId` (string) — Delivery correlation ID — the same value the execution endpoint returned to the external caller. - `webhookId` (string) — ID of the webhook that received the delivery. - `resourceType` (string) — Type of handler resource that ran: `TOOL`, `AGENT`, `MANAGER`, or `AUTESSADB`. - `resourceId` (string) — ID of the handler resource that ran. - `queuedAt` (string) — When the delivery was accepted and queued (ISO-8601 UTC timestamp). - `startedAt` (string) — When background execution started. Null while still queued. - `endedAt` (string) — When execution finished. Null until the run completes. - `durationMs` (integer) — Execution duration in milliseconds. - `status` (string) — Current status of the delivery: `QUEUED`, `PROCESSING`, `SUCCESS`, `FAILED`, or `TRANSFORM_FAILED`. - `attemptNumber` (integer) — Which retry attempt this entry represents (`1` for the first attempt). - `executionDetails` (object) — Full delivery detail. See fields below. ### executionDetails fields - `webhookName` (string) — Name of the webhook at the time of delivery. - `httpMethod` (string) — HTTP method the external system used (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`). - `sourceIp` (string) — IP address the delivery came from. - `requestHeaders` (string) — The delivery's HTTP headers, JSON-encoded. - `requestPayload` (string) — The raw request body as received. - `transformType` (string) — Transform that was applied: `JSONPATH` or `AUTESSA_SCRIPT`. - `transformedVariables` (string) — The variables extracted from the payload, JSON-encoded. - `transformError` (string) — Error message if the payload transform failed. Null otherwise. - `resourceRequestJson` (string) — The request sent to the handler resource, JSON-encoded. - `resourceResponseJson` (string) — The handler resource's response, JSON-encoded. - `errorMessage` (string) — Error message if execution failed. Null otherwise. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-webhooks/get-execution-history \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "webhookId": "V1StGXR8Z5", "status": "FAILED", "page": 0, "size": 20 }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-webhooks/get-execution-history', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': process.env.POWERUSER_KEY }, body: JSON.stringify({ webhookId: 'V1StGXR8Z5', status: 'FAILED', page: 0, size: 20 }) } ); const data = await response.json(); for (const entry of data.history) { console.log(entry.executionId, entry.status, entry.durationMs); } ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-webhooks/get-execution-history', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'webhookId': 'V1StGXR8Z5', 'status': 'FAILED', 'page': 0, 'size': 20 } ) data = response.json() for entry in data['history']: print(entry['executionId'], entry['status'], entry['durationMs']) ``` ```json title="JSON" { "history": [ { "id": "7d0a4c2e-91f3-4b6a-8c5d-1e2f3a4b5c6d", "executionId": "exec_f47ac10b58cc", "webhookId": "V1StGXR8Z5", "resourceType": "AGENT", "resourceId": "pQ7wXr2LmN", "queuedAt": "2026-07-01T08:30:12", "startedAt": "2026-07-01T08:30:13", "endedAt": "2026-07-01T08:30:19", "durationMs": 6114, "status": "FAILED", "attemptNumber": 1, "executionDetails": { "webhookName": "Order events", "httpMethod": "POST", "sourceIp": "203.0.113.42", "requestHeaders": "{\"content-type\": \"application/json\"}", "requestPayload": "{\"order\": {\"id\": \"ord_991\"}, \"event\": {\"type\": \"order.created\"}}", "transformType": "JSONPATH", "transformedVariables": "{\"orderId\": \"ord_991\", \"eventType\": \"order.created\"}", "transformError": null, "resourceRequestJson": "{\"agentId\": \"pQ7wXr2LmN\", \"input\": [...]}", "resourceResponseJson": null, "errorMessage": "Agent execution timed out after 300 seconds" } } ], "totalPages": 3, "totalElements": 47, "currentPage": 0 } ``` --- ## Share Webhook **POST** `/share` Changes who can see and manage a webhook. Requires `ADMIN` access, and the webhook must belong to your current company. Sharing has three levels: - `PRIVATE` — only the owner (and existing direct shares) can access it. - `COMPANY` — everyone in the company gets the access level in `accessLevel`. - `SPECIFIC_MEMBER` — the users in `usersToAdd` get their individual access levels. ### Request Body - `webhookId` (string, required) — ID of the webhook to share. - `level` (string, required) — Sharing level: `PRIVATE`, `COMPANY`, or `SPECIFIC_MEMBER`. - `accessLevel` (string) — Access level granted company-wide: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. Required when `level` is `COMPANY`. - `usersToAdd` (array) — Users to grant access to. Required when `level` is `SPECIFIC_MEMBER`. Each entry has `userEmail` (string), `userId` (string), and `accessLevel` (string). - `usersToRemove` (array) — Users to drop from access when editing an existing share. Same entry shape as `usersToAdd`. ### Response - `updated` (boolean) — `true` if the sharing settings were saved. - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-webhooks/share \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "webhookId": "V1StGXR8Z5", "level": "SPECIFIC_MEMBER", "usersToAdd": [ { "userEmail": "teammate@example.com", "userId": "mK4rT8vZ1x", "accessLevel": "WRITE" } ] }' ``` ```json title="JSON" { "updated": true } ``` --- ## Errors All endpoints return errors in a consistent shape. Exceptions are caught server-side and folded into the `errors` array of the response — when `errors` is present, the HTTP status is non-2xx. - `errors` (array) — Human-readable error messages, e.g. `"Webhook not found"`, `"Webhook must have a name in the configuration"`, `"Only the owner can delete this webhook"`, `"Only admins can refresh the API key"`. Common failure cases: - **401** — missing or invalid poweruser key, or the key lacks access to the target webhook (not-found and permission failures are both reported this way). - **Validation failures** — missing `webhookConfig`, or a config without a `name` on create. - **Permission failures** — `READ`-only users calling `/update`; non-owners calling `/delete`; non-`ADMIN` users calling `/refresh-api-key` or `/share`. --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Custom Webhooks execution API](/docs/api/custom-webhooks.md) — the public trigger endpoint external systems call - [Poweruser Agents API](/docs/api/poweruser/agents.md) - [Poweruser Tools API](/docs/api/poweruser/tools.md) - [Poweruser AutessaDB Tables API](/docs/api/poweruser/autessadb-tables.md) --- # API Keys Source: https://docs.autessa.com/docs/api/poweruser/keys > Poweruser endpoints for provisioning, scoping, rotating, and auditing client API keys — so a hosted app can mint the key its own background scripts use. # API Keys These endpoints let a poweruser credential — including the delegated temporary credential a hosted Custom App holds — create and manage client API keys: the scoped keys that call the client-side execution API (/clients/…). They mirror the Builder's API Management page one-for-one, which means an app can provision the key its triggers and scheduled scripts run under on first launch, rotate it, and revoke it, without a person ever seeing the secret. > Only **client** keys are managed here. Poweruser keys (`pwrusr:…`) are created, rotated and revoked from the Builder only; a delegated credential can never mint a full-scope poweruser key. Every request is capped to the caller by the same rules as the Builder: system keys are never creatable, and poweruser-linkage / end-user scoping may only name the caller unless the caller holds the `Admin` role. --- ## Base URL ``` https://api.autessa.com/autessa_poweruser/keys ``` All endpoints are `POST` with a JSON body. Authenticate with a poweruser key or temporary credential in the `Authorization` header (no `Bearer` prefix): ``` Authorization: pwrusr:{id}:{secret} ``` Hosted apps must be granted the endpoints they use in the app's **hostingPermissions → allowedPoweruserEndpoints** (`KEYS_CREATE`, `KEYS_GET`, `KEYS_ALL`, `KEYS_UPDATE`, `KEYS_DELETE`, `KEYS_ENABLE`, `KEYS_DISABLE`, `KEYS_REFRESH`, `KEYS_PERMISSIONS_ADD_GROUP`, `KEYS_PERMISSIONS_ADD_RESOURCE`, `KEYS_PERMISSIONS_DELETE_SPECIFIC`, `KEYS_PERMISSIONS_SET_ALL_FOR_TYPE`, `KEYS_PERMISSIONS_REMOVE_ALL_FOR_TYPE`, `KEYS_TRANSACTIONS_MONTHLY_STATS`). --- ## The API key object - `id` (string) — Opaque id of the key — the prefix before the `:` in the secret value. - `keyName` (string) — Display name shown in the key list. - `keyDescription` (string) — What the key is used for. - `allResources` (boolean) — When `true` the key may access every resource in the company and `permissions` is ignored. - `permissions` (array) — Per-resource-type grants: `{ resourceType: "TOOL" | "AGENT" | "MANAGER" | "AUTESSADB" | "CUSTOM_WEBHOOK" | "ESIGNATURE_ENVELOPE", resourceIds: string[] }`. An empty `resourceIds` acts as a wildcard for that type. - `isEnabled` (boolean) — A disabled key authenticates to nothing. - `rateLimitPerMinute` (integer) — Per-minute cap (default 60). - `rateLimitPerDay` (integer) — Per-day cap (default 5000; must be ≥ `rateLimitPerMinute`). - `allowedOrigins` (array) — Optional CORS allowlist; empty means unrestricted. - `metadata.autessaDbMetadata` (object) — `{ globalAllowedOperations: ["READ","WRITE","DELETE"], tablePermissions: { "": ["READ","WRITE"] } }` — per-table operation overrides win over the global set. An absent/empty list means all operations are allowed. ```json title="Scoped key for an app's background scripts" { "keyName": "support-internal-scripts", "keyDescription": "Used by the support desk's trigger and scheduled scripts.", "allResources": false, "isEnabled": true, "rateLimitPerMinute": 600, "rateLimitPerDay": 200000, "permissions": [ { "resourceType": "AUTESSADB", "resourceIds": ["fQMiNu10sCdT", "6m3BMDOhub7D"] }, { "resourceType": "TOOL", "resourceIds": ["DVb0iQNqSxOE"] } ], "metadata": { "autessaDbMetadata": { "globalAllowedOperations": ["READ"], "tablePermissions": { "fQMiNu10sCdT": ["READ", "WRITE"], "6m3BMDOhub7D": ["READ", "WRITE"] } } } } ``` --- ## Create Key **POST** `/create` Mints a new client key and returns its secret **exactly once**. ### Request Body - `apiKey` (object, required) — The desired configuration — see [The API key object](#the-api-key-object). `isSystemKey` is ignored (always `false`). ### Response - `keyValue` (string) — The full secret, formatted `{keyId}:{secret}`. Store it now; it is never returned again. - `apiKey` (object) — The stored configuration (without the secret). - `errors` (array) — Array of error messages. Absent on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/create \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKey": {"keyName": "support-internal-scripts", "allResources": false, "isEnabled": true}}' ``` ```json title="JSON" { "keyValue": "c1f2a3b4-5d6e-4789-9abc-0def12345678:Xk9…", "apiKey": { "id": "c1f2a3b4-5d6e-4789-9abc-0def12345678", "keyName": "support-internal-scripts", "isEnabled": true } } ``` --- ## Get Key **POST** `/get` Fetches one key's configuration (never the secret). Keys outside your company report as not found. ### Request Body - `apiKeyId` (string, required) — Id of the key. ### Response - `apiKey` (object) — See [The API key object](#the-api-key-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/get \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678"}' ``` --- ## List Keys **POST** `/all` Lists every client key in the company (secrets never included; the backend system key is omitted unless the caller is an Admin). Not paginated. ### Response - `apiKeys` (array) — The keys. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/all \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` --- ## Update Key **POST** `/update` Replaces the stored configuration wholesale — start from the entity returned by `/get`. Does not rotate the secret and does not itself grant per-resource permissions. ### Request Body - `apiKeyId` (string, required) — Id of the key. - `updatedApiKey` (object, required) — The complete desired configuration. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/update \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2…", "updatedApiKey": {"id": "c1f2…", "keyName": "support-internal-scripts", "rateLimitPerMinute": 120, "isEnabled": true}}' ``` --- ## Refresh Key **POST** `/refresh` Rotates the secret. The previous secret stops working the moment this succeeds; the new value is returned once. ### Request Body - `apiKeyId` (string, required) — Id of the key. ### Response - `keyValue` (string) — The new secret, `{keyId}:{secret}`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/refresh \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678"}' ``` --- ## Enable / Disable Key **POST** `/enable · /disable` `/disable` makes the key authenticate to nothing immediately (it is not deleted). `/enable` re-activates it with its existing secret. ### Request Body - `apiKeyId` (string, required) — Id of the key. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/disable \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678"}' ``` --- ## Delete Key **POST** `/delete` Permanently deletes a key. The key must already be disabled. ### Request Body - `apiKeyId` (string, required) — Id of the key. ### Response - `deleted` (boolean) — Whether the key was deleted. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/delete \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2a3b4-5d6e-4789-9abc-0def12345678"}' ``` --- ## Permissions **POST** `/permissions/…` Adjust a key's scoped grants. All are no-ops on a key with `allResources = true`. | Path | Body | Effect | |---|---|---| | `/permissions/add-resource` | `{ apiKeyId, resourceType, resourceId }` | Grant one resource. | | `/permissions/add-group` | `{ apiKeyId, permissions: { resourceType, resourceIds } }` | Grant a set of resources of one type. | | `/permissions/delete-specific` | `{ apiKeyId, resourceType, resourceId }` | Revoke one resource grant. | | `/permissions/set-all-for-type` | `{ apiKeyId, resourceType }` | Replace per-id grants with a wildcard for the type. | | `/permissions/remove-all-for-type` | `{ apiKeyId, resourceType }` | Revoke every grant of the type. | `resourceType` is one of `TOOL`, `AGENT`, `MANAGER`, `AUTESSADB`, `CUSTOM_WEBHOOK`, `ESIGNATURE_ENVELOPE`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/permissions/add-resource \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2…", "resourceType": "AUTESSADB", "resourceId": "fQMiNu10sCdT"}' ``` --- ## Monthly Usage **POST** `/transactions/monthly-stats` Aggregated request counts for a key in a given month, broken down by endpoint, agent, and tool. ### Request Body - `apiKeyId` (string, required) — Id of the key. - `year` (integer, required) — Four-digit year. - `month` (integer, required) — Month (1–12). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/keys/transactions/monthly-stats \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{"apiKeyId": "c1f2…", "year": 2026, "month": 8}' ``` --- # MCP Source: https://docs.autessa.com/docs/api/poweruser/mcp > Poweruser endpoints for registering MCP servers and inspecting their tools, resources, and prompts. # MCP Register remote MCP (Model Context Protocol) servers for your company, manage their connection and authentication settings, and inspect the tools, resources, and prompts each server exposes. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/mcp/servers`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. Registered servers are company-scoped: every member of the linked user's company sees the same server list. Once registered, a server can back an `MCP` agent (see [Agents](/docs/api/poweruser/agents.md)) or be turned into individual tools via the Tools API. --- ## The MCP server object - `id` (string) — Unique server identifier. - `companyId` (string) — The company that owns the registration. - `name` (string) — Display name. - `serverUrl` (string) — The remote MCP server's URL. - `description` (string) — What the server provides. - `authType` (string) — How Autessa authenticates to the server: `NONE`, `API_KEY`, `BEARER_TOKEN`, `BASIC_AUTH`, or `CUSTOM_HEADERS`. - `hasAuthConfig` (boolean) — Whether credentials are stored for this server. Responses expose only this boolean — the stored `authConfig` itself is **never** returned by any endpoint. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 timestamp of the last update. ```json title="MCP server object" { "id": "mS4kT8rW1x", "companyId": "cP2mK7dX4v", "name": "Internal Knowledge MCP", "serverUrl": "https://mcp.acme.internal/sse", "description": "Company wiki search and page retrieval.", "authType": "BEARER_TOKEN", "hasAuthConfig": true, "createdAt": "2026-05-12T09:30:00", "updatedAt": "2026-06-28T17:42:10" } ``` --- ## List all servers **POST** `/mcp/servers/all` Lists every MCP server registered for the company. ### Request parameters None — send an empty object `{}`. ### Response - `servers` (array) — The company's registered servers, each in the [MCP server object](#the-mcp-server-object) shape. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "servers": [ { "id": "mS4kT8rW1x", "companyId": "cP2mK7dX4v", "name": "Internal Knowledge MCP", "serverUrl": "https://mcp.acme.internal/sse", "authType": "BEARER_TOKEN", "hasAuthConfig": true, "createdAt": "2026-05-12T09:30:00", "updatedAt": "2026-06-28T17:42:10" } ], "errors": [] } ``` --- ## Get a server **POST** `/mcp/servers/get` Retrieves a single MCP server registration. ### Request parameters - `serverId` (string, required) — The server to fetch. ### Response - `server` (McpServer) — The [MCP server object](#the-mcp-server-object). Credentials are never included; check `hasAuthConfig` to see whether any are stored. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x" }' ``` ```json title="Response" { "server": { "id": "mS4kT8rW1x", "companyId": "cP2mK7dX4v", "name": "Internal Knowledge MCP", "serverUrl": "https://mcp.acme.internal/sse", "description": "Company wiki search and page retrieval.", "authType": "BEARER_TOKEN", "hasAuthConfig": true, "createdAt": "2026-05-12T09:30:00", "updatedAt": "2026-06-28T17:42:10" }, "errors": [] } ``` --- ## Register a server **POST** `/mcp/servers/create` Registers a new MCP server for the company. Credentials in `authConfig` are stored server-side and never returned by later reads — after creation the response only reports `hasAuthConfig: true`. ### Request parameters - `name` (string, required) — Display name. - `serverUrl` (string, required) — The remote server's URL. - `description` (string) — What the server provides. - `authType` (string) — `NONE`, `API_KEY`, `BEARER_TOKEN`, `BASIC_AUTH`, or `CUSTOM_HEADERS`. - `authConfig` (string) — JSON string with the auth details matching `authType` (for example a token, key/value header pairs, or username/password). Write-only. ### Response - `server` (McpServer) — The newly registered server. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/mcp/servers/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Internal Knowledge MCP", "serverUrl": "https://mcp.acme.internal/sse", "description": "Company wiki search and page retrieval.", "authType": "BEARER_TOKEN", "authConfig": "{\"token\": \"mcp-secret-token\"}" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/mcp/servers/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Internal Knowledge MCP', serverUrl: 'https://mcp.acme.internal/sse', description: 'Company wiki search and page retrieval.', authType: 'BEARER_TOKEN', authConfig: JSON.stringify({ token: 'mcp-secret-token' }) }) } ); const { server } = await response.json(); console.log(server.id); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/mcp/servers/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'name': 'Internal Knowledge MCP', 'serverUrl': 'https://mcp.acme.internal/sse', 'description': 'Company wiki search and page retrieval.', 'authType': 'BEARER_TOKEN', 'authConfig': json.dumps({'token': 'mcp-secret-token'}) } ) server = response.json()['server'] ``` ```json title="Response" { "server": { "id": "mS4kT8rW1x", "companyId": "cP2mK7dX4v", "name": "Internal Knowledge MCP", "serverUrl": "https://mcp.acme.internal/sse", "description": "Company wiki search and page retrieval.", "authType": "BEARER_TOKEN", "hasAuthConfig": true, "createdAt": "2026-07-01T10:15:00", "updatedAt": "2026-07-01T10:15:00" }, "errors": [] } ``` --- ## Update a server **POST** `/mcp/servers/update` Updates a server's registration. Send `authConfig` only when rotating credentials — it replaces the stored value and, like everywhere else, is never echoed back. ### Request parameters - `serverId` (string, required) — The server to update. - `name` (string) — New display name. - `serverUrl` (string) — New server URL. - `description` (string) — New description. - `authType` (string) — New auth type: `NONE`, `API_KEY`, `BEARER_TOKEN`, `BASIC_AUTH`, or `CUSTOM_HEADERS`. - `authConfig` (string) — Replacement JSON auth details. Write-only. ### Response - `server` (McpServer) — The updated server object. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x", "name": "Internal Knowledge MCP (prod)", "serverUrl": "https://mcp.acme.internal/sse" }' ``` ```json title="Response" { "server": { "id": "mS4kT8rW1x", "name": "Internal Knowledge MCP (prod)", "serverUrl": "https://mcp.acme.internal/sse", "authType": "BEARER_TOKEN", "hasAuthConfig": true, "updatedAt": "2026-07-01T11:02:33" }, "errors": [] } ``` --- ## Delete a server **POST** `/mcp/servers/delete` Removes the server registration. Agents or tools that referenced it stop being able to reach the server. ### Request parameters - `serverId` (string, required) — The server to delete. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Test the connection **POST** `/mcp/servers/test-connection` Connects to the server with its stored auth settings and reports whether the handshake succeeded, along with how many tools it advertises. Use this after registering or rotating credentials. ### Request parameters - `serverId` (string, required) — The server to test. ### Response - `connected` (boolean) — Whether the connection succeeded. - `message` (string) — Human-readable result (an error description when `connected` is `false`). - `toolCount` (integer) — Number of tools the server advertises, when connected. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/test-connection \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x" }' ``` ```json title="Response" { "connected": true, "message": "Connection successful", "toolCount": 7, "errors": [] } ``` --- ## List tools **POST** `/mcp/servers/list-tools` Queries the live server for the tools it exposes. ### Request parameters - `serverId` (string, required) — The server to query. ### Response - `tools` (array) — Tools the server advertises. Each has `name`, `description`, an `inputSchema` (JSON Schema for the tool's arguments), and optional `metadata` (used for synthetic tools derived from resources or prompts). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/list-tools \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x" }' ``` ```json title="Response" { "tools": [ { "name": "search_wiki", "description": "Full-text search over the company wiki.", "inputSchema": { "type": "object", "properties": { "query": { "type": "string" } }, "required": ["query"] }, "metadata": null } ], "errors": [] } ``` --- ## List resources **POST** `/mcp/servers/list-resources` Queries the live server for the resources it exposes. ### Request parameters - `serverId` (string, required) — The server to query. ### Response - `resources` (array) — Resources the server advertises. Each has `uri`, `name`, `description`, and `mimeType`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/list-resources \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x" }' ``` ```json title="Response" { "resources": [ { "uri": "wiki://pages/onboarding", "name": "Onboarding guide", "description": "New-hire onboarding wiki page.", "mimeType": "text/markdown" } ], "errors": [] } ``` --- ## List prompts **POST** `/mcp/servers/list-prompts` Queries the live server for the prompts it exposes. ### Request parameters - `serverId` (string, required) — The server to query. ### Response - `prompts` (array) — Prompts the server advertises. Each has `name`, `description`, and an `arguments` array of prompt argument definitions. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/mcp/servers/list-prompts \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "serverId": "mS4kT8rW1x" }' ``` ```json title="Response" { "prompts": [ { "name": "summarize_page", "description": "Summarize a wiki page for a given audience.", "arguments": [ { "name": "audience", "description": "Who the summary is for", "required": true } ] } ], "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (unknown server, unreachable URL). | | `401` | Invalid or missing poweruser key, or the server belongs to another company. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | Live-inspection endpoints (`test-connection`, `list-tools`, `list-resources`, `list-prompts`) depend on the remote server being reachable — connection failures come back as messages in `errors` (or `connected: false` for `test-connection`) rather than HTTP-level failures. --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) - [Tools (Poweruser API)](/docs/api/poweruser/tools.md) - [MCP servers guide](/docs/guide/agentic/mcp-servers.md) - [MCP tool types](/docs/tools/mcp.md) --- # Phone Numbers Source: https://docs.autessa.com/docs/api/poweruser/phone-numbers > Poweruser endpoints for finding and purchasing phone numbers, routing inbound calls to agents, managers, people, or keypad menus, setting recording and call limits, reading call history, turning on texting, deciding who answers texts, and sending and reading text messages through the shared inbox. # Phone Numbers Find and purchase phone numbers, wire inbound calls to your agents, tools, or managers — directly, through a press-a-digit menu, or through a call flow that can also ring people, follow business hours, and take voicemail — set recording, transcripts, and call limits, read call history, decide who answers texts, send and read text messages, and release numbers you no longer need. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/twilio`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. The typical flow is two calls: [`/purchase-number`](#purchase-a-phone-number) buys a number and returns a `configId`, then [`/configure-routing`](#configure-routing) points that number at a resource. Use [`/search-numbers`](#search-available-numbers) first if you want to pick the number yourself. A number routes one of three ways: - **`DIRECT`** — every inbound call goes straight to one resource (`directResourceType` + `directResourceId`). - **`PRESS_DIGIT`** — callers hear a menu and are routed by the digit they press, using `routingConfig.digitToResourceMap`. These numbers now run as a keypad-menu call flow. - **`FLOW`** — a [call flow](#update-the-call-flow) decides who answers: AI agents, people, a keypad menu, voicemail, or a mix, with business hours. Set it with [`/update-call-flow`](#update-the-call-flow). Separately, [`/update-call-settings`](#update-call-settings) controls voice and language, recording, transcripts, Call Redaction, call limits, caller-name lookup, blocked callers, AI call behavior, and answering machine detection for every call on the number, and [`/get-calls`](#list-calls) reads its call history. [`/audio-upload-link`](#get-an-audio-upload-link) uploads your own recordings for greetings and menus, and [`/voice-options`](#list-voice-options) lists the voices and languages you can pick. For texts, [`/update-text-flow`](#update-the-text-flow) decides who answers them (an AI agent or your team, with business hours, auto-replies, and keyword replies), [`/sms/threads`](#list-text-threads) and [`/sms/thread`](#get-a-text-thread) read the shared inbox, and [`/sms/send`](#send-a-text-message) replies. A number with nothing set up yet still takes messages: callers go to voicemail, and texts are saved to the shared inbox. See [Numbers with nothing set up](/docs/guide/agentic/phone-numbers.md#numbers-with-nothing-set-up). --- ## The phone config object - `configId` (string) — Unique phone configuration identifier. - `phoneNumber` (string) — The purchased number in E.164 format (for example `+12065550142`). - `twilioPhoneSid` (string) — The carrier's identifier for the number. - `routingType` (string) — `DIRECT`, `PRESS_DIGIT`, or `FLOW`. - `directResourceType` (string) — `DIRECT` routing — the resource kind answering calls: `AGENT`, `TOOL`, or `MANAGER`. - `directResourceId` (string) — `DIRECT` routing — the resource answering calls. - `directRoutingConfig` (object) — `DIRECT` routing — call settings: `environmentVariables`, `promptTemplateVariables`, `locallyHostedStt` (default `true`), `initialMessage`, `initialMessagePrompt`, `conferenceParticipants`, `clientDisplayMessage`. - `routingConfig` (object) — `PRESS_DIGIT` routing — `digitToResourceMap` (map of digit to a routing target with `type`, `id`, `environmentVariables`, `promptTemplateVariables`) and `locallyHostedStt`. - `callFlow` (CallFlow | null) — `FLOW` routing — the [call flow](#update-the-call-flow); `null` otherwise. - `callSettings` (CallSettings) — The number's [call settings](#update-call-settings). Always present; an empty object means every setting is at its default. - `callLogTableId` (string | null) — Your company's Phone calls AutessaDB table, or `null` until the first call creates it. The same table is shared by all your numbers. - `textFlow` (TextFlow | null) — How texts to the number are handled; see [`/update-text-flow`](#update-the-text-flow). `null` means the default. - `textLogTableId` (string | null) — Your company's [Text messages](/docs/guide/agentic/phone-numbers.md#the-text-messages-table) AutessaDB table, or `null` until the first text creates it. The same table is shared by all your numbers. - `automations` (object) — The number's [quick automations](#set-an-automation): `textBackMissedCalls` (`enabled`, `smsMessage`, `triggerId`, `toolId`), `emailVoicemails` (`enabled`, `emails`, `triggerId`, `toolId`), `textForwardSms` (`enabled`, `phones`, `triggerId`, `toolId`), and `textForwardEmail` (`enabled`, `emails`, `triggerId`, `toolId`). The ids point at the trigger and tool behind each one, or are `null` if it was never turned on. - `audioPreviewUrls` (object) — A map from each uploaded audio file the number uses (an `s3://` value from [`/audio-upload-link`](#get-an-audio-upload-link)) to a short-lived `https://` link you can play it from. Audio fields themselves come back exactly as you saved them. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide when shared with the company: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `isActive` (boolean) — Whether the number is live. Released numbers become inactive. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 timestamp of the last update. ```json title="Phone config object (DIRECT)" { "configId": "pH9nR3vC6b", "phoneNumber": "+12065550142", "twilioPhoneSid": "PNa1b2c3d4e5f60718293a4b5c6d7e8f90", "routingType": "DIRECT", "directResourceType": "AGENT", "directResourceId": "V1StGXR8Z5", "directRoutingConfig": { "environmentVariables": {}, "promptTemplateVariables": { "companyName": "Acme Corp" }, "locallyHostedStt": true, "initialMessage": "Thanks for calling Acme, how can I help?", "initialMessagePrompt": null, "conferenceParticipants": [], "clientDisplayMessage": null }, "routingConfig": null, "callFlow": null, "callSettings": { "transcript": { "save": true }, "recording": { "save": true, "announcement": "This call may be recorded." } }, "callLogTableId": "tB7xQ2mL9k", "textFlow": null, "textLogTableId": "tM3wK8pR5j", "automations": { "textBackMissedCalls": { "enabled": false, "smsMessage": null, "triggerId": null, "toolId": null }, "emailVoicemails": { "enabled": true, "emails": ["frontdesk@example.com"], "triggerId": "tR4kP8nW2q", "toolId": "tL6vH3cJ9d" }, "textForwardSms": { "enabled": false, "phones": [], "triggerId": null, "toolId": null }, "textForwardEmail": { "enabled": false, "emails": [], "triggerId": null, "toolId": null } }, "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "isActive": true, "createdAt": "2026-06-01T14:22:05", "updatedAt": "2026-06-28T17:42:10" } ``` --- ## Search available numbers **POST** `/twilio/search-numbers` Lists US numbers available to buy. Nothing is reserved — pass the number you pick to [`/purchase-number`](#purchase-a-phone-number) as `phoneNumber`. ### Request parameters - `areaCode` (string) — Three-digit US area code. - `contains` (string) — Digits the number should contain, for example `"555"`. - `limit` (integer) — How many numbers to return, 1–20 (default 10). ### Response - `numbers` (array) — Available numbers: `phoneNumber` (E.164), `locality`, `region`, and `capabilities` (`sms`, `voice`, `mms`). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/search-numbers \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "areaCode": "206", "limit": 5 }' ``` ```json title="Response (truncated)" { "numbers": [ { "phoneNumber": "+12065550142", "locality": "Seattle", "region": "WA", "capabilities": { "sms": true, "voice": true, "mms": true } } ], "errors": [] } ``` --- ## Purchase a phone number **POST** `/twilio/purchase-number` Gets a new US phone number. Numbers are free for calls, and no card is needed. Pick an exact number from [`/search-numbers`](#search-available-numbers), give just an area code, or send `{}` to let the platform choose. The number takes calls immediately. It starts without routing; configure it with [`/configure-routing`](#configure-routing). Texting is off until you turn it on with [`/sms/attach-number`](#turn-on-texting-for-a-number). ### Request parameters - `phoneNumber` (string) — The exact number to buy, in E.164 format, from `/search-numbers`. - `areaCode` (string) — Buy the first available number in this area code. - `idempotencyKey` (string) — Your own key (up to 64 characters). Retrying with the same key never buys twice. - `attachToSms` (boolean) — Also turn on texting for the number, exactly as [`/sms/attach-number`](#turn-on-texting-for-a-number) does — including its monthly texting fee and card requirement. ### Response - `success` (boolean) — Whether the purchase succeeded. - `configId` (string) — The new phone configuration — pass this to every other endpoint on this page. - `phoneNumber` (string) — The purchased number in E.164 format. - `twilioPhoneSid` (string) — The carrier's identifier for the number. - `message` (string) — Human-readable result. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/twilio/purchase-number \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/twilio/purchase-number', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({}) } ); const { configId, phoneNumber } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/twilio/purchase-number', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={} ) data = response.json() config_id = data['configId'] ``` ```json title="Response" { "success": true, "configId": "pH9nR3vC6b", "phoneNumber": "+12065550142", "twilioPhoneSid": "PNa1b2c3d4e5f60718293a4b5c6d7e8f90", "message": "Phone number purchased successfully", "errors": [] } ``` --- ## Configure routing **POST** `/twilio/configure-routing` Sets how inbound calls to a number are handled. For `DIRECT` routing, supply `directResourceType` and `directResourceId`; for `PRESS_DIGIT`, supply `routingConfig` with a `digitToResourceMap`. ### Request parameters - `configId` (string, required) — The phone configuration to update. - `routingType` (string, required) — `DIRECT` or `PRESS_DIGIT`. - `directResourceType` (string) — `DIRECT` — `AGENT`, `TOOL`, or `MANAGER`. - `directResourceId` (string) — `DIRECT` — the resource that answers calls. - `routingConfig` (object) — `PRESS_DIGIT` — `digitToResourceMap`: map of digit (`"1"`, `"2"`, …) to a target `{ type, id, environmentVariables, promptTemplateVariables }`, plus `locallyHostedStt`. - `locallyHostedStt` (boolean) — Use Autessa-hosted speech-to-text (default `true`). - `promptTemplateVariables` (object) — Values for the resource's `@@{variable}@@` prompt template placeholders. - `environmentVariables` (object) — Environment variables passed to tool executions triggered by the call. - `initialMessage` (string) — Static greeting spoken when an inbound call connects. - `initialMessagePrompt` (string) — Prompt sent to the LLM to generate a dynamic greeting when the call connects. Supports `@@{variableName}@@` placeholders. Takes the place of `initialMessage` when provided. - `conferenceParticipants` (array) — Additional phone numbers dialed into the conference when an inbound call starts; each participant joins on answer. - `clientDisplayMessage` (string) — Message shown to browser-client conference participants when they receive the call. ### Response - `success` (boolean) — Whether the routing was applied. - `message` (string) — Human-readable result. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/twilio/configure-routing \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "routingType": "DIRECT", "directResourceType": "AGENT", "directResourceId": "V1StGXR8Z5", "initialMessage": "Thanks for calling Acme, how can I help?", "promptTemplateVariables": { "companyName": "Acme Corp" } }' ``` **JavaScript:** ```javascript // PRESS_DIGIT menu: 1 → support agent, 2 → sales agent await fetch( 'https://api.autessa.com/autessa_poweruser/twilio/configure-routing', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ configId: 'pH9nR3vC6b', routingType: 'PRESS_DIGIT', routingConfig: { digitToResourceMap: { '1': { type: 'AGENT', id: 'V1StGXR8Z5' }, '2': { type: 'AGENT', id: 'kZ3mQ9pW2x' } }, locallyHostedStt: true } }) } ); ``` **Python:** ```python requests.post( 'https://api.autessa.com/autessa_poweruser/twilio/configure-routing', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'configId': 'pH9nR3vC6b', 'routingType': 'DIRECT', 'directResourceType': 'AGENT', 'directResourceId': 'V1StGXR8Z5', 'initialMessage': 'Thanks for calling Acme, how can I help?' } ) ``` ```json title="Response" { "success": true, "message": "Routing configured successfully", "errors": [] } ``` --- ## Update the call flow **POST** `/twilio/update-call-flow` Sets who answers the number: AI agents, people, a keypad menu, voicemail, or a mix, optionally with business hours. Setting a flow changes `routingType` to `FLOW`. Passing `callFlow: null` changes it back to `DIRECT`, and calls go to the number's direct resource again, which is kept while a flow is set. See [Who answers](/docs/guide/agentic/phone-numbers.md#who-answers) for how each destination behaves. The flow is checked before it's saved. Phone numbers must be in E.164 format, menu keys must be `0`–`9`, `*`, or `#`, times must be `HH:mm`, the time zone must be a valid name such as `America/New_York`, and you must be able to use every agent and multi-agent it names. A flow that fails a check returns `400` with a plain-language message. ### Request parameters - `configId` (string, required) — The phone configuration to update. - `callFlow` (CallFlow | null, required) — The flow, or `null` to go back to `DIRECT` routing. ### The call flow object - `greeting` (string) — Spoken to every caller first. - `greetingAudio` (string) — A recording to play instead of `greeting`: an `https://` link, or an `audioUri` from [`/audio-upload-link`](#get-an-audio-upload-link). - `businessHours` (object) — Optional. `timezone` (for example `America/Los_Angeles`), `weekly` (a list of `{ day, open, close }` with `day` from `MONDAY` to `SUNDAY` and times as `HH:mm`), `closedDates`, and `closedDestination` (a destination for calls outside those hours). Days not listed are closed all day. `closedDates` lists holidays and other days you're closed all day, each as `{ date, endDate, name }` with dates as `YYYY-MM-DD`. `endDate` is optional and makes an inclusive range, and `name` (such as `"Thanksgiving"`) is just a label. A closed date wins over `weekly`, so every call that day goes to `closedDestination`. - `destination` (CallDestination, required) — Where calls go (during business hours, if set). ### The destination object - `type` (string, required) — `AI_AGENT`, `AI_MULTI_AGENT`, `RING`, `MENU`, `VOICEMAIL`, or `HANG_UP`. - `message` (string) — Spoken before the destination runs, for example "Our office is closed today." - `messageAudio` (string) — A recording to play instead of `message`: an `https://` link, or an `audioUri` from [`/audio-upload-link`](#get-an-audio-upload-link). - `resourceId` (string) — `AI_AGENT` / `AI_MULTI_AGENT` — the agent or multi-agent that answers. It uses the AI call settings set with [`/configure-routing`](#configure-routing). - `ring` (object) — `RING` — rings people. See [the ring object](#the-ring-object). - `menu` (object) — `MENU` — `prompt` (the options read to the caller), `promptAudio` (a recording to play instead), `options` (a map of key to destination, for example `{ "1": {...}, "2": {...} }`), `timeoutSeconds` (how long to wait for a key after the prompt, `2` to `30`, default `6`), `invalidMessage` (spoken when the caller presses a key that isn't an option), `noInputMessage` (spoken when they press nothing in time), `retries` (how many times to repeat the prompt), and `noInput` (a destination for when the caller never presses a valid key). - `voicemail` (object) — `VOICEMAIL` — `greeting` (played before the beep), `greetingAudio` (a recording to play instead), `maxSeconds` (longest message, default `120`), `finishOnKey` (the key that ends the message, default `#`; `""` for none), `silenceSeconds` (seconds of silence that end the message, `1` to `30`, default `5`), and `transcribe` (default `true`). ### The ring object - `targets` (array, required) — Who to ring. Each target is `{ to, label, ringSeconds }`: `to` is a phone number in E.164 format or `client:` for a teammate's browser phone, `label` is an optional name such as `"Front desk"`, and `ringSeconds` overrides the ring time for that target. - `strategy` (string, required) — `SIMULTANEOUS` rings everyone at once and the first to answer gets the call; `SEQUENTIAL` rings one at a time, in order. - `ringSeconds` (integer) — How long each target rings when it has no `ringSeconds` of its own (default `20`). - `requireKeyPress` (boolean) — The person who answers must press 1 to accept the call (default `false`). Turn it on for cell phones, so a personal voicemail that picks up can't take the caller; the call moves on as if nobody answered. - `whisper` (string) — Spoken only to the person who answers, before they're connected, for example "Call from the sales line." - `callerId` (string) — What the rung phone shows: `BUSINESS_NUMBER` (default) or `CALLER`. If the carrier won't show the caller's number, the business number is shown. - `waitAudio` (string) — Played on a loop to the caller while phones ring, instead of a ringing tone: an `https://` link, or an `audioUri` from [`/audio-upload-link`](#get-an-audio-upload-link). - `maxTotalSeconds` (integer) — `SEQUENTIAL` only. Stop ringing after this many seconds in total, even if some targets haven't rung yet. - `noAnswer` (CallDestination) — Where the call goes when nobody answers. If it's not set, the caller goes to voicemail. ### Response The updated [phone config object](#the-phone-config-object). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/twilio/update-call-flow \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "callFlow": { "greeting": "Thanks for calling Acme.", "businessHours": { "timezone": "America/New_York", "weekly": [ { "day": "MONDAY", "open": "09:00", "close": "17:00" }, { "day": "FRIDAY", "open": "09:00", "close": "15:00" } ], "closedDates": [ { "date": "2026-11-26", "endDate": "2026-11-27", "name": "Thanksgiving" } ], "closedDestination": { "type": "AI_AGENT", "resourceId": "V1StGXR8Z5" } }, "destination": { "type": "MENU", "menu": { "prompt": "Press 1 for sales or 2 for support.", "timeoutSeconds": 8, "invalidMessage": "Sorry, that is not an option.", "retries": 2, "options": { "1": { "type": "RING", "ring": { "targets": [ { "to": "+14155550100", "label": "Sales desk" }, { "to": "+14155550101", "label": "Sam cell", "ringSeconds": 25 } ], "strategy": "SIMULTANEOUS", "ringSeconds": 20, "requireKeyPress": true, "whisper": "Call from the sales line.", "noAnswer": { "type": "VOICEMAIL" } } }, "2": { "type": "AI_AGENT", "resourceId": "kZ3mQ9pW2x" } }, "noInput": { "type": "VOICEMAIL" } } } } }' ``` **JavaScript:** ```javascript // back to DIRECT routing await fetch( 'https://api.autessa.com/autessa_poweruser/twilio/update-call-flow', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ configId: 'pH9nR3vC6b', callFlow: null }) } ); ``` --- ## Update call settings **POST** `/twilio/update-call-settings` Sets the number's voice and language, recording, transcripts, Call Redaction, call limits, caller-name lookup, blocked callers, AI call behavior, and answering machine detection. The settings apply to every call on it, incoming and outgoing, whether an agent or a person handles it. A call already in progress keeps the settings it started with. Calls placed with the [Make Phone Call tool](/docs/tools/phone-sms.md#settings-for-this-call) can override them for one call. ### Request parameters - `configId` (string, required) — The phone configuration to update. - `callSettings` (CallSettings, required) — The new settings. Sections you leave out use their defaults. ### The call settings object - `speech` (object) — The voice and language for everything the number says (greetings, messages, menus, voicemail greetings, the recording disclosure, the silence prompt, and transfer announcements). `voiceModel` and `voiceName` pick a voice, in the same shape as an agent's voice; leave them out to use the answering agent's voice, or a standard voice when no agent is involved. `language` is a language code such as `en-US` or `es-US`; on AI calls it also sets the language speech recognition listens for. See [`/voice-options`](#list-voice-options) for the values you can use. - `transcript` (object) — `save` — keep a transcript of each call (default `true`). `retentionDays` — delete each call's transcript and voicemail transcript this many days after the call; leave it out to keep them. - `recording` (object) — `save` — keep each call's audio (default `false`). `announcement` — the disclosure played before the call connects when recording; defaults to "This call may be recorded." and `""` plays nothing. `retentionDays` — delete each call's recording and voicemail recording this many days after the call; leave it out to keep them. Recording laws vary, and you're responsible for getting any consent they require. - `redaction` (object) — Call Redaction, which removes personal info from recordings and transcripts before they're saved. Billed at $0.01 per call. `enabled` (default `false`); `redact` — what to remove, any of `CARD_NUMBER`, `CARD_EXPIRY`, `CARD_CVV`, `SSN`, `BANK_ACCOUNT`, `ACCOUNT_NUMBER`, `DATE_OF_BIRTH`, `PHONE`, `EMAIL`, `ADDRESS` (default: all); `audioRedaction` — replace removed audio with `SILENCE` or `TONE`. It doesn't change the agent's conversation history, which still contains what was said live. - `limits` (object) — `maxDurationMinutes` — end calls after this long; `maxDurationWarning` — spoken before that limit; `maxDurationWarningSeconds` — how long before the limit the warning plays (`10` to `300`, default `30`); `silenceTimeoutSeconds` — end the call after this much silence; `silencePrompt` — asked before hanging up on silence (default "Are you still there?"); `silencePromptRepeats` — how many times it's asked before the call ends (`0` to `3`, default `1`; `0` hangs up without asking). - `callerInfo` (object) — `lookupCallerName` — look up incoming callers' names from carrier caller-ID records. The carrier charges a small fee per lookup, included in the call's price. - `screening` (object) — Blocks unwanted incoming calls. `blockedNumbers` — phone numbers in E.164 format that can't reach this number; `rejectAnonymous` — also block callers who hide their number (default `false`); `blockedMessage` — spoken to blocked callers before hanging up. Leave `blockedMessage` out and blocked calls get a busy signal without being answered. - `ai` (object) — How AI agents handle the conversation. `interruptions` — whether the caller can talk over the agent (default `true`); `interruptionSensitivity` — `LOW`, `MEDIUM` (default), or `HIGH`, how easily the caller's voice interrupts; `endOfTurn` — `FAST`, `NORMAL` (default), or `PATIENT`, how long the agent waits after the caller stops talking; `hangUpPhrases` — a list of phrases, and when the agent's reply contains one, the call ends after the reply plays. - `answeringMachine` (object) — Outgoing calls. `detection` — `OFF`, `ON` (tell people and answering machines apart), or `WAIT_FOR_BEEP` (also wait for the machine's beep before leaving a voicemail message); `timeoutSeconds` — how long to listen before deciding (`2` to `60`). Leave the section out and detection turns on by itself for calls that leave a voicemail message or generate their opening line. - `callLogTableId` (string) — Read-only. Your company's Phone calls table; ignored if sent. ### Response The updated [phone config object](#the-phone-config-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/update-call-settings \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "callSettings": { "speech": { "language": "en-US" }, "transcript": { "save": true, "retentionDays": 365 }, "recording": { "save": true, "announcement": "This call may be recorded for quality.", "retentionDays": 90 }, "redaction": { "enabled": true, "redact": ["CARD_NUMBER", "CARD_EXPIRY", "CARD_CVV", "SSN"], "audioRedaction": "TONE" }, "limits": { "maxDurationMinutes": 30, "maxDurationWarning": "We need to wrap up in a minute.", "maxDurationWarningSeconds": 60, "silenceTimeoutSeconds": 20, "silencePromptRepeats": 2 }, "callerInfo": { "lookupCallerName": true }, "screening": { "blockedNumbers": ["+15555550123"], "rejectAnonymous": true }, "ai": { "interruptionSensitivity": "LOW", "endOfTurn": "PATIENT", "hangUpPhrases": ["Have a great day"] } } }' ``` --- ## Update the text flow **POST** `/twilio/update-text-flow` Sets how texts to the number are handled: by an AI agent or with no AI, optionally with business hours, auto-replies, and keyword replies. Texts follow this setting whatever the number's call routing is. See [Texts](/docs/guide/agentic/phone-numbers.md#texts) for what each setting does. Passing `textFlow: null` goes back to the default: if the number sends calls straight to one AI agent, that agent answers texts; otherwise texts get no reply and are saved to the [shared inbox](/docs/guide/agentic/phone-numbers.md#the-shared-inbox) and the [Text messages table](/docs/guide/agentic/phone-numbers.md#the-text-messages-table). **STOP**, **START**, and **HELP** are always handled first for compliance, before keyword replies or anything else, and can't be changed here. The flow is checked before it's saved, the same way as a [call flow](#update-the-call-flow): times must be `HH:mm`, the time zone must be a valid name, and you must be able to use any agent it names. A flow that fails a check returns `400` with a plain-language message. ### Request parameters - `configId` (string, required) — The phone configuration to update. - `textFlow` (TextFlow | null, required) — The text flow, or `null` for the default. ### The text flow object - `keywordReplies` (array) — Fixed replies to specific words, each `{ keyword, reply }`. A text matches when the whole message equals `keyword`, ignoring capital letters and spaces around it. Checked before `open` or `closed`, at any hour and whoever answers. A matching text gets `reply` and goes no further. - `businessHours` (object | null) — Hours just for texts: `timezone`, `weekly`, and optional `closedDates`, in the same shape as a call flow's [`businessHours`](#the-call-flow-object), without `closedDestination` (use `closed` instead). Leave out for no hours. - `useCallFlowHours` (boolean) — `true` uses the business hours of the number's [call flow](#update-the-call-flow) instead of `businessHours`, so the two always match. - `open` (TextHandling, required) — How texts are handled during business hours, or at all times when there are no hours. - `closed` (TextHandling) — How texts are handled outside business hours. Leave out to handle them the same as `open`. ### The text handling object - `type` (string, required) — `AI_AGENT` (an agent answers) or `NO_AI` (texts wait in the shared inbox for your team). - `resourceId` (string) — `AI_AGENT` — the agent that answers. Leave out to use the agent the number sends calls to directly. - `autoReply` (object) — `NO_AI` only — an automatic reply: `message` (the text to send) and `onceEveryHours` (send it to the same person at most once in this many hours; default `24`, `0` replies to every text, at most `720`). ### Response - `config` (object) — The updated [phone config object](#the-phone-config-object), with the new `textFlow`. - `permission` (string) — Your permission level on the number. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/update-text-flow \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "textFlow": { "keywordReplies": [ { "keyword": "HOURS", "reply": "We are open Monday to Friday, 9 to 5." } ], "useCallFlowHours": true, "open": { "type": "NO_AI", "autoReply": { "message": "Thanks for your message. We will text you back shortly.", "onceEveryHours": 24 } }, "closed": { "type": "AI_AGENT", "resourceId": "V1StGXR8Z5" } } }' ``` --- ## List calls **POST** `/twilio/get-calls` Returns the number's calls, newest first, from your company's [Phone calls table](/docs/guide/agentic/phone-numbers.md#the-phone-calls-table). Incoming and outgoing calls are both included, with or without AI. ### Request parameters - `configId` (string, required) — The phone configuration whose calls to list. - `limit` (integer) — Calls per page, up to `50` (default `10`). - `after` (string) — The `next` value from the previous page. ### Response - `tableId` (string | null) — The Phone calls AutessaDB table, or `null` if no call has been logged yet. You can query it with the [AutessaDB API](/docs/api/poweruser/autessadb-tables.md). - `calls` (array) — The calls on this page. Each has `rowId` (its row in the table), `callId`, `direction` (`INBOUND` or `OUTBOUND`), `businessNumber`, `otherNumber`, `callerName`, `callerLocation`, `forwardedFrom`, `handledBy`, `conversationId` (the agent conversation, for AI calls), `startedAt` and `endedAt` (UTC, ISO-8601), `durationSeconds`, `status`, `answeredBy`, `endedBy`, `transferredTo`, `keypadResponse`, `transcript`, `recordingUrl`, `voicemailRecordingUrl`, `voicemailTranscript`, `redactionStatus`, and `processingError`. The recording links are valid for 10 minutes. See the guide for the [values of each column](/docs/guide/agentic/phone-numbers.md#the-phone-calls-table). - `next` (string | null) — Pass as `after` to get the next page; `null` on the last page. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/get-calls \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "limit": 1 }' ``` ```json title="Response" { "tableId": "tB7xQ2mL9k", "calls": [ { "rowId": "r8KdW2pQ5n", "callId": "c3Vt9LmX2a", "direction": "INBOUND", "businessNumber": "+12065550142", "otherNumber": "+14155550100", "callerName": "JANE DOE", "callerLocation": "San Francisco, CA, US", "forwardedFrom": null, "handledBy": "Front desk agent", "conversationId": "cv5Hn7Rt2w", "startedAt": "2026-10-09T17:02:11Z", "endedAt": "2026-10-09T17:05:48Z", "durationSeconds": 217, "status": "COMPLETED", "answeredBy": "HUMAN", "endedBy": "AGENT", "transferredTo": null, "keypadResponse": null, "transcript": "Agent: Thanks for calling Acme, how can I help?\nCaller: I'd like to move my appointment...", "recordingUrl": "https://...", "voicemailRecordingUrl": null, "voicemailTranscript": null, "redactionStatus": "DONE", "processingError": null } ], "next": "eyJzIjoiMjAyNi0xMC0wOVQxNzowMjoxMVoifQ" } ``` --- ## Set an automation **POST** `/twilio/set-automation` Turns one of the number's quick automations on or off. Each automation is a regular trigger on the Phone calls table (or, for forwarding texts, the Text messages table) that runs a regular tool, so you can see and edit it like any other. Turning one off disables its trigger without deleting it, and turning it back on reuses it. - **`TEXT_BACK_MISSED_CALLS`** — texts the caller from this number when an incoming call isn't answered, is busy, or goes to voicemail. The number must be able to text. - **`EMAIL_VOICEMAILS`** — emails the voicemail transcript and a link to the call when a caller leaves a voicemail. - **`FORWARD_TEXTS_SMS`** — texts every incoming text on to up to 5 phones, as "From +14155550100: " followed by the message. The forwarded texts are sent from this number, so it must be able to text and the phones must be allowed to receive texts from it (texting the number once from each phone is enough). Texts from those phones are never forwarded. - **`FORWARD_TEXTS_EMAIL`** — emails every incoming text to up to 5 people. ### Request parameters - `configId` (string, required) — The phone configuration. - `automation` (string, required) — `TEXT_BACK_MISSED_CALLS`, `EMAIL_VOICEMAILS`, `FORWARD_TEXTS_SMS`, or `FORWARD_TEXTS_EMAIL`. - `enabled` (boolean, required) — Turn the automation on or off. - `smsMessage` (string) — `TEXT_BACK_MISSED_CALLS` — the text to send. - `emails` (array) — `EMAIL_VOICEMAILS` and `FORWARD_TEXTS_EMAIL` — who gets the email, up to 5 addresses. - `phones` (array) — `FORWARD_TEXTS_SMS` — the phones to forward texts to, up to 5, in E.164 format. ### Response The updated [phone config object](#the-phone-config-object), with the automation's state in `automations`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/set-automation \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "automation": "TEXT_BACK_MISSED_CALLS", "enabled": true, "smsMessage": "Sorry we missed your call. We will call you back shortly." }' ``` --- ## Get an audio upload link **POST** `/twilio/audio-upload-link` Uploads your own recording, such as a greeting, menu prompt, voicemail greeting, or hold music, for use in a number's call flow. It takes three steps: 1. **Get a link.** Call this endpoint with the number and the file type. The response has an `uploadUrl`, an `audioUri`, and a `contentType`. 2. **Upload the file.** Send the audio file's bytes with an HTTP `PUT` to `uploadUrl`, with a `Content-Type` header set to exactly the `contentType` from step 1. Any other content type is refused. Upload before `expiresInSeconds` runs out, or get a new link. 3. **Use it.** Save `audioUri` into an audio field with [`/update-call-flow`](#update-the-call-flow): `greetingAudio`, `messageAudio`, a menu's `promptAudio`, a voicemail's `greetingAudio`, or a ring's `waitAudio`. Audio fields also accept your own `https://` links, so this step-by-step upload is only needed for files you don't host. When an audio field is set, it plays instead of the matching text. The [phone config object](#the-phone-config-object) returns a playable link for each uploaded file in `audioPreviewUrls`. ### Request parameters - `configId` (string, required) — The phone configuration the recording is for. - `fileType` (string, required) — `mp3` or `wav`. ### Response - `uploadUrl` (string) — Where to `PUT` the file. It expires after `expiresInSeconds`. - `audioUri` (string) — The value to save in an audio field once the upload is done. - `contentType` (string) — The `Content-Type` header to send with the upload, for example `audio/mpeg`. - `expiresInSeconds` (integer) — How long `uploadUrl` stays valid. ```bash title="1. Get a link" curl https://api.autessa.com/autessa_poweruser/twilio/audio-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "fileType": "mp3" }' ``` ```json title="Response" { "uploadUrl": "https://...", "audioUri": "s3://.../greeting.mp3", "contentType": "audio/mpeg", "expiresInSeconds": 900 } ``` ```bash title="2. Upload the file" curl "$UPLOAD_URL" \ -X PUT \ -H "Content-Type: audio/mpeg" \ --data-binary @greeting.mp3 ``` ```bash title="3. Use it as the greeting" curl https://api.autessa.com/autessa_poweruser/twilio/update-call-flow \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "callFlow": { "greeting": "Thanks for calling Acme.", "greetingAudio": "s3://.../greeting.mp3", "destination": { "type": "AI_AGENT", "resourceId": "V1StGXR8Z5" } } }' ``` --- ## List voice options **POST** `/twilio/voice-options` Lists the languages and voices you can use in a number's `speech` [call settings](#the-call-settings-object). It takes no parameters; send an empty JSON object. ### Response - `languages` (array) — The supported languages, each with `code` (the value for `speech.language`, such as `en-US`) and `label` (a readable name such as "English (US)"). - `voices` (array) — The voices you can pick, the same list agents offer, including your company's custom voices. Use a voice's model and name as `speech.voiceModel` and `speech.voiceName`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/voice-options \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` --- ## List all phone configs **POST** `/twilio/get-all-configs` Lists every phone configuration visible to the linked user. ### Request parameters None — send an empty object `{}`. ### Response - `configs` (array) — The phone configurations, each in the [phone config object](#the-phone-config-object) shape. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/get-all-configs \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "configs": [ { "configId": "pH9nR3vC6b", "phoneNumber": "+12065550142", "routingType": "DIRECT", "directResourceType": "AGENT", "directResourceId": "V1StGXR8Z5", "sharingLevel": "COMPANY", "isActive": true, "createdAt": "2026-06-01T14:22:05" } ], "errors": [] } ``` --- ## Get a phone config **POST** `/twilio/get-config` Retrieves one phone configuration, including its full routing details and the calling user's access level. ### Request parameters - `configId` (string, required) — The phone configuration to fetch. ### Response - `config` (PhoneConfig) — The [phone config object](#the-phone-config-object). - `permission` (string) — The calling user's access level on this config: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/get-config \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b" }' ``` ```json title="Response (truncated)" { "config": { "configId": "pH9nR3vC6b", "phoneNumber": "+12065550142", "routingType": "DIRECT", "directResourceType": "AGENT", "directResourceId": "V1StGXR8Z5", "isActive": true }, "permission": "ADMIN", "errors": [] } ``` --- ## Release a phone number **POST** `/twilio/release-number` Releases the number back to the carrier and deactivates the configuration. Inbound calls to the number stop working immediately, and the number cannot be recovered — buy a new one with [`/purchase-number`](#purchase-a-phone-number) if needed. Any monthly texting fee stops from the next month. A hosted number stops being hosted. A number linked from your own Twilio account is only unlinked — it stays in your Twilio account. ### Request parameters - `configId` (string, required) — The phone configuration whose number to release. ### Response - `success` (boolean) — Whether the release succeeded. - `message` (string) — Human-readable result. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/release-number \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b" }' ``` ```json title="Response" { "success": true, "message": "Phone number released successfully", "errors": [] } ``` --- ## Turn on texting for a number **POST** `/twilio/sms/attach-number` Turns on texting for a number by adding it to your company's [Text Messaging registration](/docs/guide/agentic/sms-setup.md). This starts the number's **monthly texting fee**, charged to your company's card on file — a card is required, and the call fails with `PAYMENT_METHOD_REQUIRED` without one. The number texts, and is billed, once the registration is approved; if it already is, that happens right away. The fee amount is shown on the number's page in the web app. Use [`/sms/detach-number`](#turn-off-texting-for-a-number) to turn texting off. ### Request parameters - `configId` (string, required) — The phone configuration to turn texting on for. ### Response - `success` (boolean) — Whether texting was turned on. - `message` (string) — Human-readable result. - `config` (PhoneConfig) — The number after the change, with its updated texting status. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/sms/attach-number \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b" }' ``` --- ## Turn off texting for a number **POST** `/twilio/sms/detach-number` Turns texting off for a number. The number keeps working for calls, and its monthly texting fee stops from the next month. ### Request parameters - `configId` (string, required) — The phone configuration to turn texting off for. ### Response Same shape as [`/sms/attach-number`](#turn-on-texting-for-a-number). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/sms/detach-number \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b" }' ``` --- ## Send a text message **POST** `/twilio/sms/send` Sends one text from a company number to a US number. The message reaches a real person, so it's checked first: your company needs a finished [Text Messaging registration](/docs/guide/agentic/sms-setup.md) (or the number comes from your own connected Twilio account), and the recipient needs an approved [consent record](/docs/guide/agentic/sms-consent.md) or a text to you within the last year. People who replied STOP are never texted. Requires write access to the phone configuration. A refused text still returns `200`: `success` is `false`, `errorCode` says why, and `errorMessage` explains it in plain language. The codes are the same as the [Send SMS tool's](/docs/tools/phone-sms.md#send-sms-). ### Request parameters - `configId` (string, required) — The phone configuration to text from. - `to` (string, required) — The recipient's US number; `(415) 555-0100` and `+14155550100` are both accepted. - `body` (string, required) — The message text, at most 1600 characters. ### Response - `success` (boolean) — Whether the carrier accepted the text. - `messageId` (string) — Autessa's ID for the logged message. - `twilioMessageSid` (string) — The carrier's identifier for the message. - `status` (string) — `QUEUED`, `ACCEPTED`, `SENDING`, or `SENT` when accepted; `BLOCKED` or `FAILED` when not sent. Delivery shows later in [`/sms/messages`](#list-text-messages). - `from` (string) — The sending number in E.164 format. - `to` (string) — The recipient in E.164 format. - `segments` (integer) — How many SMS segments the message used, when known. - `errorCode` (string) — Why the text wasn't sent — only when `success` is `false`. - `errorMessage` (string) — Plain-language explanation of `errorCode`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/sms/send \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "to": "+14155550100", "body": "Hi Sam, your order has shipped. Reply STOP to opt out." }' ``` ```json title="Response" { "success": true, "messageId": "mS4kT8wQ2n", "twilioMessageSid": "SMa1b2c3d4e5f60718293a4b5c6d7e8f90", "status": "QUEUED", "from": "+12065550142", "to": "+14155550100", "segments": 1, "errors": [] } ``` ```json title="Response (refused)" { "success": false, "errorCode": "CONSENT_REQUIRED", "errorMessage": "This person hasn't agreed to receive texts from you. Add a consent record for them in SMS Consent, or wait for them to text you first.", "errors": [] } ``` --- ## List text messages **POST** `/twilio/sms/messages` Lists a number's text messages, newest first — incoming texts, replies, and sends that were refused, with the reason. The other party's number is masked. Company owners can omit `configId` to list every company number. ### Request parameters - `configId` (string) — The phone configuration whose messages to list. - `pageSize` (integer) — Messages per page (default 25, at most 100). - `cursor` (string) — The previous page's `nextCursor`; omit for the first page. ### Response - `messages` (array) — The page of messages: `messageId`, `configId`, `direction` (`INBOUND` or `OUTBOUND`), `origin`, `status`, `ourNumber`, `counterparty` (masked), `body`, `segments`, `errorCode`, `errorMessage`, `twilioMessageSid`, `createdAt`, and `statusUpdatedAt`. - `nextCursor` (string) — Pass as `cursor` to get the next page; absent on the last page. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/sms/messages \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "pageSize": 25 }' ``` ```json title="Response (truncated)" { "messages": [ { "messageId": "mS4kT8wQ2n", "configId": "pH9nR3vC6b", "direction": "OUTBOUND", "origin": "SDK", "status": "DELIVERED", "ourNumber": "+12065550142", "counterparty": "+1415•••0100", "body": "Hi Sam, your order has shipped. Reply STOP to opt out.", "segments": 1, "createdAt": "2026-09-22T17:42:10Z" } ], "nextCursor": "eyJvZmZzZXQiOjI1fQ", "errors": [] } ``` --- ## List text threads **POST** `/twilio/sms/threads` Lists the number's [shared inbox](/docs/guide/agentic/phone-numbers.md#the-shared-inbox): one thread per person who has texted the number or been texted from it, with the most recent activity first. Read a thread's messages with [`/sms/thread`](#get-a-text-thread), and reply with [`/sms/send`](#send-a-text-message). ### Request parameters - `configId` (string, required) — The phone configuration whose inbox to list. - `limit` (integer) — Threads per page, up to `50` (default `20`). - `after` (string) — The `next` value from the previous page. ### Response - `threads` (array) — The threads on this page. Each has `otherNumber` (the person's number in E.164 format), `lastBody` and `lastDirection` (`INBOUND` or `OUTBOUND`) of the latest text, `lastAt` (UTC, ISO-8601), and `messageCount`. - `next` (string | null) — Pass as `after` to get the next page; `null` on the last page. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/sms/threads \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "limit": 20 }' ``` ```json title="Response" { "threads": [ { "otherNumber": "+14155550100", "lastBody": "Is the shop open Saturday?", "lastDirection": "INBOUND", "lastAt": "2026-10-09T17:02:11Z", "messageCount": 4 } ], "next": null } ``` --- ## Get a text thread **POST** `/twilio/sms/thread` Returns the texts between the number and one person, newest first. ### Request parameters - `configId` (string, required) — The phone configuration. - `otherNumber` (string, required) — The person's number, as returned in `otherNumber` by [`/sms/threads`](#list-text-threads). - `limit` (integer) — Messages per page, up to `200` (default `50`). - `before` (string) — The `next` value from the previous page, to load older messages. ### Response - `messages` (array) — The messages on this page. Each has `id`, `direction` (`INBOUND` or `OUTBOUND`), `body`, `sentAt` (UTC, ISO-8601), `status`, and `handledBy` (the agent's name, `Auto-reply`, `Keyword reply`, `Staff:` and the teammate's name, or `Automation`). - `next` (string | null) — Pass as `before` to get older messages; `null` when there are none. To reply, use [`/sms/send`](#send-a-text-message) with the same `configId` and `otherNumber` as `to`. The reply is sent as the business number and shows up in the thread with `handledBy` set to `Staff:` and your name. It's checked like any other text: the number must be able to text, and the person must not have opted out and must have texted you within the last year or have an approved consent record. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/twilio/sms/thread \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "configId": "pH9nR3vC6b", "otherNumber": "+14155550100", "limit": 2 }' ``` ```json title="Response" { "messages": [ { "id": "mT7rQ2kW9p", "direction": "OUTBOUND", "body": "Yes, 10 to 2 on Saturdays.", "sentAt": "2026-10-09T17:05:40Z", "status": "DELIVERED", "handledBy": "Staff: Dana Lee" }, { "id": "mS4kT8wQ2n", "direction": "INBOUND", "body": "Is the shop open Saturday?", "sentAt": "2026-10-09T17:02:11Z", "status": "RECEIVED", "handledBy": null } ], "next": "eyJiIjoiMjAyNi0xMC0wOVQxNzowMjoxMVoifQ" } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `success` and `errors` for operation-level failures (no numbers available, invalid routing target, a refused text). | | `400` | A call flow, text flow, call settings, or automation request failed a check (for example a phone number not in E.164 format, or an agent you can't use). The message says what to fix. | | `401` | Invalid or missing poweruser key, or the linked user lacks access to the config. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) - [SMS Consent (Poweruser API)](/docs/api/poweruser/sms-consent.md) - [Text Messaging setup](/docs/guide/agentic/sms-setup.md) - [Phone numbers guide](/docs/guide/agentic/phone-numbers.md) --- # SMS Consent Source: https://docs.autessa.com/docs/api/poweruser/sms-consent > Poweruser endpoints for recording and checking SMS messaging consent for A2P 10DLC compliance. # SMS Consent Record proof that a phone number's owner agreed to receive SMS from your company, check a number's consent status before texting it, and upload supporting evidence — the compliance layer behind agent-sent SMS. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/sms-consent`. Every request is a POST with a JSON body. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## The consent model US carriers require A2P 10DLC senders to hold documented consent for every number they message. In Autessa that works as follows: 1. **You record consent** with [`/sms-consent`](#create-a-consent-record) — the phone number, how consent was captured (`consentMethod`), a description, and optionally an evidence file (a screenshot of the web form, a signed document, a call recording) uploaded via [`/generate-upload-link`](#generate-an-evidence-upload-link). 2. **Autessa reviews it.** New records start unapproved; the Autessa compliance team approves them, or rejects them with `rejectionFeedback` explaining what to fix before resubmitting. 3. **You check before sending** with [`/search`](#search-consent-by-phone-number). A number is safe to message when `hasConsent` and `isApproved` are `true` and `isOptedOut` is `false`. 4. **Recipients can opt out** (for example by replying STOP). Opt-out is tracked per number and overrides any approval. Everything is scoped to the poweruser key's linked company — the company ID is derived from the key and cannot be overridden in a request, so you can only ever create or read consent records for your own company. Consent methods: `WEB_FORM` (submitted via a web form), `USER_INITIATED` (the user texted first — implicit consent), `VERBAL` (recorded verbal consent), `WRITTEN` (signed consent form), `OTHER`. --- ## Create a consent record **POST** `/sms-consent` Records consent for a phone number. Note the path: this is the **base** resource path with no suffix. The record starts unapproved and enters the compliance review queue; poll [`/search`](#search-consent-by-phone-number) to see when it is approved. ### Request parameters - `phoneNumber` (string, required) — The phone number consent applies to, in E.164 format (for example `+12065550142`). - `consentMethod` (string, required) — How consent was captured: `WEB_FORM`, `USER_INITIATED`, `VERBAL`, `WRITTEN`, or `OTHER`. - `consentDescription` (string) — What the person agreed to and how — shown to the reviewer. - `evidenceS3Uri` (string) — S3 URI of an uploaded evidence file from [`/generate-upload-link`](#generate-an-evidence-upload-link). - `termsOfServiceUrl` (string) — URL of the terms of service the person accepted. - `privacyPolicyUrl` (string) — URL of the privacy policy the person accepted. ### Response - `consentId` (string) — The new consent record. - `status` (string) — Human-readable status of the record. - `isApproved` (boolean) — Whether the record is approved. Always `false` immediately after creation — approval happens during compliance review. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/sms-consent \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "phoneNumber": "+12065550142", "consentMethod": "WEB_FORM", "consentDescription": "Opted in to order updates via the checkout form.", "evidenceS3Uri": "s3://autessa-files/company-cP2mK7dX4v/consent-8f3a.png", "termsOfServiceUrl": "https://acme.com/terms", "privacyPolicyUrl": "https://acme.com/privacy" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/sms-consent', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ phoneNumber: '+12065550142', consentMethod: 'WEB_FORM', consentDescription: 'Opted in to order updates via the checkout form.', termsOfServiceUrl: 'https://acme.com/terms', privacyPolicyUrl: 'https://acme.com/privacy' }) } ); const { consentId, isApproved } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/sms-consent', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'phoneNumber': '+12065550142', 'consentMethod': 'WEB_FORM', 'consentDescription': 'Opted in to order updates via the checkout form.', 'termsOfServiceUrl': 'https://acme.com/terms', 'privacyPolicyUrl': 'https://acme.com/privacy' } ) consent = response.json() ``` ```json title="Response" { "consentId": "sC7wJ2fN9q", "status": "PENDING_APPROVAL", "isApproved": false, "errors": [] } ``` --- ## Search consent by phone number **POST** `/sms-consent/search` Looks up the consent status of a phone number within your company. Call this before sending SMS: the number is messageable only when `hasConsent` and `isApproved` are `true` and `isOptedOut` is `false`. ### Request parameters - `phoneNumber` (string, required) — The phone number to look up, in E.164 format. ### Response - `hasConsent` (boolean) — Whether a consent record exists for this number. - `isApproved` (boolean) — Whether the record passed compliance review. - `isOptedOut` (boolean) — Whether the recipient has since opted out. Opt-out overrides approval. - `consentMethod` (string) — How consent was captured: `WEB_FORM`, `USER_INITIATED`, `VERBAL`, `WRITTEN`, or `OTHER`. - `consentedAt` (string) — ISO-8601 timestamp of when consent was recorded. - `rejectionFeedback` (string) — Reviewer feedback when the record was rejected — explains what to change before resubmitting. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/sms-consent/search \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "phoneNumber": "+12065550142" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/sms-consent/search', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ phoneNumber: '+12065550142' }) } ); const { hasConsent, isApproved, isOptedOut } = await response.json(); const canText = hasConsent && isApproved && !isOptedOut; ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/sms-consent/search', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'phoneNumber': '+12065550142'} ) data = response.json() can_text = data['hasConsent'] and data['isApproved'] and not data['isOptedOut'] ``` ```json title="Response" { "hasConsent": true, "isApproved": true, "isOptedOut": false, "consentMethod": "WEB_FORM", "consentedAt": "2026-06-15T10:04:31", "rejectionFeedback": null, "errors": [] } ``` --- ## Generate an evidence upload link **POST** `/sms-consent/generate-upload-link` Returns a pre-signed S3 URL for uploading a consent evidence file (a form screenshot, a signed document, and so on). Upload the file with an HTTP `PUT` to `uploadUrl` using the returned `mimeType` as `Content-Type`, then pass `s3Uri` as `evidenceS3Uri` when [creating the record](#create-a-consent-record). The S3 path is scoped to your company. ### Request parameters - `fileExtension` (string) — `"png"`, `"jpg"`, `"jpeg"`, or `"pdf"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to reference the uploaded file as `evidenceS3Uri`. - `mimeType` (string) — MIME type to send as `Content-Type` on the upload. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/sms-consent/generate-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileExtension": "png" }' # Then upload the evidence file: curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: image/png" \ --data-binary '@consent-form-screenshot.png' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/consent-8f3a.png", "mimeType": "image/png", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (missing or malformed `phoneNumber`, missing `consentMethod`). | | `401` | Invalid or missing poweruser key. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Phone Numbers (Poweruser API)](/docs/api/poweruser/phone-numbers.md) - [SMS consent guide](/docs/guide/agentic/sms-consent.md) - [Phone numbers guide](/docs/guide/agentic/phone-numbers.md) --- # E-Signature Envelopes Source: https://docs.autessa.com/docs/api/poweruser/esignature-envelopes > Poweruser endpoints for creating, sending, tracking, and downloading e-signature envelopes. # E-Signature Envelopes Drive the full document-signing lifecycle programmatically: upload a PDF, place fields, assign recipients to signer slots, send for signature, chase pending signers, and download the sealed result with its audit trail. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/e-signature`. Every request is a POST with a JSON body. Unlike most Poweruser resources, envelope, recipient, and field IDs are **UUID strings**; user IDs are the usual opaque ID strings. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## Envelope lifecycle An envelope is one PDF plus its recipients, fields, and audit trail. It moves through these statuses: | Status | Meaning | |--------|---------| | `DRAFT` | Being assembled — recipients, fields, and prefill values are editable. | | `SENT` | Signing links emailed; signers are working through it. Structure is frozen. | | `COMPLETED` | All required signatures collected; the sealed PDF and certificate of completion exist. | | `REJECTED` | A signer declined, with a reason recorded on the recipient. | | `VOIDED` | The owner cancelled it after sending. | | `EXPIRED` | Signing links passed their TTL before completion. | The typical build-and-send sequence: 1. [`/generate-file-upload-link`](#generate-a-file-upload-link), then `PUT` the PDF to the returned URL. 2. [`/envelope/create`](#create-an-envelope) with the returned `s3Uri`. 3. [`/envelope/recipients`](#set-recipients) to set signers, viewers, signing order, and completion policy. 4. [`/envelope/fields`](#set-fields) to place signature and input fields, and optionally [`/envelope/prefill`](#set-prefill-values) to fill `PREFILL` fields. 5. [`/envelope/send`](#send-an-envelope) to email signing links. ### Recipients and signer slots Each recipient is a `SIGNER` (assigned fields, must act) or a `VIEWER` (CC — emailed a read-only link and the sealed copy, never signs). Signers occupy **signer slots** via `signingGroup` (1-based): recipients sharing a slot form a client group, and any one of them signing satisfies the slot. A null `signingGroup` gives the recipient their own slot. Two envelope-level knobs control flow: - `signingOrder` — `PARALLEL` (everyone at once, default) or `SEQUENTIAL` (one at a time by `orderIndex`). - `completionPolicy` — `ALL` (every slot must be satisfied, default) or `ANY_ONE` (first signature completes and seals the envelope; remaining recipients become `NOT_REQUIRED`). `ANY_ONE` is only valid with `PARALLEL`. Recipient statuses: `PENDING`, `VIEWED`, `OTP_VERIFIED`, `SIGNED`, `REJECTED`, `EXPIRED`, `NOT_REQUIRED`, and `NOTIFIED` (terminal status for viewers). Signers authenticate by possession of their emailed signing link, which is the credential — there is no separate one-time-code step. `OTP_VERIFIED` and `otpVerifiedAt` are retained for backward compatibility and are not set by the current signing flow. ### Field placement Fields are placed by page and percentage coordinates (`pageIndex`, `xPct`, `yPct`, `widthPct`, `heightPct`), so layouts survive PDF scaling. Every field has a `fillSource`: - **`SIGNER`** — filled by a recipient at sign time. Requires `recipientId`. Types: `SIGNATURE`, `INITIALS`, `NAME`, `EMAIL`, `DATE_SIGNED`, `TEXT_INPUT`, `CHECKBOX`, `DROPDOWN`. - **`PREFILL`** — filled by you before sending, rendered into the PDF. Types: `TEXT_STATIC`, `DATE_STATIC`, `IMAGE_STATIC`. > Assign every signer at least one `SIGNER` field. Fields bound to a recipient > who has nothing to sign — or signers with no fields — are the most common > cause of envelopes that "complete" without the signature you expected. --- ## The envelope object Returned by create, get, recipients, fields, prefill, send, void, resend-link, and remind. - `id` (uuid) — Unique envelope identifier. - `companyId` (string) — Owning company. - `ownerUserId` (string) — The user who owns the envelope. - `title` (string) — Display title. - `status` (string) — `DRAFT`, `SENT`, `COMPLETED`, `REJECTED`, `VOIDED`, or `EXPIRED`. - `signingOrder` (string) — `PARALLEL` or `SEQUENTIAL`. - `completionPolicy` (string) — `ALL` or `ANY_ONE`. - `createdAt` (string) — ISO-8601 creation timestamp. - `sentAt` (string) — When signing links were emailed, or absent while `DRAFT`. - `completedAt` (string) — When the envelope completed. - `lastActivityAt` (string) — Timestamp of the most recent audit event. - `expiresAt` (string) — When outstanding signing links expire. - `sha256Original` (string) — SHA-256 of the uploaded PDF. - `sha256Prefilled` (string) — SHA-256 of the PDF after prefill rendering. - `sha256Sealed` (string) — SHA-256 of the sealed, completed PDF. - `viewPdfUrl` (string) — Presigned URL for the current-state PDF (original, prefilled, or sealed depending on status). - `recipients` (array) — Recipients: `id` (uuid), `email`, `name`, `orderIndex`, `signingGroup`, `recipientKind` (`SIGNER` or `VIEWER`), `status`, plus timestamps (`invitedAt`, `firstViewedAt`, `otpVerifiedAt`, `signedAt`, `rejectedAt`) and signing evidence (`rejectionReason`, `signerIp`, `signerUserAgent`, `signerCertSerial`). - `fields` (array) — Fields: `id` (uuid), `fillSource`, `recipientId`, `fieldType`, placement (`pageIndex`, `xPct`, `yPct`, `widthPct`, `heightPct`, `rotation`), `required`, `value`, `valueImageUrl` (presigned URL for image blobs), and font styling (`fontFamily`, `fontSize`, `fontColor`). - `auditTrail` (array) — Hash-chained audit events: `id`, `seq`, `eventType`, `recipientId`, `actorEmail`, `ip`, `userAgent`, `occurredAt`, `payloadJson`, `rowHash`. - `auditChainValid` (boolean) — Whether the audit-trail hash chain verifies. - `sharingLevel` (string) — Baseline visibility: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `shares` (array) — Per-user grants (`userId`, `userEmail`, `userName`, `accessLevel`, `grantedBy`, `grantedByName`, `createdAt`). The owner is not listed — they have implicit ADMIN. - `myAccessLevel` (string) — The calling user's effective access level: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `iAmOwner` (boolean) — Whether the calling user owns the envelope. ```json title="Envelope object (truncated)" { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "companyId": "cP2mK7dX4v", "ownerUserId": "uB7nT2wQ9k", "title": "Consulting Agreement — Acme Corp", "status": "SENT", "signingOrder": "PARALLEL", "completionPolicy": "ALL", "createdAt": "2026-06-29T15:10:02", "sentAt": "2026-06-29T16:00:41", "expiresAt": "2026-07-13T16:00:41", "sha256Original": "9c56cc51...", "viewPdfUrl": "https://s3.amazonaws.com/autessa-esign/...", "recipients": [ { "id": "7f2a4b1c-8d3e-4f5a-9b0c-1d2e3f4a5b6c", "email": "sam@acme.com", "name": "Sam Rivera", "orderIndex": 1, "signingGroup": 1, "recipientKind": "SIGNER", "status": "VIEWED", "invitedAt": "2026-06-29T16:00:41", "firstViewedAt": "2026-06-30T08:12:19" } ], "fields": [ { "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "fillSource": "SIGNER", "recipientId": "7f2a4b1c-8d3e-4f5a-9b0c-1d2e3f4a5b6c", "fieldType": "SIGNATURE", "pageIndex": 2, "xPct": 12.5, "yPct": 78.0, "widthPct": 30.0, "heightPct": 5.0, "required": true } ], "auditChainValid": true, "sharingLevel": "PRIVATE", "shares": [], "myAccessLevel": "ADMIN", "iAmOwner": true } ``` --- ## Generate a file upload link **POST** `/e-signature/generate-file-upload-link` Returns a pre-signed S3 URL for uploading the envelope's PDF. `PUT` the file bytes to `uploadUrl`, then pass the returned `s3Uri` to [`/envelope/create`](#create-an-envelope). ### Request parameters - `fileExtension` (string) — File extension — use `"pdf"`. ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here. - `s3Uri` (string) — S3 URI to pass as `s3Uri` on envelope or template creation. - `mimeType` (string) — MIME type to send as `Content-Type` on the upload. - `expiresInSeconds` (integer) — How long the upload URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/generate-file-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "fileExtension": "pdf" }' # Then upload the PDF: curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: application/pdf" \ --data-binary '@agreement.pdf' ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-files/...", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/file-9d1e.pdf", "mimeType": "application/pdf", "expiresInSeconds": 3600, "errors": [] } ``` --- ## Create an envelope **POST** `/e-signature/envelope/create` Registers a `DRAFT` envelope around a PDF already uploaded via [`/generate-file-upload-link`](#generate-a-file-upload-link). Add recipients and fields next, then send. To start from a reusable layout instead, see [template instantiation](/docs/api/poweruser/esignature-templates.md#create-an-envelope-from-a-template). ### Request parameters - `title` (string, required) — Envelope title, shown to signers and in emails. - `s3Uri` (string, required) — The S3 URI (or key) returned by the file-upload-link flow. ### Response - `envelope` (Envelope) — The new envelope in `DRAFT` status. See [the envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Consulting Agreement — Acme Corp", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/file-9d1e.pdf" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/e-signature/envelope/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ title: 'Consulting Agreement — Acme Corp', s3Uri: 's3://autessa-files/company-cP2mK7dX4v/file-9d1e.pdf' }) } ); const { envelope } = await response.json(); console.log(envelope.id, envelope.status); // DRAFT ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/e-signature/envelope/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'title': 'Consulting Agreement — Acme Corp', 's3Uri': 's3://autessa-files/company-cP2mK7dX4v/file-9d1e.pdf' } ) envelope = response.json()['envelope'] ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "title": "Consulting Agreement — Acme Corp", "status": "DRAFT", "signingOrder": "PARALLEL", "completionPolicy": "ALL", "recipients": [], "fields": [], "iAmOwner": true }, "errors": [] } ``` --- ## Get an envelope **POST** `/e-signature/envelope/get` Retrieves an envelope's full state — recipients, fields, audit trail, and a presigned URL for the current-state PDF. ### Request parameters - `envelopeId` (uuid, required) — The envelope to fetch. ### Response - `envelope` (Envelope) — The [envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13" }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "title": "Consulting Agreement — Acme Corp", "status": "SENT", "recipients": [ { "email": "sam@acme.com", "status": "VIEWED", "signingGroup": 1 } ], "auditChainValid": true }, "errors": [] } ``` --- ## List envelopes **POST** `/e-signature/envelope/list` Lists envelopes visible to the linked user, with filtering, search, sorting, and pagination. Entries are compact summaries — use [`/envelope/get`](#get-an-envelope) for full detail. ### Request parameters - `statusFilter` (array) — Restrict to these statuses (`DRAFT`, `SENT`, `COMPLETED`, `REJECTED`, `VOIDED`, `EXPIRED`). - `search` (string) — Title search string. - `sortBy` (string) — `"created"`, `"lastActivity"`, or `"title"`. Defaults to `lastActivity`. - `sortDir` (string) — `"asc"` or `"desc"`. Defaults to `desc`. - `page` (integer) — Page index. - `pageSize` (integer) — Entries per page. ### Response - `envelopes` (array) — Summaries: `id`, `title`, `status`, `completionPolicy`, lifecycle timestamps, `recipientCount`, `completedRecipientCount`, `requiredSignatureCount` and `collectedSignatureCount` (signer-slot progress), `recipientEmails`, `sharingLevel`, `myAccessLevel`, `iAmOwner`, `ownerUserId`. - `total` (integer) — Total envelopes across all pages. - `page` (integer) — The page returned. - `pageSize` (integer) — Entries per page. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "statusFilter": ["SENT"], "sortBy": "lastActivity", "sortDir": "desc", "page": 0, "pageSize": 20 }' ``` ```json title="Response" { "envelopes": [ { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "title": "Consulting Agreement — Acme Corp", "status": "SENT", "completionPolicy": "ALL", "recipientCount": 2, "completedRecipientCount": 0, "requiredSignatureCount": 2, "collectedSignatureCount": 0, "recipientEmails": ["sam@acme.com", "lee@acme.com"], "sharingLevel": "PRIVATE", "myAccessLevel": "ADMIN", "iAmOwner": true, "ownerUserId": "uB7nT2wQ9k" } ], "total": 14, "page": 0, "pageSize": 20, "errors": [] } ``` --- ## View the current PDF **POST** `/e-signature/envelope/view` Returns a presigned read URL for the envelope's current-state PDF — the original while drafting, the prefilled render once sent, or the sealed document when completed. ### Request parameters - `envelopeId` (uuid, required) — The envelope to view. ### Response - `pdfUrl` (string) — Time-boxed presigned URL for the PDF. - `expiresInSeconds` (integer) — How long the URL stays valid. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/view \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13" }' ``` ```json title="Response" { "pdfUrl": "https://s3.amazonaws.com/autessa-esign/...", "expiresInSeconds": 900, "errors": [] } ``` --- ## Set recipients **POST** `/e-signature/envelope/recipients` Replaces the envelope's recipient list and sets the signing order and completion policy. Include `id` on an entry to update an existing recipient; omit it to create a new one. ### Request parameters - `envelopeId` (uuid, required) — The envelope to update. - `signingOrder` (string) — `PARALLEL` or `SEQUENTIAL`. - `completionPolicy` (string) — `ALL` or `ANY_ONE`. Null means `ALL`. `ANY_ONE` requires `PARALLEL`. - `recipients` (array, required) — The full recipient list. - `recipients[].id` (uuid) — Present when updating an existing recipient; omit when adding. - `recipients[].email` (string, required) — Recipient email — signing links are sent here. - `recipients[].name` (string, required) — Recipient display name, affirmed at sign time. - `recipients[].orderIndex` (integer) — 1-based position for `SEQUENTIAL` signing; ignored for `PARALLEL`. - `recipients[].signingGroup` (integer) — Signer slot (1-based). Recipients sharing a slot form a client group — any one of them signing satisfies it. Null means own slot. - `recipients[].recipientKind` (string) — `SIGNER` (default) or `VIEWER`. ### Response - `envelope` (Envelope) — The updated [envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/recipients \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "signingOrder": "PARALLEL", "completionPolicy": "ALL", "recipients": [ { "email": "sam@acme.com", "name": "Sam Rivera", "signingGroup": 1, "recipientKind": "SIGNER" }, { "email": "legal@acme.com", "name": "Acme Legal", "recipientKind": "VIEWER" } ] }' ``` **JavaScript:** ```javascript await fetch( 'https://api.autessa.com/autessa_poweruser/e-signature/envelope/recipients', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ envelopeId: 'e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13', signingOrder: 'PARALLEL', recipients: [ { email: 'sam@acme.com', name: 'Sam Rivera', signingGroup: 1 }, // Same slot — a client group; either signature satisfies it { email: 'jordan@acme.com', name: 'Jordan Kim', signingGroup: 1 } ] }) } ); ``` **Python:** ```python requests.post( 'https://api.autessa.com/autessa_poweruser/e-signature/envelope/recipients', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'envelopeId': 'e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13', 'signingOrder': 'PARALLEL', 'recipients': [ {'email': 'sam@acme.com', 'name': 'Sam Rivera', 'signingGroup': 1} ] } ) ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "DRAFT", "recipients": [ { "id": "7f2a4b1c-8d3e-4f5a-9b0c-1d2e3f4a5b6c", "email": "sam@acme.com", "name": "Sam Rivera", "signingGroup": 1, "recipientKind": "SIGNER", "status": "PENDING" } ] }, "errors": [] } ``` --- ## Set fields **POST** `/e-signature/envelope/fields` **Fully replaces** the envelope's field set — send every field you want to keep, not just changes. Include `id` to preserve/update an existing field. ### Request parameters - `envelopeId` (uuid, required) — The envelope to update. - `fields` (array, required) — The complete field set. - `fields[].id` (uuid) — Optional on create, required when editing an existing field. - `fields[].fillSource` (string, required) — `SIGNER` or `PREFILL`. - `fields[].recipientId` (uuid) — Required when `fillSource` is `SIGNER` — the recipient who fills the field. - `fields[].fieldType` (string, required) — `SIGNER`: `SIGNATURE`, `INITIALS`, `NAME`, `EMAIL`, `DATE_SIGNED`, `TEXT_INPUT`, `CHECKBOX`, `DROPDOWN`. `PREFILL`: `TEXT_STATIC`, `DATE_STATIC`, `IMAGE_STATIC`. - `fields[].pageIndex` (integer, required) — Zero-based PDF page. - `fields[].xPct` (number, required) — Left edge as a percentage of page width. - `fields[].yPct` (number, required) — Top edge as a percentage of page height. - `fields[].widthPct` (number, required) — Width as a percentage of page width. - `fields[].heightPct` (number, required) — Height as a percentage of page height. - `fields[].rotation` (integer) — Rotation in degrees. - `fields[].required` (boolean, required) — Whether the signer must fill it. - `fields[].value` (string) — Static text (`PREFILL`) or baseline display value; for `DROPDOWN`, the options. - `fields[].valueImageS3Key` (string) — For `PREFILL` `IMAGE_STATIC` — S3 key uploaded via the file-upload-link flow. - `fields[].fontFamily` (string) — Render font. - `fields[].fontSize` (integer) — Render font size. - `fields[].fontColor` (string) — Render font color. ### Response - `envelope` (Envelope) — The updated [envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/fields \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "fields": [ { "fillSource": "SIGNER", "recipientId": "7f2a4b1c-8d3e-4f5a-9b0c-1d2e3f4a5b6c", "fieldType": "SIGNATURE", "pageIndex": 2, "xPct": 12.5, "yPct": 78.0, "widthPct": 30.0, "heightPct": 5.0, "required": true }, { "fillSource": "PREFILL", "fieldType": "TEXT_STATIC", "pageIndex": 0, "xPct": 20.0, "yPct": 30.5, "widthPct": 40.0, "heightPct": 3.0, "value": "Acme Corp" } ] }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "DRAFT", "fields": [ { "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "fieldType": "SIGNATURE" }, { "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e", "fieldType": "TEXT_STATIC" } ] }, "errors": [] } ``` --- ## Set prefill values **POST** `/e-signature/envelope/prefill` Populates `PREFILL` fields with values before sending — contract amounts, names, dates, or a static image (for example a company stamp). ### Request parameters - `envelopeId` (uuid, required) — The envelope to update. - `values` (array, required) — Values to set. - `values[].fieldId` (uuid, required) — The `PREFILL` field to populate. - `values[].value` (string) — The text or date value. - `values[].valueImageS3Key` (string) — For `IMAGE_STATIC` — S3 key of the uploaded PNG. ### Response - `envelope` (Envelope) — The updated [envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/prefill \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "values": [ { "fieldId": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e", "value": "$12,000.00" } ] }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "DRAFT" }, "errors": [] } ``` --- ## Send an envelope **POST** `/e-signature/envelope/send` Moves the envelope from `DRAFT` to `SENT`: renders prefill values into the PDF and emails each recipient a tokenized signing link. With `SEQUENTIAL` ordering only the first signer is invited; each completion invites the next. ### Request parameters - `envelopeId` (uuid, required) — The envelope to send. - `linkTtlDays` (integer) — Override the default signing-link TTL, in days. ### Response - `envelope` (Envelope) — The envelope, now `SENT`, with `sentAt` and `expiresAt` populated. - `errors` (array) — Error messages, if any occurred (for example no recipients, or a signer with no fields). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/send \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "linkTtlDays": 14 }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "SENT", "sentAt": "2026-06-29T16:00:41", "expiresAt": "2026-07-13T16:00:41" }, "errors": [] } ``` --- ## Resend a signing link **POST** `/e-signature/envelope/resend-link` Issues a fresh signing link to one recipient and emails it. Use when a link expired or the original email was lost. ### Request parameters - `envelopeId` (uuid, required) — The envelope. - `recipientId` (uuid, required) — The recipient to re-invite. ### Response - `envelope` (Envelope) — The current [envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/resend-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "recipientId": "7f2a4b1c-8d3e-4f5a-9b0c-1d2e3f4a5b6c" }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "SENT" }, "errors": [] } ``` --- ## Remind pending signers **POST** `/e-signature/envelope/remind` Sends reminder emails to recipients who have not yet signed. ### Request parameters - `envelopeId` (uuid, required) — The envelope. - `recipientIds` (array) — Recipients to remind. Null or empty reminds every pending recipient. ### Response - `envelope` (Envelope) — The current [envelope object](#the-envelope-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/remind \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13" }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "SENT" }, "errors": [] } ``` --- ## Void an envelope **POST** `/e-signature/envelope/void` Cancels a sent envelope. Outstanding signing links stop working and the envelope moves to `VOIDED`. The reason is recorded in the audit trail. ### Request parameters - `envelopeId` (uuid, required) — The envelope to void. - `reason` (string) — Why the envelope is being voided. ### Response - `envelope` (Envelope) — The envelope, now `VOIDED`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/void \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "reason": "Terms renegotiated; superseded by a new agreement." }' ``` ```json title="Response (truncated)" { "envelope": { "id": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "status": "VOIDED" }, "errors": [] } ``` --- ## Delete an envelope **POST** `/e-signature/envelope/delete` Permanently deletes an envelope. This is destructive and cannot be undone — to cancel a sent envelope while keeping its record, use [`/envelope/void`](#void-an-envelope) instead. ### Request parameters - `envelopeId` (uuid, required) — The envelope to delete. ### Response - `deleted` (boolean) — Whether the envelope was deleted. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13" }' ``` ```json title="Response" { "deleted": true, "errors": [] } ``` --- ## Download the sealed PDF **POST** `/e-signature/envelope/download` Returns a presigned download URL for the sealed PDF of a completed envelope — the final document with all signatures and the certificate of completion. ### Request parameters - `envelopeId` (uuid, required) — The completed envelope. ### Response - `url` (string) — Time-boxed presigned download URL. - `expiresInSeconds` (integer) — How long the URL stays valid. - `filename` (string) — Suggested filename for the download. - `errors` (array) — Error messages, if any occurred (for example the envelope is not completed). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/envelope/download \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "envelopeId": "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13" }' ``` ```json title="Response" { "url": "https://s3.amazonaws.com/autessa-esign/...", "expiresInSeconds": 900, "filename": "Consulting Agreement — Acme Corp (sealed).pdf", "errors": [] } ``` --- ## List the signing inbox **POST** `/e-signature/inbox/list` Lists envelopes where the **linked user is a recipient** — matched on email, independent of which company owns the envelope. This is the "waiting for my signature" view, complementing [`/envelope/list`](#list-envelopes) which shows envelopes you own or that are shared with you. ### Request parameters - `statusFilter` (array) — Restrict to these envelope statuses. - `page` (integer) — Page index. - `pageSize` (integer) — Entries per page. ### Response Same shape as [`/envelope/list`](#list-envelopes): `envelopes`, `total`, `page`, `pageSize`, `errors`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/inbox/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "statusFilter": ["SENT"], "page": 0, "pageSize": 20 }' ``` ```json title="Response (truncated)" { "envelopes": [ { "id": "f4c2d7b3-0a5e-4d9f-c8b2-3e6a9d0f1b24", "title": "NDA — Northwind Traders", "status": "SENT", "recipientCount": 1 } ], "total": 1, "page": 0, "pageSize": 20, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (unknown envelope, structural edits after send, `ANY_ONE` with `SEQUENTIAL`, download before completion). | | `401` | Invalid or missing poweruser key, or the linked user lacks access to the envelope. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [E-Signature Templates (Poweruser API)](/docs/api/poweruser/esignature-templates.md) - [Document signing guide](/docs/guide/platform/document-signing.md) --- # E-Signature Templates Source: https://docs.autessa.com/docs/api/poweruser/esignature-templates > Poweruser endpoints for building reusable e-signature templates and instantiating envelopes from them. # E-Signature Templates Turn a recurring document into a reusable template: fix the PDF and field layout once, define named roles instead of concrete signers, then stamp out ready-to-send envelopes by binding each role to real people. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/e-signature`. Every request is a POST with a JSON body. Template, role, and field IDs are **UUID strings**; user IDs are the usual opaque ID strings. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## Template → envelope instantiation A template is the same building blocks as an envelope — a PDF plus a field layout — with one difference: instead of recipients, fields bind to **roles** (for example "Client", "Contractor"). Roles carry the same signer-slot semantics as recipients (`orderIndex`, `signingGroup`). The lifecycle: 1. Upload the PDF via [`/generate-file-upload-link`](/docs/api/poweruser/esignature-envelopes.md#generate-a-file-upload-link) and register it with [`/template/create`](#create-a-template). 2. Define roles with [`/template/roles`](#set-roles) and place fields with [`/template/fields`](#set-fields), referencing `roleId` instead of `recipientId`. 3. Call [`/template/create-envelope`](#create-an-envelope-from-a-template) with a `roleAssignments` list binding each role to one or more real recipients. This clones the PDF and field layout into a normal `DRAFT` envelope — tweak it if needed, then send it via [`/envelope/send`](/docs/api/poweruser/esignature-envelopes.md#send-an-envelope). Instantiation copies; it does not link. Later edits to the template never affect envelopes already created from it. --- ## The template object Returned by create, get, set-roles, set-fields, share, and unshare. - `id` (uuid) — Unique template identifier. - `companyId` (string) — Owning company. - `ownerUserId` (string) — The user who owns the template. - `title` (string) — Display title. Envelopes created from the template inherit it unless overridden. - `description` (string) — What the template is for. - `signingOrder` (string) — Default signing order inherited by created envelopes: `PARALLEL` or `SEQUENTIAL`. - `completionPolicy` (string) — Default completion policy inherited by created envelopes: `ALL` or `ANY_ONE`. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `sha256Original` (string) — SHA-256 of the template PDF. - `viewPdfUrl` (string) — Time-boxed presigned URL for the template PDF. - `roles` (array) — Roles: `id` (uuid), `roleName`, `orderIndex`, `signingGroup` (roles sharing a slot form a client group once instantiated), `colorIndex` (used by the Builder's field editor). - `fields` (array) — Fields, mirroring envelope fields but bound to a `roleId` when `fillSource` is `SIGNER`: `id`, `fillSource`, `roleId`, `fieldType`, placement (`pageIndex`, `xPct`, `yPct`, `widthPct`, `heightPct`, `rotation`), `required`, `value`, `valueImageUrl`, and font styling (`fontFamily`, `fontSize`, `fontColor`). - `shares` (array) — Per-user grants (`userId`, `userEmail`, `userName`, `accessLevel`, `grantedBy`, `grantedByName`, `createdAt`). The owner is not listed — they have implicit ADMIN. - `myAccessLevel` (string) — The calling user's effective access level: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 timestamp of the last update. - `iAmOwner` (boolean) — Whether the calling user owns the template. ```json title="Template object (truncated)" { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "companyId": "cP2mK7dX4v", "ownerUserId": "uB7nT2wQ9k", "title": "Standard NDA", "description": "Mutual NDA for vendor engagements.", "signingOrder": "PARALLEL", "completionPolicy": "ALL", "sharingLevel": "COMPANY", "viewPdfUrl": "https://s3.amazonaws.com/autessa-esign/...", "roles": [ { "id": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "roleName": "Counterparty", "orderIndex": 1, "signingGroup": 1, "colorIndex": 0 } ], "fields": [ { "id": "f9e8d7c6-b5a4-4321-8f0e-1d2c3b4a5968", "fillSource": "SIGNER", "roleId": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "fieldType": "SIGNATURE", "pageIndex": 3, "xPct": 12.5, "yPct": 80.0, "widthPct": 30.0, "heightPct": 5.0, "required": true } ], "shares": [], "myAccessLevel": "ADMIN", "iAmOwner": true } ``` --- ## Create a template **POST** `/e-signature/template/create` Registers a template around a PDF already uploaded via [`/generate-file-upload-link`](/docs/api/poweruser/esignature-envelopes.md#generate-a-file-upload-link). ### Request parameters - `title` (string, required) — Template title. - `description` (string) — What the template is for. - `s3Uri` (string, required) — The S3 URI (or key) returned by the file-upload-link flow. - `signingOrder` (string) — Default signing order for created envelopes; null means `PARALLEL`. - `completionPolicy` (string) — Default completion policy for created envelopes; null means `ALL`. ### Response - `template` (Template) — The new [template object](#the-template-object). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/e-signature/template/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Standard NDA", "description": "Mutual NDA for vendor engagements.", "s3Uri": "s3://autessa-files/company-cP2mK7dX4v/file-4c8b.pdf", "signingOrder": "PARALLEL", "completionPolicy": "ALL" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/e-signature/template/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ title: 'Standard NDA', description: 'Mutual NDA for vendor engagements.', s3Uri: 's3://autessa-files/company-cP2mK7dX4v/file-4c8b.pdf' }) } ); const { template } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/e-signature/template/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'title': 'Standard NDA', 'description': 'Mutual NDA for vendor engagements.', 's3Uri': 's3://autessa-files/company-cP2mK7dX4v/file-4c8b.pdf' } ) template = response.json()['template'] ``` ```json title="Response (truncated)" { "template": { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "title": "Standard NDA", "signingOrder": "PARALLEL", "completionPolicy": "ALL", "roles": [], "fields": [], "iAmOwner": true }, "errors": [] } ``` --- ## Get a template **POST** `/e-signature/template/get` Retrieves a template's full detail: roles, field layout, sharing, and a presigned URL for the PDF. ### Request parameters - `templateId` (uuid, required) — The template to fetch. ### Response - `template` (Template) — The [template object](#the-template-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f" }' ``` ```json title="Response (truncated)" { "template": { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "title": "Standard NDA", "roles": [ { "id": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "roleName": "Counterparty" } ], "myAccessLevel": "ADMIN" }, "errors": [] } ``` --- ## List templates **POST** `/e-signature/template/list` Lists templates visible to the linked user, with search and pagination. ### Request parameters - `search` (string) — Title search string. - `page` (integer) — Page index. - `pageSize` (integer) — Entries per page. ### Response - `templates` (array) — Summaries: `id`, `title`, `description`, `roleCount`, `fieldCount`, `sharingLevel`, `myAccessLevel`, `createdAt`, `updatedAt`, `iAmOwner`, `ownerUserId`. - `total` (integer) — Total templates across all pages. - `page` (integer) — The page returned. - `pageSize` (integer) — Entries per page. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "search": "NDA", "page": 0, "pageSize": 20 }' ``` ```json title="Response" { "templates": [ { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "title": "Standard NDA", "description": "Mutual NDA for vendor engagements.", "roleCount": 1, "fieldCount": 4, "sharingLevel": "COMPANY", "myAccessLevel": "ADMIN", "iAmOwner": true, "createdAt": "2026-05-02T11:00:00", "updatedAt": "2026-06-20T09:45:12" } ], "total": 3, "page": 0, "pageSize": 20, "errors": [] } ``` --- ## Set roles **POST** `/e-signature/template/roles` Replaces the template's role list. Include `id` on an entry to update an existing role; omit it to create a new one. Fields reference roles, so removing a role a field points at invalidates that field binding. ### Request parameters - `templateId` (uuid, required) — The template to update. - `roles` (array, required) — The full role list. - `roles[].id` (uuid) — Present when updating an existing role; omit when adding. - `roles[].roleName` (string, required) — Role name, for example `"Client"` or `"Contractor"`. - `roles[].orderIndex` (integer) — 1-based position for `SEQUENTIAL` signing. - `roles[].signingGroup` (integer) — Signer slot (1-based); roles sharing a slot form a client group once instantiated. Null means own slot. - `roles[].colorIndex` (integer) — Color used to render the role's fields in the Builder editor. ### Response - `template` (Template) — The updated [template object](#the-template-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/roles \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "roles": [ { "roleName": "Counterparty", "orderIndex": 1, "signingGroup": 1 }, { "roleName": "Acme Signatory", "orderIndex": 2, "signingGroup": 2 } ] }' ``` ```json title="Response (truncated)" { "template": { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "roles": [ { "id": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "roleName": "Counterparty" }, { "id": "r2b3c4d5-e6f7-4a8b-9c0d-1e2f3a4b5c6d", "roleName": "Acme Signatory" } ] }, "errors": [] } ``` --- ## Set fields **POST** `/e-signature/template/fields` **Fully replaces** the template's field set. Identical to [envelope field placement](/docs/api/poweruser/esignature-envelopes.md#set-fields), except `SIGNER` fields reference a `roleId` instead of a `recipientId`. ### Request parameters - `templateId` (uuid, required) — The template to update. - `fields` (array, required) — The complete field set. - `fields[].id` (uuid) — Optional on create, required when editing an existing field. - `fields[].fillSource` (string, required) — `SIGNER` or `PREFILL`. - `fields[].roleId` (uuid) — Required when `fillSource` is `SIGNER` — the role that fills the field. - `fields[].fieldType` (string, required) — `SIGNER`: `SIGNATURE`, `INITIALS`, `NAME`, `EMAIL`, `DATE_SIGNED`, `TEXT_INPUT`, `CHECKBOX`, `DROPDOWN`. `PREFILL`: `TEXT_STATIC`, `DATE_STATIC`, `IMAGE_STATIC`. - `fields[].pageIndex` (integer, required) — Zero-based PDF page. - `fields[].xPct` (number, required) — Left edge as a percentage of page width. - `fields[].yPct` (number, required) — Top edge as a percentage of page height. - `fields[].widthPct` (number, required) — Width as a percentage of page width. - `fields[].heightPct` (number, required) — Height as a percentage of page height. - `fields[].rotation` (integer) — Rotation in degrees. - `fields[].required` (boolean, required) — Whether the signer must fill it. - `fields[].value` (string) — Static text (`PREFILL`) or baseline display value. - `fields[].valueImageS3Key` (string) — For `PREFILL` `IMAGE_STATIC` — S3 key uploaded via the file-upload-link flow. - `fields[].fontFamily` (string) — Render font. - `fields[].fontSize` (integer) — Render font size. - `fields[].fontColor` (string) — Render font color. ### Response - `template` (Template) — The updated [template object](#the-template-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/fields \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "fields": [ { "fillSource": "SIGNER", "roleId": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "fieldType": "SIGNATURE", "pageIndex": 3, "xPct": 12.5, "yPct": 80.0, "widthPct": 30.0, "heightPct": 5.0, "required": true }, { "fillSource": "SIGNER", "roleId": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "fieldType": "DATE_SIGNED", "pageIndex": 3, "xPct": 55.0, "yPct": 80.0, "widthPct": 20.0, "heightPct": 3.0, "required": true } ] }' ``` ```json title="Response (truncated)" { "template": { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "fields": [ { "id": "f9e8d7c6-b5a4-4321-8f0e-1d2c3b4a5968", "fieldType": "SIGNATURE" }, { "id": "f0f9e8d7-c6b5-4432-9a1f-2e3d4c5b6a79", "fieldType": "DATE_SIGNED" } ] }, "errors": [] } ``` --- ## Create an envelope from a template **POST** `/e-signature/template/create-envelope` Instantiates a `DRAFT` envelope from the template: clones the PDF and field layout, and binds each role to real recipients. The result is a normal envelope — adjust it if needed, then send it via [`/envelope/send`](/docs/api/poweruser/esignature-envelopes.md#send-an-envelope). Each role assignment takes one **primary** recipient first, then optional a **client group** sharing the role's signer slot — any one of them signing satisfies it. ### Request parameters - `templateId` (uuid, required) — The template to instantiate. - `title` (string) — Overrides the template title for the created envelope. - `roleAssignments` (array, required) — One entry per template role. - `roleAssignments[].roleId` (uuid, required) — The template role being bound. - `roleAssignments[].recipients` (array, required) — Primary recipient first, then the rest of the client group. At least one required. Each entry: `email`, `name`, and optionally `recipientKind` (`SIGNER` or `VIEWER`). ### Response - `envelope` (Envelope) — The new `DRAFT` envelope — see [the envelope object](/docs/api/poweruser/esignature-envelopes.md#the-envelope-object). - `errors` (array) — Error messages, if any occurred (for example an unassigned role). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/e-signature/template/create-envelope \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "title": "NDA — Northwind Traders", "roleAssignments": [ { "roleId": "r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c", "recipients": [ { "email": "dana@northwind.com", "name": "Dana Fuentes" } ] } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/e-signature/template/create-envelope', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ templateId: 't8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f', title: 'NDA — Northwind Traders', roleAssignments: [ { roleId: 'r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c', recipients: [ { email: 'dana@northwind.com', name: 'Dana Fuentes' }, // Client group — shares the role's signer slot { email: 'cfo@northwind.com', name: 'Ed Silva' } ] } ] }) } ); const { envelope } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/e-signature/template/create-envelope', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'templateId': 't8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f', 'title': 'NDA — Northwind Traders', 'roleAssignments': [ { 'roleId': 'r1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c', 'recipients': [ {'email': 'dana@northwind.com', 'name': 'Dana Fuentes'} ] } ] } ) envelope = response.json()['envelope'] ``` ```json title="Response (truncated)" { "envelope": { "id": "f4c2d7b3-0a5e-4d9f-c8b2-3e6a9d0f1b24", "title": "NDA — Northwind Traders", "status": "DRAFT", "signingOrder": "PARALLEL", "completionPolicy": "ALL", "recipients": [ { "email": "dana@northwind.com", "name": "Dana Fuentes", "signingGroup": 1, "status": "PENDING" } ] }, "errors": [] } ``` --- ## Share a template **POST** `/e-signature/template/share` Updates the template's sharing configuration — replaces the per-user grant list and optionally changes the baseline sharing level. The owner has implicit ADMIN and must not appear in `shares`. ### Request parameters - `templateId` (uuid, required) — The template to share. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `shares` (array) — Replacement per-user grants. Each entry: `userId` (string) and `accessLevel` (`READ`, `WRITE`, `PUBLISH`, or `ADMIN`). ### Response - `template` (Template) — The updated [template object](#the-template-object) with the new `shares`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "sharingLevel": "SPECIFIC_MEMBER", "shares": [ { "userId": "uK4pV8sM3d", "accessLevel": "WRITE" } ] }' ``` ```json title="Response (truncated)" { "template": { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "sharingLevel": "SPECIFIC_MEMBER", "shares": [ { "userId": "uK4pV8sM3d", "userEmail": "sam@acme.com", "accessLevel": "WRITE" } ] }, "errors": [] } ``` --- ## Unshare a template **POST** `/e-signature/template/unshare` Revokes one user's access grant. ### Request parameters - `templateId` (uuid, required) — The template. - `userId` (string, required) — The user whose grant to remove. ### Response - `template` (Template) — The updated [template object](#the-template-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/unshare \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "userId": "uK4pV8sM3d" }' ``` ```json title="Response (truncated)" { "template": { "id": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f", "sharingLevel": "SPECIFIC_MEMBER", "shares": [] }, "errors": [] } ``` --- ## Delete a template **POST** `/e-signature/template/delete` Permanently deletes a template. Envelopes already created from it are unaffected — instantiation copies rather than links. ### Request parameters - `templateId` (uuid, required) — The template to delete. ### Response - `deleted` (boolean) — Whether the template was deleted. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/template/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "templateId": "t8d4e9f1-2a6b-4c3d-8e7f-5a9b0c1d2e3f" }' ``` ```json title="Response" { "deleted": true, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (unknown template, missing role assignment, field bound to a removed role). | | `401` | Invalid or missing poweruser key, or the linked user lacks access to the template. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [E-Signature Envelopes (Poweruser API)](/docs/api/poweruser/esignature-envelopes.md) - [Document signing guide](/docs/guide/platform/document-signing.md) --- # E-Signature Folders Source: https://docs.autessa.com/docs/api/poweruser/esignature-folders > Poweruser endpoints for organizing e-signature envelopes into nestable, shareable folders. # E-Signature Folders Folders are the filing system for signing envelopes: nestable, shareable, and many-to-many with the envelopes inside them. Filing links an envelope to a folder rather than moving it, so one contract can sit under several folders at once. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/e-signature`. Every request is a POST with a JSON body. Folder and envelope IDs are **UUID strings**; user IDs are the usual opaque ID strings. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## How filing works A folder holds **links** to envelopes, not the envelopes themselves. Three consequences worth designing around: 1. **An envelope can be in many folders.** Adding it to a second folder does not remove it from the first. To move it, add to the new folder and remove from the old one. 2. **Deleting a folder never deletes envelopes.** The folder, its nested subfolders, their share grants, and all envelope links cascade away; every envelope survives and simply becomes unfiled. 3. **Folders nest** via `parentId`, and share independently of both their parent and their contents. Sharing a folder does not grant access to the envelopes filed in it — envelopes carry their own sharing. Folder membership is reflected on envelopes: both the envelope object and envelope summaries carry a `folderIds` array, where `[]` means unfiled. --- ## The folder object Returned by create, get, rename, share, and unshare, and as array entries on list. - `id` (uuid) — Unique folder identifier. - `name` (string) — Display name. - `parentId` (uuid) — The parent folder for nesting, or `null` at top level. - `ownerUserId` (string) — The user who owns the folder. The owner always holds implicit ADMIN. - `sharingLevel` (string) — Baseline visibility: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `myAccessLevel` (string) — The calling user's effective access on this folder: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `iAmOwner` (boolean) — Whether the calling user owns the folder. - `shares` (array) — Explicit per-user grants (`userId`, `userEmail`, `userName`, `accessLevel`, `grantedBy`, `createdAt`). The owner is not included. Populated on single-folder responses; `null` on [list](#list-folders) to keep the response light. - `envelopeCount` (integer) — Envelopes linked **directly** to this folder. Does not include envelopes in nested subfolders. - `createdAt` (string) — ISO 8601 creation timestamp. - `updatedAt` (string) — ISO 8601 timestamp of the last change. ```json title="Folder object" { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "name": "Northwind", "parentId": "f1a0b2c3-4d5e-6f70-8192-a3b4c5d6e7f8", "ownerUserId": "uK4pV8sM3d", "sharingLevel": "SPECIFIC_MEMBER", "myAccessLevel": "ADMIN", "iAmOwner": true, "shares": [ { "userId": "uQ7nR2tW9e", "userEmail": "sam@acme.com", "accessLevel": "READ", "grantedBy": "uK4pV8sM3d", "createdAt": "2026-07-02T09:14:22Z" } ], "envelopeCount": 12, "createdAt": "2026-06-18T11:03:41Z", "updatedAt": "2026-07-02T09:14:22Z" } ``` --- ## Create a folder **POST** `/e-signature/folder/create` Creates a folder in the caller's company. Omit `parentId` for a top-level folder, or pass one to nest. ### Request parameters - `name` (string, required) — Display name for the folder. - `parentId` (uuid) — Parent folder to nest under. Omit or `null` for top level. ### Response - `folder` (Folder) — The new [folder object](#the-folder-object), owned by the calling user and `PRIVATE` by default. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Northwind", "parentId": "f1a0b2c3-4d5e-6f70-8192-a3b4c5d6e7f8" }' ``` ```json title="Response" { "folder": { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "name": "Northwind", "parentId": "f1a0b2c3-4d5e-6f70-8192-a3b4c5d6e7f8", "ownerUserId": "uK4pV8sM3d", "sharingLevel": "PRIVATE", "myAccessLevel": "ADMIN", "iAmOwner": true, "shares": [], "envelopeCount": 0, "createdAt": "2026-06-18T11:03:41Z", "updatedAt": "2026-06-18T11:03:41Z" }, "errors": [] } ``` --- ## List folders **POST** `/e-signature/folder/list` Returns every folder in the company that the caller can see — owned, explicitly granted, or visible through a `COMPANY` baseline — ordered by name. The whole set comes back unpaginated so a client can build the tree from `parentId` in one call. ### Request parameters No parameters. Send an empty JSON object. ### Response - `folders` (array) — Visible [folder objects](#the-folder-object). `shares` is `null` on each entry — fetch a single folder with [`/folder/get`](#get-a-folder) for its grant list. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "folders": [ { "id": "f1a0b2c3-4d5e-6f70-8192-a3b4c5d6e7f8", "name": "Clients", "parentId": null, "sharingLevel": "COMPANY", "myAccessLevel": "ADMIN", "iAmOwner": true, "shares": null, "envelopeCount": 0 }, { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "name": "Northwind", "parentId": "f1a0b2c3-4d5e-6f70-8192-a3b4c5d6e7f8", "sharingLevel": "PRIVATE", "myAccessLevel": "ADMIN", "iAmOwner": true, "shares": null, "envelopeCount": 12 } ], "errors": [] } ``` --- ## Get a folder **POST** `/e-signature/folder/get` Fetches a single folder including its full per-user grant list — the authoritative source for a sharing UI. ### Request parameters - `folderId` (uuid, required) — The folder to fetch. ### Response - `folder` (Folder) — The [folder object](#the-folder-object), with `shares` populated. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15" }' ``` ```json title="Response (truncated)" { "folder": { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "name": "Northwind", "sharingLevel": "SPECIFIC_MEMBER", "myAccessLevel": "ADMIN", "shares": [ { "userId": "uQ7nR2tW9e", "userEmail": "sam@acme.com", "accessLevel": "READ" } ], "envelopeCount": 12 }, "errors": [] } ``` --- ## Rename a folder **POST** `/e-signature/folder/rename` Changes a folder's display name. Requires WRITE access or ownership. ### Request parameters - `folderId` (uuid, required) — The folder to rename. - `newName` (string, required) — The new display name. ### Response - `folder` (Folder) — The updated [folder object](#the-folder-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/rename \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "newName": "Northwind Traders" }' ``` ```json title="Response (truncated)" { "folder": { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "name": "Northwind Traders", "updatedAt": "2026-07-04T16:22:08Z" }, "errors": [] } ``` --- ## Add envelopes to a folder **POST** `/e-signature/folder/add-envelopes` Links one or more envelopes to a folder. Filing does not move an envelope — it can belong to several folders at once. The call is idempotent: envelopes already in the folder are skipped and not counted. ### Request parameters - `folderId` (uuid, required) — The destination folder. - `envelopeIds` (array, required) — The envelopes to file. ### Response - `changedCount` (integer) — How many links were actually created, excluding idempotent no-ops. - `folderId` (uuid) — The folder that was modified. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/add-envelopes \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "envelopeIds": [ "e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13", "e7d2f804-1a3b-4c5d-9e0f-8a1b2c3d4e5f" ] }' ``` ```json title="Response" { "changedCount": 2, "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "errors": [] } ``` --- ## Remove envelopes from a folder **POST** `/e-signature/folder/remove-envelopes` Unlinks envelopes from a folder. The envelopes themselves are untouched — removing the last link simply leaves an envelope unfiled. Also idempotent. ### Request parameters - `folderId` (uuid, required) — The folder to remove from. - `envelopeIds` (array, required) — The envelopes to unfile. ### Response - `changedCount` (integer) — How many links were actually removed, excluding idempotent no-ops. - `folderId` (uuid) — The folder that was modified. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/remove-envelopes \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "envelopeIds": ["e3b1c6a2-9f4d-4c8e-b7a1-2d5f8c9e0a13"] }' ``` ```json title="Response" { "changedCount": 1, "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "errors": [] } ``` --- ## Share a folder **POST** `/e-signature/folder/share` Updates the folder's sharing configuration — replaces the per-user grant list and optionally changes the baseline sharing level. The owner has implicit ADMIN and must not appear in `shares`. Sharing a folder shares the folder itself. It does not grant access to the envelopes filed inside it; those carry their own sharing. ### Request parameters - `folderId` (uuid, required) — The folder to share. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `shares` (array) — Replacement per-user grants. Each entry: `userId` (string) and `accessLevel` (`READ`, `WRITE`, `PUBLISH`, or `ADMIN`). ### Response - `folder` (Folder) — The updated [folder object](#the-folder-object) with the new `shares`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "sharingLevel": "SPECIFIC_MEMBER", "shares": [ { "userId": "uQ7nR2tW9e", "accessLevel": "READ" } ] }' ``` ```json title="Response (truncated)" { "folder": { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "sharingLevel": "SPECIFIC_MEMBER", "shares": [ { "userId": "uQ7nR2tW9e", "userEmail": "sam@acme.com", "accessLevel": "READ" } ] }, "errors": [] } ``` --- ## Unshare a folder **POST** `/e-signature/folder/unshare` Revokes a single user's explicit grant on a folder. Does not change the baseline `sharingLevel` — a `COMPANY` folder stays visible to everyone after an individual grant is removed. ### Request parameters - `folderId` (uuid, required) — The folder to revoke access on. - `userId` (string, required) — The user whose grant is revoked. ### Response - `folder` (Folder) — The updated [folder object](#the-folder-object) with the grant removed. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/unshare \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "userId": "uQ7nR2tW9e" }' ``` ```json title="Response (truncated)" { "folder": { "id": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15", "sharingLevel": "SPECIFIC_MEMBER", "shares": [] }, "errors": [] } ``` --- ## Delete a folder **POST** `/e-signature/folder/delete` Deletes a folder. Nested subfolders, share grants, and envelope links are removed with it. **Envelopes are never deleted** — they simply become unfiled. Requires ADMIN access or ownership. ### Request parameters - `folderId` (uuid, required) — The folder to delete. ### Response - `deleted` (boolean) — Whether the folder was deleted. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/e-signature/folder/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "folderId": "f2c7a91e-3b5d-4e8f-9a1c-6d0e2f4b8c15" }' ``` ```json title="Response" { "deleted": true, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (unknown folder, unknown envelope, invalid parent). | | `401` | Invalid or missing poweruser key, or the linked user lacks the required access level on the folder. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [E-Signature Envelopes (Poweruser API)](/docs/api/poweruser/esignature-envelopes.md) - [E-Signature Templates (Poweruser API)](/docs/api/poweruser/esignature-templates.md) - [Organize and share](/docs/guide/platform/document-signing/organizing.md) --- # Meeting Recorder Source: https://docs.autessa.com/docs/api/poweruser/meeting-recorder > Poweruser endpoints for managing meeting-recorder bots and their recordings: create and share bots, dispatch recordings, retry pipeline steps, mint share links, and read transcripts and summaries. # Meeting Recorder Manage meeting-recorder bots and everything they capture: create, share, and delete bots, control per-calendar auto-record behavior, dispatch the bot into meetings, retry transcription and summarization, share recordings, and pull transcripts, summaries, and presigned media URLs. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/meeting-recorder`. Every request is a POST with a JSON body. Bot, meeting, alert, and share-token IDs are **UUID strings**; linked resources (connectors, tools, users, AutessaDB tables) use the usual opaque ID strings. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. A **bot** is a recorder configuration: which calendars it watches, where its recordings land (an AutessaDB table it owns), and how recap emails are sent. Each recording is a row in that table; the endpoints on this page return a flattened, agent-friendly view of those rows, but you can also query the table directly with the [AutessaDB Query API](/docs/api/poweruser/autessadb-query.md) using the bot's `autessadbTableId`. Write operations check the linked user's access to the bot: updates, dispatch, retries, and recording shares need **EDITOR** or better; deleting or sharing the bot itself needs **ADMIN** (or ownership). Read endpoints need at least **VIEWER** access. > Two flows remain UI-only and are not exposed on this API: the direct > recording-upload endpoints (`/recordings/upload`, > `/recordings/upload/complete`, which serve the browser upload widget) and > the inbound webhook activity feed (`/bots/inbound-activity`, which backs the > settings-page debug view). Use the Builder for those. --- ## The bot object - `id` (uuid) — Unique bot identifier. - `companyId` (integer) — Owning company. - `ownerId` (integer) — Owning user. - `botDisplayName` (string) — Name the bot joins meetings under. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide when shared with the company. - `isEnabled` (boolean) — Whether the bot is active. - `botConnectorId` (string) — The external connector the bot records through. - `calendarConnectors` (array) — Per-calendar bindings, each carrying that calendar's auto-record settings and health metadata. - `autessadbTableId` (string) — The AutessaDB table holding this bot's recordings — usable directly with the AutessaDB Query API. - `summarizeLlmCallToolId` (string) — The LLM-call tool that writes meeting summaries. - `notifyToolId` (string) — The tool that sends recap notifications. - `purgeToolId` (string) — The tool that enforces retention on old recordings. - `triggerExtractAudioId` (string) — AutessaDB trigger that extracts audio from a finished recording. - `triggerTranscribeId` (string) — AutessaDB trigger that transcribes the audio. - `triggerSummarizeAndNotifyId` (string) — AutessaDB trigger that summarizes the transcript and sends the recap. - `sendParticipantEmail` (boolean) — Whether participants get a recap email after the meeting. - `shareAccessMode` (string) — Who can open the recording share link. - `includeTranscriptInEmail` (boolean) — Whether the recap email embeds the transcript. - `senderEmail` (string) — From-address used for recap emails. - `senderName` (string) — From-name used for recap emails. - `shareLinkTtlHours` (integer) — How long recording share links stay valid. - `monthlyMinuteCap` (integer) — Monthly recording-minute quota for the bot. - `quotaOverrideEnabled` (boolean) — Whether the quota cap is overridden for this bot. - `verificationStatus` (string) — `PENDING` for subdomain-MX bots awaiting DNS verification, `VERIFIED` otherwise. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 timestamp of the last update. ```json title="Bot object (truncated)" { "id": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "companyId": 1024, "ownerId": 2048, "botDisplayName": "Acme Notetaker", "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "isEnabled": true, "botConnectorId": "xC5vB9nM2k", "calendarConnectors": [ { "connectorId": "gW7qL4hT8z", "autoRecordMode": "INVITED_ONLY", "autoInviteEnabled": true } ], "autessadbTableId": "tR6yU3iO9p", "summarizeLlmCallToolId": "kZ3mQ9pW2x", "notifyToolId": "nJ8fD1sG5c", "purgeToolId": "qA2eX7cV4b", "sendParticipantEmail": true, "shareAccessMode": "PARTICIPANTS", "includeTranscriptInEmail": false, "senderEmail": "meetings@autessa.com", "senderName": "Acme Notetaker", "shareLinkTtlHours": 168, "monthlyMinuteCap": 3000, "quotaOverrideEnabled": false, "verificationStatus": "VERIFIED", "createdAt": "2026-04-10T08:00:00", "updatedAt": "2026-06-28T17:42:10" } ``` --- ## List bots **POST** `/meeting-recorder/bots/all` Lists every bot the linked user can access — owned, shared with them, or shared with the company. ### Request parameters None — send an empty object `{}`. ### Response A JSON **array** of [bot objects](#the-bot-object) (not wrapped in an envelope). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" [ { "id": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "botDisplayName": "Acme Notetaker", "isEnabled": true, "autessadbTableId": "tR6yU3iO9p", "verificationStatus": "VERIFIED" } ] ``` --- ## Get a bot **POST** `/meeting-recorder/bots/get` Retrieves one bot's full configuration. ### Request parameters - `botId` (uuid, required) — The bot to fetch. ### Response The [bot object](#the-bot-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" }' ``` ```json title="Response (truncated)" { "id": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "botDisplayName": "Acme Notetaker", "isEnabled": true, "calendarConnectors": [ { "connectorId": "gW7qL4hT8z", "autoRecordMode": "INVITED_ONLY" } ], "autessadbTableId": "tR6yU3iO9p" } ``` --- ## Create a bot **POST** `/meeting-recorder/bots` Creates a bot and provisions everything it needs: the AutessaDB recordings table (with its extract-audio, transcribe, and summarize-and-notify triggers), the summarize/notify/purge tools, and the bot's email identity. The provisioned tools and table are regular resources — you can inspect and edit them like any other. ### Request parameters - `botDisplayName` (string, required) — Name the bot joins meetings under. Must be unique within the company — check with [name-available](#check-name-availability) first. - `botAvatarS3Key` (string) — S3 key of an uploaded avatar image. - `calendarConnectorIds` (array) — `GOOGLE_CALENDAR` / `OUTLOOK_CALENDAR` connectors the bot should watch. Empty or omitted is allowed — calendar auto-join stays off until connectors are added. - `identityStrategy` (string, required) — How the bot gets its email identity: `WORKSPACE_OAUTH` (OAuth a real mailbox from your Google Workspace / M365 tenant), `SUBDOMAIN_MX` (delegate MX on a subdomain like `bot.acme.com`), or `AUTESSA_ALIAS` (zero-config alias on `bot.autessa.com`). - `strategyParams` (object) — Strategy-specific parameters. `WORKSPACE_OAUTH`: `oauth_provider`, `bot_email`, `oauth_access_token`, and optionally `oauth_refresh_token` and `oauth_expires_at`. `SUBDOMAIN_MX`: `subdomain` and `local_part`. `AUTESSA_ALIAS`: omit. ### Response - `botId` (uuid) — The new bot. - `botEmail` (string) — The email address the bot joins meetings from. - `botConnectorId` (string) — The provisioned bot connector. - `autessadbTableId` (string) — The provisioned recordings table. - `summarizeLlmCallToolId` (string) — The provisioned summarizer tool. - `notifyToolId` (string) — The provisioned notification tool. - `purgeToolId` (string) — The provisioned retention tool. - `triggerExtractAudioId` (string) — The extract-audio trigger on the recordings table. - `triggerTranscribeId` (string) — The transcription trigger. - `triggerSummarizeAndNotifyId` (string) — The summarize-and-notify trigger. - `errors` (array) — Validation or provisioning errors, when present. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botDisplayName": "Acme Notetaker", "calendarConnectorIds": ["gW7qL4hT8z"], "identityStrategy": "AUTESSA_ALIAS" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/meeting-recorder/bots', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ botDisplayName: 'Acme Notetaker', calendarConnectorIds: ['gW7qL4hT8z'], identityStrategy: 'AUTESSA_ALIAS' }) } ); const bot = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/meeting-recorder/bots', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'botDisplayName': 'Acme Notetaker', 'calendarConnectorIds': ['gW7qL4hT8z'], 'identityStrategy': 'AUTESSA_ALIAS' } ) bot = response.json() ``` ```json title="Response" { "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "botEmail": "acme-notetaker@bot.autessa.com", "botConnectorId": "xC5vB9nM2k", "autessadbTableId": "tR6yU3iO9p", "summarizeLlmCallToolId": "kZ3mQ9pW2x", "notifyToolId": "nJ8fD1sG5c", "purgeToolId": "qA2eX7cV4b", "triggerExtractAudioId": "trg_9f2c1a", "triggerTranscribeId": "trg_4b8d3e", "triggerSummarizeAndNotifyId": "trg_7a5f9c" } ``` --- ## Check name availability **POST** `/meeting-recorder/bots/name-available` Checks whether a bot display name is free within the company. When taken, the response suggests the nearest free variant. ### Request parameters - `name` (string, required) — The candidate display name. ### Response - `available` (boolean) — Whether the name is free. - `suggestion` (string) — A free variant (for example `Acme Notetaker 2`) when the name is taken; `null` otherwise. - `errors` (array) — Validation errors, when present. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/name-available \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Notetaker" }' ``` ```json title="Response" { "available": false, "suggestion": "Acme Notetaker 2" } ``` --- ## Update calendar connectors **POST** `/meeting-recorder/bots/update-calendar-connectors` Replaces the set of calendar connectors the bot watches — an empty list removes every calendar. The replacement is scoped to calendars the linked user can see: bindings for connectors they cannot access (for example teammates' `PRIVATE` calendars) are never touched. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot to update. - `calendarConnectorIds` (array, required) — The new set of calendar connectors. Empty list clears the caller-visible bindings. ### Response The updated [bot object](#the-bot-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/update-calendar-connectors \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "calendarConnectorIds": ["gW7qL4hT8z", "pM4nK8rF6d"] }' ``` ```json title="Response (truncated)" { "id": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "calendarConnectors": [ { "connectorId": "gW7qL4hT8z", "autoRecordMode": "INVITED_ONLY" }, { "connectorId": "pM4nK8rF6d", "autoRecordMode": "MANUAL" } ] } ``` --- ## Update a calendar's record mode **POST** `/meeting-recorder/bots/calendar/update-record-mode` Changes how the bot decides which meetings to auto-record on **one** calendar. Settings are per-calendar, so a single bot can be `MANUAL` on a personal calendar and `INVITED_ONLY` on a work one. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot to update. - `connectorId` (string, required) — Which linked calendar this update applies to. - `autoRecordMode` (string) — `MANUAL` (only record when explicitly dispatched), `INVITED_ONLY` (record meetings the bot is invited to), or `KEYWORD` (record meetings whose title matches a keyword). - `keywords` (array) — Required when `autoRecordMode` is `KEYWORD`. Substrings matched case-insensitively against meeting titles. - `autoInviteEnabled` (boolean) — When `false`, the calendar scan still creates recording rows but won't add the bot to the calendar invite. ### Response The updated [bot object](#the-bot-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/calendar/update-record-mode \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "connectorId": "gW7qL4hT8z", "autoRecordMode": "KEYWORD", "keywords": ["standup", "pipeline review"], "autoInviteEnabled": true }' ``` ```json title="Response (truncated)" { "id": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "calendarConnectors": [ { "connectorId": "gW7qL4hT8z", "autoRecordMode": "KEYWORD", "autoRecordKeywords": "[\"standup\",\"pipeline review\"]", "autoInviteEnabled": true } ] } ``` --- ## Get bot health **POST** `/meeting-recorder/bots/health` Returns the bot connector's health. Fully wired for `WORKSPACE_OAUTH` bots (OAuth refresh and push-subscription state); other identity strategies report `UNKNOWN`/`GREEN`. ### Request parameters - `botId` (uuid, required) — The bot to check. ### Response - `status` (string) — `GREEN`, `YELLOW` (subscription expiring soon, auto-renewal in progress), `RED` (needs reconnecting), or `UNKNOWN` (no health data yet). - `message` (string) — Human-readable explanation of the status. - `oauthRefreshStatus` (string) — State of the last OAuth token refresh. - `oauthLastRefreshAt` (string) — ISO-8601 timestamp of the last successful OAuth refresh. - `pushSubscriptionExpiresAt` (string) — When the calendar push subscription expires. - `lastCheckedAt` (string) — When the health check last ran. - `consecutiveFailureCount` (integer) — Consecutive failed health checks. - `errors` (array) — Validation errors, when present. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/health \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" }' ``` ```json title="Response" { "status": "GREEN", "message": "Bot is healthy.", "oauthRefreshStatus": "OK", "oauthLastRefreshAt": "2026-07-01T09:12:44", "pushSubscriptionExpiresAt": "2026-07-06T09:12:44", "lastCheckedAt": "2026-07-01T12:00:00", "consecutiveFailureCount": 0 } ``` --- ## Share a bot **POST** `/meeting-recorder/bots/share` Updates the bot's sharing level and per-user permission grants. When `userGrants` is provided it **replaces** all existing grants for the bot. Requires ADMIN access (or ownership). ### Request parameters - `botId` (uuid, required) — The bot to share. - `sharingLevel` (string) — `PRIVATE`, `COMPANY`, or `SPECIFIC_MEMBER`. - `companyPermissionLevel` (string) — `VIEWER`, `EDITOR`, or `ADMIN` — the level granted company-wide when `sharingLevel` is `COMPANY`. - `userGrants` (array) — Per-user grants, each `{ userId, permissionLevel }` where `userId` is a short opaque ID string and `permissionLevel` is `VIEWER`, `EDITOR`, or `ADMIN`. Replaces any existing grants. ### Response The updated [bot object](#the-bot-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "sharingLevel": "SPECIFIC_MEMBER", "userGrants": [ { "userId": "uH2jP6wQ9s", "permissionLevel": "EDITOR" } ] }' ``` ```json title="Response (truncated)" { "id": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": "READ" } ``` --- ## List bot permissions **POST** `/meeting-recorder/bots/permissions` Lists the bot's per-user permission grants. ### Request parameters - `botId` (uuid, required) — The bot whose grants to list. ### Response A JSON **array** of grant objects (not wrapped in an envelope): - `id` (uuid) — Grant identifier. - `botId` (uuid) — The bot. - `userId` (string) — The user the grant applies to. - `permissionLevel` (string) — `VIEWER`, `EDITOR`, or `ADMIN`. - `grantedBy` (string) — The user who created the grant. - `createdAt` (string) — ISO-8601 timestamp. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/permissions \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" }' ``` ```json title="Response" [ { "id": "b7e1c9d3-2f4a-4b6c-8d0e-1f2a3b4c5d6e", "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "userId": "uH2jP6wQ9s", "permissionLevel": "EDITOR", "grantedBy": "aK9dR3tY7m", "createdAt": "2026-06-20T14:03:22" } ] ``` --- ## List upcoming meetings **POST** `/meeting-recorder/bots/upcoming-meetings` Lists upcoming events across the bot's linked calendars, with each event's join URL and whether the bot is already invited. Use [event/invite](#invite-the-bot-to-an-event) and [event/uninvite](#uninvite-the-bot-from-an-event) to toggle the bot on a specific event. ### Request parameters - `botId` (uuid, required) — The bot whose calendars to read. - `forceRefresh` (boolean) — When `true`, runs the calendar scan synchronously before returning, so auto-record changes apply without waiting for the every-30-minute scheduled scan. ### Response - `events` (array) — Upcoming events. Each has `externalEventId` (string), `connectorId` (string), `calendarId`, `title`, `startTimeUtc`, `endTimeUtc`, `organizerEmail`, `attendeeCount` (integer), `meetingUrl`, `platform`, and `botInvited` (boolean). - `errors` (array) — Per-calendar fetch failures — each has `connectorId` (string), `provider` (`GOOGLE` or `OUTLOOK`), and `reason` (for example an expired OAuth grant), so you can prompt a reconnect instead of reading an empty list as "no meetings". ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/upcoming-meetings \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "forceRefresh": false }' ``` ```json title="Response (truncated)" { "events": [ { "externalEventId": "evt_8Xy2Kq", "connectorId": "gW7qL4hT8z", "calendarId": "primary", "title": "Q3 pipeline review", "startTimeUtc": "2026-07-03T17:00:00Z", "endTimeUtc": "2026-07-03T18:00:00Z", "organizerEmail": "sam@acme.com", "attendeeCount": 6, "meetingUrl": "https://meet.google.com/abc-defg-hij", "platform": "GOOGLE_MEET", "botInvited": true } ], "errors": [] } ``` --- ## Invite the bot to an event **POST** `/meeting-recorder/bots/event/invite` Adds the bot to one calendar event's attendee list. This does not dispatch the recorder immediately — the next calendar-scan tick handles dispatch when the meeting enters the scheduling horizon. To record right away, use [recordings/dispatch](#dispatch-a-recording). Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot to invite. - `connectorId` (string, required) — The calendar connector the event lives on. - `externalEventId` (string, required) — The provider event id (Google event id or Microsoft Graph event id), as returned by [upcoming-meetings](#list-upcoming-meetings). ### Response `{ "invited": true }`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/event/invite \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "connectorId": "gW7qL4hT8z", "externalEventId": "evt_8Xy2Kq" }' ``` ```json title="Response" { "invited": true } ``` --- ## Uninvite the bot from an event **POST** `/meeting-recorder/bots/event/uninvite` Removes the bot from one calendar event's attendee list. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot to remove. - `connectorId` (string, required) — The calendar connector the event lives on. - `externalEventId` (string, required) — The provider event id. ### Response `{ "uninvited": true }`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/event/uninvite \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "connectorId": "gW7qL4hT8z", "externalEventId": "evt_8Xy2Kq" }' ``` ```json title="Response" { "uninvited": true } ``` --- ## Delete a bot **POST** `/meeting-recorder/bots/delete` **Hard-deletes** a bot and everything created alongside it: the recordings AutessaDB table (including its stored media, search vectors, triggers, and permissions), the auto-provisioned tools, the summarizer agent, and all recording metadata. This is not reversible. Requires ADMIN access (or ownership). ### Request parameters - `botId` (uuid, required) — The bot to delete. ### Response `{ "deleted": true }`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/bots/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b" }' ``` ```json title="Response" { "deleted": true } ``` --- ## The recording object A flattened view of the recording's AutessaDB row. All timestamps are UTC ISO-8601 strings. - `meetingId` (uuid) — Unique recording identifier. - `title` (string) — Meeting title. - `platform` (string) — Meeting platform (Zoom, Google Meet, Teams, …). - `origin` (string) — How the recording was initiated: `CALENDAR_AUTOINVITE`, `EMAIL_INVITE`, or `MANUAL_DISPATCH`. - `meetingUrl` (string) — The meeting join URL — the stable identifier shared between calendar invites and the recording row. Use it to correlate a recording with a calendar event (for example one returned by the `LIST_CALENDAR_EVENT` tool). - `externalCalendarUrl` (string) — Link to the calendar event, when the recording came from a calendar. - `startTime` (string) — Meeting start (UTC ISO-8601). - `endTime` (string) — Meeting end (UTC ISO-8601). - `durationSeconds` (integer) — Recorded duration. - `organizerEmail` (string) — The organizer. - `expectedParticipants` (string) — Invitees from the calendar event. - `participants` (string) — Who actually attended. - `recordingVideoUrl` (string) — Presigned URL for the video recording (time-boxed). - `recordingAudioUrl` (string) — Presigned URL for the extracted audio (time-boxed). - `transcript` (string) — Full transcript text. - `transcriptLanguage` (string) — Detected transcript language. - `transcriptSource` (string) — Where the transcript came from (for example `AUTESSA_STT` or `MEETING_CAPTIONS`). - `summary` (string) — LLM-generated meeting summary. - `shareLink` (string) — Public proxy URL participants use to view the recording. - `status` (string) — Pipeline status: `SCHEDULED`, `BOT_JOINING`, `WAITING_ROOM`, `RECORDING`, `PROCESSING`, `DONE`, `FAILED`, `CANCELLED`, or `DELETED`. - `errorMessage` (string) — Failure detail when `status` is `FAILED`. - `partialRecording` (boolean) — Whether the bot missed part of the meeting. ```json title="Recording object (truncated)" { "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "title": "Q3 pipeline review", "platform": "GOOGLE_MEET", "origin": "CALENDAR_AUTOINVITE", "meetingUrl": "https://meet.google.com/abc-defg-hij", "startTime": "2026-06-30T17:00:00Z", "endTime": "2026-06-30T17:47:12Z", "durationSeconds": 2832, "organizerEmail": "sam@acme.com", "participants": "sam@acme.com, lee@acme.com", "recordingVideoUrl": "https://s3.amazonaws.com/autessa-recordings/...", "recordingAudioUrl": "https://s3.amazonaws.com/autessa-recordings/...", "transcript": "Sam: Let's start with the numbers...", "transcriptLanguage": "en", "transcriptSource": "AUTESSA_STT", "summary": "The team reviewed Q3 pipeline...", "shareLink": "https://autessa.com/recording/...", "status": "DONE", "errorMessage": null, "partialRecording": false } ``` --- ## List recordings **POST** `/meeting-recorder/recordings/list` Lists a bot's recordings, paginated, with optional filters that are AND-combined server-side. To fetch the recording for a specific calendar event, pass the event's join URL as `meetingUrl`. ### Request parameters - `botId` (uuid, required) — The bot whose recordings to list. - `meetingUrl` (string) — Exact-match filter on the meeting join URL — the correlation key with calendar events. - `meetingTitleContains` (string) — Substring filter on the title. - `startTimeFrom` (string) — UTC ISO-8601 lower bound on `startTime` (inclusive). - `startTimeTo` (string) — UTC ISO-8601 upper bound on `startTime` (exclusive). - `status` (string) — Filter on pipeline status (`SCHEDULED`, `RECORDING`, `DONE`, `FAILED`, …). - `page` (integer) — 1-based page index. Defaults to `1`. - `pageSize` (integer) — Rows per page. Defaults to `50`, max `100`. ### Response - `recordings` (array) — Matching recordings, each in the [recording object](#the-recording-object) shape. - `page` (integer) — The page returned. - `pageSize` (integer) — Rows per page. - `totalRows` (integer) — Total matching recordings. - `totalPages` (integer) — Total number of pages. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "status": "DONE", "startTimeFrom": "2026-06-01T00:00:00Z", "startTimeTo": "2026-07-01T00:00:00Z", "page": 1, "pageSize": 50 }' ``` **JavaScript:** ```javascript // Fetch the recording for a specific calendar event by its join URL const response = await fetch( 'https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/list', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ botId: '3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b', meetingUrl: 'https://meet.google.com/abc-defg-hij' }) } ); const { recordings } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/list', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'botId': '3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b', 'status': 'DONE', 'page': 1, 'pageSize': 50 } ) data = response.json() ``` ```json title="Response (truncated)" { "recordings": [ { "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "title": "Q3 pipeline review", "meetingUrl": "https://meet.google.com/abc-defg-hij", "startTime": "2026-06-30T17:00:00Z", "durationSeconds": 2832, "status": "DONE" } ], "page": 1, "pageSize": 50, "totalRows": 12, "totalPages": 1 } ``` --- ## Get a recording **POST** `/meeting-recorder/recordings/get` Retrieves one recording by ID, including transcript, summary, and presigned media URLs. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording to fetch. ### Response The [recording object](#the-recording-object). ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f" }' ``` ```json title="Response (truncated)" { "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "title": "Q3 pipeline review", "transcript": "Sam: Let's start with the numbers...", "summary": "The team reviewed Q3 pipeline...", "recordingVideoUrl": "https://s3.amazonaws.com/autessa-recordings/...", "status": "DONE" } ``` --- ## Dispatch a recording **POST** `/meeting-recorder/recordings/dispatch` Sends the bot into a meeting by its join URL — immediately, or at a scheduled join time. Creates the recording row and returns its `meetingId`. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot to dispatch. - `meetingUrl` (string, required) — Zoom / Google Meet / Teams join URL. - `joinAtUtc` (string) — UTC ISO-8601 time for the bot to join (`yyyy-MM-ddTHH:mm:ssZ`). Omit to dispatch immediately. - `title` (string) — Title override; defaults to a generic `Manual recording · {platform}`. ### Response - `meetingId` (uuid) — The new recording row — poll it with [recordings/get](#get-a-recording). - `platform` (string) — Platform detected from the URL. - `startTimeUtc` (string) — When the bot joins (UTC ISO-8601). - `errors` (array) — Validation errors (bad URL, quota exhausted, …), when present. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/dispatch \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingUrl": "https://meet.google.com/abc-defg-hij", "title": "Q3 pipeline review" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/dispatch', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ botId: '3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b', meetingUrl: 'https://meet.google.com/abc-defg-hij', joinAtUtc: '2026-07-03T17:00:00Z' }) } ); const { meetingId } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/dispatch', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'botId': '3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b', 'meetingUrl': 'https://meet.google.com/abc-defg-hij' } ) meeting_id = response.json()['meetingId'] ``` ```json title="Response" { "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "platform": "GOOGLE_MEET", "startTimeUtc": "2026-07-02T18:30:00Z" } ``` --- ## Record an upcoming calendar event **POST** `/meeting-recorder/recordings/dispatch-from-event` Marks an upcoming calendar event for recording — the same mechanic as the auto-invite scan: the bot is added to the event's attendee list and the recorder is scheduled as the meeting approaches. The work runs **asynchronously** via an immediate calendar scan, so the response carries no `meetingId`; find the recording row afterwards with [recordings/list](#list-recordings) using the event's join URL. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot to schedule. - `externalEventId` (string, required) — The provider event id (Google event id or Microsoft Graph event id). - `calendarId` (string) — Calendar id; defaults to `primary`. ### Response - `meetingId` (uuid) — Always `null` — the row is created by the asynchronous scan. - `platform` (string) — Always `CALENDAR`. - `startTimeUtc` (string) — Always `null`. - `errors` (array) — Validation errors, when present. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/dispatch-from-event \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "externalEventId": "evt_8Xy2Kq", "calendarId": "primary" }' ``` ```json title="Response" { "meetingId": null, "platform": "CALENDAR", "startTimeUtc": null } ``` --- ## Retry transcription **POST** `/meeting-recorder/recordings/retry-transcription` Clears the recording's transcript and re-runs speech-to-text on the stored audio. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording to retranscribe. ### Response A **plain string** (not JSON): `ok`, `no_row`, `no_audio`, or `stt_error`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/retry-transcription \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f" }' ``` ```text title="Response" ok ``` --- ## Retry summarization **POST** `/meeting-recorder/recordings/retry-summary` Clears the recording's summary and re-fires the summarize-and-notify trigger, which regenerates the summary from the transcript and resends the recap. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording to resummarize. ### Response A **plain string** (not JSON): `retrigger_enqueued`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/retry-summary \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f" }' ``` ```text title="Response" retrigger_enqueued ``` --- ## Retry processing **POST** `/meeting-recorder/recordings/retry-processing` Self-service recovery for a `FAILED` recording. The endpoint first diagnoses **why** it failed from structured pipeline state; only the retryable case — the meeting was recorded but ingest failed — actually re-pulls the media. For every other cause it returns the diagnosis unchanged so you can take the action that helps (`RETRY_TRANSCRIPTION`, `REDISPATCH`, or nothing). Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The failed recording. ### Response - `cause` (string) — `RETRYABLE_INGEST` (recorded but ingest failed — retry fixes it), `RETRY_TRANSCRIPTION` (audio landed but transcription failed), `NOT_DISPATCHED` (the bot never joined — re-dispatch instead), `NOT_RECORDED` (the bot joined but captured no media), `NOT_APPLICABLE_UPLOAD` (manually uploaded recording), or `NOT_FAILED` (nothing to retry). - `canRetryProcessing` (boolean) — `true` only for `RETRYABLE_INGEST`. - `message` (string) — Human-readable explanation. - `suggestedAction` (string) — `RETRY_PROCESSING`, `RETRY_TRANSCRIPTION`, `REDISPATCH`, or `NONE`. - `status` (string) — Set after a retry attempt: `QUEUED`, `EMPTY_BUNDLE`, `ERROR`, or `NOT_APPLICABLE`; `null` when no retry was attempted. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/retry-processing \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f" }' ``` ```json title="Response" { "cause": "RETRYABLE_INGEST", "canRetryProcessing": true, "message": "The meeting was recorded but processing failed — retrying.", "suggestedAction": "RETRY_PROCESSING", "status": "QUEUED" } ``` --- ## Resend notifications **POST** `/meeting-recorder/recordings/resend-notifications` Re-sends the recap notification for a recording to its configured recipients. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording whose recap to resend. ### Response A **plain string** (not JSON): `sent:N failed:M` on success, or `skipped` (participant email disabled on the bot), `no_row`, or `no_recipients`. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/resend-notifications \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f" }' ``` ```text title="Response" sent:4 failed:0 ``` --- ## Share a recording by email **POST** `/meeting-recorder/recordings/share` Shares a recording with an explicit list of recipients — each gets their own tokenized link by email. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording to share. - `recipientEmails` (array, required) — Recipient email addresses. Normalized and de-duplicated server-side; at least one valid address is required. - `requiresOtp` (boolean) — Whether recipients must pass an email OTP check before viewing. Defaults to the bot's configured share access mode. ### Response - `sent` (integer) — Links successfully emailed. - `failed` (integer) — Deliveries that failed. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "recipientEmails": ["lee@acme.com", "pat@acme.com"], "requiresOtp": true }' ``` ```json title="Response" { "sent": 2, "failed": 0 } ``` --- ## Create a share link **POST** `/meeting-recorder/recordings/share-link` Mints a single shareable link for a recording without emailing anyone — copy-link semantics. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording to link. - `ttlHours` (integer) — Link lifetime. Defaults to the bot's configured TTL (or 168 hours). - `requiresOtp` (boolean) — Whether viewers must pass an email OTP check. Defaults to the bot's configured share access mode. ### Response - `tokenId` (uuid) — The share token — pass to [share-tokens/revoke](#revoke-share-tokens) to invalidate all of a recording's tokens. - `publicUrl` (string) — The shareable URL. - `expiresAt` (string) — ISO-8601 expiry timestamp. - `requiresOtp` (boolean) — The effective OTP setting. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/share-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "ttlHours": 72 }' ``` ```json title="Response" { "tokenId": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f", "publicUrl": "https://autessa.com/recording/c4d5e6f7...", "expiresAt": "2026-07-05T18:30:00", "requiresOtp": false } ``` --- ## Revoke share tokens **POST** `/meeting-recorder/recordings/share-tokens/revoke` Revokes **every** outstanding share token for a recording — emailed links and copy-links alike stop working immediately. Requires EDITOR access. ### Request parameters - `botId` (uuid, required) — The bot that owns the recording. - `meetingId` (uuid, required) — The recording whose tokens to revoke. ### Response The bare number `1` on success. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/recordings/share-tokens/revoke \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f" }' ``` ```json title="Response" 1 ``` --- ## List alerts **POST** `/meeting-recorder/alerts/list` Lists the linked user's meeting-recorder alerts — bot health problems, failed recordings, quota warnings, and similar. Alerts belong to the user, not to a single bot. ### Request parameters Both parameters are optional; an empty body `{}` works. - `unreadOnly` (boolean) — Only alerts not yet dismissed. Defaults to `true`. - `limit` (integer) — Maximum alerts to return. Defaults to `20`. ### Response A JSON **array** of alert objects (not wrapped in an envelope): - `id` (uuid) — Alert identifier — pass to [alerts/dismiss](#dismiss-an-alert). - `userId` (integer) — The user the alert belongs to. - `companyId` (integer) — The company scope. - `alertType` (string) — What happened (for example a failed recording or a bot needing reconnection). - `severity` (string) — Alert severity. - `title` (string) — Short headline. - `body` (string) — Detail text. - `meetingId` (uuid) — Related recording, when applicable. - `botId` (uuid) — Related bot, when applicable. - `linkUrl` (string) — Deep link into the Builder for the affected resource. - `dismissedAt` (string) — When the alert was dismissed; `null` while unread. - `actedAt` (string) — When the user acted on the alert, if they did. - `createdAt` (string) — ISO-8601 creation timestamp. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/alerts/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "unreadOnly": true, "limit": 20 }' ``` ```json title="Response" [ { "id": "e5f6a7b8-9c0d-4e1f-8a2b-3c4d5e6f7a8b", "userId": 2048, "companyId": 1024, "alertType": "RECORDING_FAILED", "severity": "ERROR", "title": "Recording failed", "body": "The bot couldn't join 'Q3 pipeline review'.", "meetingId": "9d2c4e6f-1a3b-4c5d-8e7f-0a1b2c3d4e5f", "botId": "3f8a1b2c-4d5e-4f60-8a7b-9c0d1e2f3a4b", "linkUrl": "https://autessa.com/builder/meeting-recorder/...", "dismissedAt": null, "actedAt": null, "createdAt": "2026-07-01T18:05:12" } ] ``` --- ## Dismiss an alert **POST** `/meeting-recorder/alerts/dismiss` Dismisses one alert for the linked user. ### Request parameters - `alertId` (uuid, required) — The alert to dismiss. ### Response `{ "dismissed": true }` — `false` when the alert doesn't exist or belongs to another user. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/alerts/dismiss \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "alertId": "e5f6a7b8-9c0d-4e1f-8a2b-3c4d5e6f7a8b" }' ``` ```json title="Response" { "dismissed": true } ``` --- ## Dismiss all alerts **POST** `/meeting-recorder/alerts/dismiss-all` Dismisses every unread alert for the linked user. ### Request parameters None — send an empty object `{}`. ### Response `{ "dismissed": N }` where `N` is the number of alerts dismissed. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/meeting-recorder/alerts/dismiss-all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "dismissed": 3 } ``` --- ## Errors Most endpoints on this page surface failures as HTTP errors without a body envelope: | Status | When | |--------|------| | `200` | Success. | | `400` | Missing or malformed `botId`/`meetingId`/`alertId` (must be UUIDs), a recording that was not found, an inaccessible connector id, or a missing required field. | | `401` | Invalid or missing poweruser key, or the linked user lacks the required access level (EDITOR for updates, dispatch, retries, and recording shares; ADMIN for `bots/delete` and `bots/share`). | | `403` | Scoped key without the endpoint in its allow-list. | The envelope-wrapped endpoints — [create a bot](#create-a-bot), [name-available](#check-name-availability), [health](#get-bot-health), [dispatch](#dispatch-a-recording), and [dispatch-from-event](#record-an-upcoming-calendar-event) — instead report validation and provisioning failures in the response's `errors` array: | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [AutessaDB Query (Poweruser API)](/docs/api/poweruser/autessadb-query.md) - [Connectors (Poweruser API)](/docs/api/poweruser/connectors.md) - [Meeting recorder guide](/docs/guide/platform/meeting-recorder.md) --- # Custom Apps Source: https://docs.autessa.com/docs/api/poweruser/custom-apps > Poweruser endpoints for creating custom apps, editing their source files, publishing builds, and controlling sharing and hosting. # Custom Apps Manage App Builder apps end to end: create and clone apps, read and write their draft source files, publish versions and track builds, control code sharing and end-user hosting, and read the history of Vibecode AI coding sessions. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/custom-apps-v2`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## The app object - `appId` (string) — Unique app identifier. - `companyId` (string) — The company that owns the app. - `ownerId` (string) — User ID of the app's owner. - `appName` (string) — Display name. - `description` (string) — What the app does. - `sharingLevel` (string) — Who can see and edit the source code: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level every company member gets when `sharingLevel` is `COMPANY`: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 timestamp of the last update. - `latestVersion` (integer) — Highest version number created by [`/publish`](#publish-an-app), or absent if never published. - `publishedVersion` (integer) — The currently live published version, or absent if no build has succeeded yet. - `userAccessLevel` (string) — The calling user's effective access level on the app: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `hostingMode` (string) — How end users access the deployed app — orthogonal to `sharingLevel`: `DEFAULT` (Autessa login required), `PUBLIC` (anyone with the URL), `AUTH_CODE` (shared access code), `CUSTOM_AUTH` (app-specific accounts backed by an AutessaDB table), or `TOKEN` (a private access link per person). - `publicSlug` (string) — URL slug for non-default hosting modes. - `publicUrl` (string) — Full end-user URL derived from the slug, when one is set. - `hasAuthCode` (boolean) — Whether an `AUTH_CODE` access code is stored. The code itself is hashed and never returned. - `authTableId` (string) — The AutessaDB table backing `CUSTOM_AUTH` signups, when provisioned. - `authPageConfig` (string) — JSON string with auth page theming for `CUSTOM_AUTH` hosting. - `requireEmailVerification` (boolean) — Whether `CUSTOM_AUTH` signups must confirm an emailed code before the account is created. Absent for apps not using `CUSTOM_AUTH`. - `enabledProviders` (array) — The sign-in methods a `CUSTOM_AUTH` app offers: any of `PASSWORD`, `GOOGLE`, and `MICROSOFT`. Absent for apps not using `CUSTOM_AUTH`. - `hostingPermissions` (string) — JSON string describing which company resources the hosted app may call (tables, agents, tools, managers, poweruser endpoints, e-sign envelopes). Set as a structured object via [`/share`](#share-an-app); returned here as the stored JSON string. ```json title="App object" { "appId": "V1StGXR8Z5", "companyId": "cP2mK7dX4v", "ownerId": "uB7nT2wQ9k", "appName": "Field Inspection Portal", "description": "Logs site inspections against the Inspections table.", "sharingLevel": "SPECIFIC_MEMBER", "createdAt": "2026-05-12T09:30:00", "updatedAt": "2026-06-28T17:42:10", "latestVersion": 5, "publishedVersion": 4, "userAccessLevel": "ADMIN", "hostingMode": "AUTH_CODE", "publicSlug": "field-inspections", "publicUrl": "https://builder.autessa.com/app/field-inspections", "hasAuthCode": true, "hostingPermissions": "{\"tables\":[{\"tableId\":\"tQ6bF3jN8s\",\"canRead\":true,\"canWrite\":true,\"canDelete\":false}],\"esignatureActsAsUser\":false}" } ``` --- ## Build lifecycle and access levels Every app has a single **draft** workspace of source files, edited through the [file endpoints](#list-files) (and the Vibecode assistant in the UI). [`/publish`](#publish-an-app) snapshots the draft as a new numbered version and triggers an **asynchronous build** — the publish response returns immediately with `buildStatus: "BUILDING"`. Poll [`/build-status`](#get-build-status) until the build reaches `SUCCESS` or `FAILED`; on the first successful check the version is marked `PUBLISHED` and the CDN cache is invalidated, so [`/build-url`](#get-a-preview-url) and the app's public slug serve the new build. Endpoints enforce the caller's access level on the app (the poweruser key acts as its linked user): | Operation | Required access | |-----------|-----------------| | `get`, `all`, `clone` (source app), `build-status`, `versions`, `build-files`, `build-url`, `files/list`, `files/get`, `files/get-batch`, `refresh-libraries`, `get-sharing-details`, `vibecode/*` | Any access (READ or higher) | | `update`, `files/create`, `files/update`, `files/delete`, `files/rename`, `files/save-batch` | WRITE, PUBLISH, or ADMIN | | `publish` | PUBLISH or ADMIN | | `delete`, `share`, `transfer-ownership` | ADMIN | `check-slug` performs no per-app access check — it only validates the slug. --- ## Create an app **POST** `/custom-apps-v2/create` Creates a new app owned by the linked user and bootstraps its draft workspace with the chosen starter template, including the read-only Autessa library files. The response lists every bootstrapped file path. ### Request parameters - `appName` (string, required) — Display name. - `description` (string) — What the app does. - `templateType` (string) — Starter template for the new app. One of `BOOTSTRAP` (minimal starter), `LANDING_PAGE` (marketing site with hero, features, pricing, and FAQ sections), `SAAS_APP` (product-app shell with a built-in UI component kit and sample pages), or `DASHBOARD` (analytics starter with KPI tiles, charts, and data tables). Defaults to `BOOTSTRAP` when omitted, matching the previous behavior. ### Response - `app` (App) — The newly created [app object](#the-app-object). - `files` (array) — Paths of the bootstrapped template files. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appName": "Field Inspection Portal", "description": "Logs site inspections against the Inspections table.", "templateType": "SAAS_APP" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ appName: 'Field Inspection Portal', description: 'Logs site inspections against the Inspections table.', templateType: 'SAAS_APP' }) } ); const { app, files } = await response.json(); console.log(app.appId, files.length); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'appName': 'Field Inspection Portal', 'description': 'Logs site inspections against the Inspections table.', 'templateType': 'SAAS_APP' } ) app = response.json()['app'] ``` ```json title="Response (truncated)" { "app": { "appId": "V1StGXR8Z5", "companyId": "cP2mK7dX4v", "ownerId": "uB7nT2wQ9k", "appName": "Field Inspection Portal", "description": "Logs site inspections against the Inspections table.", "sharingLevel": "PRIVATE", "userAccessLevel": "ADMIN", "hostingMode": "DEFAULT", "hasAuthCode": false }, "files": [ "index.html", "src/App.jsx", "src/main.jsx", "src/autessa/autessa-client.js", "package.json" ], "errors": [] } ``` --- ## Get an app **POST** `/custom-apps-v2/get` Retrieves an app and the file paths in its draft workspace. Calling this also refreshes the app's `src/autessa/` library files to the current platform version (same effect as [`/refresh-libraries`](#refresh-libraries)). ### Request parameters - `appId` (string, required) — The app to fetch. ### Response - `app` (App) — The [app object](#the-app-object). - `files` (array) — Paths of every file in the draft workspace. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/get', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'V1StGXR8Z5' }) } ); const { app, files } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/get', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'appId': 'V1StGXR8Z5'} ) data = response.json() ``` ```json title="Response (truncated)" { "app": { "appId": "V1StGXR8Z5", "appName": "Field Inspection Portal", "latestVersion": 5, "publishedVersion": 4, "userAccessLevel": "ADMIN" }, "files": [ "index.html", "src/App.jsx", "src/components/InspectionForm.jsx", "src/autessa/autessa-client.js" ], "errors": [] } ``` --- ## List all apps **POST** `/custom-apps-v2/all` Lists every app the linked user can access — owned, shared with them directly, or shared with the company. Each entry is a full [app object](#the-app-object) with `userAccessLevel` set to the caller's access on that app. ### Request parameters None — send an empty object `{}`. ### Response - `apps` (array) — The accessible apps. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "apps": [ { "appId": "V1StGXR8Z5", "appName": "Field Inspection Portal", "sharingLevel": "SPECIFIC_MEMBER", "publishedVersion": 4, "userAccessLevel": "ADMIN", "hostingMode": "AUTH_CODE" }, { "appId": "aH5cJ1yD7q", "appName": "Quote Calculator", "sharingLevel": "COMPANY", "userAccessLevel": "READ", "hostingMode": "DEFAULT" } ], "errors": [] } ``` --- ## Update an app **POST** `/custom-apps-v2/update` Updates the app's name and description. This endpoint only touches metadata — edit source code with the [file endpoints](#list-files), and change sharing or hosting with [`/share`](#share-an-app). Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `appId` (string, required) — The app to update. - `appName` (string) — New display name. - `description` (string) — New description. ### Response - `app` (App) — The updated [app object](#the-app-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "appName": "Field Inspection Portal", "description": "Logs and reviews site inspections." }' ``` ```json title="Response (truncated)" { "app": { "appId": "V1StGXR8Z5", "appName": "Field Inspection Portal", "description": "Logs and reviews site inspections.", "updatedAt": "2026-07-02T10:15:00" }, "errors": [] } ``` --- ## Delete an app **POST** `/custom-apps-v2/delete` Permanently deletes the app — its draft workspace, version snapshots, and build outputs are all removed. This cannot be undone. Requires ADMIN access on the app. ### Request parameters - `appId` (string, required) — The app to delete. ### Response - `success` (boolean) — `true` when the app was deleted. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response" { "success": true, "errors": [] } ``` --- ## Clone an app **POST** `/custom-apps-v2/clone` Creates a new private app owned by the calling user, copying the source app's draft files. Version history, sharing settings, and hosting configuration are **not** copied. Any access level on the source app is sufficient. ### Request parameters - `appId` (string, required) — The source app to clone. - `appName` (string) — Name for the clone. Defaults to the source name with `" (Copy)"` appended. ### Response Same shape as [`/create`](#create-an-app): - `app` (App) — The new [app object](#the-app-object) (private, owned by the caller). - `files` (array) — Paths of the copied draft files. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/clone \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "appName": "Field Inspection Portal (Staging)" }' ``` ```json title="Response (truncated)" { "app": { "appId": "nW2yE9gK4t", "appName": "Field Inspection Portal (Staging)", "sharingLevel": "PRIVATE", "userAccessLevel": "ADMIN", "hostingMode": "DEFAULT" }, "files": [ "index.html", "src/App.jsx", "src/components/InspectionForm.jsx" ], "errors": [] } ``` --- ## Publish an app **POST** `/custom-apps-v2/publish` Snapshots the draft workspace as a new numbered version and triggers an asynchronous build. The response returns immediately with `buildStatus: "BUILDING"` — poll [`/build-status`](#get-build-status) to follow the build. The version only goes live once its build succeeds. Requires PUBLISH or ADMIN access. ### Request parameters - `appId` (string, required) — The app to publish. ### Response - `app` (App) — The [app object](#the-app-object). - `version` (integer) — The new version number. - `buildId` (string) — Identifier of the triggered build. - `buildStatus` (string) — Always `"BUILDING"` on a successful publish. - `errors` (array) — Error messages, if any occurred (for example a failed build trigger — the version is rolled back in that case). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/publish \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` **JavaScript:** ```javascript const base = 'https://api.autessa.com/autessa_poweruser/custom-apps-v2'; const headers = { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }; const publish = await fetch(`${base}/publish`, { method: 'POST', headers, body: JSON.stringify({ appId: 'V1StGXR8Z5' }) }); const { version } = await publish.json(); // Poll until the build finishes let status; do { await new Promise((r) => setTimeout(r, 5000)); const check = await fetch(`${base}/build-status`, { method: 'POST', headers, body: JSON.stringify({ appId: 'V1StGXR8Z5', version }) }); status = await check.json(); } while (status.status === 'BUILDING'); console.log(status.status, status.outputUrl); ``` **Python:** ```python base = 'https://api.autessa.com/autessa_poweruser/custom-apps-v2' headers = {'Authorization': os.environ['POWERUSER_KEY']} version = requests.post(f'{base}/publish', headers=headers, json={'appId': 'V1StGXR8Z5'}).json()['version'] while True: time.sleep(5) status = requests.post(f'{base}/build-status', headers=headers, json={'appId': 'V1StGXR8Z5', 'version': version}).json() if status['status'] != 'BUILDING': break print(status['status']) ``` ```json title="Response (truncated)" { "app": { "appId": "V1StGXR8Z5", "appName": "Field Inspection Portal", "latestVersion": 5, "publishedVersion": 4 }, "version": 6, "buildId": "3f6c1a9e-8b2d-4e07-9c41-5a12de88f0b3", "buildStatus": "BUILDING", "errors": [] } ``` --- ## Get build status **POST** `/custom-apps-v2/build-status` Returns the build state of a version. Omit `version` to check the latest one. When the latest version's build first reports `SUCCESS`, this call also marks the version as `PUBLISHED` and invalidates the CDN cache so the new build is served. ### Request parameters - `appId` (string, required) — The app to check. - `version` (integer) — Version to check. Defaults to the latest version. ### Response - `version` (integer) — The version that was checked. Absent when the app has no versions yet. - `buildId` (string) — Identifier of the build, when available. - `status` (string) — `BUILDING`, `SUCCESS`, `FAILED`, or `NOT_STARTED`. - `error` (string) — Build error output, when `status` is `FAILED`. - `startedAt` (string) — When the build started. - `completedAt` (string) — When the build finished. - `outputUrl` (string) — URL of the published app, present when `status` is `SUCCESS`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/build-status \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "version": 6 }' ``` ```json title="Response" { "version": 6, "status": "SUCCESS", "startedAt": "2026-07-02T10:20:04", "completedAt": "2026-07-02T10:21:37", "outputUrl": "https://apps.autessa.com/custom-apps-v2/.../published/index.html", "errors": [] } ``` --- ## List versions **POST** `/custom-apps-v2/versions` Lists all versions of the app, newest first, with each version's state and current build status. ### Request parameters - `appId` (string, required) — The app to inspect. ### Response - `versions` (array) — Version records, newest first. - `versions[].versionId` (string) — Unique version identifier. - `versions[].version` (integer) — The version number. - `versions[].state` (string) — `DRAFT`, `PUBLISHED`, or `ARCHIVED`. - `versions[].buildId` (string) — Identifier of the version's build. - `versions[].buildStatus` (string) — `BUILDING`, `SUCCESS`, `FAILED`, or `NOT_STARTED`. - `versions[].sourceS3Path` (string) — Storage path of the version's source snapshot. - `versions[].buildS3Path` (string) — Storage path of the build output, when built. - `versions[].createdAt` (string) — ISO-8601 creation timestamp. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/versions \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response (truncated)" { "versions": [ { "versionId": "vJ8pL4sB6m", "version": 6, "state": "PUBLISHED", "buildId": "3f6c1a9e-8b2d-4e07-9c41-5a12de88f0b3", "buildStatus": "SUCCESS", "sourceS3Path": "custom-apps-v2/.../versions/6/source/", "buildS3Path": "custom-apps-v2/.../versions/6/build/", "createdAt": "2026-07-02T10:19:58" }, { "versionId": "vC3xR7hN1z", "version": 5, "state": "ARCHIVED", "buildStatus": "SUCCESS", "createdAt": "2026-06-28T17:40:12" } ], "errors": [] } ``` --- ## Get build files **POST** `/custom-apps-v2/build-files` Lists the compiled output files of a build, each with a presigned download URL. Omit `version` to read the latest published build; pass a `version` to read a specific one (fails with an error if that version's build did not succeed). ### Request parameters - `appId` (string, required) — The app whose build to read. - `version` (integer) — Version whose build output to list. Defaults to the latest published version. ### Response - `version` (integer) — The version that was read. - `files` (array) — Build output files. Each has `path` (relative to the build root), `url` (a presigned download URL), `contentType`, and optionally `size` in bytes. - `expiresInSeconds` (integer) — How long the presigned URLs stay valid (currently one hour). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/build-files \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response (truncated)" { "version": 6, "files": [ { "path": "index.html", "url": "https://s3.amazonaws.com/...&X-Amz-Expires=3600", "contentType": "text/html" }, { "path": "assets/index-Bx91k2.js", "url": "https://s3.amazonaws.com/...&X-Amz-Expires=3600", "contentType": "application/javascript" } ], "expiresInSeconds": 3600, "errors": [] } ``` --- ## Get a preview URL **POST** `/custom-apps-v2/build-url` Returns a signed URL for opening the latest published build in a browser. The URL carries a short-lived token that the CDN validates before serving the app. Fails with an error if the app has no published version. ### Request parameters - `appId` (string, required) — The app to open. ### Response - `previewUrl` (string) — Signed URL that loads the published app. - `expiresAt` (integer) — Unix epoch seconds when the URL stops being accepted. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/build-url \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response" { "previewUrl": "https://apps.autessa.com/auth?token=...&redirect=/custom-apps-v2/.../published/index.html", "expiresAt": 1783420800, "errors": [] } ``` --- ## List files **POST** `/custom-apps-v2/files/list` Lists every file path in the app's draft workspace, plus the paths of the platform-managed Autessa library files (under `src/autessa/`). Library files are refreshed to the current platform version by this call and are read-only — writes to them are managed by the platform, not the file endpoints. ### Request parameters - `appId` (string, required) — The app whose files to list. ### Response - `files` (array) — All file paths in the draft workspace. - `readOnlyFiles` (array) — Paths of the platform-managed Autessa library files. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/list \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response" { "files": [ "index.html", "src/App.jsx", "src/components/InspectionForm.jsx", "src/autessa/autessa-client.js" ], "readOnlyFiles": [ "src/autessa/autessa-client.js" ], "errors": [] } ``` --- ## Get a file **POST** `/custom-apps-v2/files/get` Reads one file's content from the draft workspace. ### Request parameters - `appId` (string, required) — The app the file belongs to. - `filePath` (string, required) — Path of the file to read (as returned by [`/files/list`](#list-files)). ### Response - `filePath` (string) — The requested path. - `content` (string) — The file's content. - `errors` (array) — Error messages, if any occurred (for example `"File not found"`). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "filePath": "src/App.jsx" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/get', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'V1StGXR8Z5', filePath: 'src/App.jsx' }) } ); const { content } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/get', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'appId': 'V1StGXR8Z5', 'filePath': 'src/App.jsx'} ) content = response.json()['content'] ``` ```json title="Response" { "filePath": "src/App.jsx", "content": "import React from 'react';\n\nexport default function App() { ... }", "errors": [] } ``` --- ## Get files in batch **POST** `/custom-apps-v2/files/get-batch` Reads several files in one call. ### Request parameters - `appId` (string, required) — The app the files belong to. - `filePaths` (array, required) — Paths of the files to read. ### Response - `files` (object) — Map of file path to content. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/get-batch \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "filePaths": ["src/App.jsx", "index.html"] }' ``` ```json title="Response" { "files": { "src/App.jsx": "import React from 'react'; ...", "index.html": " ..." }, "errors": [] } ``` --- ## Create a file **POST** `/custom-apps-v2/files/create` Writes a new file into the draft workspace. If the path already exists its content is replaced — `files/create` and [`/files/update`](#update-a-file) behave identically. Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `appId` (string, required) — The app to write into. - `filePath` (string, required) — Path of the file to create. - `content` (string, required) — The file's content. ### Response - `success` (boolean) — `true` when the file was written. - `filePath` (string) — The written path. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "filePath": "src/components/StatusBadge.jsx", "content": "export default function StatusBadge({ status }) { ... }" }' ``` ```json title="Response" { "success": true, "filePath": "src/components/StatusBadge.jsx", "errors": [] } ``` --- ## Update a file **POST** `/custom-apps-v2/files/update` Replaces a file's content in the draft workspace. Draft edits never affect published versions until you [`/publish`](#publish-an-app). Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `appId` (string, required) — The app to write into. - `filePath` (string, required) — Path of the file to update. - `content` (string, required) — The new content. ### Response - `success` (boolean) — `true` when the file was written. - `filePath` (string) — The written path. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "filePath": "src/App.jsx", "content": "import React from 'react'; // updated ..." }' ``` ```json title="Response" { "success": true, "filePath": "src/App.jsx", "errors": [] } ``` --- ## Delete a file **POST** `/custom-apps-v2/files/delete` Removes a file from the draft workspace. Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `appId` (string, required) — The app the file belongs to. - `filePath` (string, required) — Path of the file to delete. ### Response - `success` (boolean) — `true` when the file was deleted. - `filePath` (string) — The deleted path. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "filePath": "src/components/StatusBadge.jsx" }' ``` ```json title="Response" { "success": true, "filePath": "src/components/StatusBadge.jsx", "errors": [] } ``` --- ## Rename a file **POST** `/custom-apps-v2/files/rename` Moves a file to a new path in the draft workspace. Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `appId` (string, required) — The app the file belongs to. - `oldPath` (string, required) — Current path of the file. - `newPath` (string, required) — New path for the file. ### Response - `success` (boolean) — `true` when the file was renamed. - `filePath` (string) — The file's new path. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/rename \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "oldPath": "src/Badge.jsx", "newPath": "src/components/Badge.jsx" }' ``` ```json title="Response" { "success": true, "filePath": "src/components/Badge.jsx", "errors": [] } ``` --- ## Save files in batch **POST** `/custom-apps-v2/files/save-batch` Writes several files in one call — the batch equivalent of [`/files/create`](#create-a-file) / [`/files/update`](#update-a-file). Existing paths are overwritten, new paths are created. Requires WRITE, PUBLISH, or ADMIN access. ### Request parameters - `appId` (string, required) — The app to write into. - `files` (object, required) — Map of file path to content. ### Response - `savedPaths` (array) — Paths that were written. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/save-batch \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "files": { "src/App.jsx": "import React from ...", "src/api.js": "export async function listInspections() { ... }" } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/save-batch', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'V1StGXR8Z5', files: { 'src/App.jsx': 'import React from ...', 'src/api.js': 'export async function listInspections() { ... }' } }) } ); const { savedPaths } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/files/save-batch', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'appId': 'V1StGXR8Z5', 'files': { 'src/App.jsx': 'import React from ...', 'src/api.js': 'export async function listInspections() { ... }' } } ) saved = response.json()['savedPaths'] ``` ```json title="Response" { "savedPaths": ["src/App.jsx", "src/api.js"], "errors": [] } ``` --- ## Refresh libraries **POST** `/custom-apps-v2/refresh-libraries` Rewrites the app's `src/autessa/` library files with the current platform versions and removes legacy copies from old locations. This also happens automatically on [`/get`](#get-an-app) and [`/files/list`](#list-files) — call it explicitly when you want to force a refresh without another read. ### Request parameters - `appId` (string, required) — The app whose libraries to refresh. ### Response - `refreshedFiles` (array) — Paths of the library files that were rewritten. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/refresh-libraries \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response" { "refreshedFiles": [ "src/autessa/autessa-client.js" ], "errors": [] } ``` --- ## Share an app **POST** `/custom-apps-v2/share` Configures two independent things in one call: **code sharing** (which company members can see and edit the source) and **hosting mode** (how end users access the deployed app). Send only the parts you want to change — fields left out are untouched. Non-default hosting modes (`PUBLIC`, `AUTH_CODE`, `CUSTOM_AUTH`, `TOKEN`) require a valid, available `publicSlug` — check it first with [`/check-slug`](#check-slug-availability). Switching to `CUSTOM_AUTH` provisions an AutessaDB auth table for the app's end users; this is idempotent, so re-enabling `CUSTOM_AUTH` reuses the existing table and keeps signed-up users. Set `requireEmailVerification` to make new signups confirm an emailed code before the account is created, and `enabledProviders` to choose between email and password, Google, and Microsoft sign-in. Switching to `TOKEN` provisions a separate AutessaDB table for the people who get access links; switching modes never deletes either table. Access links themselves are managed in the App Builder. An `AUTH_CODE` code is stored hashed and never returned. Requires ADMIN access on the app. ### Request parameters - `appId` (string, required) — The app to configure. - `sharingLevel` (string) — Code sharing level: `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — When `sharingLevel` is `COMPANY`, the access level every member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `usersToAdd` (array) — When `sharingLevel` is `SPECIFIC_MEMBER`, users to grant access. Each entry: `userId` (string), optional `userEmail`, and `accessLevel`. - `usersToRemove` (array) — Users whose access to revoke, same entry shape. - `hostingMode` (string) — `DEFAULT`, `PUBLIC`, `AUTH_CODE`, `CUSTOM_AUTH`, or `TOKEN`. - `authCode` (string) — The shared access code, when `hostingMode` is `AUTH_CODE`. Stored hashed. - `publicSlug` (string) — URL slug for non-default hosting modes. Must be valid and available. - `hostingPermissions` (object) — Which company resources the hosted app may call on behalf of end users: `tables` (array of `tableId` + `canRead`/`canWrite`/`canDelete`), `agentIds`, `toolIds`, `managerIds` (arrays of ID strings), `allowedPoweruserEndpoints` (array of endpoint paths), `envelopes` (array of `envelopeId` + `level`), and `esignatureActsAsUser` (boolean). - `authTableName` (string) — Name for the AutessaDB auth table provisioned for `CUSTOM_AUTH`. - `authPageConfig` (string) — JSON string with auth page theming for `CUSTOM_AUTH`. May also be sent on its own, without a `hostingMode` change. - `requireEmailVerification` (boolean) — For `CUSTOM_AUTH`: require new signups to confirm a code emailed to their address before the account is created. Defaults to `false`. May be sent on its own, without a `hostingMode` change. Omit to leave the current setting alone. Accounts that already exist are unaffected when this is switched on. - `enabledProviders` (array) — For `CUSTOM_AUTH`: the sign-in methods to offer, any of `PASSWORD`, `GOOGLE`, and `MICROSOFT`. Must name at least one. Omit to leave the current methods alone. New apps offer `PASSWORD` only. ### Response - `sharingLevel` (string) — The resulting code sharing level. - `companyPermissionLevel` (string) — The resulting company-wide access level, when shared with the company. - `hostingMode` (string) — The resulting hosting mode. - `sharedUsers` (array) — The resulting per-user grants: `userEmail`, `userId`, `accessLevel`. - `errors` (array) — Error messages, if any occurred (for example `"publicSlug is already taken"`). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "sharingLevel": "SPECIFIC_MEMBER", "usersToAdd": [ { "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "hostingMode": "AUTH_CODE", "authCode": "site-crew-2026", "publicSlug": "field-inspections", "hostingPermissions": { "tables": [ { "tableId": "tQ6bF3jN8s", "canRead": true, "canWrite": true, "canDelete": false } ], "agentIds": ["gD4vM8kP1c"], "esignatureActsAsUser": false } }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/share', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'V1StGXR8Z5', sharingLevel: 'SPECIFIC_MEMBER', usersToAdd: [{ userId: 'uB7nT2wQ9k', accessLevel: 'WRITE' }], hostingMode: 'AUTH_CODE', authCode: 'site-crew-2026', publicSlug: 'field-inspections' }) } ); const { hostingMode, sharedUsers } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/share', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'appId': 'V1StGXR8Z5', 'sharingLevel': 'SPECIFIC_MEMBER', 'usersToAdd': [{'userId': 'uB7nT2wQ9k', 'accessLevel': 'WRITE'}], 'hostingMode': 'AUTH_CODE', 'authCode': 'site-crew-2026', 'publicSlug': 'field-inspections' } ) data = response.json() ``` ```json title="Response" { "sharingLevel": "SPECIFIC_MEMBER", "hostingMode": "AUTH_CODE", "sharedUsers": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "errors": [] } ``` --- ## Check slug availability **POST** `/custom-apps-v2/check-slug` Checks whether a public slug is valid and free before you commit to it in [`/share`](#share-an-app). Pass `appId` so the app's own current slug counts as available. A race with another caller can still lose the slug between check and share — [`/share`](#share-an-app) re-validates and returns `"publicSlug is already taken"` in that case. ### Request parameters - `publicSlug` (string, required) — The slug to check. - `appId` (string) — The app the slug is intended for; excludes that app's existing slug from the availability check. ### Response - `available` (boolean) — Whether the slug can be used. - `reason` (string) — When `available` is `false`: `"empty"`, `"invalid_format"`, or `"taken"`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/check-slug \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "publicSlug": "field-inspections" }' ``` ```json title="Response" { "available": true, "errors": [] } ``` --- ## Transfer ownership **POST** `/custom-apps-v2/transfer-ownership` Transfers the app to another member of the company. Requires ADMIN access on the app. ### Request parameters - `appId` (string, required) — The app to transfer. - `newOwnerId` (string, required) — User ID of the new owner. ### Response - `app` (App) — The updated [app object](#the-app-object) with the new `ownerId`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/transfer-ownership \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "newOwnerId": "uB7nT2wQ9k" }' ``` ```json title="Response (truncated)" { "app": { "appId": "V1StGXR8Z5", "ownerId": "uB7nT2wQ9k", "appName": "Field Inspection Portal" }, "errors": [] } ``` --- ## Get sharing details **POST** `/custom-apps-v2/get-sharing-details` Returns the app's code sharing configuration, hosting mode, and the calling user's own access level. ### Request parameters - `appId` (string, required) — The app to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `hostingMode` (string) — `DEFAULT`, `PUBLIC`, `AUTH_CODE`, or `CUSTOM_AUTH`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId`, `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/get-sharing-details \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5" }' ``` ```json title="Response" { "sharingLevel": "SPECIFIC_MEMBER", "hostingMode": "AUTH_CODE", "sharedWith": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "WRITE" } ], "userAccessLevel": "ADMIN", "errors": [] } ``` --- ## List Vibecode sessions **POST** `/custom-apps-v2/vibecode/sessions` Lists the calling user's Vibecode AI assistant sessions for an app, newest first, capped at the 10 most recent. The interactive Vibecode chat itself is UI-only (a WebSocket in the App Builder) and is not exposed through the Poweruser API — these endpoints are read-only session history. By default only open sessions are returned; set `includeClosed` to also see finished ones. ### Request parameters - `appId` (string, required) — The app whose sessions to list. - `includeClosed` (boolean) — Also include `CLOSED` sessions. Defaults to `false` (active sessions only). ### Response - `sessions` (array) — Session records, newest first (max 10). - `sessions[].conversationId` (string) — Session identifier — pass to [`/vibecode/session`](#get-a-vibecode-session). - `sessions[].appId` (string) — The app the session belongs to. - `sessions[].state` (string) — `ACTIVE`, `PENDING_CONFIRMATION` (the AI proposed changes awaiting user confirmation), or `CLOSED`. - `sessions[].createdAt` (string) — ISO-8601 creation timestamp. - `sessions[].updatedAt` (string) — ISO-8601 timestamp of the last activity. - `sessions[].closedAt` (string) — When the session was closed, if it was. - `sessions[].summary` (string) — Short AI-generated summary, set when the session closes. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/vibecode/sessions \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "appId": "V1StGXR8Z5", "includeClosed": true }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/vibecode/sessions', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ appId: 'V1StGXR8Z5', includeClosed: true }) } ); const { sessions } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/vibecode/sessions', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'appId': 'V1StGXR8Z5', 'includeClosed': True} ) sessions = response.json()['sessions'] ``` ```json title="Response" { "sessions": [ { "conversationId": "vibe_9f2c7a1d4e", "appId": "V1StGXR8Z5", "state": "CLOSED", "createdAt": "2026-06-30T14:02:11", "updatedAt": "2026-06-30T14:38:47", "closedAt": "2026-06-30T14:38:47", "summary": "Added inspection photo upload form" } ], "errors": [] } ``` --- ## Get a Vibecode session **POST** `/custom-apps-v2/vibecode/session` Fetches one Vibecode session with its full conversation log — user messages, assistant replies, and every tool call the assistant made against the app's files. Sessions are personal: only the user who ran the session can read it, even if others have access to the app. ### Request parameters - `conversationId` (string, required) — The session to fetch (from [`/vibecode/sessions`](#list-vibecode-sessions)). ### Response - `session` (VibecodeSession) — The session record, same shape as entries in [`/vibecode/sessions`](#list-vibecode-sessions). - `logs` (array) — The conversation log, in order. - `logs[].logType` (string) — `USER_MESSAGE`, `ASSISTANT_MESSAGE`, `TOOL_CALL`, `TOOL_RESULT`, or `SYSTEM`. - `logs[].content` (string) — Message text, for user and assistant entries. - `logs[].toolName` (string) — Tool name, for `TOOL_CALL` and `TOOL_RESULT` entries. - `logs[].toolCallId` (string) — Matches a `TOOL_CALL` to its `TOOL_RESULT`. - `logs[].toolArguments` (string) — Tool arguments as a JSON string, for `TOOL_CALL` entries. - `logs[].toolResult` (string) — The tool's result, for `TOOL_RESULT` entries. - `logs[].timestamp` (string) — ISO-8601 timestamp of the entry. - `errors` (array) — Error messages, if any occurred (for example `"Conversation not found"` or `"Access denied"`). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/custom-apps-v2/vibecode/session \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "conversationId": "vibe_9f2c7a1d4e" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/vibecode/session', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ conversationId: 'vibe_9f2c7a1d4e' }) } ); const { session, logs } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/custom-apps-v2/vibecode/session', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'conversationId': 'vibe_9f2c7a1d4e'} ) logs = response.json()['logs'] ``` ```json title="Response (truncated)" { "session": { "conversationId": "vibe_9f2c7a1d4e", "appId": "V1StGXR8Z5", "state": "CLOSED", "summary": "Added inspection photo upload form" }, "logs": [ { "logType": "USER_MESSAGE", "content": "Add a photo upload to the inspection form.", "timestamp": "2026-06-30T14:02:15" }, { "logType": "TOOL_CALL", "toolName": "write_file", "toolCallId": "call_01", "toolArguments": "{\"filePath\":\"src/components/PhotoUpload.jsx\", ...}", "timestamp": "2026-06-30T14:02:44" }, { "logType": "TOOL_RESULT", "toolName": "write_file", "toolCallId": "call_01", "toolResult": "File written", "timestamp": "2026-06-30T14:02:45" }, { "logType": "ASSISTANT_MESSAGE", "content": "Done — I added a PhotoUpload component and wired it into the form.", "timestamp": "2026-06-30T14:03:02" } ], "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation and access messages (`"No access to this app"`, `"Only admin can delete apps"`, `"File not found"`, `"publicSlug is already taken"`, `"No published version available"`, `"Conversation not found"`). | | `401` | Invalid or missing poweruser key. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | Access-level denials come back as messages in `errors` on a `200` response rather than as HTTP-level failures — see the [access table](#build-lifecycle-and-access-levels) for what each endpoint requires. --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) - [App Builder guide](/docs/guide/platform/app-builder.md) - [Vibecode guide](/docs/guide/autobots/vibecode.md) --- # Voice Cloning Source: https://docs.autessa.com/docs/api/poweruser/voice-cloning > Poweruser endpoints for uploading voice samples, creating instant and deep (fine-tuned) voice clones, and managing sharing and ownership. # Voice Cloning Create custom voices from audio samples — instantly from a single recording, or as a deep clone fine-tuned on a training dataset — then share them across your company and use them as any agent's voice. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/voice`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. --- ## Instant clones vs deep clones There are two ways to clone a voice, with different quality/effort trade-offs and separate company entitlements: - **Instant clones** are created synchronously from a single WAV sample in one [`/instant-clone/create`](#create-an-instant-clone) call. Available when voice cloning is enabled for the company. - **Deep clones** are fine-tuned voice models trained on a dataset of samples. They follow a three-step lifecycle — [`/deep-clone/create-dataset`](#create-a-deep-clone-dataset), then [`/deep-clone/upload-files`](#upload-files-to-a-dataset), then [`/deep-clone/start-finetune`](#start-a-fine-tune) — and train asynchronously. Poll [`/deep-clone/get-status`](#get-fine-tune-status) until the clone reaches `COMPLETED`. Available only when voice **fine-tuning** is enabled for the company (a separate, higher-tier entitlement). If the relevant entitlement is missing, the create call returns a `400` with `"Voice cloning is not enabled for this company"` (instant) or `"Voice fine-tuning is not enabled for this company"` (deep). A deep clone moves through these statuses: `PENDING` (dataset created) → `UPLOADING` (samples being added) → `TRAINING` (fine-tune running) → `COMPLETED` or `FAILED`. Once an instant clone is created or a deep clone completes, the voice appears under `customVoiceNames` in the voice options returned by the [Agents API](/docs/api/poweruser/agents.md) and can be set as an agent's `voiceConfig.voiceName`. Both clone types use the platform-wide sharing model. Endpoints enforce the linked user's access level on the clone: | Operation | Required access | |-----------|-----------------| | `get`, `get-all`, `get-status`, `get-sharing-details` | Any access (READ or higher) | | `upload-files`, `start-finetune` | WRITE or ADMIN | | `share`, `delete`, `transfer-ownership` | ADMIN | --- ## Generate an audio upload link **POST** `/voice/generate-audio-upload-link` Returns a pre-signed S3 URL for uploading a WAV voice sample. Upload the file with an HTTP `PUT` to `uploadUrl` (with `Content-Type: audio/wav`), then pass the returned `s3Uri` as the sample reference to [`/instant-clone/create`](#create-an-instant-clone) or [`/deep-clone/upload-files`](#upload-files-to-a-dataset). Request one link per file — each call returns a unique upload destination. ### Request parameters None — send an empty object `{}` (WAV format is currently fixed). ### Response - `uploadUrl` (string) — Pre-signed URL; `PUT` the file bytes here with `Content-Type: audio/wav`. - `s3Uri` (string) — S3 URI referencing the uploaded file. Pass this to the clone endpoints. - `expiresInSeconds` (integer) — How long the upload URL stays valid (currently `600` — ten minutes). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/voice/generate-audio-upload-link \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' # Then upload the sample: curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: audio/wav" \ --data-binary '@voice-sample.wav' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/voice/generate-audio-upload-link', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({}) } ); const { uploadUrl, s3Uri } = await response.json(); // Upload the WAV bytes await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': 'audio/wav' }, body: wavFileBytes }); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/voice/generate-audio-upload-link', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={} ) link = response.json() with open('voice-sample.wav', 'rb') as f: requests.put( link['uploadUrl'], headers={'Content-Type': 'audio/wav'}, data=f ) s3_uri = link['s3Uri'] ``` ```json title="Response" { "uploadUrl": "https://s3.amazonaws.com/autessa-voice/...", "s3Uri": "s3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav", "expiresInSeconds": 600, "errors": [] } ``` --- ## Create an instant clone **POST** `/voice/instant-clone/create` Creates a voice clone from a single uploaded WAV sample. The clone is created synchronously — the response already carries the provider voice ID and the voice is immediately usable. New clones start `PRIVATE`; use [`/instant-clone/share`](#share-an-instant-clone) to grant access. Requires the company's voice cloning entitlement; without it the call returns a `400` with `"Voice cloning is not enabled for this company"`. ### Request parameters - `voiceName` (string, required) — Display name for the voice. Must be non-blank. - `audioS3Uri` (string, required) — S3 URI of the uploaded WAV sample, from [`/generate-audio-upload-link`](#generate-an-audio-upload-link). ### Response - `voiceCloneId` (string) — The new instant clone's ID. - `voiceName` (string) — The stored display name. - `serviceVoiceId` (string) — The underlying speech provider's voice identifier. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "voiceName": "Support Narrator", "audioS3Uri": "s3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/voice/instant-clone/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ voiceName: 'Support Narrator', audioS3Uri: 's3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav' }) } ); const { voiceCloneId } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/voice/instant-clone/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'voiceName': 'Support Narrator', 'audioS3Uri': 's3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav' } ) voice_clone_id = response.json()['voiceCloneId'] ``` ```json title="Response" { "voiceCloneId": "V1StGXR8Z5", "voiceName": "Support Narrator", "serviceVoiceId": "a0e99841-438c-4a64-b679-ae501e7d6091", "errors": [] } ``` --- ## Get an instant clone **POST** `/voice/instant-clone/get` Retrieves a single instant clone, including its sharing state and the calling user's access level. Requires any access to the clone. ### Request parameters - `instantVoiceCloneId` (string, required) — The instant clone to fetch. ### Response - `voiceCloneId` (string) — The clone's ID. - `voiceName` (string) — Display name. - `serviceVoiceId` (string) — The speech provider's voice identifier. - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `userAccessLevel` (string) — The calling user's effective access level on this clone. - `createdAt` (string) — ISO-8601 creation timestamp. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "instantVoiceCloneId": "V1StGXR8Z5" }' ``` ```json title="Response" { "voiceCloneId": "V1StGXR8Z5", "voiceName": "Support Narrator", "serviceVoiceId": "a0e99841-438c-4a64-b679-ae501e7d6091", "sharingLevel": "PRIVATE", "companyPermissionLevel": null, "userAccessLevel": "ADMIN", "createdAt": "2026-06-28T17:42:10", "errors": [] } ``` --- ## List all instant clones **POST** `/voice/instant-clone/get-all` Lists every instant clone visible to the linked user, grouped by how they have access. ### Request parameters None — send an empty object `{}`. ### Response Each entry is a summary with `id`, `voiceName`, `serviceVoiceId`, `sharingLevel`, and `createdAt`. - `ownedClones` (array) — Instant clones the linked user owns. - `companySharedClones` (array) — Clones shared with the whole company by other users. - `sharedWithMeClones` (array) — Clones shared specifically with the linked user. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/get-all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "ownedClones": [ { "id": "V1StGXR8Z5", "voiceName": "Support Narrator", "serviceVoiceId": "a0e99841-438c-4a64-b679-ae501e7d6091", "sharingLevel": "PRIVATE", "createdAt": "2026-06-28T17:42:10" } ], "companySharedClones": [], "sharedWithMeClones": [], "errors": [] } ``` --- ## Delete an instant clone **POST** `/voice/instant-clone/delete` Permanently deletes an instant clone. Agents configured to use the voice fall back to their built-in voice. Requires ADMIN access on the clone. ### Request parameters - `instantVoiceCloneId` (string, required) — The instant clone to delete. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "instantVoiceCloneId": "V1StGXR8Z5" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Share an instant clone **POST** `/voice/instant-clone/share` Sets the clone's sharing level and grants per-user access. When `shareLevel` is `SPECIFIC_MEMBER`, `userIds` and `permissionLevels` are parallel arrays — the user at index *i* receives the permission at index *i* (defaulting to `READ` when `permissionLevels` is shorter or omitted). Requires ADMIN access on the clone. ### Request parameters - `instantVoiceCloneId` (string, required) — The instant clone to share. - `shareLevel` (string, required) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — When `shareLevel` is `COMPANY`, the access level every company member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `userIds` (array) — When `shareLevel` is `SPECIFIC_MEMBER`, the users to grant access to. - `permissionLevels` (array) — Access level for each entry in `userIds`, positionally matched. Missing entries default to `READ`. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "instantVoiceCloneId": "V1StGXR8Z5", "shareLevel": "SPECIFIC_MEMBER", "userIds": ["uB7nT2wQ9k"], "permissionLevels": ["READ"] }' ``` ```json title="Response" { "errors": [] } ``` --- ## Get instant clone sharing details **POST** `/voice/instant-clone/get-sharing-details` Returns the clone's current sharing configuration and the calling user's own access level. Requires any access to the clone. ### Request parameters - `instantVoiceCloneId` (string, required) — The instant clone to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId` (string), `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/get-sharing-details \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "instantVoiceCloneId": "V1StGXR8Z5" }' ``` ```json title="Response" { "sharingLevel": "SPECIFIC_MEMBER", "companyPermissionLevel": null, "sharedWith": [ { "userEmail": "sam@acme.com", "userId": "uB7nT2wQ9k", "accessLevel": "READ" } ], "userAccessLevel": "ADMIN", "errors": [] } ``` --- ## Transfer instant clone ownership **POST** `/voice/instant-clone/transfer-ownership` Transfers the clone to another member of the company. Requires ADMIN access on the clone. ### Request parameters - `instantVoiceCloneId` (string, required) — The instant clone to transfer. - `newOwnerId` (string, required) — User ID of the new owner. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/instant-clone/transfer-ownership \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "instantVoiceCloneId": "V1StGXR8Z5", "newOwnerId": "uB7nT2wQ9k" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Create a deep clone dataset **POST** `/voice/deep-clone/create-dataset` Starts a deep clone by creating its training dataset. The new clone starts in status `PENDING` with sharing level `PRIVATE`. Next, add samples with [`/deep-clone/upload-files`](#upload-files-to-a-dataset). Requires the company's voice **fine-tuning** entitlement; without it the call returns a `400` with `"Voice fine-tuning is not enabled for this company"`. ### Request parameters - `voiceName` (string, required) — Display name for the voice. Must be non-blank. ### Response - `voiceCloneId` (string) — The new deep clone's ID. Use it as `deepVoiceCloneId` in the rest of the deep-clone endpoints. - `datasetId` (string) — The speech provider's dataset identifier. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/create-dataset \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "voiceName": "Brand Voice" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/voice/deep-clone/create-dataset', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ voiceName: 'Brand Voice' }) } ); const { voiceCloneId } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/voice/deep-clone/create-dataset', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'voiceName': 'Brand Voice'} ) voice_clone_id = response.json()['voiceCloneId'] ``` ```json title="Response" { "voiceCloneId": "dV7kM3pQ9w", "datasetId": "ds_4f8c1a2b9d", "errors": [] } ``` --- ## Upload files to a dataset **POST** `/voice/deep-clone/upload-files` Adds WAV training samples to the clone's dataset and moves the clone to status `UPLOADING`. Upload each sample first via [`/generate-audio-upload-link`](#generate-an-audio-upload-link) and pass the resulting S3 URIs. If any upload fails, the clone is marked `FAILED` and the message is returned in `errors`. Requires WRITE or ADMIN access on the clone, and that the dataset exists (`"Dataset not created yet"` otherwise). ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone whose dataset to fill. - `audioS3Uris` (array, required) — S3 URIs of the uploaded WAV samples. ### Response - `filesUploaded` (integer) — Number of files added to the dataset in this call. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/upload-files \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w", "audioS3Uris": [ "s3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav", "s3://autessa-voice/voice-clones/audio/prod/1234/2c9d7e.wav" ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/voice/deep-clone/upload-files', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ deepVoiceCloneId: 'dV7kM3pQ9w', audioS3Uris: [ 's3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav', 's3://autessa-voice/voice-clones/audio/prod/1234/2c9d7e.wav' ] }) } ); const { filesUploaded } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/voice/deep-clone/upload-files', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'deepVoiceCloneId': 'dV7kM3pQ9w', 'audioS3Uris': [ 's3://autessa-voice/voice-clones/audio/prod/1234/8f3a2b.wav', 's3://autessa-voice/voice-clones/audio/prod/1234/2c9d7e.wav' ] } ) files_uploaded = response.json()['filesUploaded'] ``` ```json title="Response" { "filesUploaded": 2, "errors": [] } ``` --- ## Start a fine-tune **POST** `/voice/deep-clone/start-finetune` Kicks off fine-tuning on the clone's dataset and moves the clone to status `TRAINING`. Training is asynchronous — poll [`/deep-clone/get-status`](#get-fine-tune-status) until it reports `COMPLETED` (or `FAILED`). If the fine-tune cannot be started, the clone is marked `FAILED` with the message in `errors`. Requires WRITE or ADMIN access on the clone, and that the dataset exists. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to train. ### Response - `fineTuneJobId` (string) — The speech provider's fine-tune job identifier. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/start-finetune \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w" }' ``` ```json title="Response" { "fineTuneJobId": "ft_9c2e5b7a1d", "errors": [] } ``` --- ## Get fine-tune status **POST** `/voice/deep-clone/get-status` Returns the clone's training status. While the clone is still in progress (`PENDING`, `UPLOADING`, `TRAINING`) this endpoint live-polls the speech provider and updates the stored progress; once the clone is `COMPLETED` or `FAILED` the cached result is returned without polling. On completion, `serviceVoiceId` is populated and the voice becomes usable. Requires any access to the clone. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to check. ### Response - `status` (string) — `PENDING`, `UPLOADING`, `TRAINING`, `COMPLETED`, or `FAILED`. - `progress` (integer) — Training progress, `0`–`100`. - `serviceVoiceId` (string) — The speech provider's voice identifier. Populated once training completes; `null` before that. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/get-status \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w" }' ``` **JavaScript:** ```javascript async function waitForClone(deepVoiceCloneId) { while (true) { const response = await fetch( 'https://api.autessa.com/autessa_poweruser/voice/deep-clone/get-status', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ deepVoiceCloneId }) } ); const data = await response.json(); if (data.status === 'COMPLETED' || data.status === 'FAILED') { return data; } await new Promise((r) => setTimeout(r, 30_000)); } } ``` **Python:** ```python def wait_for_clone(deep_voice_clone_id): while True: response = requests.post( 'https://api.autessa.com/autessa_poweruser/voice/deep-clone/get-status', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'deepVoiceCloneId': deep_voice_clone_id} ) data = response.json() if data['status'] in ('COMPLETED', 'FAILED'): return data time.sleep(30) ``` ```json title="Response" { "status": "TRAINING", "progress": 62, "serviceVoiceId": null, "errors": [] } ``` --- ## List all deep clones **POST** `/voice/deep-clone/get-all` Lists every deep clone visible to the linked user, grouped by how they have access. ### Request parameters None — send an empty object `{}`. ### Response Each entry is a summary with `id`, `voiceName`, `serviceVoiceId`, `status`, `progress`, `sharingLevel`, and `createdAt`. - `ownedClones` (array) — Deep clones the linked user owns. - `companySharedClones` (array) — Clones shared with the whole company by other users. - `sharedWithMeClones` (array) — Clones shared specifically with the linked user. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/get-all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response" { "ownedClones": [ { "id": "dV7kM3pQ9w", "voiceName": "Brand Voice", "serviceVoiceId": "b1f88932-527d-4b75-c78a-bf612e8e7182", "status": "COMPLETED", "progress": 100, "sharingLevel": "COMPANY", "createdAt": "2026-06-25T11:08:44" } ], "companySharedClones": [], "sharedWithMeClones": [], "errors": [] } ``` --- ## Get a deep clone **POST** `/voice/deep-clone/get` Fetches a single deep clone. This returns the same status payload as [`/deep-clone/get-status`](#get-fine-tune-status) — `status`, `progress`, and `serviceVoiceId` — including the same live provider poll while training is in progress. Requires any access to the clone. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to fetch. ### Response - `status` (string) — `PENDING`, `UPLOADING`, `TRAINING`, `COMPLETED`, or `FAILED`. - `progress` (integer) — Training progress, `0`–`100`. - `serviceVoiceId` (string) — The speech provider's voice identifier, once training completes. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w" }' ``` ```json title="Response" { "status": "COMPLETED", "progress": 100, "serviceVoiceId": "b1f88932-527d-4b75-c78a-bf612e8e7182", "errors": [] } ``` --- ## Share a deep clone **POST** `/voice/deep-clone/share` Sets the clone's sharing level and grants per-user access — same semantics as [sharing an instant clone](#share-an-instant-clone): with `SPECIFIC_MEMBER`, `userIds` and `permissionLevels` are positionally matched arrays, defaulting to `READ`. Requires ADMIN access on the clone. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to share. - `shareLevel` (string, required) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — When `shareLevel` is `COMPANY`, the access level every company member gets: `READ`, `WRITE`, `PUBLISH`, or `ADMIN`. - `userIds` (array) — When `shareLevel` is `SPECIFIC_MEMBER`, the users to grant access to. - `permissionLevels` (array) — Access level for each entry in `userIds`, positionally matched. Missing entries default to `READ`. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/share \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w", "shareLevel": "COMPANY", "companyPermissionLevel": "READ" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Get deep clone sharing details **POST** `/voice/deep-clone/get-sharing-details` Returns the clone's current sharing configuration and the calling user's own access level. Requires any access to the clone. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to inspect. ### Response - `sharingLevel` (string) — `PRIVATE`, `SPECIFIC_MEMBER`, or `COMPANY`. - `companyPermissionLevel` (string) — Access level granted company-wide, when `sharingLevel` is `COMPANY`. - `sharedWith` (array) — Per-user grants: `userEmail`, `userId` (string), `accessLevel`. - `userAccessLevel` (string) — The calling user's effective access level. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/get-sharing-details \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w" }' ``` ```json title="Response" { "sharingLevel": "COMPANY", "companyPermissionLevel": "READ", "sharedWith": [], "userAccessLevel": "ADMIN", "errors": [] } ``` --- ## Transfer deep clone ownership **POST** `/voice/deep-clone/transfer-ownership` Transfers the clone to another member of the company. Requires ADMIN access on the clone. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to transfer. - `newOwnerId` (string, required) — User ID of the new owner. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/transfer-ownership \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w", "newOwnerId": "uB7nT2wQ9k" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Delete a deep clone **POST** `/voice/deep-clone/delete` Permanently deletes a deep clone, whatever its training status. Agents configured to use the voice fall back to their built-in voice. Requires ADMIN access on the clone. ### Request parameters - `deepVoiceCloneId` (string, required) — The deep clone to delete. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/voice/deep-clone/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "deepVoiceCloneId": "dV7kM3pQ9w" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages. | | `400` | Feature gating or validation: voice cloning / fine-tuning not enabled for the company, blank `voiceName`, missing `audioS3Uri`, or dataset not created yet. | | `401` | The linked user lacks the required access level (see the [access table](#instant-clones-vs-deep-clones)). | | `403` | Scoped key without the endpoint in its allow-list. | | `404` | Voice clone not found. | | `408` | Unexpected server error; `errors` contains the message. | | `500` | Speech-provider call failed (clone creation, dataset upload, fine-tune start, or status poll); `errors` contains the message. | Failures during [`/deep-clone/upload-files`](#upload-files-to-a-dataset) or [`/deep-clone/start-finetune`](#start-a-fine-tune) also mark the clone `FAILED`, which is then reported by [`/deep-clone/get-status`](#get-fine-tune-status). --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Custom voices guide](/docs/guide/custom-voices.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) --- # Conversation Sync Source: https://docs.autessa.com/docs/api/poweruser/conversation-sync > Poweruser endpoints for managing conversation sync configs that export closed agent conversations into AutessaDB tables. # Conversation Sync Conversation sync configs automatically export finished agent conversations into your AutessaDB tables — the full transcript lands in a column of a table you choose, matched to an existing row or inserted as a new one, so downstream tools, triggers, and reports can work with conversation data like any other row. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/conversation-sync`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. ## How syncing works Each config is a standing rule that runs asynchronously whenever a conversation is logged for your company: 1. **Filter** — only `enabled` configs run. A config can restrict itself to specific agents (`agentIds`), to conversations that do or don't have an evaluation (`requiresEvaluation`), and to production traffic only (`allowSandboxConversations: false` skips Builder sandbox conversations and syncs only conversations started with an API key). 2. **Write** — for every entry in `tableUpdates`, the conversation's transcript JSON is written into `conversationJsonColumn` on rows of the target AutessaDB table where `matchColumn` equals the conversation ID. `additionalColumns` are updated at the same time, with [template variables](#template-variables) substituted. 3. **No match** — if no row matches, the entry's `onNoMatch` behavior decides: `SKIP` drops the write, `INSERT` creates a new row (upsert) that includes the match column set to the conversation ID. Multiple configs can match the same conversation; each runs independently, and a failure in one config or one table update does not stop the others. Because the data lands in regular AutessaDB rows, you can chain triggers on the target table to post-process transcripts. --- ## The conversation sync config object - `id` (string) — Unique config identifier. - `name` (string) — Display name. Must be unique within the company. - `description` (string) — What the config is for. - `enabled` (boolean) — Whether the config runs. Disabled configs are kept but never match. - `agentIds` (array) — Agents whose conversations this config syncs. `null` means all agents. - `requiresEvaluation` (boolean) — `true` syncs only conversations that have an evaluation, `false` only those without one, `null` doesn't filter on evaluation at all. - `allowSandboxConversations` (boolean) — When `false` (the default), only conversations started with an API key are synced; Builder sandbox test conversations are skipped. - `tableUpdates` (array) — The AutessaDB writes to perform for each matching conversation. See [table update entries](#table-update-entries) below. - `createdAt` (string) — ISO-8601 creation timestamp. - `updatedAt` (string) — ISO-8601 timestamp of the last update. ### Table update entries - `tableId` (string) — The AutessaDB table to update. - `matchColumn` (string) — Column name matched against the conversation ID to find the target row (for example `"conversation_id"`). - `conversationJsonColumn` (string) — Column that receives the conversation transcript as a JSON array of log entries. - `additionalColumns` (map) — Extra columns to set on the matched row. Values are literal strings with [template variables](#template-variables) substituted. Target columns must be `SHORT_TEXT` or `LONG_TEXT`. - `onNoMatch` (string) — `SKIP` (default) — do nothing when no row matches. `INSERT` — insert a new row containing the conversation JSON, the additional columns, and the match column set to the conversation ID. - `includeToolCalls` (boolean) — Whether the JSON written to `conversationJsonColumn` includes `TOOL_REQUEST`/`TOOL_RESPONSE` log entries. Defaults to `true`. ### Template variables Values in `additionalColumns` may embed these placeholders: | Variable | Substituted with | |----------|------------------| | `@@{conversationId}` | The conversation's ID. | | `@@{closeDate}` | When the conversation closed. | | `@@{agentId}` | The agent's ID. | | `@@{agentVersion}` | The agent version that ran the conversation. | | `@@{apiKeyId}` | The API key that started the conversation, if any. | | `@@{companyId}` | Your company's ID. | | `@@{conversation}` | Full transcript JSON — always includes tool calls. | | `@@{shortened_conversation}` | Transcript JSON without tool calls. | ```json title="Conversation sync config object" { "id": "V1StGXR8Z5", "name": "Closed support tickets", "description": "Writes finished support conversations back onto the ticket row.", "enabled": true, "agentIds": ["aG9dK2mP4q"], "requiresEvaluation": null, "allowSandboxConversations": false, "tableUpdates": [ { "tableId": "tB6xN4rW8z", "matchColumn": "conversation_id", "conversationJsonColumn": "transcript", "additionalColumns": { "status": "closed", "closed_at": "@@{closeDate}", "agent_version": "@@{agentVersion}" }, "onNoMatch": "INSERT", "includeToolCalls": true } ], "createdAt": "2026-06-12T09:30:00", "updatedAt": "2026-06-28T17:42:10" } ``` --- ## Create a config **POST** `/conversation-sync/create` Creates a new sync config for the company. The name must be unique within the company — creation fails with an error message otherwise. The config starts matching conversations immediately unless you pass `enabled: false`. ### Request parameters - `name` (string, required) — Display name, unique within the company. - `description` (string) — What the config is for. - `enabled` (boolean) — Whether the config runs. Defaults to `true`. - `agentIds` (array) — Restrict to these agents. Omit or send an empty array to sync all agents. - `requiresEvaluation` (boolean) — `true` = only evaluated conversations, `false` = only unevaluated ones. Omit to sync regardless of evaluation. - `allowSandboxConversations` (boolean) — Also sync Builder sandbox conversations. Defaults to `false` (API-key conversations only). - `tableUpdates` (array, required) — The writes to perform, in the [table update entry](#table-update-entries) shape. ### Response - `configId` (string) — The new config's ID. - `message` (string) — Confirmation message. - `errors` (array) — Error messages, if any occurred (for example `"A conversation sync config with this name already exists"`). **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/conversation-sync/create \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Closed support tickets", "description": "Writes finished support conversations back onto the ticket row.", "agentIds": ["aG9dK2mP4q"], "tableUpdates": [ { "tableId": "tB6xN4rW8z", "matchColumn": "conversation_id", "conversationJsonColumn": "transcript", "additionalColumns": { "status": "closed", "closed_at": "@@{closeDate}" }, "onNoMatch": "INSERT" } ] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/conversation-sync/create', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Closed support tickets', description: 'Writes finished support conversations back onto the ticket row.', agentIds: ['aG9dK2mP4q'], tableUpdates: [ { tableId: 'tB6xN4rW8z', matchColumn: 'conversation_id', conversationJsonColumn: 'transcript', additionalColumns: { status: 'closed', closed_at: '@@{closeDate}' }, onNoMatch: 'INSERT' } ] }) } ); const { configId } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/conversation-sync/create', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'name': 'Closed support tickets', 'description': 'Writes finished support conversations back onto the ticket row.', 'agentIds': ['aG9dK2mP4q'], 'tableUpdates': [ { 'tableId': 'tB6xN4rW8z', 'matchColumn': 'conversation_id', 'conversationJsonColumn': 'transcript', 'additionalColumns': {'status': 'closed', 'closed_at': '@@{closeDate}'}, 'onNoMatch': 'INSERT' } ] } ) config_id = response.json()['configId'] ``` ```json title="Response" { "configId": "V1StGXR8Z5", "message": "Conversation sync config created successfully", "errors": [] } ``` --- ## Get a config **POST** `/conversation-sync/get` Retrieves a single sync config. Configs are company-scoped — requesting a config that belongs to another company returns an error. ### Request parameters - `id` (string, required) — The config to fetch. ### Response - `config` (ConversationSyncConfig) — The [conversation sync config object](#the-conversation-sync-config-object). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/conversation-sync/get \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "V1StGXR8Z5" }' ``` ```json title="Response (truncated)" { "config": { "id": "V1StGXR8Z5", "name": "Closed support tickets", "enabled": true, "agentIds": ["aG9dK2mP4q"], "allowSandboxConversations": false, "tableUpdates": [ { "tableId": "tB6xN4rW8z", "matchColumn": "conversation_id", "conversationJsonColumn": "transcript", "onNoMatch": "INSERT", "includeToolCalls": true } ], "createdAt": "2026-06-12T09:30:00", "updatedAt": "2026-06-28T17:42:10" }, "errors": [] } ``` --- ## List all configs **POST** `/conversation-sync/all` Lists every sync config in the company, enabled or not. ### Request parameters None — send an empty object `{}`. ### Response - `configs` (array) — All configs, each in the [conversation sync config object](#the-conversation-sync-config-object) shape. - `totalCount` (integer) — Number of configs returned. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/conversation-sync/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "configs": [ { "id": "V1StGXR8Z5", "name": "Closed support tickets", "enabled": true, "agentIds": ["aG9dK2mP4q"], "allowSandboxConversations": false, "tableUpdates": [{ "tableId": "tB6xN4rW8z", "matchColumn": "conversation_id" }] } ], "totalCount": 1, "errors": [] } ``` --- ## Update a config **POST** `/conversation-sync/update` Partially updates a config: only the fields you send are changed, everything else keeps its current value. Renames are checked for uniqueness. Note that `tableUpdates` is replaced wholesale when present — send the complete list you want, not a delta. ### Request parameters - `id` (string, required) — The config to update. - `name` (string) — New display name, unique within the company. - `description` (string) — New description. - `enabled` (boolean) — Enable or disable the config. - `agentIds` (array) — New agent filter. Send an empty array `[]` to clear the filter and sync all agents. - `requiresEvaluation` (boolean) — New evaluation filter. - `allowSandboxConversations` (boolean) — New sandbox allowance. - `tableUpdates` (array) — Full replacement list of [table update entries](#table-update-entries). ### Response - `message` (string) — Confirmation message. - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/conversation-sync/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "V1StGXR8Z5", "enabled": false, "agentIds": [] }' ``` **JavaScript:** ```javascript await fetch( 'https://api.autessa.com/autessa_poweruser/conversation-sync/update', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ id: 'V1StGXR8Z5', enabled: false, agentIds: [] }) } ); ``` **Python:** ```python requests.post( 'https://api.autessa.com/autessa_poweruser/conversation-sync/update', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'id': 'V1StGXR8Z5', 'enabled': False, 'agentIds': []} ) ``` ```json title="Response" { "message": "Conversation sync config updated successfully", "errors": [] } ``` --- ## Delete a config **POST** `/conversation-sync/delete` Permanently deletes a sync config. Conversations that were already synced stay in their AutessaDB tables; only future syncing stops. ### Request parameters - `id` (string, required) — The config to delete. ### Response - `message` (string) — Confirmation message. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/conversation-sync/delete \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "V1StGXR8Z5" }' ``` ```json title="Response" { "message": "Conversation sync config deleted successfully", "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for validation messages (duplicate config name, `"You do not have access to this config"` for another company's config). | | `401` | Invalid or missing poweruser key. | | `403` | Scoped key without the endpoint in its allow-list. | | `408` | Server-side error; `errors` contains the message. | Sync execution itself is asynchronous — write failures against the target table (unknown column names, unsupported column types) are logged server-side and never surface through these endpoints, so double-check column names in `tableUpdates` against the target table's schema. --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [Agent history guide](/docs/guide/agentic/agent-history.md) - [Agents (Poweruser API)](/docs/api/poweruser/agents.md) - [Managers (Poweruser API)](/docs/api/poweruser/managers.md) --- # Company Source: https://docs.autessa.com/docs/api/poweruser/company > Poweruser endpoints for company settings, team membership, roles, and usage limits. # Company Manage the company the poweruser key belongs to: rename it or transfer ownership, list members and their permissions, invite and remove team members, and read a full snapshot of usage against every company limit. > All paths on this page are relative to `https://api.autessa.com/autessa_poweruser/company`. Every request is a POST with a JSON body, and every ID is an opaque string. See the [Poweruser API overview](/docs/api/poweruser.md) for authentication and conventions. The key acts as its linked user: team-changing endpoints (`/update`, `/user/invite`, `/user/remove`) require the linked user to hold the `MANAGE_TEAM_MEMBERS` permission; read endpoints work for any member. Two things are deliberately **not** exposed on the poweruser surface: - **Billing management** — payment methods, invoices, and commitment changes are UI-only. The read-only [`/usage-status`](#get-usage-status) endpoint still reports billing state. - **Invite acceptance** — processing and accepting an invitation belongs to the invited user's own browser session, not to an API key acting for an existing member. [`/user/invite`](#invite-a-user) sends the invitation email; the invitee finishes in the UI. Two safety guards apply only to poweruser keys (the in-app UI does not need them): - [`/user/remove`](#remove-a-user) refuses to remove the key's **own linked user** — a key cannot orphan itself from the company. - [`/user/invite`](#invite-a-user) refuses roles whose permissions are not a **subset of the inviting user's own permissions** — a key cannot escalate privileges by inviting someone more powerful than its linked user. --- ## Permissions Members' access is expressed as permissions, granted through roles. The permission values that appear in [`/members`](#list-members) and [`/role/all`](#list-roles) responses: | Permission | Grants | |------------|--------| | `MANAGE_TEAM_MEMBERS` | Invite/remove members, update the company. | | `AGENTIC_WORKFLOW` | Build and run agents, managers, and tools. | | `BILLING` | Manage billing (UI-only surface). | | `AUTESSADB` | Create and manage AutessaDB tables. | | `API_KEY` | Create and manage API keys. | | `SCHEDULED_EVENTS` | Create and manage scheduled events. | | `APP_VIEWER` | View shared custom apps. | | `APP_BUILDER` | Create custom apps (implies `APP_VIEWER`). | | `E_SIGNATURE` | Upload, prepare, send, and manage PDF signing envelopes. | | `MEETING_RECORDER` | Create and manage meeting-recorder bots. | --- ## Update the company **POST** `/company/update` Renames the company and/or transfers its ownership. Requires `MANAGE_TEAM_MEMBERS`. Ownership transfer (`newOwnerId`) has extra rules: only the **current owner** may transfer, personal companies cannot be transferred, and the transfer is validated against the new owner's permissions and their owned-companies limit. ### Request parameters - `newCompanyName` (string) — New display name for the company. - `newOwnerId` (string) — User to transfer ownership to. Must be a member of the company. ### Response - `company` (Company) — The updated company: `id` (string), `name`, `isPersonal`, `isOwner` (whether the linked user owns it), and `companyMetadata` (tier limits and display settings, including `companyLimit`). - `errors` (array) — Error messages, if any occurred. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/company/update \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "newCompanyName": "Acme Corp" }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/company/update', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ newCompanyName: 'Acme Corp' }) } ); const { company } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/company/update', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={'newCompanyName': 'Acme Corp'} ) company = response.json()['company'] ``` ```json title="Response (truncated)" { "company": { "id": "cP2mK7dX4v", "name": "Acme Corp", "isPersonal": false, "isOwner": true, "companyMetadata": { "companyLimit": { "sharedUserLimit": 25 } } }, "errors": [] } ``` --- ## List members **POST** `/company/members` Lists every member of the company with their effective permissions. ### Request parameters None — send an empty object `{}`. ### Response - `members` (array) — One entry per member. - `members[].user` (User) — The member: `userId` (string), `email`, `name`, `isConfirmed` (whether the account is confirmed), and `userMetadata` (per-user preference settings). - `members[].permissions` (array) — The member's permissions in this company. Each entry has `permission` (a value from the [permissions table](#permissions)) and `id` (string). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/company/members \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "members": [ { "user": { "userId": "uB7nT2wQ9k", "email": "sam@acme.com", "name": "Sam Rivera", "isConfirmed": true }, "permissions": [ { "permission": "MANAGE_TEAM_MEMBERS", "id": "pJ8rD3sF6m" }, { "permission": "AGENTIC_WORKFLOW", "id": "pQ4tH9vB2n" } ] } ], "errors": [] } ``` --- ## List roles **POST** `/company/role/all` Lists the roles available in the company — built-in and custom — with the permissions each grants. Use the returned `id` values in [`/user/invite`](#invite-a-user). ### Request parameters None — send an empty object `{}`. ### Response - `roles` (array) — Available roles. Each has `id` (string), `name`, `description`, `isBuiltIn` (whether it is a platform-provided role), and `associatedPermissions` — an array of `{ permission, id }` entries the role grants (see the [permissions table](#permissions)). - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/company/role/all \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "roles": [ { "id": "rK5pW8nQ2d", "name": "Builder", "description": "Builds agents and tables.", "isBuiltIn": true, "associatedPermissions": [ { "permission": "AGENTIC_WORKFLOW", "id": "pQ4tH9vB2n" }, { "permission": "AUTESSADB", "id": "pL7xC2kM5w" } ] } ], "errors": [] } ``` --- ## Invite a user **POST** `/company/user/invite` Sends an invitation email to join the company with the given roles. The invitee accepts in the Autessa UI — there is no poweruser endpoint for accepting invitations. Requires `MANAGE_TEAM_MEMBERS`. Rules enforced: - **Privilege-escalation guard (poweruser-only):** every requested role's permissions must be a subset of the linked user's own permissions. A role that grants anything the inviter doesn't hold is rejected with a `422` and a message like `"Cannot invite a user with role 'Builder': it grants permissions the inviting user does not have."` - Unknown role IDs are ignored; at least one valid role must remain or the request fails with `"No valid roles provided for user"`. - The company must be below its member limit (`companyLimit.sharedUserLimit`). ### Request parameters - `email` (string, required) — Email address to invite. - `roleIds` (array, required) — Roles the invitee receives on joining, from [`/role/all`](#list-roles). ### Response - `errors` (array) — Error messages, if any occurred. Empty on success — the invitation email has been sent. **cURL:** ```bash curl https://api.autessa.com/autessa_poweruser/company/user/invite \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "new.hire@acme.com", "roleIds": ["rK5pW8nQ2d"] }' ``` **JavaScript:** ```javascript const response = await fetch( 'https://api.autessa.com/autessa_poweruser/company/user/invite', { method: 'POST', headers: { 'Authorization': process.env.POWERUSER_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'new.hire@acme.com', roleIds: ['rK5pW8nQ2d'] }) } ); const { errors } = await response.json(); ``` **Python:** ```python response = requests.post( 'https://api.autessa.com/autessa_poweruser/company/user/invite', headers={'Authorization': os.environ['POWERUSER_KEY']}, json={ 'email': 'new.hire@acme.com', 'roleIds': ['rK5pW8nQ2d'] } ) ``` ```json title="Response" { "errors": [] } ``` --- ## Remove a user **POST** `/company/user/remove` Removes a member from the company. Requires `MANAGE_TEAM_MEMBERS`. Rules enforced: - **Self-removal guard (poweruser-only):** the key can never remove its own linked user — the request is rejected with a `422` and the message `"A poweruser key cannot remove its own linked user."` - The company **owner** cannot be removed; transfer ownership first with [`/update`](#update-the-company). ### Request parameters - `userId` (string, required) — The member to remove. ### Response - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/company/user/remove \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{ "userId": "uB7nT2wQ9k" }' ``` ```json title="Response" { "errors": [] } ``` --- ## Get usage status **POST** `/company/usage-status` Returns a read-only snapshot of the company's usage against every limit — the same data behind the Builder's usage dashboard. Each section pairs current usage with its limit and remaining headroom. ### Request parameters None — send an empty object `{}`. ### Response - `entityUsage` (object) — Entity creation counts vs. limits. For each of tools, agents, managers, custom apps, and tables: used, limit, and remaining counters (for example `agentsUsed`, `agentsLimit`, `agentsRemaining`; likewise `toolsUsed`, `managersUsed`, `customAppsUsed`, `tablesUsed`, and so on). - `executionUsage` (object) — Execution rate limits for agents, tools, and managers. For each: executions this minute and this month with the matching limit and remaining counts (`agentExecutionsThisMinute`, `agentLimitPerMinute`, `agentRemainingPerMinute`, `agentExecutionsThisMonth`, `agentLimitPerMonth`, `agentRemainingPerMonth`, and the same six for `tool` and `manager`). - `llmCreditUsage` (object) — LLM credit consumption: `dailyCreditsUsed`/`Remaining`/`dailyCreditLimit`, the same trio for `weekly` and `monthly` windows, plus `inputTokenWeight` and `outputTokenWeight` (how tokens convert to credits) and `enabled`. - `phoneCallUsage` (object) — Phone usage: `currentPhoneNumbers`, `maxPhoneNumbers`, `usedMinutesThisMonth`, `remainingMinutesThisMonth`, `maxMinutesPerMonth`. - `storageUsage` (object) — AutessaDB storage: `currentUnits`, `maxUnits`, `remainingUnits`, `usagePercentage`, and the per-column-type unit costs (`regularColumnCost`, `vectorizableColumnCost`, `fileColumnCost`, `autocomputedColumnCost`). - `semanticSearchUsage` (object) — Semantic search requests: used/limit/remaining for the current minute (`requestsThisMinute`, `limitPerMinute`, `remainingThisMinute`), day, and month. - `browserUsage` (object) — Browser service usage on a rolling 30-day window: used/limit/remaining for search (`searchUsedThisMonth`, `searchLimitPerMonth`, `searchRemaining`), non-JS scrapes, and JS scrapes, plus the shared per-minute rate (`requestsThisMinute`, `requestsLimitPerMinute`, `requestsRemainingPerMinute`). - `vibecodeUsage` (object) — Vibecode credits: `dailyCreditsUsed`/`Remaining`/`dailyCreditLimit`, the same trio for the `weekly` window, `inputTokenWeight`, `outputTokenWeight`, and `enabled`. - `aiTaskUsage` (object) — AI-task metering and billing for the current period. All money fields are in cents. Task counts: `currentMonthTasks`, `billableTasks`, `overageTasks`, `voiceTaskCount`, `evaledTaskCount`, `ragInvocationCount`, `voiceSeconds`, `freeSandboxRemaining`. Rates and plan: `currencyCode`, `perTaskBaseCents`, `monthlyCommitmentCents` (`null` = pay-as-you-go), `commitCreditCents`, `pendingCommitmentCents`, `voicePerMinuteCents`, `ragSurchargeCents`, `accruedCostCents`, `projectedMonthlyCostCents`, `prepaidBalanceCents`, `invoiceable`. Budget controls: `userHardBudgetCents`, `userHardTaskCap`, `userSoftWarnPercents`, `userSoftBudgetCents`, `budgetWarningLevel`, `hardLimitReached`. Payment state: `paymentState`, `hasCardOnFile`, `cardBrand`, `cardLast4`, `pastDueSinceIso`. - `errors` (array) — Error messages, if any occurred. ```bash title="cURL" curl https://api.autessa.com/autessa_poweruser/company/usage-status \ -X POST \ -H "Authorization: $POWERUSER_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ```json title="Response (truncated)" { "entityUsage": { "toolsUsed": 12, "toolsLimit": 50, "toolsRemaining": 38, "agentsUsed": 6, "agentsLimit": 25, "agentsRemaining": 19 }, "executionUsage": { "agentExecutionsThisMinute": 3, "agentLimitPerMinute": 60, "agentRemainingPerMinute": 57, "agentExecutionsThisMonth": 4210, "agentLimitPerMonth": 100000, "agentRemainingPerMonth": 95790 }, "llmCreditUsage": { "dailyCreditsUsed": 182000, "dailyCreditsRemaining": 818000, "dailyCreditLimit": 1000000, "inputTokenWeight": 1.0, "outputTokenWeight": 4.0, "enabled": true }, "phoneCallUsage": { "currentPhoneNumbers": 2, "maxPhoneNumbers": 5, "usedMinutesThisMonth": 340.5, "remainingMinutesThisMonth": 659.5, "maxMinutesPerMonth": 1000 }, "storageUsage": { "currentUnits": 1840, "maxUnits": 10000, "remainingUnits": 8160, "usagePercentage": 18.4 }, "semanticSearchUsage": { "requestsThisMinute": 1, "limitPerMinute": 30, "remainingThisMinute": 29 }, "browserUsage": { "searchUsedThisMonth": 220, "searchLimitPerMonth": 2000, "searchRemaining": 1780 }, "vibecodeUsage": { "dailyCreditsUsed": 52000, "dailyCreditsRemaining": 448000, "dailyCreditLimit": 500000, "enabled": true }, "aiTaskUsage": { "currentMonthTasks": 4180, "billableTasks": 4100, "accruedCostCents": 287000, "currencyCode": "usd", "perTaskBaseCents": 70, "monthlyCommitmentCents": 100000, "commitCreditCents": 41300, "projectedMonthlyCostCents": 430500, "paymentState": "OK", "hasCardOnFile": true, "cardBrand": "visa", "cardLast4": "4242", "hardLimitReached": false }, "errors": [] } ``` --- ## Errors | Status | When | |--------|------| | `200` | Success — check `errors` for any messages. | | `401` | Invalid or missing poweruser key. | | `403` | Scoped key without the endpoint in its allow-list. | | `422` | A guard or validation rejected the request: linked user lacks `MANAGE_TEAM_MEMBERS`, removing the key's own linked user or the company owner, a role exceeding the inviter's permissions, no valid roles, member limit reached, or an invalid ownership transfer. `errors` explains which. | | `408` | Server-side error; `errors` contains the message. | --- ## Related - [Poweruser API overview](/docs/api/poweruser.md) - [User (Poweruser API)](/docs/api/poweruser/user.md) - [Team guide](/docs/guide/company/team.md) # Tool Types --- # Tool Types Source: https://docs.autessa.com/docs/tools > Reference for every tool type on the Autessa platform — how types work, and where each family is documented. # Tool Types Every tool on Autessa is an instance of a tool type: the type determines what the tool does when it executes and which configuration fields it takes. This section is the complete catalog — 309 types across 32 family pages — with the exact configuration reference for each type. ## How tool types work A tool is a name, a description, a **type**, and that type's configuration. The name and description are what an agent's model reads to decide when to call the tool; the type and config are what the platform executes when it does. The same type can back any number of tools — three `API_CALL` tools with three different URLs are three different capabilities to an agent. ### Config fields vs. arguments Config fields are saved on the tool and are the same on every execution. Anything dynamic — an order ID, a message body, a search query — is supplied at call time through the tool's **arguments**, and lands in the config via `@@{ARG_NAME}` placeholders that are substituted at execution time. The example configs throughout this section use exactly that pattern: literal values for fixed choices, `@@{...}` placeholders for the values an agent passes on each call. See the [Tools guide](/docs/guide/agentic/tools.md) for defining arguments (including private arguments for sensitive input). ### Environment variables The same `@@{...}` syntax also resolves [environment variables](/docs/guide/agentic/environment-variables.md) — company-wide named values like API keys, base URLs, and IDs that are injected into every agent and tool execution. Use them to keep secrets and shared constants out of saved configs: a header value of `Bearer @@{MY_API_KEY}` reads the current key at execution time. A couple of variables are built in and always available with no setup: `@@{currentDate}` (the current UTC date, e.g. `January 8, 2026`) and `@@{currentTime}` (the current UTC time, e.g. `2:30 PM`) — useful for date-stamping prompts and outputs. Your own variables take precedence on a name clash. See [built-in variables](/docs/guide/agentic/environment-variables.md#built-in-variables). ### Connector-backed types Most types (274 of the 309) execute against a [connector](/docs/guide/connectors.md) — a linked external account such as Gmail, QuickBooks, or Notion. These configs have a `connectorId` field that takes the ID of the connector to act through. Tools never hold credentials: the connector owns the OAuth tokens, the platform refreshes them, and swapping the `connectorId` repoints the same tool at a different account. ### System-only types A small number of types are provisioned by platform features rather than created by hand — currently the [Meeting Recorder](/docs/tools/meeting-recorder.md) notify type. Their entries carry a system-only note: you cannot create or reconfigure tools of these types yourself, but the provisioned tools are visible in your tools list and can be invoked from triggers and scripts like any other. ### Attaching tools to agents Tools become capabilities when you attach them to an [agent](/docs/guide/agentic/agents.md) under **Associated Tools** (MCP agents skip this — their tools sync from the server). The model chooses among the attached tools by their names, descriptions, and argument descriptions, so those matter as much as the config. Tools are reusable: one tool can serve several agents, an AutessaDB trigger, a scheduled event, and direct API calls at once. ### Testing a tool Every tool can be run standalone from the tool editor's **Execute Tool** button — with one input per argument — either as a plain request/response or with streaming status updates for longer-running types. The same execution is available programmatically through the [Tools Poweruser API](/docs/api/poweruser/tools.md). ### Reading these pages Each family page has one section per type: prose on what the type does and when an agent should have it, an example config, and a "Configuration fields" table generated straight from the platform source — so the field lists are always exhaustive and current. IDs in examples are always realistic opaque strings (e.g. `V1StGXR8Z5`). ## Directory ### Platform | Page | What's on it | Types | | --- | --- | --- | | [Core](/docs/tools/core.md) | AutessaScript, API calls, LLM calls, scheduling, manager signals, file content extraction | 7 | | [AutessaDB](/docs/tools/autessadb.md) | Insert, update, delete, query, and semantically search AutessaDB tables | 6 | | [MCP](/docs/tools/mcp.md) | Bridge agents to registered MCP servers: tools, resources, and prompts | 3 | | [Media & Web](/docs/tools/media-web.md) | Stock media, live web search, scraping, audio extraction, transcription | 8 | ### Communication | Page | What's on it | Types | | --- | --- | --- | | [Email](/docs/tools/email.md) | Single emails and fan-out batches sent from Autessa | 2 | | [Phone & SMS](/docs/tools/phone-sms.md) | Outbound phone calls, live-call controls, and text messages | 9 | | [Gmail](/docs/tools/gmail.md) | Read, search, draft, and send mail through a connected Gmail account | 11 | | [Outlook Mail](/docs/tools/outlook-mail.md) | Read, search, draft, and send mail through a connected Outlook mailbox | 11 | | [Calendars](/docs/tools/calendars.md) | Create, list, update, and delete events on Google Calendar and Outlook Calendar | 8 | ### Files & documents | Page | What's on it | Types | | --- | --- | --- | | [Box](/docs/tools/box.md) | List, search, and download files in a connected Box account | 3 | | [Notion](/docs/tools/notion.md) | Search, read, query, and write Notion pages and databases | 11 | ### Social media | Page | What's on it | Types | | --- | --- | --- | | [LinkedIn](/docs/tools/linkedin.md) | Draft, publish, and list LinkedIn posts | 5 | | [Twitter / X](/docs/tools/twitter.md) | Draft, publish, sync, and measure tweets | 7 | | [Bluesky](/docs/tools/bluesky.md) | Draft, publish, and measure Bluesky posts | 6 | | [Facebook](/docs/tools/facebook.md) | Draft and publish Page posts, sync content, read Page and ads insights | 11 | | [Instagram](/docs/tools/instagram.md) | Draft, publish, sync, and measure Instagram posts and carousels | 7 | | [Reddit](/docs/tools/reddit.md) | Draft, publish, reply to, and measure Reddit posts | 8 | | [Threads](/docs/tools/threads.md) | Draft, publish, sync, and measure Threads posts | 7 | | [TikTok](/docs/tools/tiktok.md) | Upload draft videos to the TikTok inbox, read video and channel stats | 5 | | [YouTube](/docs/tools/youtube.md) | List a channel's uploads and pull YouTube Analytics | 3 | ### Newsletters & publishing | Page | What's on it | Types | | --- | --- | --- | | [Mailchimp](/docs/tools/mailchimp.md) | Create and send campaigns, manage audience contacts, read engagement | 15 | | [Beehiiv](/docs/tools/beehiiv.md) | Read newsletter posts, subscribers, and publication growth stats | 7 | | [Ghost](/docs/tools/ghost.md) | Browse, draft, publish, and measure posts on a Ghost publication | 8 | ### Analytics & data | Page | What's on it | Types | | --- | --- | --- | | [Google Analytics](/docs/tools/google-analytics.md) | Run GA4 standard and realtime reports, conversions, and metadata lookups | 6 | | [BigQuery](/docs/tools/bigquery.md) | Browse datasets and schemas, run SQL with cost controls | 4 | ### CRM & support | Page | What's on it | Types | | --- | --- | --- | | [Zendesk](/docs/tools/zendesk.md) | Fetch, search, and list Zendesk Support tickets | 3 | | [HubSpot](/docs/tools/hubspot.md) | Search CRM objects, read pipelines and forms, send logged emails | 12 | | [Zoho CRM](/docs/tools/zoho-crm.md) | Search, read, and upsert Zoho CRM contacts, accounts, and records | 11 | ### Accounting & finance | Page | What's on it | Types | | --- | --- | --- | | [QuickBooks](/docs/tools/quickbooks.md) | The full QuickBooks Online surface: sales, purchasing, items, reports, payments, and composite workflows | 96 | | [Acumatica](/docs/tools/acumatica.md) | Read and write Acumatica ERP entities, attachments, and business actions | 10 | | [Autessa Payments](/docs/tools/autessa-payments.md) | White-label payments: invoices, charges, refunds, payout status | 4 | ### Meetings | Page | What's on it | Types | | --- | --- | --- | | [Meeting Recorder](/docs/tools/meeting-recorder.md) | Dispatch recording bots and the system-provisioned notify tool | 2 | ## Related - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [AutessaScript](/docs/autessascript.md) - [Tools Poweruser API](/docs/api/poweruser/tools.md) --- # Core Source: https://docs.autessa.com/docs/tools/core > Core tool types: AutessaScript, API calls, LLM calls, scheduling, manager signals, and file content extraction. # Core The general-purpose building blocks every agent can use — run code, call HTTP APIs, prompt a model, schedule future work, and read file contents — with no connector required. ## AutessaScript **TOOL** `AUTESSA_SCRIPT` Runs an [AutessaScript](/docs/autessascript.md) program in a hardened JavaScript sandbox — no file system, network sockets, threads, or process access; the script works through the platform bindings instead. All runtime variables (the agent's arguments plus environment variables such as `AUTESSA_COMPANY_ID`) are available to the script, making this the type to reach for when a task needs logic, branching, or several platform calls chained in one tool. `supportedLanguage` currently has a single value, `AUTESSASCRIPT`. The script's final response string is returned to the agent verbatim; console output and errors are captured in the execution's debugging info. ### Example ```json title="Tool config" { "toolType": "AUTESSA_SCRIPT", "supportedLanguage": "AUTESSASCRIPT", "script": "const orderId = '@@{ORDER_ID}';\nconsole.log('Processing order ' + orderId);\nJSON.stringify({ orderId: orderId, status: 'processed' });" } ``` **Configuration fields** - `supportedLanguage` (string (enum)) — Required string (enum) value. - `script` (string) — Required string value. ## API Call **TOOL** `API_CALL` Makes an HTTP request to an external API. Only `GET`, `POST`, and `PATCH` are allowed, `uriParams` are URL-encoded onto the query string, and a JSON `Content-Type` is added automatically when a body is present. Every URL is validated before the request is sent — requests to private networks, internal infrastructure, and non-HTTP(S) schemes are rejected with a validation error — and redirects (up to 5) are re-validated hop by hop. Requests time out after 60 seconds. The result is `{"statusCode": ..., "body": ..., "headers": {...}}` with the raw response body as a string for the agent to interpret. ### Example ```json title="Tool config" { "toolType": "API_CALL", "url": "https://api.example.com/v1/orders", "method": "POST", "headers": { "Authorization": "Bearer @@{EXAMPLE_API_TOKEN}" }, "uriParams": { "expand": "customer" }, "body": "{\"orderId\": \"@@{ORDER_ID}\", \"note\": \"@@{NOTE}\"}" } ``` **Configuration fields** - `url` (string) — Required string value. - `method` (string) — Required string value. - `headers` (object) — Required object value. - `uriParams` (object) — Required object value. - `body` (string) — Required string value. ## LLM Call **TOOL** `LLM_CALL` Sends `promptTemplate` — with its `@@{...}` placeholders already resolved — to a model as a single prompt and returns the completion. `model` picks a capability tier by alias rather than a vendor model name: `SMALL_INSTRUCTION_FOLLOWING` (the default), `BIG_INSTRUCTION_FOLLOWING`, `BIG_INSTRUCTION_FOLLOWING_REASONING`, `VIBE_CODING`, `AUTESSA_120B`. Calls stream and consume LLM credits — an LLM Call invoked outside a conversation (from a trigger or scheduled event) is metered as its own AI task. `imageUrl` supplies an image for a vision call: pass a presigned `https://` URL or a `data:` URL (an `s3://` URI is presigned automatically), and the image is sent alongside the prompt with a vision-capable `model` such as `AUTESSA_120B`, so an image-only document with no extractable text can still be reasoned over. Calls time out after 120 seconds by default; raise the ceiling with `timeoutSeconds` for long generations run outside a conversation. The result is `{"success": true, "content": ..., "usage": {"promptTokens": ..., "completionTokens": ..., "totalTokens": ...}}`. ### Example ```json title="Tool config" { "toolType": "LLM_CALL", "promptTemplate": "Summarize the following support ticket in three bullet points:\n\n@@{TICKET_BODY}", "model": "SMALL_INSTRUCTION_FOLLOWING" } ``` **Configuration fields** - `promptTemplate` (string) — Required string value. - `model` (object) — Required object value. - `timeoutSeconds` (integer) — Per-call timeout (seconds). Null = current default behavior (120s). Capped at 21600 (6h) so long jobs on the WORKER runtime aren't cut off. - `maxOutputTokens` (integer) — Generation cap for this tool's completions. Null = omit, leaving the served model's own default — which for a self-hosted vLLM checkpoint can be effectively unbounded. A prompt tool that expects a short structured answer should set this, so a runaway completion cannot keep generating for minutes and hold the call open until its timeout. - `thinking` (string) — Reasoning toggle for self-hosted vLLM models: `"ON"` / `"OFF"`, same values as the SIMPLE-agent row field. Null = omit `chat_template_kwargs` entirely, i.e. the server's `--default-chat-template-kwargs` decides — exactly the pre-existing behavior, so every tool that does not set this is byte-identical on the wire. Ignored (not an error) on hosted models, mirroring the agent path's gate. - `imageUrl` (string · optional) — Optional image for a VISION call. When set (a presigned https URL or a `data:` URL), the user message carries an `image_url` content block alongside the prompt text — pair with a vision-capable `#model` (e.g. `AutessaModel#AUTESSA_120B`) so an image document with no extractable text can still be judged. Unset/blank preserves the existing text-only behavior exactly. - `imageUrls` (string · optional) — Optional MULTI-image payload for a VISION call: a JSON ARRAY of url strings (e.g. `["s3://b/p1.jpg","s3://b/p2.jpg"]`), each an `s3://` URI, a presigned https URL, or a `data:` URL. Every entry becomes its own `image_url` content block after the prompt text, in array order. Merges with (does not replace) the singular `#imageUrl`: that one goes first, then these. A hard cap of 8 images total is applied — the vLLM serving config runs `--limit-mm-per-prompt {"image": 8`}, so a 9th block would be rejected by the model server. Extras are trimmed with a warning rather than failing the call, and a malformed array is warned about and treated as empty: a judgment with fewer images than hoped must degrade, not die. Held as a String rather than a `List` on purpose — it is the shape a templated `@@{...`} variable produces, and it keeps substitution identical to `#imageUrl`. ## Schedule Event **TOOL** `SCHEDULE_EVENT` Creates a scheduled event that fires in the future — this is how an agent sets up its own follow-ups and recurring jobs. `scheduleSpec.pattern` picks the timing style: `EVERY` (`{"every": {"value": 30, "unit": "MINUTES"}}`, also `HOURS`/`DAYS`), `CRON` (`{"cron": {"expression": "0 9 ? * 2 *", "timezone": "America/Los_Angeles"}}`, a 6-field AWS cron in an IANA timezone), or `ONCE` (`{"once": {"at": "2026-07-04T09:00:00", "timezone": "America/Los_Angeles"}}`, a future local time). `actionType` picks what fires — `INVOKE_AGENT` (runs an agent with an input, via `agentConfig`), `INVOKE_TOOL` (executes a tool with static parameters, via `toolConfig`), or `UPDATE_AUTESSADB` (inserts or updates rows, via `autessaDbConfig`) — and only the matching config object is read. Passing an existing `eventId` updates that event instead of creating a new one; the event's name and description are generated automatically from the creating agent's context. The result includes the handle you need for later edits: `{"success": true, "eventId": ..., "scheduleArn": ..., "eventName": ..., "scheduleExpression": ..., "actionType": ..., "enabled": ...}`. ### Example ```json title="Tool config" { "toolType": "SCHEDULE_EVENT", "enabled": true, "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 ? * 2 *", "timezone": "America/Los_Angeles" } }, "actionType": "INVOKE_AGENT", "agentConfig": { "agentExecutionRequest": { "agentId": "V1StGXR8Z5", "input": "@@{WEEKLY_DIGEST_INSTRUCTIONS}" }, "continueOnError": false, "description": "Monday morning digest run" } } ``` **Configuration fields** - `eventId` (string · optional) — Optional: Event ID for updating an existing scheduled event. If null, a new event is created. - `eventName` (string · optional) — Optional: display name for the created event. Supports `@@{var`} placeholders resolved at invocation time. When blank, the executor auto-generates a name from the invoking agent/tool context. - `enabled` (boolean · optional) — Optional: whether the event is enabled. Defaults to true. - `scheduleSpec` (object) — Required: schedule specification (EVERY / CRON / ONCE). - `actionType` (string (enum)) — Required: which action type fires when the event triggers. - `agentConfig` (object) — Required when actionType = INVOKE_AGENT. - `toolConfig` (object) — Required when actionType = INVOKE_TOOL. - `autessaDbConfig` (object) — Required when actionType = UPDATE_AUTESSADB. ## Manager Signal **TOOL** `MANAGER_SIGNAL` A control signal for sub-agents working under a manager: calling a signal tool (for example "Complete" or "Help") tells the manager the sub-agent has finished its task or needs assistance. The signal is intercepted by the manager's invocation layer rather than executed like a normal tool — the tool's name and description carry all the meaning, and the config has no fields. If one is ever executed directly, it acknowledges and does nothing. ### Example ```json title="Tool config" { "toolType": "MANAGER_SIGNAL" } ``` **Configuration fields** _No configuration fields beyond `toolType`._ ## Get File Content **TOOL** `GET_FILE_CONTENT` Extracts the text content of a file so the agent can read it — PDFs and documents become text rather than raw bytes. `url` accepts an `s3://` URI (for files in Autessa storage, presigned automatically) or an existing `https://` URL; external URLs pass SSRF validation first. Extraction runs server-side with a 5-minute budget for large files. The result is `{"content": ...}` with the extracted text. To view an image or pass a file to the model as an attachment instead of text, use [Fetch and View](/docs/tools/media-web.md#fetch-and-view-). ### Example ```json title="Tool config" { "toolType": "GET_FILE_CONTENT", "url": "@@{FILE_URL}" } ``` **Configuration fields** - `url` (string) — Required string value. ## Get File Content from Table **TOOL** `GET_FILE_CONTENT_FROM_TABLE` Extracts text from a file stored in an [AutessaDB](/docs/guide/autessadb/tables.md) row: it looks up `rowId` in the table, reads the file in `fileColumnName` (which must be a FILE, IMAGE, VIDEO, or AUDIO column), and runs the same server-side extraction as [Get File Content](#get-file-content-) — without the agent ever handling a URL. The result is `{"success": true, "rowId": ..., "filePath": ..., "content": ..., "contentLength": ...}`. ### Example ```json title="Tool config" { "toolType": "GET_FILE_CONTENT_FROM_TABLE", "tableId": "V1StGXR8Z5", "rowId": "@@{ROW_ID}", "fileColumnName": "contract_pdf" } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `rowId` (integer) — Required integer value. - `fileColumnName` (string) — Required string value. ## Related - [Tool Types overview](/docs/tools.md) - [AutessaScript reference](/docs/autessascript.md) - [Tools guide](/docs/guide/agentic/tools.md) - [AutessaDB tool types](/docs/tools/autessadb.md) - [Media & Web tool types](/docs/tools/media-web.md) --- # Email Source: https://docs.autessa.com/docs/tools/email > Tool types for sending single emails and fan-out batches from Autessa. # Email Send transactional email straight from an agent — a single message to one recipient, or the same message fanned out to a whole list. No connector is required. All email sends from Autessa's own sending domain: the from address is always `autessa-builder+{companyId}+noreply@mailerautessa.com`, scoped to your company. The `fromName` field controls only the display name recipients see — the underlying address (and where replies land) cannot be changed, so put a real contact address in the body if you expect replies. To send from your own mailbox instead, use the [Gmail](/docs/tools/gmail.md) or [Outlook Mail](/docs/tools/outlook-mail.md) tool types. ## Send Email **TOOL** `SEND_EMAIL` Sends one email to a single recipient synchronously and reports the result. The body is plain text unless `isHtml` is `true`, and `ccEmails` is capped at 3 addresses — a longer list fails with `INVALID_CC_LIST` before anything sends. On success the tool returns `{"success": true, "recipientEmail": ..., "subject": ..., "fromEmail": ...}` (plus `ccCount` when CCs were included); failures return `{"success": false, "errorCode": ..., "errorMessage": ...}`. ### Example ```json title="Tool config" { "toolType": "SEND_EMAIL", "recipientEmail": "@@{RECIPIENT_EMAIL}", "ccEmails": ["ops@example.com"], "fromName": "Acme Support", "subject": "@@{SUBJECT}", "messageBody": "@@{MESSAGE_BODY}", "isHtml": false } ``` **Configuration fields** - `recipientEmail` (string) — Required string value. - `ccEmails` (array) — Required array value. - `fromName` (string) — Required string value. - `messageBody` (string) — Required string value. - `isHtml` (boolean) — Required boolean value. - `subject` (string) — Required string value. ## Send Email Fan-out **TOOL** `SEND_EMAIL_FANOUT` Sends the same subject and body individually to every address in `recipientEmails` — one outbound email per entry, so recipients never see each other. Addresses are trimmed, lowercased, and de-duplicated first, and delivery is best-effort: a failure for one recipient is recorded and the batch continues. There is no CC support on the fan-out variant. The result summarizes the batch: `{"success": ..., "sentCount": ..., "failedCount": ..., "totalAttempted": ..., "failures": [...]}` — `success` is `true` only when every send succeeded, and `failures` lists each failed address with its error. ### Example ```json title="Tool config" { "toolType": "SEND_EMAIL_FANOUT", "recipientEmails": ["@@{RECIPIENT_LIST}"], "fromName": "Acme Announcements", "subject": "March product update", "messageBody": "

What's new

@@{UPDATE_SUMMARY}", "isHtml": true } ``` **Configuration fields** - `recipientEmails` (array) — List of email addresses. One outbound email per entry. Duplicates are de-duped. - `fromName` (string) — List of email addresses. One outbound email per entry. Duplicates are de-duped. - `subject` (string) — List of email addresses. One outbound email per entry. Duplicates are de-duped. - `messageBody` (string) — List of email addresses. One outbound email per entry. Duplicates are de-duped. - `isHtml` (boolean) — List of email addresses. One outbound email per entry. Duplicates are de-duped. ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Gmail tool types](/docs/tools/gmail.md) - [Outlook Mail tool types](/docs/tools/outlook-mail.md) - [Mailchimp tool types](/docs/tools/mailchimp.md) --- # Phone & SMS Source: https://docs.autessa.com/docs/tools/phone-sms > Tool types for placing outbound phone calls, controlling a live call (hang up, transfer, voicemail, keypad, recording, caller info), and sending text messages. # Phone & SMS Let an agent place real outbound phone calls, control the call it's on (hang up, transfer, take a voicemail, press keys, pause the recording, look up the caller), and send text messages. These tools ride on your company's [phone numbers](/docs/guide/agentic/phone-numbers.md): every call originates from a phone configuration you own, referenced by `phoneConfigId`. The phone configuration also decides which agent answers the line, so a call placed by a tool becomes a live voice conversation with that agent. Calls are metered against your company's call-minute limit. Texts additionally need your company's [Text Messaging registration](/docs/guide/agentic/sms-setup.md) and the recipient's [consent](/docs/guide/agentic/sms-consent.md). ## Make Phone Call **TOOL** `MAKE_PHONE_CALL` Places an outbound call from the phone number in `phoneConfigId` to `toPhoneNumber` and returns as soon as the carrier accepts it — the conversation itself runs asynchronously through the voice pipeline. The opening line can be static (`initialMessage`) or generated live: when `initialMessagePrompt` is set, the phone configuration's agent runs the prompt before the call connects and speaks the generated response when the recipient answers. Setting `initialMessagePrompt` or `voicemailAudioUrl` implicitly enables answering-machine detection, so a voicemail can be left when no one picks up — `voicemailAudioUrl` accepts an audio URL, or plain text that is converted to speech with the agent's voice. `conferenceParticipants` dials additional numbers into the call as it starts, and `skipInitialMessage` keeps the AI silent for a human-to-human bridge. The result confirms initiation, not outcome: `{"success": true, "callSid": ..., "fromPhoneNumber": ..., "toPhoneNumber": ..., "status": ..., "amdEnabled": ...}`, plus fields describing the generated message, voicemail, and conference setup when used. How the call actually went (answered, voicemail left, which key was pressed, the transcript) lands in your company's [Phone calls table](/docs/guide/agentic/phone-numbers.md#the-phone-calls-table) once the call ends. ### Call modes `mode` decides what happens when the other person picks up. It defaults to `AI_CONVERSATION`. | Mode | What happens | Fields it uses | | --- | --- | --- | | `AI_CONVERSATION` | The number's agent holds a live conversation, as described above. | `initialMessage`, `initialMessagePrompt`, `voicemailAudioUrl`, and the rest | | `PLAY_MESSAGE` | A spoken message is played and no AI is involved. Use it for reminders, alerts, and confirmations. | `message`, `keypadOptions`, `repeatMessage` | | `CONNECT_TO_PEOPLE` | Once the other person answers, your staff are rung and joined to the call. No AI is involved. | `ring` | **Play a message.** `message` is the text that's spoken. Add `keypadOptions` to ask for a key press, for example `{"1": "Confirmed", "2": "Reschedule"}`. The label of the key the person presses is saved in the call's `keypad_response` column in the Phone calls table, so a [trigger](/docs/guide/agentic/phone-numbers.md#build-your-own-automations) can act on it. `repeatMessage` sets how many times the message repeats when no key is pressed (default `1`). If an answering machine picks up, the voicemail settings work the same way as in an AI call. **Connect to people.** `ring` lists who to ring once the call is answered, in the same shape as a number's [ring settings](/docs/guide/agentic/phone-numbers.md#ringing-people): `targets` (a list of `{ "to": ..., "label": ..., "ringSeconds": ... }`, where `to` is a phone number in E.164 format or `client:` for a teammate's browser phone), `strategy` (`SIMULTANEOUS` or `SEQUENTIAL`), `ringSeconds`, `requireKeyPress`, `whisper`, `callerId`, `waitAudio`, `maxTotalSeconds`, and `noAnswer` (what to do when nobody picks up). If nobody picks up and you didn't set `noAnswer`, the person you called hears a short apology and the call ends. ### Ringing, retries, and calling hours - **`ringTimeoutSeconds`**: how long to let the phone ring before the call counts as not answered, from `5` to `120` (default `30`). Keep it under about 25 seconds if you'd rather not reach voicemail, or raise it to give people time to get to the phone. - **`retry`**: try again when the call doesn't go through. `onBusy`, `onNoAnswer`, and `onFailed` choose which outcomes to retry, `maxAttempts` is the total number of tries including the first, from `1` to `5` (default `1`, meaning no retries), and `delayMinutes` is the wait between tries, from `1` to `1440` (default `10`). - **`callingHours`**: only call during these hours, in the time zone you give (usually the recipient's). `timezone` (for example `America/Chicago`), `weekly` (a list of `{ "day": "MONDAY", "open": "09:00", "close": "17:00" }`, in the same format as [business hours](/docs/guide/agentic/phone-numbers.md#business-hours-and-holidays)), and `outsideHours`: `WAIT` (default) places the call as soon as the next window opens, and `FAIL` doesn't place it and returns an error instead. Retries that land outside the hours wait for the next window too. Waiting calls and retries aren't hidden. Each one is a one-time [scheduled job](/docs/guide/scheduled-jobs.md) that runs this same tool with the same settings, so you can see when the next try will happen, and cancel it, on the Scheduled Jobs page. ```json title="Retry a reminder call, weekdays 9 to 6 only" { "ringTimeoutSeconds": 25, "retry": { "onBusy": true, "onNoAnswer": true, "maxAttempts": 3, "delayMinutes": 60 }, "callingHours": { "timezone": "America/Chicago", "weekly": [ { "day": "MONDAY", "open": "09:00", "close": "18:00" }, { "day": "TUESDAY", "open": "09:00", "close": "18:00" }, { "day": "WEDNESDAY", "open": "09:00", "close": "18:00" }, { "day": "THURSDAY", "open": "09:00", "close": "18:00" }, { "day": "FRIDAY", "open": "09:00", "close": "18:00" } ], "outsideHours": "WAIT" } } ``` ### Settings for this call Every call follows the [call settings](/docs/guide/agentic/phone-numbers.md#recording-transcripts-and-limits) of the number it's placed from: transcript, recording, Call Redaction, call limits, caller info, voice and language, AI call behavior, and answering machine detection. Set `callSettings` to override them for this call only. Each section you include (`transcript`, `recording`, `redaction`, `limits`, `callerInfo`, `speech`, `ai`, `answeringMachine`) replaces that whole section of the number's settings, and sections you leave out keep the number's values. The sections are described in the [call settings object](/docs/api/poweruser/phone-numbers.md#the-call-settings-object). ```json title="Record this call and stop it after 10 minutes" { "callSettings": { "recording": { "save": true, "announcement": "This call may be recorded." }, "limits": { "maxDurationMinutes": 10 } } } ``` ### Example ```json title="Tool config" { "toolType": "MAKE_PHONE_CALL", "phoneConfigId": "V1StGXR8Z5", "toPhoneNumber": "@@{TO_PHONE_NUMBER}", "initialMessage": "", "initialMessagePrompt": "Greet @@{CUSTOMER_NAME} and say you are calling about their open support ticket.", "environmentVariables": {}, "promptTemplateVariables": {}, "voicemailAudioUrl": "Hi, this is Acme Support calling about your ticket. We'll try again tomorrow.", "conferenceParticipants": [], "skipInitialMessage": false, "clientDisplayMessage": "" } ``` ```json title="Tool config: appointment reminder with a key press" { "toolType": "MAKE_PHONE_CALL", "phoneConfigId": "V1StGXR8Z5", "toPhoneNumber": "@@{TO_PHONE_NUMBER}", "mode": "PLAY_MESSAGE", "message": "Hi, this is Acme Dental reminding you of your appointment tomorrow at 10 AM. Press 1 to confirm, or 2 if you need to reschedule.", "keypadOptions": { "1": "Confirmed", "2": "Reschedule" }, "repeatMessage": 2 } ``` **Configuration fields** - `phoneConfigId` (string (ID)) — Which phone number to call from — the only true config-level field (persisted on the tool). References a phone configuration by ID. - `toPhoneNumber` (string) — Required string value. - `initialMessage` (string) — Required string value. - `initialMessagePrompt` (string) — Prompt sent to the LLM to dynamically generate the initial message before the call connects. When provided, the agent conversation starts before the call is placed, the LLM generates a response to this prompt, and that response is spoken when the recipient answers. AMD (Answering Machine Detection) is automatically enabled when this is set, allowing the message to be left as voicemail if no one answers. - `environmentVariables` (object) — Prompt sent to the LLM to dynamically generate the initial message before the call connects. When provided, the agent conversation starts before the call is placed, the LLM generates a response to this prompt, and that response is spoken when the recipient answers. AMD (Answering Machine Detection) is automatically enabled when this is set, allowing the message to be left as voicemail if no one answers. - `promptTemplateVariables` (object) — Prompt sent to the LLM to dynamically generate the initial message before the call connects. When provided, the agent conversation starts before the call is placed, the LLM generates a response to this prompt, and that response is spoken when the recipient answers. AMD (Answering Machine Detection) is automatically enabled when this is set, allowing the message to be left as voicemail if no one answers. - `voicemailAudioUrl` (string) — URL to a WAV/MP3 audio file to play as voicemail when AMD detects an answering machine. When set, AMD is automatically enabled. If a machine answers, this audio is played and the call hangs up. - `voicemailBehavior` (string) — What to do when an answering machine answers instead of a person: `LEAVE_MESSAGE` (default), `HANG_UP`, or `STAY_ON`. With `LEAVE_MESSAGE` the call ends as soon as Twilio confirms the message finished playing. `HANG_UP` ends the call without saying anything, for calls that are only worth making if a person picks up. - `conferenceParticipants` (array) — Additional phone numbers to dial into the conference when the call starts. Each participant will be called and joined to the conference when they answer. - `skipInitialMessage` (boolean) — When true, the AI agent will not speak an initial message when the call connects. Useful when conference participants are set and the call should be a direct human-to-human connection with the AI silently listening. - `clientDisplayMessage` (string) — Custom message passed to all browser client participants via connection.customParameters. Applied to all client: participants in the conference when they are dialed. - `mode` (string · optional) — `AI_CONVERSATION` (default): the number's AI talks with the callee. `PLAY_MESSAGE`: play `#message` (optionally asking for a key from `#keypadOptions`). `CONNECT_TO_PEOPLE`: once the callee answers, ring the people in `#ring` into the call. Optional. - `message` (string · optional) — PLAY_MESSAGE: what is said, in the number's AI voice. Optional for other modes. - `keypadOptions` (object · optional) — PLAY_MESSAGE: digit to label, e.g. {"1": "Confirmed"}; the chosen label lands in the call log's keypad_response. Optional. - `repeatMessage` (integer · optional) — PLAY_MESSAGE: how many times the message plays when no valid key is pressed. Optional; default 1. - `ring` (object · optional) — CONNECT_TO_PEOPLE: who to ring and how; noAnswer defaults to a short apology and hang-up. Optional for other modes. - `callSettings` (object · optional) — Per-call override of the from-number's call settings; each section given replaces that whole section. Optional. - `ringTimeoutSeconds` (integer · optional) — How long the callee's phone rings before the call counts as not answered: 5..120 seconds. Optional; default 30. - `retry` (object · optional) — Call again when the call doesn't connect (busy, no answer, failed), up to maxAttempts calls in all, delayMinutes apart. Each retry is a scheduled event you can see and cancel. Optional; default no retry. - `callingHours` (object · optional) — Only place the call inside these hours (in their own time zone). Outside them the call waits for the next opening (outsideHours WAIT, the default, as a scheduled event) or fails (FAIL). Optional. ## Add Conference Participant **TOOL** `PHONE_ADD_CONFERENCE_PARTICIPANT` Dials another participant into the conference of the phone call the agent is currently on — give this tool to an agent that answers or places calls so it can loop in a human on request. `phoneNumber` accepts a regular phone number or `client:{identity}` for a browser-based client, and the participant joins asynchronously when they answer. Once a participant is added, the AI switches to listen-only mode so the humans can talk directly. Outside an active call the tool is a graceful no-op: it returns success with a note that the person will be looped in, without dialing anyone. During a call the result is `{"success": true, "message": ..., "phoneNumber": ..., "conferenceRoom": ..., "participantCallSid": ...}`. ### Example ```json title="Tool config" { "toolType": "PHONE_ADD_CONFERENCE_PARTICIPANT", "phoneNumber": "@@{PARTICIPANT_PHONE_NUMBER}", "displayMessage": "Connecting you with the caller now." } ``` **Configuration fields** - `phoneNumber` (string) — Required string value. - `displayMessage` (string) — Custom message passed to browser clients via connection.customParameters ## Call controls These tools let an agent that is on a phone call act on the call itself: hang up, transfer, take a message, press keys, pause the recording, or look up who it's talking to. Give them to any agent or multi-agent that answers or places calls. From the number's detail page, the **Call tools** card shows which ones the answering agent already has and adds the rest in one click. They work on the call the agent is currently on, so none of them takes a phone number or call ID for "this call". When the agent isn't on a call (for example, in a chat or a test run in the builder), each one does nothing and returns `{"success": true, "onCall": false, "message": ...}`, so it's safe to give them to an agent that also handles chat and text messages. Every result has the shape `{"success": ..., "onCall": ..., "message": ...}`, plus the extra fields listed for each tool. `message` is plain language, so the agent can repeat it to the caller if it needs to. All fields accept `@@{VAR_NAME}` placeholders. ## Hang Up Call **TOOL** `PHONE_HANG_UP` Ends the call when the agent decides the conversation is over. The agent finishes what it's saying first, so the caller hears the goodbye before the line goes quiet. The call is logged with `ended_by` set to `AGENT`. - **`reason`**: kept for your records. - **`goodbyeMessage`**: a fixed goodbye spoken just before hanging up, such as "Thanks for calling Acme. Goodbye!" Use it when you want every call to end with the same words, whatever the agent last said. - **`delaySeconds`**: wait this long before hanging up, from `0` to `10`, so a caller who's still saying goodbye isn't cut off mid-word. Tell the agent when to use it in its instructions, for example: "When the caller says they have everything they need, say goodbye and then hang up." For calls that should end whenever the agent says a particular phrase, the number's [hang-up phrases](/docs/guide/agentic/phone-numbers.md#ai-call-behavior) do the same job without a tool. ### Example ```json title="Tool config" { "toolType": "PHONE_HANG_UP", "reason": "Caller's question was answered", "goodbyeMessage": "Thanks for calling Acme. Have a great day!", "delaySeconds": 1 } ``` **Configuration fields** - `reason` (string · optional) — Why the agent is ending the call, kept with the call record. Optional. - `goodbyeMessage` (string · optional) — Said to the caller just before hanging up, after the agent's own reply, e.g. "Thanks for calling, goodbye." Optional. - `delaySeconds` (integer · optional) — Extra seconds to wait after the last words before hanging up, 0 to 10. Optional; defaults to no wait. ## Transfer Call **TOOL** `PHONE_TRANSFER_CALL` Hands the caller to a person. `to` is a phone number in E.164 format (for example `+14155550100`), `client:` to ring a teammate's browser phone, or the name of an entry in the tool's `directory`. - **`mode`**: `COLD` (default) connects the caller and the agent leaves the call as soon as the person answers. `WARM` keeps the agent on the line in listen-only mode, so it can hear the handoff without speaking. - **`announcement`**: what the agent says to the caller before transferring, such as "Let me connect you with our billing team." - **`ringSeconds`**: how long to ring the person before giving up (default `25`). - **`onNoAnswer`**: what happens if the person doesn't pick up. `RESUME_AI` (default) brings the agent back so it can keep helping the caller, `TAKE_VOICEMAIL` lets the caller leave a message, and `HANG_UP` ends the call. - **`whisper`**: a short message only the person answering hears before they're connected, such as "Transfer from the AI receptionist: caller needs a refund." The caller doesn't hear it. - **`requireKeyPress`**: the person who answers must press 1 to accept the call. Turn it on when you transfer to cell phones, so the person's own voicemail can't pick up the caller. If they don't press 1, the `onNoAnswer` step runs. - **`clientDisplayMessage`**: shown on the teammate's screen when `to` is a browser phone, for example "Caller needs help with a refund." The caller never sits in silence: if the person is busy, doesn't answer, or hangs up, the `onNoAnswer` step runs. The person the call was transferred to is saved in the call's `transferred_to` column. [Add Conference Participant](#add-conference-participant-) is the older way to bring someone in. It works like a warm transfer with no fallback, so use Transfer Call for new agents. ### Transfer directory Give the tool a `directory` and the agent can choose who to transfer to by name, without seeing or typing phone numbers. Each entry has a `name`, a `to` (a phone number or `client:`), and an optional `description` that tells the agent when to pick it. The names and descriptions are listed in the tool's description, so the agent knows its choices, and it passes the entry's `name` as `to`. Leave `to` as a placeholder such as `@@{TRANSFER_TO}` so the agent fills it in. `allowAnyNumber` controls whether the agent can also transfer to a number that isn't in the directory. It defaults to `false` when the tool has a directory, so the agent can only reach the people you listed, and `true` when it doesn't, so `to` can be any number. ### Examples ```json title="Tool config: one fixed number" { "toolType": "PHONE_TRANSFER_CALL", "to": "@@{BILLING_TEAM_PHONE}", "mode": "COLD", "announcement": "I'll connect you with our billing team now. One moment.", "ringSeconds": 25, "onNoAnswer": "TAKE_VOICEMAIL" } ``` ```json title="Tool config: a directory the agent picks from" { "toolType": "PHONE_TRANSFER_CALL", "to": "@@{TRANSFER_TO}", "directory": [ { "name": "Billing", "to": "+14155550100", "description": "Refunds, invoices, and payment problems." }, { "name": "Scheduling", "to": "+14155550101", "description": "Booking, moving, or canceling appointments." }, { "name": "Office manager", "to": "client:", "description": "Complaints, or when the caller asks for a manager." } ], "allowAnyNumber": false, "announcement": "Let me connect you now. One moment.", "whisper": "Call transferred by the Acme receptionist.", "requireKeyPress": true, "onNoAnswer": "RESUME_AI" } ``` **Configuration fields** - `to` (string) — Who to bring on: a phone number like +15551234567, `client:` for an Autessa browser phone, or, when a `#directory` is set, the name of one of its entries. - `mode` (string · optional) — `COLD` (default): the agent drops off once they pick up. `WARM`: the agent stays on, listening only. Optional. - `announcement` (string · optional) — Said to the caller before dialling, e.g. "Let me connect you with our billing team." Optional. - `ringSeconds` (integer · optional) — How many seconds to ring before giving up. Optional; defaults to 25. - `onNoAnswer` (string · optional) — What happens when nobody picks up: `RESUME_AI` (default), `TAKE_VOICEMAIL` or `HANG_UP`. Optional. - `clientDisplayMessage` (string · optional) — Shown on the browser phone being rung, when `to` is a browser phone. Optional. - `whisper` (string · optional) — Said only to the person being brought on, before they are connected, e.g. "Transfer from the front desk: a caller asking about a refund." The caller doesn't hear it. Optional. - `requireKeyPress` (boolean · optional) — The person must press 1 to accept the call, so their own voicemail can't pick it up and leave the caller talking to a recording. Optional; defaults to false. - `directory` (array · optional) — The people this tool can transfer to, each with a name the agent uses as `to`. When set, the agent passes a name ("Billing") and the tool dials that entry's number. Optional. - `allowAnyNumber` (boolean · optional) — Whether `to` may be a number or browser phone that is not in the `#directory`. Optional; defaults to true without a directory and false with one, so an agent given a directory can only reach the people in it. - `name` (string) — What the agent calls them, e.g. "Billing" or "Dr. Lee". Matched without regard to case. - `to` (string) — Their phone number like +15551234567, or `client:` for an Autessa browser phone. - `description` (string · optional) — When to transfer to them, e.g. "Refunds and invoice questions". Optional. ## Take a Voicemail **TOOL** `PHONE_TAKE_VOICEMAIL` Lets the caller leave a message, for example when they ask for someone who isn't available. The agent stops talking, the greeting is played, there's a beep, and the caller's message is recorded. The recording and its transcript are saved in the call's `voicemail_recording` and `voicemail_transcript` columns, and the call's status becomes `VOICEMAIL_RECEIVED`. The call ends when the caller finishes, so this is the agent's last step on the call. That's what the [Email me new voicemails](/docs/guide/agentic/phone-numbers.md#quick-automations) automation listens for. - **`greeting`**: played before the beep. Leave it empty for a standard greeting. - **`greetingAudio`**: a recording to play instead of `greeting`, as an `https://` link or an audio file you [uploaded for the number](/docs/api/poweruser/phone-numbers.md#get-an-audio-upload-link). - **`maxSeconds`**: the longest message the caller can leave (default `120`). - **`finishOnKey`**: the key the caller presses to finish the message (default `#`). Set it to `""` so no key ends the message. - **`transcribe`**: set to `false` to keep only the audio (default `true`). - **`notifyEmails`**: up to 5 email addresses that get the voicemail as soon as it's saved, with its transcript and a link to the call. Use it when this agent's voicemails should go to different people than the number's [Email me new voicemails](/docs/guide/agentic/phone-numbers.md#quick-automations) list. If the number has [Call Redaction](/docs/guide/agentic/phone-numbers.md#removing-personal-info-from-recordings) turned on, the voicemail goes through it too. ### Example ```json title="Tool config" { "toolType": "PHONE_TAKE_VOICEMAIL", "greeting": "Please leave your name, number, and a short message after the beep, and we'll call you back today.", "maxSeconds": 120, "finishOnKey": "#", "transcribe": true, "notifyEmails": ["frontdesk@example.com"] } ``` **Configuration fields** - `greeting` (string · optional) — What the caller hears before the tone, e.g. "Please leave your name and number." Optional. - `greetingAudio` (string · optional) — A recording played instead of `#greeting`: an https link to an mp3 or wav file, or an `s3://` audio URI from the phone number's audio upload link. Optional. - `maxSeconds` (integer · optional) — The longest message the caller can leave, in seconds (1 to 600). Optional; defaults to 120. - `finishOnKey` (string · optional) — The key the caller presses to finish their message early: one of 0-9, `*` or `#`. An empty value means no key ends it. Optional; defaults to `#`. - `transcribe` (boolean · optional) — Whether to transcribe the message into the Phone calls table. Optional; defaults to true. - `notifyEmails` (array · optional) — Email addresses (at most 5) told about each new message, with its transcript. Optional. ## Press Phone Keys **TOOL** `PHONE_PRESS_KEYS` Presses keys on the phone keypad, the way a person would, so an agent placing a call can get through another company's phone menu ("press 2 for billing") or enter an extension. `digits` can contain `0`–`9`, `*`, and `#`. Add `w` for a half-second pause between keys, for example `1ww4421#`. Leave `digits` as a placeholder the agent fills in when it hears the menu, or fix it for a menu that never changes. Some phone systems miss key presses that are too short or too close together. If presses aren't recognized, slow them down: - **`toneMs`**: how long each key is held, from `40` to `1000` milliseconds (default `160`). - **`gapMs`**: the pause between keys, from `40` to `2000` milliseconds (default `100`). ### Example ```json title="Tool config" { "toolType": "PHONE_PRESS_KEYS", "digits": "@@{DIGITS}", "toneMs": 250, "gapMs": 200 } ``` **Configuration fields** - `digits` (string) — The keys to press, in order: 0-9, `*` and `#`. Use `w` for a half-second pause. - `toneMs` (integer · optional) — How long each key's tone plays, in milliseconds (40 to 1000). Optional; defaults to 160. - `gapMs` (integer · optional) — Silence between keys, in milliseconds (40 to 2000). Optional; defaults to 100. ## Pause or Resume Call Recording **TOOL** `PHONE_PAUSE_RECORDING` Pauses the call recording, or resumes it. `action` is `PAUSE` or `RESUME`. Use it around anything you don't want in the recording, such as a card number read out loud: tell the agent to pause before asking for the number and resume once it's been collected. When the number isn't recording the call, the tool does nothing and says so in `message`. Set **`autoResumeSeconds`** on a `PAUSE` to resume the recording by itself after that many seconds, from `5` to `600`. It's a safety net: if the agent forgets to resume, the rest of the call is still recorded. Pausing only affects the recording. The agent still hears everything, and what was said stays in the agent's conversation history. To remove personal info from recordings and transcripts automatically, use [Call Redaction](/docs/guide/agentic/phone-numbers.md#removing-personal-info-from-recordings). ### Example ```json title="Tool config" { "toolType": "PHONE_PAUSE_RECORDING", "action": "PAUSE", "autoResumeSeconds": 90 } ``` **Configuration fields** - `action` (string) — `PAUSE` or `RESUME`. - `autoResumeSeconds` (integer · optional) — When pausing, start recording again on its own after this many seconds (5 to 600), so a forgotten RESUME never loses the rest of the call. Ignored when resuming. Optional; by default the recording stays paused until resumed. ## Get Caller Info **TOOL** `PHONE_GET_CALLER_INFO` Tells the agent who it's talking to and whether they've called before. During a call the result adds: | Field | What it holds | | --- | --- | | `phoneNumber` | The other person's number. | | `name` | Their name from the carrier's caller-ID records, when the number has [caller-name lookup](/docs/guide/agentic/phone-numbers.md#caller-info) turned on and a name was found. | | `city`, `state`, `country` | Where the number is registered, as reported by the carrier. | | `forwardedFrom` | The number the call was forwarded from, if it was forwarded and the carrier reports it. | | `direction` | `INBOUND` or `OUTBOUND`. | | `recentCalls` | Earlier calls with this number from your [Phone calls table](/docs/guide/agentic/phone-numbers.md#the-phone-calls-table), newest first, each with `startedAt`, `direction`, `durationSeconds`, `status`, and `transcriptExcerpt` (the first 500 characters of the transcript). | Fields the carrier didn't provide are empty. The same caller details are also available without a tool call, as [built-in variables](/docs/guide/agentic/environment-variables.md#built-in-variables) like `@@{callerName}`. All settings are optional: - **`fields`**: which of the fields above to return, for example `["name", "recentCalls"]`. Leave it out to get them all. Returning less keeps the agent focused and its context short. - **`historyCount`**: how many earlier calls to include in `recentCalls`, from `0` to `20` (default `5`). - **`includeTranscripts`**: set to `false` to leave `transcriptExcerpt` out of `recentCalls` (default `true`), for example when past calls may contain details this agent shouldn't see. - **`lookupName`**: look up the caller's name from carrier caller-ID records right now, even if the number doesn't have [caller-name lookup](/docs/guide/agentic/phone-numbers.md#caller-info) turned on (default `false`). The carrier charges a small fee for each lookup, which is added to the call's price. ### Example ```json title="Tool config" { "toolType": "PHONE_GET_CALLER_INFO", "fields": ["name", "city", "state", "recentCalls"], "historyCount": 3, "includeTranscripts": true, "lookupName": false } ``` **Configuration fields** - `fields` (array · optional) — Which details to return, from: phoneNumber, name, city, state, country, forwardedFrom, direction, recentCalls. Optional; by default all of them. - `historyCount` (integer · optional) — How many earlier calls with this person to include (0 to 20). Optional; defaults to 5. - `includeTranscripts` (boolean · optional) — Whether each earlier call includes the start of its transcript. Optional; defaults to true. - `lookupName` (boolean · optional) — Look up the caller's name with their phone carrier right now, when it isn't already known. Each lookup is a paid Twilio caller-name lookup. Optional; defaults to false. ## Send SMS **TOOL** `SEND_SMS` Sends one text message from the phone number in `phoneConfigId` to `toPhoneNumber`. Only US recipients are supported; formats like `(415) 555-0100` and `+14155550100` are both accepted. `message` is at most 1600 characters — the carrier splits longer texts into segments, and the result reports how many were used. `clientDisplayMessage` is never sent to the recipient; it's echoed back in the result so the calling app can show it next to the outcome. Every send is checked before anything goes out: - the phone number belongs to your company and is active; - your company has finished [Text Messaging registration](/docs/guide/agentic/sms-setup.md), or the number comes from your own connected Twilio account; - the recipient has an approved [consent record](/docs/guide/agentic/sms-consent.md), or texted your company within the last year; - the recipient hasn't opted out by replying STOP. On success the result is `{"success": true, "messageId": ..., "twilioMessageSid": ..., "status": ..., "from": ..., "to": ..., "segments": ...}`. The `status` is the carrier's acceptance (`QUEUED`, `ACCEPTED`, `SENDING`, or `SENT`); delivery is tracked afterwards and shows in the number's message history. ### When a text isn't sent A refused or failed text returns `{"success": false, "errorCode": ..., "errorMessage": ...}`. The `errorMessage` is written for people and says what to do, so an agent can pass it on as-is. | Error code | Why | | --- | --- | | `SMS_NOT_REGISTERED` | Your company hasn't registered for texting yet. | | `REGISTRATION_PENDING` | The registration is still in carrier review. | | `REGISTRATION_ACTION_REQUIRED` | The registration was rejected and needs a fix. | | `REGISTRATION_SUSPENDED` | Carriers suspended the registration. | | `NUMBER_NOT_ATTACHED` | Texting isn't set up for this number yet. | | `CONSENT_REQUIRED` | The recipient hasn't consented and hasn't texted you in the last year. | | `OPTED_OUT` | The recipient replied STOP. They're texted again only after they reply START. | | `INVALID_NUMBER` | The recipient isn't a valid US phone number. | | `NUMBER_INACTIVE` | The phone number was released or is no longer active. | | `PHONE_CONFIG_NOT_FOUND` | No phone number matches `phoneConfigId`. | | `ACCESS_DENIED` | The phone number belongs to another company, or you don't have access to it. | | `BYO_CREDENTIALS_INVALID`, `BYO_ACCOUNT_SUSPENDED`, `BYO_ACCOUNT_DISCONNECTED` | The number comes from your own Twilio account, and that account needs attention. | | `INVALID_CONFIG` | `phoneConfigId`, `toPhoneNumber`, or `message` is missing. | | `SEND_FAILED` | The carrier didn't accept the text. Try again later. | ### Send SMS example ```json title="Tool config" { "toolType": "SEND_SMS", "phoneConfigId": "V1StGXR8Z5", "toPhoneNumber": "@@{TO_PHONE_NUMBER}", "message": "Hi @@{CUSTOMER_NAME}, your appointment with Acme is confirmed for tomorrow at 10am. Reply STOP to opt out.", "clientDisplayMessage": "" } ``` ## Related - [Tool Types overview](/docs/tools.md) - [Phone numbers guide](/docs/guide/agentic/phone-numbers.md) - [Text Messaging setup](/docs/guide/agentic/sms-setup.md) - [SMS consent](/docs/guide/agentic/sms-consent.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Core tool types](/docs/tools/core.md) --- # Media & Web Source: https://docs.autessa.com/docs/tools/media-web > Tool types for stock media, live web search, scraping, audio extraction, and transcription. # Media & Web Give agents eyes and ears on the outside world: search the live web, scrape pages, fetch files and images into the conversation, source stock media, and turn audio and video into text. None of these types require a connector. The live search and scrape tools are metered per company; outputs that produce files land in Autessa-managed S3 storage and come back as short-lived presigned or tiny URLs. ## Extract Audio **TOOL** `EXTRACT_AUDIO` Downloads the media at `videoFileUrl` (presigned URL, `https://`, or `s3://` URI), transcodes its audio track to WAV, and uploads the result to Autessa storage under your company. The defaults — 16 kHz mono — match what speech-to-text expects, making this the natural first step before [Transcribe](#transcribe-). Currently the output is always WAV regardless of the requested `outputFormat`. The result contains `{"s3Uri": ..., "presignedUrl": ..., "bytes": ..., "outputFormat": ...}`; the presigned URL is valid for 1 hour. Nothing is written to AutessaDB — chain an [AutessaDB update](/docs/tools/autessadb.md) if you want the file recorded on a row. ### Example ```json title="Tool config" { "toolType": "EXTRACT_AUDIO", "videoFileUrl": "@@{VIDEO_FILE_URL}", "outputFormat": "WAV", "sampleRateHz": 16000, "channels": 1 } ``` **Configuration fields** - `videoFileUrl` (string) — Presigned URL / s3:// URI / https URL of the source media. Required. - `outputFormat` (string) — "WAV" or "MP3". Defaults to WAV. - `sampleRateHz` (integer) — Sample rate in Hz. Defaults to 16000 — matches STT expectations. - `channels` (integer) — Channel count. Defaults to 1 (mono). ## Transcribe **TOOL** `TRANSCRIBE` Runs Autessa's long-form speech-to-text pipeline over the audio at `audioFileUrl` and returns the transcript inline — no files are written anywhere. An optional `languageHint` (ISO code such as `en` or `es`) can steer recognition. The result is `{"transcriptText": ..., "chars": ...}` plus an `isSilent` flag when the audio contained no speech. ### Example ```json title="Tool config" { "toolType": "TRANSCRIBE", "audioFileUrl": "@@{AUDIO_FILE_URL}", "languageHint": "en" } ``` **Configuration fields** - `audioFileUrl` (string) — s3:// URI of a file in the caller's workspace, or an http(s) link; any common audio/video format. Required. - `languageHint` (string · optional) — Optional ISO language hint (e.g., "en", "es"). ## Stock Image Generator **TOOL** `STOCK_IMAGE_GENERATOR` Searches Unsplash and Pexels in parallel for stock photos matching `query` and returns direct image URLs from both providers. `numberOfResults` is capped at 5 per provider. With `returnTinyUrl` enabled, each URL is wrapped in a short Autessa link (valid 1 hour) that is easier to embed in emails or AutessaDB rows. The result is `{"unsplash_images": [...], "pexels_images": [...]}` — arrays of URLs. A provider that errors contributes an empty array rather than failing the call. ### Example ```json title="Tool config" { "toolType": "STOCK_IMAGE_GENERATOR", "query": "@@{IMAGE_SEARCH_QUERY}", "numberOfResults": 5, "returnTinyUrl": true } ``` **Configuration fields** - `query` (string) — Required string value. - `numberOfResults` (integer) — Required integer value. - `returnTinyUrl` (boolean) — Required boolean value. ## Stock Video Generator **TOOL** `STOCK_VIDEO_GENERATOR` Searches Pexels for stock video clips matching `query`, preferring HD renditions when available. `numberOfResults` is capped at 5, and `returnTinyUrl` works the same as for stock images (1-hour short links). The result is `{"pexels_videos": [...]}` — an array of direct video URLs. ### Example ```json title="Tool config" { "toolType": "STOCK_VIDEO_GENERATOR", "query": "@@{VIDEO_SEARCH_QUERY}", "numberOfResults": 3, "returnTinyUrl": false } ``` **Configuration fields** - `query` (string) — Required string value. - `numberOfResults` (integer) — Required integer value. - `returnTinyUrl` (boolean) — Required boolean value. ## Live Search Engine **TOOL** `LIVE_SEARCH_ENGINE` Runs a live web search through Autessa's managed browser service and returns the raw search results for the agent to read. `numResults` defaults to 5. Searches count against your company's browser-search limit; when the limit is hit the call retries briefly, then returns a rate-limit error with the remaining quota. The result is `{"statusCode": ..., "body": ..., "headers": {}}` where `body` carries the search results as returned by the search backend. ### Example ```json title="Tool config" { "toolType": "LIVE_SEARCH_ENGINE", "query": "@@{SEARCH_QUERY}", "numResults": 5 } ``` **Configuration fields** - `query` (string) — Required string value. - `numResults` (integer) — Required integer value. ## Scrape URL **TOOL** `SCRAPE_URL` Fetches a public web page through the managed browser service and returns its content. Set `waitForJs` to `true` for JavaScript-heavy pages — the browser renders the page before extracting content, and JS scrapes are metered in a separate (more expensive) quota bucket than plain fetches. Every URL is validated before the request is sent; requests to private networks and internal infrastructure are rejected with a validation error. The result is `{"statusCode": ..., "body": ..., "headers": {}}` with the scraped content in `body`. ### Example ```json title="Tool config" { "toolType": "SCRAPE_URL", "url": "@@{PAGE_URL}", "waitForJs": true } ``` **Configuration fields** - `url` (string) — Required string value. - `waitForJs` (boolean) — Required boolean value. ## Fetch and View **TOOL** `FETCH_AND_VIEW` Fetches an image or document from a URL and places it directly into the conversation as multimodal content, so the agent can actually look at it on its next turn — unlike [Scrape URL](#scrape-url-), which returns page text. Supports `https://`, `http://`, and `s3://` URIs (S3 objects are presigned for 1 hour), and `contentType` (`image` or `file`) is auto-detected from the extension when omitted. The result confirms the fetch and carries an `enrichment` payload that the platform injects into the next model request as an image or file block: `{"success": true, "message": ..., "enrichment": [...]}`. ### Example ```json title="Tool config" { "toolType": "FETCH_AND_VIEW", "url": "@@{FILE_OR_IMAGE_URL}", "contentType": "image" } ``` **Configuration fields** - `url` (string) — The URL to fetch. Supports https://, http://, and s3:// URIs. External URLs must be from allowed domains. - `contentType` (string · optional) — Optional content type hint: "image" or "file". If not provided, will be auto-detected from the URL extension. ## Perplexity Search **TOOL** `PERPLEXITY_SEARCH` Queries the Perplexity search API, which returns search results with extracted page content — richer than plain result links. `maxResults` (default 10) controls how many results come back, `maxTokens` (default 25000) bounds the total content payload, and `maxTokensPerPage` (default 2048) bounds how much extracted content each page contributes. The result is `{"statusCode": ..., "body": ..., "headers": {...}}` with Perplexity's response JSON in `body`. ### Example ```json title="Tool config" { "toolType": "PERPLEXITY_SEARCH", "query": "@@{RESEARCH_QUERY}", "maxResults": 10, "maxTokens": 25000, "maxTokensPerPage": 2048 } ``` **Configuration fields** - `query` (string) — Required string value. - `maxResults` (integer) — Required integer value. - `maxTokens` (integer) — Required integer value. - `maxTokensPerPage` (integer) — Required integer value. ## Related - [Tool Types overview](/docs/tools.md) - [Core tool types](/docs/tools/core.md) - [AutessaDB tool types](/docs/tools/autessadb.md) - [Tools guide](/docs/guide/agentic/tools.md) --- # AutessaDB Source: https://docs.autessa.com/docs/tools/autessadb > Tool types for inserting, updating, deleting, querying, and semantically searching AutessaDB tables, browsing and searching Drives, and querying analytics tables with SQL. # AutessaDB The read/write surface between agents and your data: insert and update rows, filter and delete with structured conditions, run vector search over any column, browse and search the files in a Drive, and answer questions from analytics tables with SQL. No connector is required — every tool takes a `tableId` identifying one of your [AutessaDB tables](/docs/guide/autessadb/tables.md). Writes go through the same pipeline as user edits, so table triggers fire and computed columns recompute. The write tools take a `values` map of column name to a typed value object — set the slot matching the column type (`textValue`, `integerValue`, `floatValue`, `booleanValue`, `multiSelectValue`, or `filePath` for file-type columns). On read paths, file-type columns come back with short-lived download links in `presignedUrl`. The filter-based tools share one condition format, the same one used by the [poweruser query API](/docs/api/poweruser/autessadb-query.md): `whereGroups` is a list of groups, each with a `logicalOperator` (`AND`/`OR`) combining its `conditions` (`columnName`, `operator`, `value`), and `groupsLogicalOperator` combines the groups. Operators include `EQUALS`, `NOT_EQUALS`, `GREATER_THAN`, `LESS_THAN`, `LIKE`, `ILIKE`, `CONTAINS`, `STARTS_WITH`, `ENDS_WITH`, `IN`, `NOT_IN`, `BETWEEN`, `IS_NULL`, and `IS_NOT_NULL`. ## Insert Row **TOOL** `AUTESSADB_INSERT` Inserts one row into the table with the given column values. Triggers on the table are queued and computed columns are calculated asynchronously, exactly as if the row had been created in the UI. The result is `{"insertedId": ..., "computationStatus": ..., "newRow": {...}}` with the full inserted row. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_INSERT", "tableId": "V1StGXR8Z5", "values": { "customer_name": { "textValue": "@@{CUSTOMER_NAME}" }, "priority": { "integerValue": 2 }, "status": { "textValue": "OPEN" } } } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `values` (object) — Required object value. ## Update Row **TOOL** `AUTESSADB_UPDATE` Updates a single row identified by `rowId`, changing only the columns present in `values` — other columns are untouched. Table triggers fire and computed columns recompute after the update. The result is `{"updatedId": ..., "computationStatus": ..., "oldRow": {...}, "newRow": {...}}`, so the agent can see exactly what changed. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_UPDATE", "tableId": "V1StGXR8Z5", "rowId": "@@{ROW_ID}", "values": { "status": { "textValue": "RESOLVED" }, "resolution_notes": { "textValue": "@@{RESOLUTION_NOTES}" } } } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `rowId` (integer) — Required integer value. - `values` (object) — Required object value. - `deleteOrphanedFiles` (boolean) — Opt-in: when true, a FILE/IMAGE/AUDIO/VIDEO cell whose stored S3 URI is REPLACED or NULLED by this update has its prior S3 object deleted AFTER the transaction commits (best-effort; a delete failure is logged, never fails the write). Default false = fully backward compatible. ## Delete Rows **TOOL** `AUTESSADB_DELETE` Batch-deletes every row matching `whereGroups`. `groupsLogicalOperator` defaults to `OR`, so with multiple groups a row matching any group is deleted — set it to `AND` explicitly when you mean an intersection. A deletion that would exceed the platform's batch-size cap is rolled back entirely, and `dryRun: true` skips the delete (it currently returns a placeholder message rather than a matching-row count, so preview counts with [Query Table](#query-table-) instead). The result is `{"tableId": ..., "deletedCount": ..., "deletedRows": [...]}`. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_DELETE", "tableId": "V1StGXR8Z5", "whereGroups": [ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "ARCHIVED" }, { "columnName": "created_at", "operator": "LESS_THAN", "value": "@@{CUTOFF_DATE}" } ] } ], "groupsLogicalOperator": "AND", "dryRun": false } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `whereGroups` (array) — Required array value. - `groupsLogicalOperator` (string) — "AND" or "OR" — how to combine multiple whereGroups. Defaults to "OR". - `dryRun` (boolean) — When true, returns the matching row count but does not delete. Defaults to false. ## Bulk Insert **TOOL** `AUTESSADB_BULK_INSERT` Inserts many rows into the table in a single call. `rows` is a list of value maps, each the same shape as an `AUTESSADB_INSERT` `values`. Unlike single-row insert, **table triggers are suppressed by default** — bulk loads are usually ingestion where firing per-row triggers is rarely intended. Set `triggerMode` to `PER_ROW` or `BATCH_CONTEXT` to opt in. `atomicity` is `ALL_OR_NOTHING` (default; the whole call is one transaction) or `BEST_EFFORT_PER_CHUNK` (partial progress allowed). `indexingMode` and `computeMode` (`IMMEDIATE`/`DEFERRED`/`SKIP`) control when indexing and computed columns run. The row count per call is capped. The result is `{"insertedCount": ..., "computationStatus": ..., "results": [{ "inputIndex": ..., "insertedId": ..., "errors": [...] }, ...]}`. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_BULK_INSERT", "tableId": "V1StGXR8Z5", "rows": [ { "name": { "textValue": "Ada" } }, { "name": { "textValue": "Grace" } } ], "atomicity": "ALL_OR_NOTHING" } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `rows` (array) — Required array value. - `atomicity` (string (enum)) — Required string (enum) value. - `indexingMode` (string (enum)) — Required string (enum) value. - `computeMode` (string (enum)) — Required string (enum) value. - `triggerMode` (string (enum)) — Required string (enum) value. ## Bulk Update **TOOL** `AUTESSADB_BULK_UPDATE` Updates many rows by id in a single call. `updates` is a list of `{ "id": ..., "values": {...} }`, where each `values` is a partial patch (only the supplied columns change). Same mode fields and defaults as Bulk Insert (triggers suppressed by default). The result is `{"updatedCount": ..., "computationStatus": ..., "results": [{ "inputIndex": ..., "updatedId": ..., "errors": [...] }, ...]}`. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_BULK_UPDATE", "tableId": "V1StGXR8Z5", "updates": [ { "id": "@@{ROW_ID_1}", "values": { "status": { "textValue": "RESOLVED" } } }, { "id": "@@{ROW_ID_2}", "values": { "status": { "textValue": "OPEN" } } } ] } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `updates` (array) — Required array value. - `atomicity` (string (enum)) — Required string (enum) value. - `indexingMode` (string (enum)) — Required string (enum) value. - `computeMode` (string (enum)) — Required string (enum) value. - `triggerMode` (string (enum)) — Required string (enum) value. - `deleteOrphanedFiles` (boolean) — Opt-in: when true, any FILE/IMAGE/AUDIO/VIDEO cell whose stored S3 URI is REPLACED or NULLED by this bulk update has its prior S3 object deleted AFTER commit (best-effort; a delete failure is logged, never fails the write). Default false = fully backward compatible. ## Bulk Delete **TOOL** `AUTESSADB_BULK_DELETE` Deletes many rows by id in a single call. `ids` is the list of row ids to remove. Only `atomicity` and `triggerMode` apply (nothing to index or compute on a delete); triggers are suppressed by default. The result is `{"deletedCount": ..., "results": [{ "inputIndex": ..., "deletedId": ..., "errors": [...] }, ...]}`. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_BULK_DELETE", "tableId": "V1StGXR8Z5", "ids": [12345, 12346, 12347] } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `ids` (array) — Required array value. - `atomicity` (string (enum)) — Required string (enum) value. - `triggerMode` (string (enum)) — Required string (enum) value. ## Query Table **TOOL** `AUTESSADB_QUERY_TABLE` Runs a structured, filtered query over the table and returns matching rows with pagination. `pageSize` defaults to 50 and is silently capped at 100; leave `whereGroups` empty to page through the whole table. File-type columns in the results carry short-lived download links. Add `orderBy` to control sort order — a list of `{ "columnName": ..., "direction": "ASC" | "DESC" }` terms in priority order. For large tables, use seek pagination instead of `page`: pass the prior result's `nextCursor` back as `cursor` (reusing the same `orderBy`), and set `includeTotal` to `true` only when you actually need the total count. The result is `{"rows": [...], "totalCount": ..., "currentPage": ..., "totalPages": ..., "nextCursor": ..., "hasMore": ..., "schema": {...}}` — the schema lets the agent interpret column types without a separate lookup, and `nextCursor`/`hasMore` drive cursor pagination. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_QUERY_TABLE", "tableId": "V1StGXR8Z5", "whereGroups": [ { "logicalOperator": "OR", "conditions": [ { "columnName": "assignee", "operator": "EQUALS", "value": "@@{ASSIGNEE}" }, { "columnName": "assignee", "operator": "IS_NULL", "value": "" } ] } ], "groupsLogicalOperator": "AND", "orderBy": [{ "columnName": "created_at", "direction": "DESC" }], "pageSize": 50 } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `whereGroups` (array) — Required array value. - `groupsLogicalOperator` (string) — Required string value. - `orderBy` (array) — Required array value. - `page` (integer) — Required integer value. - `pageSize` (integer) — Required integer value. - `cursor` (string) — Required string value. - `includeTotal` (boolean) — Required boolean value. - `includeFileUrls` (boolean) — Required boolean value. ## Aggregate **TOOL** `AUTESSADB_AGGREGATE` Runs a grouped aggregation over the table: filter with `whereGroups`, group by up to a few columns (`groupBy`), and compute `aggregates` (`COUNT`/`SUM`/`AVG`/`MIN`/`MAX`, each under a unique `alias`). Optionally filter groups with `having` (referencing aliases), sort with `orderBy`, and cap the result with `limit`. Columns hidden from the acting identity are treated as not existing, and columns visible only in masked form cannot be grouped or aggregated. The result is `{"groups": [{ "key": {...}, "values": {...} }, ...]}` — one entry per group, with the grouping key and each alias's numeric value. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_AGGREGATE", "tableId": "V1StGXR8Z5", "groupBy": ["status"], "aggregates": [ { "function": "COUNT", "alias": "count" }, { "function": "AVG", "column": "age", "alias": "avg_age" } ], "orderBy": [{ "columnName": "count", "direction": "DESC" }] } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `whereGroups` (array) — Required array value. - `groupsLogicalOperator` (string) — Required string value. - `groupBy` (array) — Required array value. - `aggregates` (array) — Required array value. - `having` (array) — Required array value. - `orderBy` (array) — Required array value. - `limit` (integer) — Required integer value. ## Semantic Query **TOOL** `AUTESSADB_SEMANTIC_QUERY` Runs vector-embedding search over one column (`columnName`) of the table and returns scored matches as structured JSON — the tool for RAG-style retrieval where the agent needs scores, pagination, and row details. `limit` defaults to 10 (max 100) and `scoreThreshold` (0–1) defaults to 0, so by default you get the top matches regardless of similarity. Semantic searches are rate-limited per company. The result is `{"results": [...], "totalResults": ..., "currentPage": ..., "totalPages": ..., "metadata": {...}, "schema": {...}}`. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_SEMANTIC_QUERY", "tableId": "V1StGXR8Z5", "query": "@@{SEARCH_QUERY}", "columnName": "document_text", "limit": 10, "scoreThreshold": 0.5, "page": 1, "includeDetails": true } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `query` (string) — Required string value. - `columnName` (string) — Required string value. - `limit` (integer) — Required integer value. - `scoreThreshold` (number) — Required number value. - `page` (integer) — Required integer value. - `includeDetails` (boolean) — Required boolean value. - `includeFileUrls` (boolean) — Required boolean value. ## General Search **TOOL** `AUTESSADB_GENERAL_SEARCH` The same vector search as [Semantic Query](#semantic-query-), packaged for direct reading: results come back as a readable markdown string (`## MATCH #1 (score: 0.84, recordId: ...)` followed by the content) instead of JSON. Defaults are tuned for precision — `topK` defaults to 5 and `scoreThreshold` to 0.7 — and there is no pagination. For file, image, and video columns it returns the content of the column's auto-generated notes companion rather than the raw file. Prefer this type when the agent just needs to read what matched; prefer Semantic Query when downstream steps need structured fields. ### Example ```json title="Tool config" { "toolType": "AUTESSADB_GENERAL_SEARCH", "tableId": "V1StGXR8Z5", "query": "@@{SEARCH_QUERY}", "columnName": "meeting_transcript", "topK": 5, "scoreThreshold": 0.7 } ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `query` (string) — Required string value. - `columnName` (string) — Required string value. - `topK` (integer) — Required integer value. - `scoreThreshold` (number) — Required number value. - `includeFileUrls` (boolean) — Required boolean value. ## Drive **TOOL** `AUTESSADB_DRIVE` Gives an agent access to an [AutessaDB Drive](/docs/guide/autessadb/drive.md): browse folders, find files, grep exact text, read pages and lines, run ranked search (meaning-based when the Drive's semantic search is on), look at files, and write text files. `tableId` must be a Drive. `op` picks the operation: | `op` | What it does | Main arguments | | --- | --- | --- | | `ls` | List a folder's contents, folders first | `path`, `limit`, `cursor` | | `tree` | Outline a folder and its subfolders | `path`, `maxDepth`, `maxEntries` | | `find` | Find files by name, glob, type, date, or size | `path`, `glob`, `nameRegex`, `type`, `modifiedAfter` | | `stat` | Details of one file: size, version, page count, processing status | `path` | | `grep` | Lines containing an exact string or matching a regular expression | `pattern`, `regex`, `caseInsensitive`, `path`, `glob`, `context` | | `read` | Text of a page range or line range | `path`, `pages` or `lines`, `maxChars` | | `search` | Ranked keyword search; meaning-based when semantic search is enabled | `query`, `mode`, `path`, `topK` | | `fetch` | A 10-minute download link; with `view: true`, the image or PDF pages are shown to the model | `path`, `view`, `pages` (at most 5), `version` | | `write` | Create, replace, or append to a text file (up to 5 MB); missing folders are created | `path`, `content`, `mode` (`overwrite`, `create`, `append`), `expectedVersion` | | `edit` | Replace exact text in a text file | `path`, `oldText`, `newText`, `replaceAll`, `expectedVersion` | | `mkdir` | Create a folder and any missing parents | `path` | | `move` | Move into a folder, or rename by giving a new path | `path`, `destinationPath`, `confirm` | | `trash` | Move to the trash | `path` | `write`, `edit`, `mkdir`, `move` and `trash` need Editor access on the item or folder for the acting identity. Every write and edit is a new version of the file. `edit` fails with `EDIT_NO_MATCH` when `oldText` is not in the file and with `EDIT_AMBIGUOUS` when it occurs more than once; `write` and `edit` fail with `VERSION_CONFLICT` when `expectedVersion` is stale. Each error carries a `hint` that names the next call. The arguments and their limits match the [Drive API](/docs/api/autessadb-drive.md). Any argument can be a `@@{...}` placeholder the agent fills at run time. Set `rootPath` to confine the tool to one folder: every path the agent sends is resolved inside it, and nothing outside it is visible even if the acting identity could see more. The tool sees only what its acting identity can open. When an agent acts for a user, results are exactly what that user would see in the explorer. When a Drive is bound to an agent, the agent receives one narrow tool per operation (`drive_ls`, `drive_tree`, `drive_find`, `drive_stat`, `drive_grep`, `drive_read`, `drive_search`, `drive_fetch`, `drive_write`, `drive_edit`, `drive_mkdir`, `drive_move`, and `drive_trash`) with guidance to use search for topics, grep for exact strings, read the page or lines before quoting, and edit files by reading them first and copying the exact text to replace. The result is `{"output": "...", "data": {...}}`. `output` is formatted for the model: listings as aligned text, grep hits as `path (vN) p3:L12: ...match...`, and `read` as numbered lines. `data` carries the same structured response as the API, including `nextCursor` and `truncated`. Cite results as `path p.N L.M`. A `fetch` with `view: true` also returns the images the model sees, in the same way as [Fetch and View](/docs/tools/media-web.md#fetch-and-view-). ### Example config ```json title="Tool config" { "toolType": "AUTESSADB_DRIVE", "tableId": "V1StGXR8Z5", "op": "grep", "rootPath": "/Shared/Legal", "pattern": "@@{PATTERN}", "caseInsensitive": true, "context": 1 } ``` ```text title="output" /Shared/Legal/Contracts/acme-msa.pdf (v2) p4:L12: 12.2 **Termination for Convenience**. Either party may terminate this Agreement /Shared/Legal/Contracts/globex-msa.pdf (v1) p7:L3: 9.1 **Termination for Convenience**. Customer may terminate upon thirty (30) days 2 matches in 2 files. 1 file in scope is still processing. ``` **Configuration fields** - `tableId` (string (ID)) — Required string (ID) value. References an AutessaDB table by ID. - `op` (string) — ls | tree | find | stat | grep | read | search | fetch | write | edit | mkdir | move | trash | comment - `rootPath` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `path` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `nodeId` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `glob` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `nameRegex` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `type` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `mime` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `modifiedAfter` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `modifiedBefore` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `sizeGt` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `sizeLt` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `pattern` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `regex` (boolean) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `caseInsensitive` (boolean) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `outputMode` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `context` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `maxMatchesPerFile` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `pages` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `lines` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `maxChars` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `query` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `mode` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `topK` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `maxDepth` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `maxEntries` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `limit` (integer) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `cursor` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `sort` (string) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `foldersOnly` (boolean) — Hard scope ceiling, e.g. `/Shared/Contracts`; null = the whole Drive (as the identity sees it). - `content` (string) — write: the text; append mode adds it to the end. - `oldText` (string) — edit: the exact text to replace, and its replacement. - `newText` (string) — edit: the exact text to replace, and its replacement. - `replaceAll` (boolean) — edit: the exact text to replace, and its replacement. - `expectedVersion` (integer) — write / edit: fail with VERSION_CONFLICT unless the file is at this version. - `view` (boolean) — fetch: show the image / PDF pages to the model. - `version` (integer) — fetch: an earlier version. - `destinationPath` (string) — move: the folder to move into, or the new full path. - `confirm` (boolean) — move: proceed although access changes. - `action` (string) — comment: `add` (default) or `list`. - `body` (string) — comment add: the comment text. - `page` (integer) — comment add: anchor the comment to this page (1-based). - `quote` (string) — comment add: the exact words the comment is about (found in the file's text and highlighted). - `range` (string) — comment add (spreadsheets): the cell or range the comment is about, such as B3:D7. - `sheet` (string) — comment add (spreadsheets): the sheet of range. - `parentId` (string) — comment add: reply to this comment's thread (its id from a list). - `includeResolved` (boolean) — comment list: include resolved threads. ## Describe Analytics Tables **TOOL** `ANALYTICS_DESCRIBE` Tells an agent what one or more [analytics tables](/docs/guide/autessadb/analytics.md) contain, so it can write correct SQL. The agent should call it before its first query. Columns the acting identity cannot see are left out. The result is markdown, one section per table: SQL name, description, grain, row count, last load time, the partition columns ("filter on these"), each column with its type, description, synonyms, and (with `includeStats`) its most common values, the measures as `name := sql`, joins, and verified queries. ### Example config ```json title="Tool config" { "toolType": "ANALYTICS_DESCRIBE", "tableIds": ["V1StGXR8Z5", "Hq4ZpN8sXe"], "includeStats": true } ``` **Configuration fields** - `tableIds` (array) — The analytics tables this tool may describe (and nothing else). References an AutessaDB table by ID. - `includeStats` (boolean) — Include column statistics (null counts, ranges, top values). ## Query Analytics Tables **TOOL** `ANALYTICS_QUERY` Runs one read-only SQL query, written in ANSI SQL, over the analytics tables listed in `tableIds`. Usually `sql` is `@@{sql}` so the agent writes the query; it can also be fixed SQL with placeholders for values. `mode` is `RUN` (default), `PREVIEW` (a quick sample), or `ESTIMATE` (report how much data the query would scan, without running it). Queries can only read the tables in `tableIds`, and row-level and field-level security apply. Queries typically take a few seconds. `maxRows` caps the rows returned to the agent (default 200, maximum 1,000). The tool waits up to `maxWaitSeconds` (default 25) for the query; if it is still running, the result is `{"state": "RUNNING", "jobId": "...", "hint": "call ANALYTICS_GET_RESULT"}`. A finished result is `{"jobId", "state", "columns", "rows", "totalRows", "truncated", "bytesScanned", "elapsedMs", "markdown"}`, where `markdown` is a table of up to 50 rows for the model to read. A failed query returns `{"errorCode", "errorMessage", "hint"}` with codes such as `PARSE_ERROR`, `UNKNOWN_TABLE`, `UNKNOWN_COLUMN` (the hint lists the real columns), and `SCAN_LIMIT` (the hint names the partition columns to filter on). See the [Analytics API errors](/docs/api/autessadb-analytics.md#errors). Download links are never put in the model's context. ### Example config ```json title="Tool config" { "toolType": "ANALYTICS_QUERY", "tableIds": ["V1StGXR8Z5", "Hq4ZpN8sXe"], "sql": "@@{sql}", "mode": "RUN", "maxRows": 200, "maxWaitSeconds": 25 } ``` **Configuration fields** - `tableIds` (array) — The analytics tables the SQL may reference. References an AutessaDB table by ID. - `sql` (string) — The SQL (normally filled by the LLM through the `@@{sql`} argument). - `mode` (string) — RUN (default) | PREVIEW | ESTIMATE. - `maxRows` (integer) — Rows returned to the agent (default 200, at most 1000). - `maxWaitSeconds` (integer) — How long to wait for the result before returning the jobId (default 25 s). - `maxBytesScanned` (integer) — Refuse queries that would scan more than this many bytes (default: the company limit). ## Get Analytics Result **TOOL** `ANALYTICS_GET_RESULT` Fetches the result of a query started by [Query Analytics Tables](#query-analytics-tables): waits up to `waitSeconds` for a running job, then returns one page of rows (`pageSize` up to 200) in the same shape as a finished query. Use it when a query returned `RUNNING`, or to page past the first rows of a large result. ### Example config ```json title="Tool config" { "toolType": "ANALYTICS_GET_RESULT", "jobId": "@@{jobId}", "page": "@@{page}", "pageSize": 200, "waitSeconds": 20 } ``` **Configuration fields** - `tableIds` (array) — The analytics tables whose jobs this tool may read. References an AutessaDB table by ID. - `jobId` (string) — The job id returned by ANALYTICS_QUERY (normally `@@{jobId`}). - `page` (integer) — 1-based result page (default 1). - `pageSize` (integer) — Rows per page (default and max 200). - `waitSeconds` (integer) — Wait up to this long for a running job to finish (default 20 s, max 25 s). The three analytics tools are usually created together, bound to the same tables, with **Create agent tools** on the table's page or the [`create-agent-tools`](/docs/api/poweruser/autessadb-analytics.md#create-agent-tools) endpoint. ## Related - [Tool Types overview](/docs/tools.md) - [AutessaDB tables guide](/docs/guide/autessadb/tables.md) - [AutessaDB Drive guide](/docs/guide/autessadb/drive.md) and [Drive API](/docs/api/autessadb-drive.md) - [Analytics tables guide](/docs/guide/autessadb/analytics.md) and [Analytics API](/docs/api/autessadb-analytics.md) - [Poweruser AutessaDB query API](/docs/api/poweruser/autessadb-query.md) - [Core tool types](/docs/tools/core.md) — `GET_FILE_CONTENT_FROM_TABLE` extracts text from files stored in table rows - [Tools guide](/docs/guide/agentic/tools.md) --- # Documents Source: https://docs.autessa.com/docs/tools/documents > Tool types for extracting, scanning, locating, redacting, caching, and bulk-ingesting documents. # Documents Composable building blocks for document workflows: pull text and layout out of a file, scan that text for sensitive patterns, turn matched strings into page geometry, burn redactions into a flattened PDF, remember decisions by content hash, and expand an uploaded archive into table rows. None of these types require a connector. Each is a small, single-purpose block designed to be chained — most often with a [Tool Pipeline](/docs/tools/pipelines.md) or an [AutessaScript](/docs/autessascript.md) orchestrator — so one extraction can feed several detectors and model calls without re-reading the file. The extract, locate-spans, and redaction-burn blocks take an `s3Uri` pointing at a file in Autessa storage (an `s3://` URI is presigned automatically) or an existing `https://` URL. They are pure with respect to your data — nothing is written to an AutessaDB row unless you add a follow-up step that does so. A typical redaction flow chains these blocks in order: **Extract Text** to get the document's text, then **Detect Patterns** and/or model calls to decide what is sensitive, then **Locate Spans** to turn those strings into boxes, then **Burn Redactions** to produce the final flattened PDF. The **Decision Cache** blocks let a later run of the same document reuse the earlier verdict instead of repeating the work, and **Bulk Intake** is the front door that turns an uploaded archive of documents into rows ready to be processed. ## Extract Text **TOOL** `DOCUMENT_EXTRACT` Extracts text and per-page layout from the document at `s3Uri`. Native-text PDFs are read directly; scanned or image pages are rasterized and read with vision so image-only documents still come back as text. Handles PDF, DOCX, TXT, MD, CSV, XLSX, EML, MSG, and common image formats. `maxPages` caps how many pages are read (default: all), and `rasterDpi` overrides the rasterization resolution for image pages. This block does not write anywhere — persist its result with a follow-up step if you need it stored. The result is `{"sha256": ..., "mime": ..., "text": ..., "pages": [{ "n": ..., "text": ..., "width": ..., "height": ... }, ...], "metadata": {...}}` — the full text, a per-page breakdown with dimensions, the document's MIME type, and the SHA-256 of its canonical bytes (the key the [Decision Cache](#look-up-decision-cache-) is keyed on). ### Example ```json title="Tool config" { "toolType": "DOCUMENT_EXTRACT", "s3Uri": "@@{DOCUMENT_S3_URI}", "maxPages": 100 } ``` **Configuration fields** - `s3Uri` (string) — s3:// URI or presigned https URL of the source document. Required. - `maxPages` (integer · optional) — Optional cap on how many pages to extract. Null = all pages. - `rasterDpi` (integer · optional) — Optional rasterization DPI for image/scanned pages. Null = service default. - `canonicalOutputS3Uri` (string · optional) — Optional s3:// URI the compute service renders the document's CANONICAL PDF to (native-layout via LibreOffice for office docs; all sheets/pages become consecutive PDF pages). When set, the executor presigns the PUT and passes an `output` block to Python, and the success envelope gains `canonicalS3Uri` + `canonicalPageCount`. When null/blank, extract behaves as before (no canonical render, no output block). The caller supplies the target URI — nothing is derived. - `convertedXlsxOutputS3Uri` (string · optional) — Optional s3:// URI the compute service writes the LibreOffice-converted `.xlsx` twin to when the source is a LEGACY spreadsheet (.xls / .ods). Harmless to supply for anything else: the service simply does not convert, omits the `converted_xlsx` response block, and nothing is written — so callers may set it unconditionally rather than sniffing file types on the Java side. When the conversion happened, the success envelope gains `convertedXlsxS3Uri` so the caller can persist `converted_xlsx_file` without re-deriving the uri. That artifact is the ONE spreadsheet surface a legacy workbook has — grid viewer, reviewer cell marks and the openpyxl scrub all read it — which is exactly why it is converted once here and stored, never re-derived later: two LibreOffice passes may disagree, and cell coordinates must not move under a mark. - `timeoutSeconds` (integer) — Per-call HTTP timeout (seconds) for the compute service. Absent: DERIVED from the source's size (see DocumentExtractToolExecutor#timeoutSeconds), 60..1800 s. Cap 21600 (6h) when given explicitly. ## Detect Patterns **TOOL** `DOCUMENT_DETECT` Scans a piece of `text` for sensitive patterns deterministically — no model involved. This is the "find the obvious matches" primitive that a classification workflow composes alongside model-based judgment. `presets` selects from a catalog of built-in detectors: `SSN`, `CREDIT_CARD` (validated with the Luhn checksum), `US_DRIVERS_LICENSE`, `BANK_ROUTING_ACCOUNT`, `PHONE`, `EMAIL`, `GPS_COORDS`, `GPS_NEAR_TERMS`, and `EXIF_GPS`. `customRegexes` adds your own detectors — each a `name`, a `pattern`, optional `flags` (a subset of `imsux`), optional `requiredContextKeywords` (a match only counts when one of these words appears nearby), a `code` label, an optional `target` (`TEXT`, the default, or `FILENAME` to match against `filename`), and an optional `luhn` flag to require the checksum. Rules can instead live as rows of an AutessaDB table via `rulesTableId`, so editing a row changes the very next document scanned with no redeploy; a term or name list can be supplied through `termsTableId` and `termsColumn`. `filename` and `metadata` are optional companion inputs some detectors read (for example, `EXIF_GPS` fires on a truthy GPS signal in `metadata`). The result lists the hits found — each with the matched text, the detector or rule that fired, its label `code`, and the location within the text — so a downstream step can decide what to do with them. ### Example ```json title="Tool config" { "toolType": "DOCUMENT_DETECT", "text": "@@{steps.extract.text}", "presets": ["SSN", "CREDIT_CARD", "EMAIL", "PHONE"], "customRegexes": [ { "name": "employee_id", "pattern": "EMP-\\d{6}", "code": "INTERNAL_ID" } ] } ``` **Configuration fields** - `text` (string) — The text to scan. Fed from a prior DOCUMENT_EXTRACT step. Required (s3Uri not yet supported). - `filename` (string · optional) — Optional filename to scan (fed via `@@{filename`}). Custom rules whose `CustomRegex#target` is `FILENAME` match against this rather than `#text`, mirroring the Python filename prefilter rules (bwc/wolf_depredation/personnel). - `metadata` (string · optional) — Optional document metadata (fed via `@@{metadata`}) — a JSON object string or a plain value. The `EXIF_GPS` preset fires when this metadata carries a truthy `exifGps` (a JSON field `exifGps` that is true/non-empty, or the whole value being truthy). Detect operates on text/metadata only — it never parses image bytes itself. - `s3Uri` (string) — Reserved: scanning straight from a document is not yet supported; use DOCUMENT_EXTRACT first. - `presets` (array) — Preset detector names from the catalog: SSN, CREDIT_CARD, US_DRIVERS_LICENSE, BANK_ROUTING_ACCOUNT, PHONE, EMAIL, GPS_COORDS, GPS_NEAR_TERMS, EXIF_GPS. `GPS_NEAR_TERMS` — a GPS coordinate within ±200 chars of any term drawn from `#termsTableId`/`#termsColumn` (needs both set). `EXIF_GPS` — a truthy `exifGps` signal in `#metadata`. - `customRegexes` (array) — Customer-authored custom regex detectors baked into this tool's config. - `rulesTableId` (string (ID) · optional) — Optional AutessaDB table of rule rows (name, pattern, flags, context_keywords, code, enabled). References an AutessaDB table by ID. - `termsTableId` (string (ID) · optional) — Optional AutessaDB table whose `#termsColumn` values are matched as a term/name list. References an AutessaDB table by ID. - `termsColumn` (string) — Column of `#termsTableId` holding the term/name to match. - `name` (string) — Required string value. - `pattern` (string) — Required string value. - `flags` (string) — Required string value. - `requiredContextKeywords` (array) — Required array value. - `code` (string) — Required string value. - `target` (string) — Which input this rule scans: `TEXT` (default) matches against the extracted document text; `FILENAME` matches against `@@{filename`}. Case-insensitive; a blank/absent value means TEXT. FILENAME rules mirror the Python filename prefilters. - `luhn` (boolean) — When true, a raw match only counts if it passes the Luhn checksum (the credit-card check). Lets a custom card-like rule opt into the same validation the `CREDIT_CARD` preset uses. Default false/absent = no Luhn gate. ## Locate Spans **TOOL** `DOCUMENT_LOCATE_SPANS` Finds every occurrence of a set of verbatim text spans in the document at `s3Uri` and returns geometry boxes for them. This is the bridge between "these strings are sensitive" — the output of the detectors and model calls — and "here are the boxes to draw or burn." Each entry in `spans` is the literal `text` to locate, plus an optional `code` label and a `reason`. This block writes nothing; it just returns geometry. The result is a list of located boxes — each carrying the page number, position and size, the matched text, and the `code`/`reason` carried through — in the shape [Burn Redactions](#burn-redactions-) accepts directly, so a pipeline can splice this output straight into that block's `redactions`. ### Example ```json title="Tool config" { "toolType": "DOCUMENT_LOCATE_SPANS", "s3Uri": "@@{DOCUMENT_S3_URI}", "spans": [ { "text": "123-45-6789", "code": "SSN", "reason": "Social security number" }, { "text": "jane@example.com", "code": "EMAIL", "reason": "Personal email" } ] } ``` **Configuration fields** - `s3Uri` (string) — s3:// URI or presigned https URL of the source document. Required. - `spans` (array) — Text spans to locate. Required (non-empty). - `timeoutSeconds` (integer) — Per-call HTTP timeout (seconds). Absent: DERIVED from `totalPages` and the measured per-page locate cost (`ComputeCostModel#pageLinearTimeoutSeconds`, 60..1800 s), the 1800 s ceiling when the page count is unknown. Cap 21600 (6h) when given explicitly. - `totalPages` (integer) — The surface's page count, when the caller knows it (the orchestrator does, from extract). Sizes the timeout (a hinted span's search can still widen ring by ring to the whole document, so the document, not the hint, is the worst case), and a surface longer than one locate slice is located in parallel page slices and merged (DocumentLocateSpansToolExecutor#locateSliced). Absent: one whole-document call, as before. - `text` (string) — Required string value. - `code` (string) — Required string value. - `reason` (string) — Required string value. - `pageFirst` (integer · optional) — PAGE HINT (1-based, inclusive): the page range of the window whose proposer found this span. Optional — both null means "search the whole document", the behaviour every caller had before hints existed. A hinted span is searched nearest-first, outward from the hint, and stops at its first hit; compute clamps a nonsensical range to the document rather than failing the call. - `pageLast` (integer · optional) — PAGE HINT (1-based, inclusive): the page range of the window whose proposer found this span. Optional — both null means "search the whole document", the behaviour every caller had before hints existed. A hinted span is searched nearest-first, outward from the hint, and stops at its first hit; compute clamps a nonsensical range to the document rather than failing the call. ## Burn Redactions **TOOL** `REDACTION_BURN` Burns a set of redaction boxes into the document at `s3Uri`, removing the underlying content (not just hiding it) and writing a flattened PDF to `outputS3Uri`. Each box becomes a solid black block, and — unless `showRuleText` is set to `false` — a label naming the triggering rule (its `code`) is drawn over the block. `redactions` is the list of boxes to burn, in the same shape [Locate Spans](#locate-spans-) emits, so the two chain directly. The result reports the written output document — the flattened PDF at `outputS3Uri`, with the sensitive content permanently removed. ### Example ```json title="Tool config" { "toolType": "REDACTION_BURN", "s3Uri": "@@{DOCUMENT_S3_URI}", "outputS3Uri": "@@{OUTPUT_S3_URI}", "redactions": "@@{steps.locate.boxes}", "showRuleText": true } ``` **Configuration fields** - `s3Uri` (string) — s3:// URI or presigned https URL of the source document. Required. - `outputS3Uri` (string) — s3:// URI the burned PDF is written to; the executor presigns the PUT. Required. - `redactions` (array) — Merged redaction boxes to burn: same shape DOCUMENT_LOCATE_SPANS emits ({page, x, y, w, h, text, code, reason, status}). Left as a raw list so pipeline mapping can splice locate-spans output straight through. - `showRuleText` (boolean) — Whether the white overlay label names the rule/citation on the block. Default true. - `timeoutSeconds` (integer) — Per-call HTTP timeout (seconds). Absent: DERIVED from the pages this call burns (the page range, else `totalPages`) and the measured per-page burn cost (`ComputeCostModel#pageLinearTimeoutSeconds`, 60..1800 s), and the 1800 s ceiling when the page count is unknown. Cap 21600 (6h) when given explicitly. - `pageRange` (object) — Burn only pages `[start, end)` (0-based, end exclusive) and write ONLY those pages to `outputS3Uri`: one SLICE of a long document's burn (burn_and_attach.js joins the slices). Box `page` numbers stay the ORIGINAL 1-based document pages; the compute service maps them. Absent: the whole document, exactly as before. Requires `totalPages`. - `totalPages` (integer) — The whole reviewed canonical PDF's page count. When present the compute service validates EVERY box against it (not only the slice's), so a box past the end fails a slice exactly as it failed the whole burn. Also the page basis of the derived timeout when no range is given. - `metadataRedactions` (array · optional) — Dotted metadata paths a reviewer marked REDACT (the FirstPass `metadata_redactions` cell's `fields`: e.g. `["exif.gps", "doc.author"]`). The compute service strips these from the burned PDF's metadata best-effort — a scrub failure never fails the burn. Optional; empty/absent means no metadata scrub. - `rasterizeOutput` (boolean) — Whether the released hard PDF is RASTERIZED to an image-only document (the print-and-scan guarantee: no text layer anywhere, so nothing is extractable). Default TRUE — this is the safety-critical release mode. Set false for a searchable vector release (covered content is still removed by the vector burn; only the rest keeps a live text layer). Absent => default. - `rasterDpi` (integer) — DPI for the rasterized release (default 200 in the compute service; clamped 72..600). Higher = crisper but larger. Only meaningful when `rasterizeOutput` is true. Absent => default. - `ocrTextLayer` (boolean) — Lay an invisible OCR text layer over the RASTERIZED release (the searchable-image production shape): Tesseract reads the burned raster only — black blocks included — so the layer cannot contain covered content, while the release becomes searchable/selectable again. Best-effort in the compute service (missing Tesseract degrades to the image-only release; the response's `ocr_text_pages` says how many pages got the layer). Only meaningful when `rasterizeOutput` is true. Absent/false => image-only release, wire-identical to before. - `stripAllLinks` (boolean) — Break EVERY hyperlink in the release (the FirstPass "Break all links" document flag, `links_sanitized`): after the box burn, the compute service deletes every remaining link annotation on every page — external URIs and internal GoTo links alike — and counts them in `links_stripped`. No box, no exemption code: this is not a redaction of content, only of the live links. A rasterized release carries no annotations anyway, so it matters for the vector release. Absent/false => only links under an applied redaction are removed, wire-identical to before. ## Look Up Decision Cache **TOOL** `DOCUMENT_CACHE_LOOKUP` Looks up a document's prior decisions by its `sha256` content hash. When an earlier run already classified and marked up an identical document, a lookup returns those decisions so a later run reuses them instead of repeating the analysis. Decisions are stored per company and encrypted at rest. The result is `{"hit": ..., "classifications": ..., "redactions": ..., "version": ..., "provenance": ..., "hitCount": ...}`. On a miss, `hit` is `false` and the decision fields are empty. On a hit, `classifications` and `redactions` carry the stored verdicts, `version` is the current revision of the cached record, `provenance` describes who last reviewed it and when, and `hitCount` reflects how often it has been reused. ### Example ```json title="Tool config" { "toolType": "DOCUMENT_CACHE_LOOKUP", "sha256": "@@{steps.extract.sha256}" } ``` **Configuration fields** - `sha256` (string) — SHA-256 hex of the canonical document bytes. Required. ## Store Decision Cache **TOOL** `DOCUMENT_CACHE_STORE` Stores a document's decisions under its `sha256` content hash so a later run of the same document can reuse them. `classificationsJson` and `redactionsJson` are opaque JSON strings the caller defines — the cache round-trips them unchanged and encrypts them at rest. `expiresInDays` sets a retention window (omit for no expiry). Providing `provenanceJson` — non-sensitive review metadata such as who adjudicated the document and when — records an adjudication history: each store keeps the decisions versioned and provenance-aware. Setting `supersede` to `true` replaces an existing record for the same hash and bumps its version, which is how an edited-after-the-fact decision overwrites an earlier one; without it, a store is a plain write. `sourceRequestId` optionally tags the decisions with the request that produced them. The result is `{"stored": ..., "version": ..., "superseded": ...}` — whether the write landed, the resulting record version, and whether it replaced a prior record. ### Example ```json title="Tool config" { "toolType": "DOCUMENT_CACHE_STORE", "sha256": "@@{steps.extract.sha256}", "classificationsJson": "@@{steps.classify.result}", "redactionsJson": "@@{steps.locate.boxes}", "provenanceJson": "{\"reviewedBy\":\"@@{REVIEWER}\",\"adjudicatedAt\":\"@@{currentDate}\"}", "expiresInDays": 365 } ``` **Configuration fields** - `sha256` (string) — SHA-256 hex of the canonical document bytes. Required. - `classificationsJson` (string) — Classification verdicts as a JSON string. Stored encrypted. - `redactionsJson` (string) — Soft-redaction boxes as a JSON string. Stored encrypted. - `sourceRequestId` (string · optional) — UUID of the request that produced these decisions (optional, for provenance). - `expiresInDays` (integer) — Agency retention: expires_at = now + N days. Null = never expires. - `provenanceJson` (string) — E9 learning loop: non-sensitive provenance JSON ({sourceRequestId, reviewedBy, adjudicatedAt, …}) stored plaintext for the UI. When present (or `#supersede` is set), the store routes through `storeAdjudicated` (version/provenance-aware) instead of the plain backstop `store`. - `supersede` (boolean) — E9: when true, an existing learned record for this hash is SUPERSEDED — its version is bumped and decisions/provenance are replaced (set by finalize when the doc was edited after a cache hit, or a prior cache row already exists). Absent/false → plain store (no version bump). - `adjudicationTier` (string) — Adjudication tier for this entry: `HUMAN_ADJUDICATED` (a reviewer acted on the doc) or `AI_ONLY` (a pure AI proposal). Finalize derives it per document. The tier only ratchets up — a HUMAN_ADJUDICATED value upgrades an existing AI_ONLY entry; an AI_ONLY/absent value never downgrades an existing HUMAN_ADJUDICATED entry. Absent/blank ⇒ defaults to AI_ONLY on a new row, leaves an existing row's tier alone. When present the store routes through `storeAdjudicated`. ## Bulk Intake **TOOL** `DOCUMENT_BULK_INTAKE` Expands one uploaded archive (a `.zip`) at `archiveS3Uri` into rows of a target AutessaDB table — the front door for turning a delivery of many documents into records ready to process. Each archive entry is streamed to storage (the archive is never buffered whole), its SHA-256 is computed as it streams, and the rows are inserted in chunks. `columnMapping` names which target columns receive which per-entry facts: `fileColumn` (a FILE column, required) gets the stored file, and the optional `nameColumn`, `pathColumn`, `shaColumn`, `sizeColumn`, and `mimeColumn` receive the entry's name, path within the archive, content hash, byte size, and MIME type; `extraStatic` writes fixed values (each a template) on every row. Email entries (`.eml`) are expanded so their attachments become their own rows, with `parentShaColumn` linking an attachment back to its parent. `allowedExtensions` narrows which file types are accepted (a sensible document/spreadsheet/email/image default applies otherwise); entries that are the wrong type or over `maxEntryBytes` are skipped and reported per-entry rather than failing the whole intake. Only the columns you map are written, so rows stay user-editable. Set `enableIndexing` to `true` to index rows for semantic search as they land, and `uploadsTableId` to record one lifecycle row per archive (its filename, size, status, document count, and any error). Intake is crash-safe and resumable: a large archive is expanded as a chain of independent slices (bounded by `chunkSize`, `batchEntriesPerRun`, `timeBudgetSeconds`, and `timeoutSeconds`), each checkpointing its progress before the next begins. Re-running or retrying an interrupted intake is idempotent — already-inserted entries are reconciled and skipped, so no document is ingested twice. Long intakes are typically run as [work queue](/docs/api/workqueues.md) items. The result reports the intake's progress — how many entries were expanded into rows, how many were skipped, and any per-entry errors. ### Example ```json title="Tool config" { "toolType": "DOCUMENT_BULK_INTAKE", "archiveS3Uri": "@@{ARCHIVE_S3_URI}", "targetTableId": "V1StGXR8Z5", "columnMapping": { "fileColumn": "document", "nameColumn": "file_name", "shaColumn": "sha256", "sizeColumn": "size_bytes", "extraStatic": { "batch_id": "@@{BATCH_ID}" } }, "uploadsTableId": "3oPtLQmZ7v", "enableIndexing": false } ``` **Configuration fields** - `archiveS3Uri` (string) — s3:// URI (or presigned https URL) of the archive to expand. Required; typically `@@{archiveS3Uri`}. - `targetTableId` (string (ID)) — The AutessaDB table each archive entry becomes a row of. Required. References an AutessaDB table by ID. - `columnMapping` (object) — Which target-table columns receive which per-entry facts. Required (`fileColumn` at minimum). - `uploadsTableId` (string (ID) · optional) — Optional per-archive lifecycle table (filename/size/status/n_documents/error columns by name). References an AutessaDB table by ID. - `chunkSize` (integer) — Rows per bulk-insert chunk. Null ⇒ {@value #DEFAULT_CHUNK_SIZE}. Clamped to at least 1. - `allowedExtensions` (array · optional) — Optional allow-list of lower-case file extensions (no dot). When non-empty, an entry whose extension is not listed is skipped and reported per-entry. Null/empty ⇒ the executor falls back to `#DEFAULT_ALLOWED_EXTENSIONS` (common document/spreadsheet/email/image types); a non-empty configured list wins. - `enableIndexing` (boolean) — Whether inserted rows are indexed. Default false ⇒ indexingMode SKIP (indexing deferred to a later touchpoint); true ⇒ IMMEDIATE. - `timeoutSeconds` (integer) — Per-invocation timeout (seconds). Null ⇒ {@value #DEFAULT_TIMEOUT_SECONDS}; clamped to [1, {@value #MAX_TIMEOUT_SECONDS}]. - `batchEntriesPerRun` (integer) — LEGACY (pre-split slice model): zip entries expanded per run. Still parsed so in-flight payloads deserialize across deploy; the phase model reads `#enqueueBatchSize` instead. - `timeBudgetSeconds` (integer) — Soft wall-clock budget (seconds) for a run; a run that exceeds it stops at the next chunk boundary and continues in a fresh slice. Null ⇒ {@value #DEFAULT_TIME_BUDGET_SECONDS} (45min). Keep it under `#timeoutSeconds` so a run checkpoints rather than being killed. - `maxArchiveBytes` (integer) — Max archive size accepted (bytes). Null ⇒ {@value #DEFAULT_MAX_ARCHIVE_BYTES} (20 GiB). - `maxEntryBytes` (integer) — Per-entry size cap (bytes). Null ⇒ {@value #DEFAULT_MAX_ENTRY_BYTES} (2 GiB). Top-level entries stream to S3 so this is a runaway backstop, not a working ceiling; an over-cap entry is skipped with a per-entry error row rather than failing the whole intake. - `queueId` (string) — The work queue (UUID string) continuation slices are enqueued onto. Usually resolved at runtime from the executing item's own queue (`AUTESSA_WORK_QUEUE_ID`); this config value is the fallback for a direct (non-work-item) invocation that still wants self-chaining. - `entryOffset` (integer) — LEGACY (pre-split slice model): zero-based entry offset a slice started at. Still parsed so an in-flight pre-split payload deserializes across deploy; the INDEX phase treats it as an `#enqueueOffset` floor so such an item resumes rather than re-walking from zero. - `uploadRowId` (string (ID)) — The lifecycle (uploads) row id shared across all slices of one archive. The first slice inserts the row and stamps its id into every continuation payload (`@@{uploadRowId`}); later slices update that same row (progress + terminal status) instead of inserting a new one. References an AutessaDB table by ID. - `groupKey` (string) — The queue group key continuation slices join (same group counters as the parent). Usually resolved from the executing item's own group (`AUTESSA_WORK_GROUP_KEY`); this config/payload value is the fallback. - `ingestUnmatched` (boolean) — NEVER-IGNORE mode. When true, an entry whose extension is NOT in `#allowedExtensions` is INGESTED as an ordinary document row (bytes staged to S3, name/sha/size/mime recorded) instead of being recorded-and-skipped — the tenant's downstream pipeline decides what to do with it (in FirstPass: route it to human review rather than drop it). The SAFETY guards are unaffected and still skip: a zip-slip / absolute / `..` entry path and an entry over `#maxEntryBytes`. Those are attack and runaway-size guards, not "we don't recognise this type" — conflating the two would let this flag re-open a path-traversal hole. Null/false ⇒ unchanged platform behaviour (unknown extensions are counted skips). - `maxArchiveDepth` (integer) — How many levels of ARCHIVE this intake expands. Null ⇒ {@value #DEFAULT_ARCHIVE_DEPTH} (the uploaded archive only, the historical behaviour). With `maxArchiveDepth > 1`, a `.zip` entry found at depth `d < maxArchiveDepth` is staged to S3 and gets its OWN intake work item (its own lifecycle row, its own `uploadRowId`, hence its own deterministic doc-key space) enqueued on the same queue/group with `archiveDepth = d + 1` — the same self-chaining mechanism continuation slices use. A zip AT the max depth is not expanded: it is ingested as a document row when `#ingestUnmatched` is on, else recorded-and-skipped. - `archiveDepth` (integer) — The archive-nesting depth of THIS run: 1 for the uploaded archive, `d+1` for an archive expanded out of a depth-`d` archive. Carried in the nested work item's payload (`@@{archiveDepth`}); absent/blank ⇒ 1. - `phase` (string) — Which phase this invocation runs: `#PHASE_INDEX` enumerates the archive's central directory once and enqueues one `#PHASE_EXTRACT` work item per inner file (self-chaining when the walk exceeds its budget), each EXTRACT lands exactly one entry (bounded ranged GET → inflate → S3 → one row insert, whose BULK_INSERT trigger enqueues the document's pipeline immediately), and one `#PHASE_FINALIZE` item converges the archive to its terminal lifecycle status once every EXTRACT is done. Absent/blank ⇒ INDEX — which is also what a pre-split payload (no phase at all) resolves to, keeping in-flight items valid across deploy. - `entryIndex` (integer) — EXTRACT: this entry's zero-based ordinal in the NAME-SORTED entry list — the `entryIndex` half of the deterministic doc key `"{uploadRowId`:{entryIndex}"}. Assigned once at INDEX time and carried here, so it is stable regardless of extraction order or retries. - `entryName` (string) — EXTRACT: the raw central-directory entry name (sanitized again at extract time). - `dataOffset` (integer) — LEGACY EXTRACT: absolute byte offset of this entry's compressed DATA within the archive object. INDEX no longer populates this — resolving it required commons-compress to parse every LOCAL FILE HEADER at open, which walks essentially the whole archive over ranged S3 reads (~10 silent minutes for a 7.3 GB zip). INDEX now opens central-directory-only and carries `#localHeaderOffset` instead; EXTRACT derives the data offset from ONE 64-byte ranged GET. Still parsed so an in-flight pre-deploy payload deserializes and extracts correctly: when it carries a usable `dataOffset` EXTRACT uses it directly and skips the LFH read. - `localHeaderOffset` (integer) — EXTRACT: absolute byte offset of this entry's LOCAL FILE HEADER within the archive object, read straight off the central directory (no local-header parse needed, ZIP64 included). The extract run reads 64 bytes there, parses the LFH's own name/extra lengths, and derives `dataOffset = localHeaderOffset + 30 + nameLen + extraLen`. The LFH's extra length routinely DIFFERS from the central directory's (zip writers pad the local extra for alignment, and ZIP64/Unicode extra fields are frequently present in only one of the two) — which is exactly why the data offset must be derived from the LOCAL header and never from the central-directory extra length. - `compressedSize` (integer) — EXTRACT: this entry's compressed byte count (the bounded ranged GET's length). - `uncompressedSize` (integer) — EXTRACT: this entry's uncompressed byte count (the S3 put's content length). - `compressionMethod` (integer) — EXTRACT: zip compression method — 0 STORED (pass-through) or 8 DEFLATED (raw-deflate inflate). - `entryLastModifiedMillis` (integer · optional) — EXTRACT: this entry's LAST-MODIFIED time as epoch millis, read from the zip central directory by the INDEX walk; absent when the archive did not carry one, or for a whole-object entry (a single-file upload / manifest member, which was never inside a container). Carried in the payload because it exists ONLY at INDEX. That walk opens the central directory — the one place a per-entry timestamp lives — and EXTRACT reads a byte window, never a directory record. Without this field a document's own date would be unrecoverable for every non-email document in the corpus, and the ingestion time (which says nothing about when the record happened) would be the only thing left to write. Optional and additive: a payload from a pre-deploy INDEX run simply lacks it and that document falls to the next rung of the date ladder, exactly as before. - `enqueueOffset` (integer) — INDEX: the entry ordinal the enqueue walk resumes from. Checkpointed to the lifecycle row after every enqueue batch (the row is authoritative over this payload value, exactly like the legacy `entryOffset` rule), so a crashed/re-leased INDEX run re-enqueues at most one batch — and re-enqueued EXTRACTs no-op against their doc key anyway. - `totalEntries` (integer) — INDEX continuation / FINALIZE: total file entries in the archive, once its directory is read. - `finalizeAttempt` (integer) — FINALIZE: self-chain attempt counter driving the re-check delay backoff. - `enqueueBatchSize` (integer) — INDEX: EXTRACT items enqueued per batch. Null ⇒ {@value #DEFAULT_ENQUEUE_BATCH_SIZE}. - `manifestS3Uri` (string · optional) — INDEX (MANIFEST variant): s3:// URI of a JSON manifest naming objects ALREADY UPLOADED to S3, which the walk enumerates INSTEAD of a zip central directory. This is how a whole FOLDER is taken in: the browser uploads each file with the ordinary multipart upload and then names them all in one manifest, rather than zipping tens of thousands of files in a tab. Shape — `{"files":[{"s3Uri":"s3://…","relativePath":"case-7/photos/a.jpg", "sizeBytes":12345`]}}. `relativePath` plays EXACTLY the role a zip entry name plays: it is the `source_path` of the row, the input to the zip-slip sanitizer, the extension the allow-list is checked against, and the name an OS-artifact skip is decided by. That equivalence is the whole design — the named-skip contract, the ordinal (name-sorted), the doc-key formula and the lifecycle bookkeeping all carry over from the zip walk with no second implementation. `sizeBytes` is optional (the per-entry cap is simply not decidable at index time without it, and EXTRACT's own bounded read still enforces it). A URI, never inline JSON: a folder can be tens of thousands of files, and a work-item payload is not a place to put a megabyte of paths. The walk itself is chunked across self-chained INDEX items on the same time budget as the zip walk, so there is no file-count cap. When set, `#archiveS3Uri` is still required (it identifies the upload's lifecycle row and is what `findUploadRowByArchive` resolves on) but is never opened as a zip. - `documentTagsTableId` (string (ID)) — The tag MEMBERSHIP table (`firstpass_document_tags`) — one row per (document, tag). When set (together with a resolvable `#uploadTagLabel`), every document row this intake lands is tagged with the name of the UPLOAD it came from, so a reviewer can filter a corpus down to "everything out of PRR-23276-batch2.zip" with the same indexed read a hand-applied tag uses. The rows written here are IDENTICAL in shape to the ones the app's tag op service writes — same columns, same slug fold, same display mirror on the document — because a second tag mechanism would be a second thing every tag reader has to know about. Absent ⇒ no tagging at all, silently and correctly: a tenant provisioned before the tag tables existed keeps ingesting exactly as it did. References an AutessaDB table by ID. - `tagsTableId` (string (ID)) — The tag VOCABULARY table (`firstpass_tags`) — one row per tag, per request, carrying the human `label` the picker renders. Set alongside `#documentTagsTableId` so the upload's tag is a FIRST-CLASS tag: it appears in the tag picker and the facet list beside hand-made ones rather than existing only as membership rows nothing can name. The vocabulary row is upserted once per upload by the INDEX phase. References an AutessaDB table by ID. - `uploadTagLabel` (string) — The upload's display name, as the tag LABEL every row of this upload carries (its slug is derived from it). Resolved ONCE by the INDEX phase — which is the only run that can see the whole source (an archive's own filename, a folder manifest's common top-level directory, a single file's name) — and carried on every EXTRACT payload from there, exactly as the entry byte-window facts are. Deriving it per-EXTRACT instead would mean re-reading the manifest once per file and would let two files of one folder disagree about which upload they came from. - `fileColumn` (string) — The FILE column that receives each uploaded entry's S3 URI. Required. - `nameColumn` (string · optional) — Optional column receiving the entry's file name (basename). - `nameKeyColumn` (string · optional) — Optional column receiving the entry's file name NORMALIZED for search (lower-cased, trimmed). The indexed twin of `nameColumn`: same value, written in the same pass, so a filename predicate runs against an index instead of scanning. Omit it and nothing is written — the mapping is optional exactly like `uploadKeyColumn`. - `pathColumn` (string · optional) — Optional column receiving the entry's path within the archive. - `shaColumn` (string · optional) — Optional column receiving the entry's sha256 hex. - `sizeColumn` (string · optional) — Optional column receiving the entry's size in bytes (INTEGER). - `mimeColumn` (string · optional) — Optional column receiving the entry's guessed MIME type. - `parentShaColumn` (string · optional) — Optional column receiving the sha256 of the parent .eml for an extracted attachment row (empty for top-level entries). Wires the intake output to the documents table's `parent_sha256` column for email attachment linkage. - `parentDocKeyColumn` (string · optional) — Optional column receiving the PARENT's `doc_key` for an extracted attachment row (empty for top-level entries) — the stable attachment→email edge. Distinct from `#parentShaColumn`, which carries the parent's CONTENT hash: byte-identical emails (the same message in several custodians' mailboxes) share a sha256, so it cannot identify WHICH parent row a child belongs to. The parent's doc key is deterministic and unique per row, so it can. - `docKeyColumn` (string · optional) — Optional column receiving the deterministic per-entry doc key `"{uploadRowId`:{entryIndex}"} (attachments: `"{uploadRowId`:{entryIndex}:a{n}"}). This is the crash-safe idempotency key: a resume/retry reconciles the boundary chunk against existing values of this column and skips any already-inserted key, so a crash between an insert and the offset write re-inserts nothing. - `intakeUploadIdColumn` (string · optional) — Optional column receiving the LIFECYCLE ROW ID of the archive this row expanded from — the durable per-archive join key, written on parents and attachments alike (children are built by the same method, so they inherit it without a second code path). Distinct from `#docKeyColumn`, which embeds the same id as the first segment of a composite text key: parsing an id back out of a doc key is a string operation on every row, whereas its own column answers "which documents came from this zip?" directly. - `uploadKeyColumn` (string · optional) — Optional SECOND column receiving the same archive row id as `#intakeUploadIdColumn`, written in the same pass onto the same rows. Two columns for one value is not duplication for its own sake: on the FirstPass documents table the older column is unindexed and can never become indexed (column provisioning is additive by name — it adds what is missing and cannot retype or re-index what is there), while a filter over "everything that came from this upload" must run against an INDEXED column or it scans the whole corpus. Writing both here is what keeps the indexed twin populated from ingestion onward, instead of only from whenever a backfill last ran. - `extraStatic` (object · optional) — Optional fixed columns written on every row: column name → value template resolved from the tool's `@@{...`} invocation variables (already substituted by the parser). ## Related - [Tool Pipeline](/docs/tools/pipelines.md) — chain these blocks into one declarative multi-step tool - [AutessaDB tool types](/docs/tools/autessadb.md) — persist extracted text and detections onto rows - [LLM Call](/docs/tools/core.md#llm-call-) — pair deterministic detection with model-based classification - [Work queues API](/docs/api/workqueues.md) — run bulk intake and per-document processing at scale - [AutessaDB tables guide](/docs/guide/autessadb/tables.md) --- # Pipelines Source: https://docs.autessa.com/docs/tools/pipelines > The Tool Pipeline type: chain published tools into one declarative, multi-step tool with a shared variable context. # Pipelines A declarative way to compose several tools into one. A Tool Pipeline runs an ordered set of steps — each a call to one of your published tools — over a single input in one pass, threading each step's output into the next through a shared variable context. A pipeline is the declarative alternative to writing an [AutessaScript](/docs/autessascript.md) orchestrator: when a task is "run tool A, then feed its result into tools B and C," a pipeline expresses that as configuration rather than code. Every tool a pipeline calls is a normal published tool, so the same tools remain usable on their own, on agents, and from triggers. ## Tool Pipeline **TOOL** `TOOL_PIPELINE` Runs `steps` in order over one input, sharing a variable context across all of them. Each step names the published tool to run (`toolId`) and maps that tool's `@@{...}` placeholders through `variableMapping`. **Shared context and deferred templates.** The pipeline's own invocation variables are available to every step under `@@{input.}`, and each completed step's parsed JSON result is available under `@@{steps..}`. A step's `variableMapping` builds each of the invoked tool's inputs from a template over those namespaces — for example `{ "text": "@@{steps.extract.text}", "hash": "@@{input.sha256}" }`. These `@@{steps.…}` / `@@{input.…}` references are resolved per step at run time, so one extraction step can feed many downstream steps without repeating the work. **Parallel execution.** Steps do not run strictly in list order. The pipeline derives dependencies automatically — step B depends on step A whenever B's `variableMapping`, `runIf`, or `stopIf` references `@@{steps.A.…}` — and unions those with any names you list in a step's `dependsOn`. It then runs the steps in dependency order, executing every step whose dependencies are satisfied concurrently before moving on. Independent branches overlap while dependent chains stay ordered; a dependency cycle is rejected when the tool is saved. **Conditions.** `runIf` gates whether a step runs, and `stopIf` — evaluated after a step completes — ends the pipeline early when it is met. Each is a single comparison of the form ` `, where `` is a dotted context path (such as `steps.detect.matched`) and `` is one of `==`, `!=`, `contains`, `exists`, or `truthy`. A blank condition always passes. **Mutating steps.** A step that performs a side effect — writing a row, sending an email, burning a PDF, storing a cache entry — should be marked `mutating: true` so it is never run concurrently with another step; it is isolated into its own position in the run order per its dependencies. Steps calling a tool type that is inherently a writer are treated as mutating automatically. Read-only steps need no annotation. **Timeouts.** Every step is bounded by a timeout — its own `timeoutSeconds` if set, otherwise the pipeline's `perStepTimeoutSeconds` (default 30 minutes). A step that exceeds its timeout is recorded as an error, which honors `continueOnError` like any other failure. Set `continueOnError: true` on a step whose failure should not abort the rest of the pipeline. **Checkpointing.** A pipeline records each step's status as it goes, so a long run's progress is observable and a step failure is attributed to the exact step. When `resultMapping` is set, the pipeline's `result` is that mapping resolved over the shared context; otherwise `result` is the full `steps` object. The result is `{"steps": {...}, "stepStatus": {...}, "result": ...}` — the per-step results keyed by `stepName`, each step's terminal status (succeeded, failed, timed out, or skipped), and the final `result` (the resolved `resultMapping`, or the full `steps` object when no mapping is given). ### Example This pipeline extracts a document's text once, then runs a deterministic detector and a model classification against that same text in parallel, and finally records the outcome on a row. ```json title="Tool config" { "toolType": "TOOL_PIPELINE", "steps": [ { "stepName": "extract", "toolId": "V1StGXR8Z5", "variableMapping": { "DOCUMENT_S3_URI": "@@{input.s3Uri}" } }, { "stepName": "detect", "toolId": "3oPtLQmZ7v", "variableMapping": { "TEXT": "@@{steps.extract.text}" } }, { "stepName": "classify", "toolId": "kzT9pLm3Qw", "variableMapping": { "TICKET_BODY": "@@{steps.extract.text}" } }, { "stepName": "record", "toolId": "9bRcW2Xy4Z", "mutating": true, "runIf": "steps.detect.matched truthy", "variableMapping": { "ROW_ID": "@@{input.rowId}", "FINDINGS": "@@{steps.detect.hits}", "CATEGORY": "@@{steps.classify.content}" } } ], "resultMapping": { "category": "@@{steps.classify.content}", "findings": "@@{steps.detect.hits}" } } ``` **Configuration fields** - `steps` (array) — Ordered steps run as a dependency DAG in topological waves. Required (non-empty). - `resultMapping` (object · optional) — Optional final result mapping: output key → template over the shared context (`@@{steps.*.*`} / `@@{input.*`}). When present, the pipeline's `result` is this mapping resolved; when absent, `result` is the full `steps` object. - `perStepTimeoutSeconds` (integer) — Per-step timeout (seconds) applied to every step lacking its own `Step#timeoutSeconds`. Null ⇒ {@value #DEFAULT_PER_STEP_TIMEOUT_SECONDS} (30 min). Clamped to [1, {@value #MAX_PER_STEP_TIMEOUT_SECONDS}]. - `stepName` (string) — Name this step's result is keyed under in the shared context (steps..*). Required. - `toolId` (string (ID)) — Published tool to invoke. Required. Sqid-decoded on the way in. References a tool by ID. - `variableMapping` (object) — Target-variable → template map filling the invoked tool's @@{var} placeholders. - `runIf` (string · optional) — Optional condition gating whether this step runs. - `stopIf` (string · optional) — Optional condition evaluated after the step; true ⇒ stop the pipeline. - `continueOnError` (boolean) — If true, a step error is recorded but the pipeline continues. Default false. - `dependsOn` (array) — Explicit extra dependency edges: this step will not start until every named step has completed. These are unioned with the edges derived automatically from `@@{steps..…`} references in the mapping/conditions. - `timeoutSeconds` (integer) — Per-step timeout (seconds) overriding the pipeline's `ToolPipelineToolConfig#perStepTimeoutSeconds`. Null ⇒ use the pipeline default. Clamped to [1, {@value #MAX_PER_STEP_TIMEOUT_SECONDS}]. - `mutating` (boolean) — Safety flag: a mutating step performs a side effect (writes a row, sends an email, burns a PDF, stores a cache entry). Default false — opt-in, so read/judge tools need no annotation. Effect: a mutating step is never scheduled concurrently with any other step. The planner isolates it in its own single-step wave, placed per its dependency edges (before/after as the DAG dictates); two independent mutating steps serialize relative to each other in list order, deterministically. This is the guard for pipelines that mix parallel read/judge tools with writers: the parallel waves are safe because reads don't collide, but writers must not race — mark them `mutating`. Auto-default. When the referenced tool's type is a known-mutating type (AUTESSADB_INSERT/UPDATE/DELETE, AUTESSADB_BULK_INSERT/UPDATE/DELETE, SEND_EMAIL, SEND_EMAIL_FANOUT, GMAIL_SEND_EMAIL, OUTLOOK_SEND_EMAIL, HUBSPOT_SEND_EMAIL, MAKE_PHONE_CALL, SEND_SMS, SCHEDULE_EVENT, REDACTION_BURN, DOCUMENT_CACHE_STORE), the step is treated as mutating even if this flag is unset (logged at debug). A caller cannot override an auto-detected mutating type back to false — the flag can only escalate, never relax the safety of a known writer. Ambiguous types (API_CALL, MCP, connector upserts, etc.) stay caller-declared: set `mutating: true` on them when they write. ## Related - [AutessaScript](/docs/autessascript.md) — the code-based alternative for complex orchestration - [Document tool types](/docs/tools/documents.md) — the blocks a document pipeline most often chains - [Tools guide](/docs/guide/agentic/tools.md) — defining tools and their arguments - [AutessaDB tool types](/docs/tools/autessadb.md) — the writer steps a mutating pipeline records to --- # MCP Source: https://docs.autessa.com/docs/tools/mcp > Tool types that bridge agents to registered MCP servers: tools, resources, and prompts. # MCP Bridge your agents to any Model Context Protocol server — call its tools, read its resources, and expand its prompts as ordinary Autessa tools. All three types point at a server registered under [MCP servers](/docs/guide/agentic/mcp-servers.md) via `mcpServerId`. Autessa speaks JSON-RPC over HTTP (plain JSON or SSE responses), re-reads the server's auth configuration on every call so rotated tokens pick up immediately, and applies SSRF protection to the server URL. Requests time out after 30 seconds with automatic retries. The easiest way to create these tools is the create-from-MCP flow, which imports a server's advertised tools with their schemas in one step. ## MCP Tool Call **TOOL** `MCP` Calls one named tool on the MCP server. The runtime arguments the agent supplies are type-coerced against `mcpToolInputSchema` — integers, numbers, booleans, arrays, and objects are converted from strings to the JSON types the schema declares — and sent as the tool-call arguments. The result returned to the agent is the text content of the MCP tool's response; image or binary content blocks in the response are dropped. Failures come back as `{"error": true, "errorCode": ..., "errorMessage": ...}`. ### Example ```json title="Tool config" { "toolType": "MCP", "mcpServerId": "V1StGXR8Z5", "mcpToolName": "search_issues", "mcpToolInputSchema": "{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\"},\"limit\":{\"type\":\"integer\"}},\"required\":[\"query\"]}" } ``` **Configuration fields** - `mcpServerId` (string (ID)) — Required string (ID) value. References an MCP server by ID. - `mcpToolName` (string) — Required string value. - `mcpToolInputSchema` (string) — JSON schema for the tool's input parameters ## MCP Resource **TOOL** `MCP_RESOURCE` Reads a single resource from the MCP server by URI. Text resources are returned to the agent as-is; binary resources come back as a base64 payload labeled with the mime type. Only the first content element of the resource is read. ### Example ```json title="Tool config" { "toolType": "MCP_RESOURCE", "mcpServerId": "V1StGXR8Z5", "resourceUri": "file:///reports/latest-summary.md", "resourceMimeType": "text/markdown" } ``` **Configuration fields** - `mcpServerId` (string (ID)) — Required string (ID) value. References an MCP server by ID. - `resourceUri` (string) — Required string value. - `resourceMimeType` (string) — Required string value. ## MCP Prompt **TOOL** `MCP_PROMPT` Invokes a named prompt on the MCP server, passing the agent's runtime arguments as the prompt arguments. The server assembles its prompt messages and the tool returns them flattened as readable text, one `**role**: content` block per message, which the agent can then act on. ### Example ```json title="Tool config" { "toolType": "MCP_PROMPT", "mcpServerId": "V1StGXR8Z5", "promptName": "triage_ticket", "promptArgumentsSchema": "{\"type\":\"object\",\"properties\":{\"ticketId\":{\"type\":\"string\"}},\"required\":[\"ticketId\"]}" } ``` **Configuration fields** - `mcpServerId` (string (ID)) — Required string (ID) value. References an MCP server by ID. - `promptName` (string) — Required string value. - `promptArgumentsSchema` (string) — JSON schema for the prompt's arguments ## Related - [Tool Types overview](/docs/tools.md) - [MCP servers guide](/docs/guide/agentic/mcp-servers.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Core tool types](/docs/tools/core.md) --- # Gmail Source: https://docs.autessa.com/docs/tools/gmail > Tool types for reading, searching, drafting, and sending mail through a connected Gmail account. # Gmail Give agents a real Gmail mailbox: list and search messages, read full bodies and attachments, manage labels, and draft or send mail as the connected account. Every tool type on this page requires a **Gmail connector** — an OAuth connection to the Google account whose mailbox the agent should work in. The tool config points at the connector with `connectorId`, and Autessa refreshes the OAuth token automatically before each call. See [Connectors](/docs/guide/connectors.md) for how to link an account. Reading tools return decoded message bodies (not just snippets), so agents can act on full email content without extra round-trips. ## List Unread **TOOL** `GMAIL_LIST_UNREAD` Lists unread messages in the mailbox, optionally narrowed to specific label IDs, and fetches each result in full. The response contains `totalResults` plus a `messages` array where every entry carries `id`, `threadId`, the key headers (From, To, Subject, Date, Cc, Bcc, Reply-To, Message-ID), the `snippet`, and the decoded plain-text `body`. If an unread message already has a draft reply in progress, its `draftId` is included so the agent can update the existing draft instead of creating a duplicate. `maxResults` defaults to 10 when unset. ### Example ```json title="Tool config" { "toolType": "GMAIL_LIST_UNREAD", "connectorId": "V1StGXR8Z5", "maxResults": 25, "includeSpamTrash": false, "labelIds": ["INBOX"] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `includeSpamTrash` (boolean) — Required boolean value. - `labelIds` (array · optional) — Optional filter ## Mark as Read **TOOL** `GMAIL_MARK_AS_READ` Removes the `UNREAD` label from a single message, marking it read in the connected mailbox. Agents typically call this after processing a message surfaced by List Unread so the same email is not handled twice. The result confirms the `messageId` that was updated; no other labels are touched. ### Example ```json title="Tool config" { "toolType": "GMAIL_MARK_AS_READ", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. ## Move to Label **TOOL** `GMAIL_MOVE_TO_LABEL` Modifies the labels on a message in one call: every ID in `addLabelIds` is applied and every ID in `removeLabelIds` is stripped. Because Gmail folders are just labels, this is also how an agent archives or files mail — removing `INBOX` while adding a custom label moves the message out of the inbox and into that label. The result echoes the `addedLabels` and `removedLabels` that were applied. ### Example ```json title="Tool config" { "toolType": "GMAIL_MOVE_TO_LABEL", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "addLabelIds": ["Label_1234567890"], "removeLabelIds": ["INBOX"] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `addLabelIds` (array) — Required array value. - `removeLabelIds` (array) — Required array value. ## Create Draft Reply **TOOL** `GMAIL_CREATE_DRAFT_REPLY` Drafts a threaded reply to an existing message: the executor reads the original's headers, sets `In-Reply-To` and `References` so the reply lands in the same conversation, prefixes the subject with `Re:`, and addresses the original sender — with `replyAll` unset or true, the original To and Cc recipients are included too (the connected account's own address is always filtered out). If the thread already ends in a draft, that draft is updated in place rather than duplicated, and the result carries `updated: true`. The result includes the `draftId`, `threadId`, and resolved recipient lists; at least one of `replyBody` or `replyBodyHtml` must be provided, and nothing is sent until a Send Draft call. ### Example ```json title="Tool config" { "toolType": "GMAIL_CREATE_DRAFT_REPLY", "connectorId": "V1StGXR8Z5", "originalMessageId": "@@{messageId}", "replyBody": "@@{replyText}", "replyAll": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `originalMessageId` (string) — Required string value. - `replyBody` (string) — Required string value. - `replyBodyHtml` (string · optional) — Optional - `replyAll` (boolean · optional) — Optional; null is treated as true (reply all) - `attachments` (array · optional) — Optional; re-uploaded in full when the draft is updated ## Create Draft **TOOL** `GMAIL_CREATE_DRAFT` Creates a fresh draft (not attached to any thread) addressed from the connected account. Supply plain text in `body`, HTML in `bodyHtml`, or both — when HTML is present the draft is built as a `multipart/alternative` message so recipients without HTML rendering still see the text part; at least one of the two body fields is required. The result returns the `draftId` to pass to Send Draft, and the draft sits in the user's Gmail Drafts folder for human review before anything goes out. ### Example ```json title="Tool config" { "toolType": "GMAIL_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "to": "@@{recipientEmail}", "subject": "Your onboarding checklist", "body": "@@{draftText}", "cc": "success@acme.example" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `to` (string) — Required string value. - `subject` (string) — Required string value. - `body` (string) — Required string value. - `bodyHtml` (string · optional) — Optional - `cc` (string · optional) — Optional - `bcc` (string · optional) — Optional - `attachments` (array · optional) — Optional ## Send Draft **TOOL** `GMAIL_SEND_DRAFT` Sends an existing draft by its `draftId` — the counterpart to Create Draft and Create Draft Reply in a review-then-send workflow. Gmail delivers the draft exactly as it stands (including any human edits made in the meantime) and removes it from the Drafts folder. The result returns the sent message's `messageId` and `threadId`. ### Example ```json title="Tool config" { "toolType": "GMAIL_SEND_DRAFT", "connectorId": "V1StGXR8Z5", "draftId": "@@{draftId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `draftId` (string) — Required string value. ## Send Email **TOOL** `GMAIL_SEND_EMAIL` Sends a message immediately from the connected account, with no draft step and no human checkpoint — reserve it for agents trusted to send autonomously, and prefer the draft tools when a review step is wanted. Like Create Draft it accepts `body`, `bodyHtml`, or both (sent as `multipart/alternative`), plus optional `cc` and `bcc`. The result returns the new `messageId` and `threadId`. ### Example ```json title="Tool config" { "toolType": "GMAIL_SEND_EMAIL", "connectorId": "V1StGXR8Z5", "to": "@@{recipientEmail}", "subject": "@@{subject}", "body": "@@{messageText}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `to` (string) — Required string value. - `subject` (string) — Required string value. - `body` (string) — Required string value. - `bodyHtml` (string · optional) — Optional - `cc` (string · optional) — Optional - `bcc` (string · optional) — Optional - `attachments` (array · optional) — Optional ## Get Message by ID **TOOL** `GMAIL_GET_MESSAGE_BY_ID` Fetches one message in full and returns an `email` object with headers, `labelIds`, `snippet`, `internalDate`, `sizeEstimate`, and the decoded `body` (plain text preferred, falling back to HTML). It is also the richest way to reach attachments: each attachment is downloaded, staged, and listed with `fileName`, `mimeType`, `attachmentId`, and ready-to-use `url` and `downloadUrl` links that expire after 15 minutes. Use it after a list or search call when the agent needs the complete content of a specific message. ### Example ```json title="Tool config" { "toolType": "GMAIL_GET_MESSAGE_BY_ID", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. ## Get Attachment Content **TOOL** `GMAIL_GET_ATTACHMENT_CONTENT` Downloads a single attachment and runs it through Autessa's file-content extraction, returning the attachment's text content directly in the result (`fileName`, `mimeType`, `content`) — not a link and not base64. This lets an agent read a PDF invoice or Word document from an email in one step. Use the `attachmentId` values surfaced by Get Message by ID or Search Messages; if the file's content cannot be extracted the tool fails with `EXTRACTION_FAILED` rather than returning raw bytes. ### Example ```json title="Tool config" { "toolType": "GMAIL_GET_ATTACHMENT_CONTENT", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "attachmentId": "@@{attachmentId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `attachmentId` (string) — Required string value. ## Get Attachment Download Link **TOOL** `GMAIL_GET_ATTACHMENT_DOWNLOAD_LINK` Downloads an attachment, stages it, and returns links instead of content: a short `downloadUrl` on the Autessa domain plus the underlying `presignedUrl`, both valid for 15 minutes (`expiresInSeconds: 900`). The result also reports `fileName`, `mimeType`, and `fileSizeBytes`. Use this when the file should be handed to a person, another tool, or an AutessaDB FILE column rather than read as text. ### Example ```json title="Tool config" { "toolType": "GMAIL_GET_ATTACHMENT_DOWNLOAD_LINK", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "attachmentId": "@@{attachmentId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `attachmentId` (string) — Required string value. - `saveToTableId` (string (ID) · optional) — Optional: store the attachment as a file in this AutessaDB table (same company) instead of returning it; the result's `s3Uri` is what to write into that column's filePath. Lets a script (which has no IO) put email attachments into a table. Set together with saveToColumnName. References an AutessaDB table by ID. - `saveToColumnName` (string · optional) — Optional (required with saveToTableId): the FILE, IMAGE, VIDEO or AUDIO column the file is stored for. It must accept the file's type; a FILE column takes any type. ## Search Messages **TOOL** `GMAIL_SEARCH_MESSAGES` Searches the mailbox by combining the structured fields into a single Gmail query — `from`, `to`, and `subject` become their matching operators, `hasAttachment` adds `has:attachment`, `after`/`before` add date bounds, `labelIds`, `isUnread`, and `isStarred` add their filters, and anything in `query` is appended as free-form Gmail search syntax (so `query` alone can express any search Gmail supports). Each hit is returned in full: headers, `labelIds`, `snippet`, `internalDate`, the decoded `body`, and attachment metadata (`fileName`, `mimeType`, `attachmentId` — no download links; follow up with the attachment tools). `maxResults` defaults to 10. ### Example ```json title="Tool config" { "toolType": "GMAIL_SEARCH_MESSAGES", "connectorId": "V1StGXR8Z5", "from": "@@{senderEmail}", "query": "invoice", "hasAttachment": true, "after": "2026/06/01", "isUnread": true, "maxResults": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `from` (string) — Required string value. - `to` (string) — Required string value. - `subject` (string) — Required string value. - `query` (string) — Required string value. - `hasAttachment` (boolean) — Required boolean value. - `after` (string) — Required string value. - `before` (string) — Required string value. - `labelIds` (array) — Required array value. - `isUnread` (boolean) — Required boolean value. - `isStarred` (boolean) — Required boolean value. - `maxResults` (integer) — Required integer value. - `includeSpamTrash` (boolean) — Required boolean value. ## Related - [Connectors](/docs/guide/connectors.md) - [Outlook Mail tool types](/docs/tools/outlook-mail.md) - [Calendar tool types](/docs/tools/calendars.md) - [Email tool types](/docs/tools/email.md) - [Tools in the agentic guide](/docs/guide/agentic/tools.md) --- # Outlook Mail Source: https://docs.autessa.com/docs/tools/outlook-mail > Tool types for reading, searching, drafting, and sending mail through a connected Outlook mailbox via Microsoft Graph. # Outlook Mail Work a Microsoft 365 mailbox from an agent: list and search Outlook messages, read full bodies and attachments, file mail into folders, and draft or send as the connected account. Every tool type on this page requires an **Outlook connector** — an OAuth connection to the Microsoft account whose mailbox the agent should work in. Calls go through the Microsoft Graph API, and Autessa refreshes the OAuth token automatically before each request. See [Connectors](/docs/guide/connectors.md) for how to link an account. One family-wide convention: listing and search tools return compact six-character message IDs instead of Graph's very long native IDs. These short IDs are valid for four hours — if a downstream call reports that a short ID has expired, re-run the list or search to get fresh ones. Folder fields accept both real folder IDs and well-known names such as `inbox`, `sentitems`, `drafts`, and `deleteditems`. ## List Unread **TOOL** `OUTLOOK_LIST_UNREAD` Lists unread messages in one folder — the inbox by default, or whatever `folderId` names. The response contains `totalResults` and a `messages` array where each entry carries the short `id`, `subject`, `from`, `to` and `cc` recipient lists, `receivedDateTime`, a `bodyPreview` excerpt, and `hasAttachments`. Previews keep the listing light; follow up with Get Message by ID when the agent needs a full body. `maxResults` defaults to 10. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_LIST_UNREAD", "connectorId": "V1StGXR8Z5", "maxResults": 25, "folderId": "inbox" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `folderId` (string) — Required string value. ## Mark as Read **TOOL** `OUTLOOK_MARK_AS_READ` Sets `isRead` on a single message, marking it read in the connected mailbox. Agents typically call this after handling a message surfaced by List Unread so the same email is not processed twice. Accepts either a short ID from a previous listing or a raw Graph message ID, and echoes the `messageId` back on success. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_MARK_AS_READ", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. ## Move to Folder **TOOL** `OUTLOOK_MOVE_TO_FOLDER` Moves a message into another mail folder — how an agent archives, files, or triages Outlook mail. `destinationFolderId` accepts a real folder ID or a well-known name like `archive` or `deleteditems`. Note that Microsoft Graph assigns a moved message a new identity: the result returns `newMessageId`, and any earlier ID for that message stops referring to the moved copy. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_MOVE_TO_FOLDER", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "destinationFolderId": "archive" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `destinationFolderId` (string) — Required string value. ## Create Draft Reply **TOOL** `OUTLOOK_CREATE_DRAFT_REPLY` Drafts a threaded reply to an existing message using Graph's native reply machinery, so the subject, conversation, and quoting behave exactly as if the user hit Reply in Outlook. With `replyAll` unset or true the original To and Cc recipients are kept; the connected mailbox's own address is always filtered out of the recipient lists. If the conversation already ends in a draft, that draft is updated in place instead of duplicated and the result carries `updated: true`. The result returns the `draftId` plus the resolved `toRecipients` and `ccRecipients`; provide the reply text in `body` or `bodyHtml` (HTML wins when both are set), and nothing is sent until a Send Draft call. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_CREATE_DRAFT_REPLY", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "body": "@@{replyText}", "replyAll": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `body` (string) — Required string value. - `bodyHtml` (string) — Required string value. - `replyAll` (boolean · optional) — Optional; null is treated as true (reply all) - `attachments` (array · optional) — Optional; replaced in full when the draft is updated ## Create Draft **TOOL** `OUTLOOK_CREATE_DRAFT` Creates a fresh draft in the mailbox's Drafts folder addressed from the connected account. `to`, `cc`, and `bcc` take comma-separated address lists; the message is sent as HTML when `bodyHtml` is set, otherwise as plain text from `body`. The result returns the draft's short `draftId` to pass to Send Draft, leaving the draft available for human review in Outlook before anything goes out. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "to": "@@{recipientEmail}", "subject": "Your onboarding checklist", "body": "@@{draftText}", "cc": "success@acme.example" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `to` (string) — Required string value. - `subject` (string) — Required string value. - `body` (string) — Required string value. - `bodyHtml` (string) — Required string value. - `cc` (string) — Required string value. - `bcc` (string) — Required string value. - `attachments` (array · optional) — Optional ## Send Draft **TOOL** `OUTLOOK_SEND_DRAFT` Sends an existing draft — the counterpart to Create Draft and Create Draft Reply in a review-then-send workflow. Pass the draft's ID in `messageId` (short IDs and raw Graph IDs both work); Outlook delivers the draft exactly as it stands, including any human edits made since the agent created it, and files it in Sent Items. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_SEND_DRAFT", "connectorId": "V1StGXR8Z5", "messageId": "@@{draftId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. ## Send Email **TOOL** `OUTLOOK_SEND_EMAIL` Sends a message immediately from the connected account with no draft step and no human checkpoint — reserve it for agents trusted to send autonomously, and prefer the draft tools when a review step is wanted. At least one of `body` or `bodyHtml` is required (HTML wins when both are set), recipients are comma-separated lists, and a copy is saved to Sent Items. The result confirms the `to` and `subject` that were sent. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_SEND_EMAIL", "connectorId": "V1StGXR8Z5", "to": "@@{recipientEmail}", "subject": "@@{subject}", "body": "@@{messageText}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `to` (string) — Required string value. - `subject` (string) — Required string value. - `body` (string) — Required string value. - `bodyHtml` (string) — Required string value. - `cc` (string) — Required string value. - `bcc` (string) — Required string value. - `attachments` (array · optional) — Optional ## Get Message by ID **TOOL** `OUTLOOK_GET_MESSAGE_BY_ID` Fetches one message in full — the only Outlook mail tool that returns the complete body rather than a preview. The result's `email` object carries the subject, `from`, the `to`/`cc`/`bcc` recipient lists, `receivedDateTime`, `bodyPreview`, the full `bodyContent` (HTML or text as stored), `conversationId`, `isRead`, `hasAttachments`, and any follow-up `flag`. Use it after a list or search call when the agent needs everything about a specific message. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_GET_MESSAGE_BY_ID", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. ## Get Attachment Content **TOOL** `OUTLOOK_GET_ATTACHMENT_CONTENT` Fetches a single attachment and returns its bytes decoded as UTF-8 text in `content`, alongside `name`, `contentType`, and `size`. This is the right tool for text-based attachments — CSV exports, logs, plain-text or HTML files — where the agent should read the content inline. Binary formats (PDF, Office documents, images) are not text-decoded meaningfully; use Get Attachment Download Link to hand those off as base64 data instead. Attachment IDs come from Search Messages results on messages with `hasAttachments: true`. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_GET_ATTACHMENT_CONTENT", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "attachmentId": "@@{attachmentId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `attachmentId` (string) — Required string value. ## Get Attachment Download Link **TOOL** `OUTLOOK_GET_ATTACHMENT_DOWNLOAD_LINK` Fetches a single attachment and returns its raw data for downstream use: the result carries `name`, `contentType`, `size`, and the file's unmodified base64 payload in `contentBase64`. Unlike the Gmail equivalent, this tool currently returns the base64 data directly rather than a hosted download URL — suitable for passing the file to another tool or storing it, less so for handing a clickable link to a person. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_GET_ATTACHMENT_DOWNLOAD_LINK", "connectorId": "V1StGXR8Z5", "messageId": "@@{messageId}", "attachmentId": "@@{attachmentId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `messageId` (string) — Required string value. - `attachmentId` (string) — Required string value. - `saveToTableId` (string (ID) · optional) — Optional: store the attachment as a file in this AutessaDB table (same company) instead of returning it; the result's `s3Uri` is what to write into that column's filePath. Lets a script (which has no IO) put email attachments into a table. Set together with saveToColumnName. References an AutessaDB table by ID. - `saveToColumnName` (string · optional) — Optional (required with saveToTableId): the FILE, IMAGE, VIDEO or AUDIO column the file is stored for. It must accept the file's type; a FILE column takes any type. ## Search Messages **TOOL** `OUTLOOK_SEARCH_MESSAGES` Searches the mailbox in one of two modes. When `query` is set, it runs a Graph full-text search with that string and the structured fields are ignored. Otherwise the structured fields are combined into a filter: exact sender match on `from`, substring match on `subject`, `hasAttachment`, an `after`/`before` received-date window (dates in `yyyy-MM-dd`), and `isUnread`. Scope the search to one folder with `folderId`, or leave it unset to search the whole mailbox. Results mirror List Unread (short `id`, `subject`, `from`, recipients, `receivedDateTime`, `bodyPreview`, `isRead`, `hasAttachments`), and messages with attachments additionally include an `attachments` array of `{id, name, contentType, size}` metadata for the attachment tools. `maxResults` defaults to 10. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_SEARCH_MESSAGES", "connectorId": "V1StGXR8Z5", "from": "@@{senderEmail}", "subject": "invoice", "hasAttachment": true, "after": "2026-06-01", "isUnread": true, "maxResults": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `from` (string) — Required string value. - `to` (string) — Required string value. - `subject` (string) — Required string value. - `query` (string) — Required string value. - `hasAttachment` (boolean) — Required boolean value. - `after` (string) — Required string value. - `before` (string) — Required string value. - `folderId` (string) — Required string value. - `isUnread` (boolean) — Required boolean value. - `maxResults` (integer) — Required integer value. ## Related - [Connectors](/docs/guide/connectors.md) - [Gmail tool types](/docs/tools/gmail.md) - [Calendar tool types](/docs/tools/calendars.md) - [Email tool types](/docs/tools/email.md) - [Tools in the agentic guide](/docs/guide/agentic/tools.md) --- # Calendars Source: https://docs.autessa.com/docs/tools/calendars > Tool types for creating, listing, updating, and deleting events on Google Calendar and Outlook Calendar. # Calendars Let agents manage real calendars: create meetings with attendees and video links, look up availability windows, reschedule, and cancel — on Google Calendar or Outlook Calendar. The Google tool types require a **Google Calendar connector**; the Outlook tool types require an **Outlook Calendar connector**. Both are OAuth connections to the account whose calendars the agent should manage — see [Connectors](/docs/guide/connectors.md). Both families share the same conventions for dates and times: dates are `yyyy-MM-dd`, times are 24-hour `HH:mm`, and `timeZone` is an IANA identifier like `America/New_York` (when omitted, the connector's default time zone is used). Both providers also share one recurrence model. The `recurrence` field on the create and update tools takes a `RecurrenceRule` object: ```json title="RecurrenceRule" { "frequency": "WEEKLY", "interval": 2, "byDay": ["MO", "WE", "FR"], "until": "2026-12-31", "excludeDates": ["2026-11-26"] } ``` `frequency` is `DAILY`, `WEEKLY`, `MONTHLY`, or `YEARLY`; `interval` spaces out occurrences (every 2 weeks, etc.); end the series with either `count` or `until` (mutually exclusive). `byDay` takes weekday codes (`MO`–`SU`, with position prefixes like `1MO` or `-1FR` for monthly patterns), and `byMonthDay`, `byMonth`, and `bySetPos` cover day-of-month, month, and "first/last occurrence" patterns. For Google, the rule is converted to standard RRULE strings, and `excludeDates`/`includeDates` become EXDATE/RDATE entries; passing `"frequency": "NONE"` on an update clears recurrence. For Outlook, the same rule is converted to a Microsoft Graph recurrence pattern — `excludeDates` and `includeDates` are not supported there. ## Google Calendar Google tools can span multiple calendars on the connected account. Create and update return the event as a simplified object (`eventId`, `name`, `description`, start/end date and time fields, `location`, `attendees`, `isBlocking`, `isOrganizer`, and any `recurrence`), so agents never have to parse raw Google API payloads. ## Create Event (Google) **TOOL** `GOOGLE_CALENDAR_CREATE_EVENT` Creates an event on the given calendar — or, when `calendarId` is omitted, on the connector's first enabled calendar. Two conveniences default to on: unless `addGoogleMeet` is false a Google Meet conference is attached and its link comes back as `meetUrl`, and unless `includeOrganizerAsAttendee` is false the connected account is added as an attendee so the event blocks their availability. `attendees` is a list of email addresses (a single comma-separated string also works), and a `recurrence` rule makes the event repeat. The result returns the `eventId` and the simplified `event` object. ### Example ```json title="Tool config" { "toolType": "GOOGLE_CALENDAR_CREATE_EVENT", "connectorId": "V1StGXR8Z5", "calendarId": "primary", "summary": "@@{meetingTitle}", "description": "@@{agenda}", "startDate": "@@{meetingDate}", "startTime": "14:00", "endDate": "@@{meetingDate}", "endTime": "14:30", "timeZone": "America/New_York", "attendees": ["@@{attendeeEmail}"], "location": "Google Meet", "addGoogleMeet": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `summary` (string) — Required string value. - `description` (string) — Required string value. - `startDate` (string) — Required string value. - `startTime` (string) — Required string value. - `endDate` (string) — Required string value. - `endTime` (string) — Required string value. - `timeZone` (string) — Required string value. - `attendees` (array) — Required array value. - `location` (string) — Required string value. - `recurrence` (object · optional) — Recurrence rule for creating recurring events (optional). See RecurrenceRule class for JSON structure and examples. - `includeOrganizerAsAttendee` (boolean) — When null or true, the organizer (connector account) is added to attendees as self. - `addGoogleMeet` (boolean) — When null or true, a Google Meet conference is created and its link returned. ## List Events (Google) **TOOL** `GOOGLE_CALENDAR_LIST_EVENTS` Lists events across one or more calendars at once — `calendarIds` is a list, and results from every calendar are merged into a single `events` array ordered by start time, with `totalEventCount` and the `responseTimeZone` used for formatting. Narrow the window with the start/end date and time fields, filter by free-text `query`, and cap results with `maxResults`. It doubles as a single-event lookup: set `eventId` (with exactly one calendar in `calendarIds`) to fetch just that event. This is the availability primitive — agents check a time window here before proposing or booking a slot. ### Example ```json title="Tool config" { "toolType": "GOOGLE_CALENDAR_LIST_EVENTS", "connectorId": "V1StGXR8Z5", "calendarIds": ["primary"], "startDate": "@@{windowStart}", "startTime": "00:00", "endDate": "@@{windowEnd}", "endTime": "23:59", "timeZone": "America/New_York", "maxResults": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarIds` (array) — Required array value. - `startDate` (string) — Required string value. - `startTime` (string) — Required string value. - `endDate` (string) — Required string value. - `endTime` (string) — Required string value. - `timeZone` (string) — Required string value. - `maxResults` (integer) — Required integer value. - `query` (string) — Required string value. - `eventId` (string) — Required string value. ## Update Event (Google) **TOOL** `GOOGLE_CALENDAR_UPDATE_EVENT` Updates an existing event with patch semantics: the executor first fetches the current event, and any date, time, or attendee field left unset keeps its existing value — so an agent can move a meeting without restating the guest list. Supplying `attendees` replaces the list; a `recurrence` rule rewrites the series, and `"frequency": "NONE"` removes recurrence entirely. The result returns the `eventId` and the updated simplified `event`; an unknown `eventId` fails with `EVENT_NOT_FOUND`. ### Example ```json title="Tool config" { "toolType": "GOOGLE_CALENDAR_UPDATE_EVENT", "connectorId": "V1StGXR8Z5", "calendarId": "primary", "eventId": "@@{eventId}", "startDate": "@@{newDate}", "startTime": "@@{newStartTime}", "endDate": "@@{newDate}", "endTime": "@@{newEndTime}", "timeZone": "America/New_York" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `eventId` (string) — Required string value. - `summary` (string) — Required string value. - `description` (string) — Required string value. - `startDate` (string) — Required string value. - `startTime` (string) — Required string value. - `endDate` (string) — Required string value. - `endTime` (string) — Required string value. - `timeZone` (string) — Required string value. - `attendees` (array) — Required array value. - `location` (string) — Required string value. - `recurrence` (object · optional) — Recurrence rule for updating recurring events (optional). See RecurrenceRule class for JSON structure and examples. ## Delete Event (Google) **TOOL** `GOOGLE_CALENDAR_DELETE_EVENT` Deletes an event from the given calendar, cancelling it for all attendees. The result confirms the `eventId` and `calendarId` that were removed. Get the `eventId` from a prior List Events call — deleting a recurring event's series ID cancels the whole series. ### Example ```json title="Tool config" { "toolType": "GOOGLE_CALENDAR_DELETE_EVENT", "connectorId": "V1StGXR8Z5", "calendarId": "primary", "eventId": "@@{eventId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `eventId` (string) — Required string value. ## Outlook Calendar Outlook tools operate on one calendar per call, defaulting to the connector's primary calendar when `calendarId` is omitted. Events come back in a simplified Graph shape (`id`, `subject`, `bodyPreview`, `start`/`end` with time zones, `location`, `attendees`, `organizer`, `webLink`), and event IDs are compact six-character short IDs valid for four hours — if an update or delete reports an expired ID, re-run List Events for fresh ones. ## Create Event (Outlook) **TOOL** `OUTLOOK_CALENDAR_CREATE_EVENT` Creates an event on the given calendar (or the account's default calendar when `calendarId` is omitted), inviting everyone in `attendees`. A `recurrence` rule is converted to Microsoft Graph's native recurrence pattern — daily through yearly, including relative patterns like "first Monday of each month" via `byDay` plus `bySetPos` — though `excludeDates`/`includeDates` are not supported on Outlook. There is no automatic video-conference option here, unlike the Google equivalent. The result returns the short `eventId` and the simplified `event`. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_CALENDAR_CREATE_EVENT", "connectorId": "V1StGXR8Z5", "subject": "@@{meetingTitle}", "description": "@@{agenda}", "startDate": "@@{meetingDate}", "startTime": "14:00", "endDate": "@@{meetingDate}", "endTime": "14:30", "timeZone": "America/New_York", "attendees": ["@@{attendeeEmail}"], "location": "Teams" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `subject` (string) — Required string value. - `description` (string) — Required string value. - `startDate` (string) — Required string value. - `startTime` (string) — Required string value. - `endDate` (string) — Required string value. - `endTime` (string) — Required string value. - `timeZone` (string) — Required string value. - `attendees` (array) — Required array value. - `location` (string) — Required string value. - `recurrence` (object) — Required object value. ## List Events (Outlook) **TOOL** `OUTLOOK_CALENDAR_LIST_EVENTS` Lists events from one calendar within a date window, expanding recurring events into their individual occurrences (Graph calendar-view semantics). When no window is given it defaults to the next seven days starting today, which makes an unconfigured call a useful "what's coming up" check. Filter with free-text `query` and cap with `maxResults`; the result returns `totalEventCount` and the simplified `events` array with short event IDs for follow-up update or delete calls. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_CALENDAR_LIST_EVENTS", "connectorId": "V1StGXR8Z5", "startDate": "@@{windowStart}", "endDate": "@@{windowEnd}", "timeZone": "America/New_York", "maxResults": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `startDate` (string) — Required string value. - `startTime` (string) — Required string value. - `endDate` (string) — Required string value. - `endTime` (string) — Required string value. - `timeZone` (string) — Required string value. - `maxResults` (integer) — Graph `$top` page size, not a cap on the events returned: the client follows `@odata.nextLink` and returns the whole window, setting `truncated` if its page limit is reached. - `query` (string) — Graph `$top` page size, not a cap on the events returned: the client follows `@odata.nextLink` and returns the whole window, setting `truncated` if its page limit is reached. ## Update Event (Outlook) **TOOL** `OUTLOOK_CALENDAR_UPDATE_EVENT` Updates an existing event with patch semantics — only the fields you supply are changed, and start/end are only rewritten when both the date and time parts are present. `eventId` takes a short ID from a previous List Events call (expired short IDs fail with `EVENT_ID_EXPIRED`; re-list to refresh). A `recurrence` rule replaces the series pattern. The result returns a fresh short `eventId` and the updated simplified `event`. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_CALENDAR_UPDATE_EVENT", "connectorId": "V1StGXR8Z5", "eventId": "@@{eventId}", "startDate": "@@{newDate}", "startTime": "@@{newStartTime}", "endDate": "@@{newDate}", "endTime": "@@{newEndTime}", "timeZone": "America/New_York" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `eventId` (string) — Required string value. - `subject` (string) — Required string value. - `description` (string) — Required string value. - `startDate` (string) — Required string value. - `startTime` (string) — Required string value. - `endDate` (string) — Required string value. - `endTime` (string) — Required string value. - `timeZone` (string) — Required string value. - `attendees` (array) — Required array value. - `location` (string) — Required string value. - `recurrence` (object) — Required object value. ## Delete Event (Outlook) **TOOL** `OUTLOOK_CALENDAR_DELETE_EVENT` Deletes an event from the calendar, cancelling it for all attendees. `eventId` takes a short ID from a previous List Events call and fails with `EVENT_ID_EXPIRED` once the four-hour window has passed — re-list to get a fresh ID. The result confirms the `eventId` that was removed. ### Example ```json title="Tool config" { "toolType": "OUTLOOK_CALENDAR_DELETE_EVENT", "connectorId": "V1StGXR8Z5", "eventId": "@@{eventId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `calendarId` (string) — Required string value. - `eventId` (string) — Required string value. ## Related - [Connectors](/docs/guide/connectors.md) - [Gmail tool types](/docs/tools/gmail.md) - [Outlook Mail tool types](/docs/tools/outlook-mail.md) - [Meeting Recorder tool types](/docs/tools/meeting-recorder.md) - [Tools in the agentic guide](/docs/guide/agentic/tools.md) --- # Box Source: https://docs.autessa.com/docs/tools/box > Tool types for listing, searching, and downloading files in a connected Box account. # Box Give agents read access to a Box account: browse folder trees, search content by name and type, and produce short-lived download links for individual files. All three tool types require a **Box connector** — an OAuth connection to the Box account the agent should read from. The tool config points at the connector with `connectorId`, and Autessa refreshes the OAuth token automatically before each call; if Box invalidates the refresh token the tools ask for re-authentication. See [Connectors](/docs/guide/connectors.md) for how to link an account. This family is read-only: agents can find and fetch files but never modify the Box account. ## List Files **TOOL** `BOX_LIST_FILES` Lists files and folders, with `folderPrefix` behaving differently by mode. With `recursive` true, it is a slash-delimited folder path (for example `Contracts/2026`): the executor walks from the Box root matching folder names, then collects the entire subtree depth-first up to `limit` items. With `recursive` false, `folderPrefix` is treated as a search string instead, matching items anywhere in the account. The result returns an `entries` array of Box items — files and folders, each with its `id`, `name`, `type`, `size`, and `modified_at` — plus `total_count`. `limit` defaults to 100. ### Example ```json title="Tool config" { "toolType": "BOX_LIST_FILES", "connectorId": "V1StGXR8Z5", "folderPrefix": "Contracts/2026", "recursive": true, "limit": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `folderPrefix` (string) — Required string value. - `recursive` (boolean) — Required boolean value. - `limit` (integer) — Required integer value. ## Search Files **TOOL** `BOX_SEARCH_FILES` Runs a Box content search restricted to files (folders are excluded) and returns the matches as a `files` array with `total_count`. `fileExtensions` takes a comma-separated list like `pdf,docx` and is enforced strictly: it both narrows the Box query and filters the results so only file names ending in one of the extensions come back. Use the returned `id` values with Download File to fetch a specific match. `limit` defaults to 100. ### Example ```json title="Tool config" { "toolType": "BOX_SEARCH_FILES", "connectorId": "V1StGXR8Z5", "searchQuery": "@@{searchTerms}", "fileExtensions": "pdf,docx", "limit": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `searchQuery` (string) — Required string value. - `fileExtensions` (string) — Required string value. - `limit` (integer) — Required integer value. ## Download File **TOOL** `BOX_DOWNLOAD_FILE` Produces a download link for one file by its Box `fileId`: the executor looks up the file's metadata, obtains Box's authenticated download URL, and wraps it in a short Autessa-hosted link. The result carries `file_name`, `file_size`, the file's full `file_path` inside Box, and the `download_url` — valid for **120 seconds** (`expires_in_seconds: 120`), so the link should be consumed immediately by the next step rather than stored. The tool returns a link, not the file's content; pair it with a content-extraction tool when the agent needs to read the file's text. ### Example ```json title="Tool config" { "toolType": "BOX_DOWNLOAD_FILE", "connectorId": "V1StGXR8Z5", "fileId": "@@{fileId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `fileId` (string) — Required string value. ## Related - [Connectors](/docs/guide/connectors.md) - [Notion tool types](/docs/tools/notion.md) - [Core tool types](/docs/tools/core.md) - [Tools in the agentic guide](/docs/guide/agentic/tools.md) --- # Notion Source: https://docs.autessa.com/docs/tools/notion > Tool types for searching, reading, querying, and writing Notion pages and databases. # Notion Connect agents to a Notion workspace: search pages and databases (optionally federating across Notion AI's connected sources), read page content, run database queries, and create or update pages and records. Every tool type on this page requires a **Notion connector** — an OAuth connection to the workspace the agent should work in, scoped to the pages and databases shared with the integration. The tool config points at the connector with `connectorId`, and Autessa refreshes the OAuth token automatically. See [Connectors](/docs/guide/connectors.md) for how to link a workspace. Successful results arrive under a `data` key and generally contain Notion's native API objects (pages, databases, blocks), so agents can read titles, properties, and content exactly as Notion returns them. Fields ending in `Json` (`propertiesJson`, `childrenJson`, `filterJson`, `sortsJson`) are JSON-encoded strings passed through to the Notion API verbatim — full expressive power for agents that know Notion's schema, with convenience fields like `title`, `plainText`, and Submit Form's flat `fields` map covering the common cases without any raw JSON. ## Search **TOOL** `NOTION_SEARCH` Searches the workspace by keyword. With `includeConnectedSources` true, the search runs through Notion AI's federated search, which can also surface results from sources connected to Notion such as Slack, Google Drive, and Jira — and falls back automatically to a plain workspace search when Notion AI is not enabled on the workspace. Otherwise (and on fallback) it is a standard Notion search over pages and databases, where `filter` narrows results to `"page"` or `"database"`. Results come back under `data` in Notion's native search-result shape. ### Example ```json title="Tool config" { "toolType": "NOTION_SEARCH", "connectorId": "V1StGXR8Z5", "query": "@@{searchTerms}", "filter": "page", "includeConnectedSources": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Required string value. - `filter` (string · optional) — Optional: "page", "database", or null for both. Honored on REST fallback only. - `includeConnectedSources` (boolean) — When true, prefer Notion AI's federated search (Slack/Drive/Jira) via MCP. Falls back automatically to workspace REST search if Notion AI isn't enabled. ## Recent Updates **TOOL** `NOTION_RECENT_UPDATES` Returns pages and databases edited within the last `days` days, newest first — the tool for "what changed in the workspace this week" briefings. `days` defaults to 7 and is capped at 90, and `filter` restricts results to `"page"` or `"database"`. The result's `data` carries the resolved `days` and `cutoffIso`, a `count`, and the matching items in `results`. ### Example ```json title="Tool config" { "toolType": "NOTION_RECENT_UPDATES", "connectorId": "V1StGXR8Z5", "days": 7, "filter": "page" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `days` (integer) — Default 7 if unset. Capped at 90 to keep paging bounded. - `filter` (string · optional) — Optional: "page", "database", or null for both. ## Fetch Page **TOOL** `NOTION_FETCH_PAGE` Fetches one page in full: the result's `data` contains the `page` object (metadata and properties) and, unless `includeBlocks` is false, a `blocks` array with the page's content as raw Notion block objects, assembled by following pagination up to roughly 2,000 blocks. Use it after Search or Discover Pages when the agent needs to actually read a page rather than just know it exists. ### Example ```json title="Tool config" { "toolType": "NOTION_FETCH_PAGE", "connectorId": "V1StGXR8Z5", "pageId": "@@{pageId}", "includeBlocks": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `pageId` (string) — Required string value. - `includeBlocks` (boolean) — When true (default), follow paginated /blocks/{id}/children to assemble the full block tree. ## Query Database **TOOL** `NOTION_QUERY_DATABASE` Runs a query against one database with Notion's full filter and sort syntax: `filterJson` and `sortsJson` are JSON-encoded Notion filter and sorts payloads passed through unchanged, so anything expressible in the Notion API works here. Returns a single page of up to `pageSize` rows (capped at 100) in Notion's native query-result shape under `data`, including `has_more` and `next_cursor` when more rows exist. ### Example ```json title="Tool config" { "toolType": "NOTION_QUERY_DATABASE", "connectorId": "V1StGXR8Z5", "databaseId": "@@{databaseId}", "filterJson": "{\"property\": \"Status\", \"select\": {\"equals\": \"Open\"}}", "sortsJson": "[{\"timestamp\": \"last_edited_time\", \"direction\": \"descending\"}]", "pageSize": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `databaseId` (string) — Required string value. - `filterJson` (string) — JSON-encoded Notion filter object (passthrough). - `sortsJson` (string) — JSON-encoded sorts array (passthrough). - `pageSize` (integer) — JSON-encoded sorts array (passthrough). ## Query Data Sources **TOOL** `NOTION_QUERY_DATA_SOURCES` Asks Notion AI to answer a question across the workspace's connected data sources, optionally grouping results with `groupBy`. This capability runs exclusively through Notion's hosted AI tooling: it requires a workspace plan with Notion AI connected search (Enterprise), and on workspaces without it the tool fails with `CAPABILITY_UNAVAILABLE` rather than falling back. For plain workspace search, use the Search tool instead. ### Example ```json title="Tool config" { "toolType": "NOTION_QUERY_DATA_SOURCES", "connectorId": "V1StGXR8Z5", "query": "@@{question}", "groupBy": "source" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Required string value. - `groupBy` (string) — Required string value. ## Discover Pages **TOOL** `NOTION_DISCOVER_PAGES` Enumerates the pages the connector can see — up to 100 per call — optionally narrowed by a `query` substring. Where Search is a question-driven lookup, Discover Pages is an inventory pass: agents use it to learn what exists in the workspace and collect page IDs before fetching, updating, or appending to specific pages. ### Example ```json title="Tool config" { "toolType": "NOTION_DISCOVER_PAGES", "connectorId": "V1StGXR8Z5", "query": "meeting notes" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string · optional) — Optional substring to narrow the page list. ## Discover Databases **TOOL** `NOTION_DISCOVER_DATABASES` The database counterpart to Discover Pages: lists the databases shared with the connector (up to 100 per call), optionally narrowed by `query`. The returned database objects include their property schemas, which is exactly what an agent needs before building a Query Database filter or a Submit Form call. ### Example ```json title="Tool config" { "toolType": "NOTION_DISCOVER_DATABASES", "connectorId": "V1StGXR8Z5", "query": "@@{databaseName}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Required string value. ## Create Page **TOOL** `NOTION_CREATE_PAGE` Creates a page under an existing parent: set `parentType` to `"page_id"` for a subpage or `"database_id"` for a new database row. `title` is a convenience that fills the title property directly (for database parents it lands in the `Name`/`Title` property when one exists); `propertiesJson` carries a raw Notion properties object for database rows with more fields, and `childrenJson` seeds the page with raw Notion content blocks. The created page object comes back under `data`, including its new page ID. ### Example ```json title="Tool config" { "toolType": "NOTION_CREATE_PAGE", "connectorId": "V1StGXR8Z5", "parentId": "@@{parentPageId}", "parentType": "page_id", "title": "@@{pageTitle}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `parentId` (string) — Parent page or database ID. - `parentType` (string) — "page_id" or "database_id". - `title` (string) — Convenience for the page title; written into the title property. - `propertiesJson` (string · optional) — Optional raw Notion properties object (JSON-encoded). Used when the parent is a database. - `childrenJson` (string · optional) — Optional raw Notion children blocks array (JSON-encoded). ## Update Page **TOOL** `NOTION_UPDATE_PAGE` Updates a page's properties and archived state. `propertiesJson` is a raw Notion properties object applied as-is — only the properties it names are changed — and `archived` moves the page to trash when true or restores it when false. This tool does not touch page content blocks; use Append Blocks to add content. The updated page object comes back under `data`. ### Example ```json title="Tool config" { "toolType": "NOTION_UPDATE_PAGE", "connectorId": "V1StGXR8Z5", "pageId": "@@{pageId}", "propertiesJson": "{\"Status\": {\"select\": {\"name\": \"Done\"}}}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `pageId` (string) — Required string value. - `propertiesJson` (string) — JSON-encoded Notion properties object (passthrough). - `archived` (boolean) — JSON-encoded Notion properties object (passthrough). ## Append Blocks **TOOL** `NOTION_APPEND_BLOCKS` Appends content to the end of a page or block. Provide either `childrenJson` — a raw Notion blocks array, for full control over headings, lists, callouts, and other block types — or `plainText`, a convenience where each line becomes a paragraph block. One of the two is required, and `childrenJson` wins when both are set. `parentBlockId` accepts a page ID or any block ID that can hold children. ### Example ```json title="Tool config" { "toolType": "NOTION_APPEND_BLOCKS", "connectorId": "V1StGXR8Z5", "parentBlockId": "@@{pageId}", "plainText": "@@{summaryText}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `parentBlockId` (string) — Required string value. - `childrenJson` (string) — JSON-encoded Notion children blocks array. Either this OR plainText must be set. - `plainText` (string) — Convenience: a plain string is split into paragraph blocks. ## Submit Form **TOOL** `NOTION_SUBMIT_FORM` Adds a row to a database from a flat map of property names to string values — no Notion property JSON required. The executor reads the database's live schema and converts each value to the property's actual type: title, rich text, number, select, multi-select (comma-separated values), checkbox, date, email, phone, and URL are all handled, and field names that no longer exist in the schema are skipped rather than failing the submission. This makes it the robust choice for intake-style workflows where users may rename or rearrange database columns. The created page object comes back under `data`. ### Example ```json title="Tool config" { "toolType": "NOTION_SUBMIT_FORM", "connectorId": "V1StGXR8Z5", "databaseId": "@@{databaseId}", "fields": { "Name": "@@{customerName}", "Email": "@@{customerEmail}", "Status": "New", "Tags": "inbound,website" } } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `databaseId` (string) — Required string value. - `fields` (object) — Flat field map: {propertyName: stringValue}. The executor reads the live database schema to resolve each value to the appropriate property type (title / rich_text / number / select / multi_select / date / etc.). ## Related - [Connectors](/docs/guide/connectors.md) - [Box tool types](/docs/tools/box.md) - [AutessaDB tool types](/docs/tools/autessadb.md) - [MCP tool types](/docs/tools/mcp.md) - [Tools in the agentic guide](/docs/guide/agentic/tools.md) --- # LinkedIn Source: https://docs.autessa.com/docs/tools/linkedin > Tool types for drafting, publishing, and listing LinkedIn posts through a LinkedIn connector. # LinkedIn Let agents write LinkedIn posts as reviewable drafts, publish approved drafts to the connected profile or organization, and list what's in the pipeline. Every tool on this page requires a LinkedIn connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Drafts live in an AutessaDB drafts table that the connector provisions, so a human can review and edit a row before anything goes live — publishing is a separate, explicit step. A published row is stamped `status = PUBLISHED` with its `linkedin_post_urn`, and can't be published twice. ## Create a draft **TOOL** `LINKEDIN_CREATE_DRAFT` Inserts a new row into the connector's drafts table with `status = DRAFT` — nothing is sent to LinkedIn yet. `postContent` becomes the post body, and the optional `imageUrl` (JPG, PNG, or GIF) is stored alongside it and only uploaded to LinkedIn at publish time. The tool returns the new `recordId`, which the other LinkedIn tools use to reference the draft. ### Example ```json title="Tool config" { "toolType": "LINKEDIN_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "postContent": "@@{POST_CONTENT}", "imageUrl": "@@{IMAGE_URL}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postContent` (string) — Required string value. - `imageUrl` (string · optional) — Optional - URL of image to attach (JPG, PNG, or GIF) ## Update a post **TOOL** `LINKEDIN_UPDATE_POST` Edits an existing draft row in place. `postContent` and `imageUrl` are both optional — only the fields you provide are changed, so you can swap the image without touching the copy. This edits the AutessaDB row, not LinkedIn: it has no effect on a post that has already been published. ### Example ```json title="Tool config" { "toolType": "LINKEDIN_UPDATE_POST", "connectorId": "V1StGXR8Z5", "recordId": 42, "postContent": "@@{REVISED_CONTENT}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the post to update - `postContent` (string · optional) — New content (optional - only updates if provided) - `imageUrl` (string · optional) — New image URL (optional - only updates if provided) ## Publish a post **TOOL** `LINKEDIN_PUBLISH_POST` Reads the draft row, uploads its image to LinkedIn if one is set, and creates the post publicly on the connected account's main feed. The row must still be in `DRAFT` status — publishing an already-published record fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` and the returned `postUrn` is stored in the `linkedin_post_urn` column. ### Example ```json title="Tool config" { "toolType": "LINKEDIN_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to publish ## List drafts **TOOL** `LINKEDIN_LIST_DRAFTS` Returns rows from the drafts table where `status = DRAFT`, newest first, up to `limit` (default 10). Each entry includes the `recordId`, content, and image URL, so an agent can present pending drafts for approval and then publish the chosen one by ID. ### Example ```json title="Tool config" { "toolType": "LINKEDIN_LIST_DRAFTS", "connectorId": "V1StGXR8Z5", "limit": 10 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `limit` (integer) — Maximum number of drafts to return (default: 10) ## List published posts **TOOL** `LINKEDIN_LIST_PUBLISHED` The counterpart to listing drafts: returns rows where `status = PUBLISHED`, up to `limit` (default 10), including each post's stored LinkedIn post URN. Useful for reviewing what the agent has already shipped before drafting something new. ### Example ```json title="Tool config" { "toolType": "LINKEDIN_LIST_PUBLISHED", "connectorId": "V1StGXR8Z5", "limit": 10 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `limit` (integer) — Maximum number of published posts to return (default: 10) ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Twitter / X tool types](/docs/tools/twitter.md) - [Facebook tool types](/docs/tools/facebook.md) - [Bluesky tool types](/docs/tools/bluesky.md) --- # Twitter / X Source: https://docs.autessa.com/docs/tools/twitter > Tool types for drafting, publishing, syncing, and measuring tweets through a Twitter/X connector. # Twitter / X Draft tweets into a reviewable AutessaDB table, publish approved drafts to X, pull the account's existing tweets into the same table, and read engagement metrics per tweet. Every tool on this page requires a Twitter/X connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Tweets live as rows in an AutessaDB table the connector provisions: agent-written drafts start at `status = DRAFT`, publishing flips the row to `PUBLISHED` and records the `tweet_id`, and the sync tool imports tweets posted outside Autessa so the table stays the single picture of the account. ## Create a draft **TOOL** `TWITTER_CREATE_DRAFT` Inserts a new draft row with `status = DRAFT` — nothing is posted to X yet. `tweetContent` is capped at 280 characters, and the optional `mediaUrl` image is stored on the row and only uploaded to X at publish time. Returns the new `recordId` for use with the update, publish, and stats tools. ### Example ```json title="Tool config" { "toolType": "TWITTER_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "tweetContent": "@@{TWEET_CONTENT}", "mediaUrl": "@@{IMAGE_URL}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `tweetContent` (string) — Max 280 characters - `mediaUrl` (string · optional) — Optional - URL of image to attach ## Update a draft **TOOL** `TWITTER_UPDATE_DRAFT` Edits an existing draft row in place; `tweetContent` (still max 280 characters) and `mediaUrl` are both optional and only the provided fields change. This edits the AutessaDB row only — it cannot modify a tweet that has already been published. ### Example ```json title="Tool config" { "toolType": "TWITTER_UPDATE_DRAFT", "connectorId": "V1StGXR8Z5", "recordId": 42, "tweetContent": "@@{REVISED_TWEET}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to update - `tweetContent` (string · optional) — New content (optional - only updates if provided, max 280 chars) - `mediaUrl` (string · optional) — New image URL (optional - only updates if provided) ## Publish a tweet **TOOL** `TWITTER_PUBLISH_TWEET` Reads the draft row, uploads its media if a `media_url` is set, and posts the tweet to the connected X account. The row must still be in `DRAFT` status — republishing fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` with the new `tweet_id` stored for later stats lookups. ### Example ```json title="Tool config" { "toolType": "TWITTER_PUBLISH_TWEET", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to publish ## Get tweet stats **TOOL** `TWITTER_GET_TWEET_STATS` Looks up the `tweet_id` on a published row and fetches live engagement metrics from the X API: likes, retweets, replies, quotes, bookmarks, and impressions, plus URL clicks and profile clicks when the account has access to non-public metrics. When impressions are available the tool also computes `totalEngagements` and an `engagementRate` percentage, and it returns a direct `tweetUrl` to the post. ### Example ```json title="Tool config" { "toolType": "TWITTER_GET_TWEET_STATS", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID to get tweet_id from ## Get all tweets **TOOL** `TWITTER_GET_ALL_TWEETS` Lists rows from the connector's tweets table — drafts and published tweets together by default, or filtered with `status` (`DRAFT`, `PUBLISHED`, `SCHEDULED`). `pageSize` defaults to 50 and caps at 100. This reads the AutessaDB table, so it reflects synced tweets as well as agent-created ones. ### Example ```json title="Tool config" { "toolType": "TWITTER_GET_ALL_TWEETS", "connectorId": "V1StGXR8Z5", "status": "DRAFT", "pageSize": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional - filter by status: DRAFT, PUBLISHED, SCHEDULED - `pageSize` (integer · optional) — Optional - max tweets to retrieve (default 50, max 100) ## Get tweet by ID **TOOL** `TWITTER_GET_TWEET_BY_ID` Fetches a single row from the tweets table by its AutessaDB `rowId`, returning the tweet's content, status, media URL, and stored `tweet_id`. Use it to inspect one specific draft or published tweet without paging through the full list. ### Example ```json title="Tool config" { "toolType": "TWITTER_GET_TWEET_BY_ID", "connectorId": "V1StGXR8Z5", "rowId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `rowId` (integer) — Required - the AutessaDB row ID of the tweet to fetch ## Sync tweets **TOOL** `TWITTER_SYNC_TWEETS` Imports the connected account's timeline from the X API into the AutessaDB table as `PUBLISHED` rows, skipping tweets that are already present. Results are cached for 24 hours — a second sync inside that window returns the cached summary unless `forceRefresh` is `true`. Large backfills that exceed the execution window continue as a background job (`backgroundJob: true` in the response); the summary reports `syncedCount`, `newTweetsCount`, and `skippedCount`. ### Example ```json title="Tool config" { "toolType": "TWITTER_SYNC_TWEETS", "connectorId": "V1StGXR8Z5", "forceRefresh": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `forceRefresh` (boolean · optional) — Optional: bypass daily cache if true ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Bluesky tool types](/docs/tools/bluesky.md) - [Threads tool types](/docs/tools/threads.md) - [LinkedIn tool types](/docs/tools/linkedin.md) --- # Bluesky Source: https://docs.autessa.com/docs/tools/bluesky > Tool types for drafting, publishing, and measuring Bluesky posts through a Bluesky connector. # Bluesky Draft Bluesky posts into a reviewable AutessaDB table, publish approved drafts to the connected account over the AT Protocol, and read back per-post engagement. Every tool on this page requires a Bluesky connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Posts live as rows in an AutessaDB table the connector provisions: drafts start at `status = DRAFT`, and publishing creates an `app.bsky.feed.post` record on the account, flips the row to `PUBLISHED`, and stores the post's AT URI (`at://did:plc:.../app.bsky.feed.post/...`) for later stats lookups. ## Create a draft **TOOL** `BLUESKY_CREATE_DRAFT` Inserts a new draft row with `status = DRAFT` — nothing is posted to Bluesky yet. `postContent` is capped at 300 characters, and the optional `mediaUrl` image is stored on the row and only uploaded (as an image embed) at publish time. Returns the new `recordId` for the update, publish, and stats tools. ### Example ```json title="Tool config" { "toolType": "BLUESKY_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "postContent": "@@{POST_CONTENT}", "mediaUrl": "@@{IMAGE_URL}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postContent` (string) — Max 300 characters - `mediaUrl` (string · optional) — Optional - URL of image to attach ## Update a draft **TOOL** `BLUESKY_UPDATE_DRAFT` Edits an existing draft row in place; `postContent` (still max 300 characters) and `mediaUrl` are both optional and only the provided fields change. This edits the AutessaDB row only — it has no effect on a post that has already been published. ### Example ```json title="Tool config" { "toolType": "BLUESKY_UPDATE_DRAFT", "connectorId": "V1StGXR8Z5", "recordId": 42, "postContent": "@@{REVISED_CONTENT}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to update - `postContent` (string · optional) — Optional - new content (max 300 characters) - `mediaUrl` (string · optional) — Optional - new media URL ## Publish a post **TOOL** `BLUESKY_PUBLISH_POST` Reads the draft row, uploads its image as a blob if a media URL is set, and creates the post on the connected account via the AT Protocol. The row must still be in `DRAFT` status — republishing fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` and the post's AT URI is stored on the row. ### Example ```json title="Tool config" { "toolType": "BLUESKY_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to publish ## Get post stats **TOOL** `BLUESKY_GET_POST_STATS` Looks up the stored `post_uri` on a published row and fetches live counts from Bluesky: likes, reposts, and replies, along with the post's current text. Only works for rows that have been published (or synced) and therefore carry an AT URI. ### Example ```json title="Tool config" { "toolType": "BLUESKY_GET_POST_STATS", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID to get post_uri from ## Get all posts **TOOL** `BLUESKY_GET_ALL_POSTS` Lists rows from the connector's posts table — drafts and published posts together by default, or filtered with `status` (`DRAFT`, `PUBLISHED`, `SCHEDULED`). `pageSize` defaults to 50 and caps at 100. Reads the AutessaDB table, so results include anything a human has added or edited there too. ### Example ```json title="Tool config" { "toolType": "BLUESKY_GET_ALL_POSTS", "connectorId": "V1StGXR8Z5", "status": "DRAFT", "pageSize": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional filter: DRAFT, PUBLISHED, SCHEDULED - `pageSize` (integer · optional) — Optional, default 50, max 100 ## Get post by ID **TOOL** `BLUESKY_GET_POST_BY_ID` Fetches a single row from the posts table by its AutessaDB `rowId`, returning the post's content, status, media URL, and stored AT URI. Use it to inspect one specific draft or published post without paging through the full list. ### Example ```json title="Tool config" { "toolType": "BLUESKY_GET_POST_BY_ID", "connectorId": "V1StGXR8Z5", "rowId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `rowId` (integer) — AutessaDB record ID to fetch ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Twitter / X tool types](/docs/tools/twitter.md) - [Threads tool types](/docs/tools/threads.md) - [LinkedIn tool types](/docs/tools/linkedin.md) --- # Facebook Source: https://docs.autessa.com/docs/tools/facebook > Tool types for drafting and publishing Facebook posts, syncing Page content, and reading Page and ads insights. # Facebook Draft Facebook posts into a reviewable AutessaDB table, publish them to a profile, Page, or group, sync a Page's existing posts, and pull Page and Ads insights for reporting. Every tool on this page requires a Facebook connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Posts live as rows in an AutessaDB table the connector provisions: drafts start at `status = DRAFT` and carry a publish target (`PROFILE`, `PAGE`, or `GROUP`), and publishing flips the row to `PUBLISHED` with the Facebook post ID stored for stats lookups. The insights tools are read-only reporting calls against the Graph API and the Marketing API. ## Create a draft post **TOOL** `FACEBOOK_CREATE_DRAFT_POST` Inserts a new draft row with `status = DRAFT` — nothing is posted to Facebook yet. Alongside `postContent` and the optional `mediaUrl` image, the draft records where it will be published: `targetType` is `PROFILE`, `PAGE`, or `GROUP`, with `targetId` holding the Page or group ID (`null` for `PROFILE`) and `targetName` a human-readable label for reviewers. Returns the new `recordId`. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_CREATE_DRAFT_POST", "connectorId": "V1StGXR8Z5", "postContent": "@@{POST_CONTENT}", "mediaUrl": "@@{IMAGE_URL}", "targetType": "PAGE", "targetId": "104857361234567", "targetName": "Acme Robotics" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postContent` (string) — The post text content - `mediaUrl` (string · optional) — Optional - URL of image to attach - `targetType` (string) — PROFILE, PAGE, or GROUP - `targetId` (string) — Page or Group ID (null for PROFILE) - `targetName` (string) — Display name for the target ## Update a draft post **TOOL** `FACEBOOK_UPDATE_DRAFT_POST` Edits an existing draft row in place. Content, media, and all three target fields are optional — only the fields you provide change, so you can retarget a draft from one Page to another without rewriting it. This edits the AutessaDB row only; it cannot modify a post that has already been published. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_UPDATE_DRAFT_POST", "connectorId": "V1StGXR8Z5", "recordId": 42, "postContent": "@@{REVISED_CONTENT}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to update - `postContent` (string · optional) — Optional - new post content - `mediaUrl` (string · optional) — Optional - new URL of image to attach - `targetType` (string · optional) — Optional - PROFILE, PAGE, or GROUP - `targetId` (string · optional) — Optional - new Page or Group ID - `targetName` (string · optional) — Optional - new display name for the target ## Publish a post **TOOL** `FACEBOOK_PUBLISH_POST` Reads the draft row and publishes it to the target recorded on the row — Page posts are made with the Page's own access token, and `PAGE`/`GROUP` targets require a target ID. The row must still be in `DRAFT` status; republishing fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` with the Facebook post ID stored for later stats lookups. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to publish ## Get post stats **TOOL** `FACEBOOK_GET_POST_STATS` Looks up the Facebook post ID on a published row and fetches live engagement from the Graph API — like and comment totals plus the post's message and created time. Only works for rows that have been published (or synced) and therefore carry a Facebook post ID. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_GET_POST_STATS", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the published post ## Get all posts **TOOL** `FACEBOOK_GET_ALL_POSTS` Lists rows from the connector's posts table — drafts and published posts together by default, or filtered with `status` (`DRAFT`, `PUBLISHED`). `pageSize` defaults to 50 and caps at 100. Reads the AutessaDB table, so results include synced Page posts as well as agent-created drafts. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_GET_ALL_POSTS", "connectorId": "V1StGXR8Z5", "status": "DRAFT", "pageSize": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional - filter by status: DRAFT, PUBLISHED - `pageSize` (integer · optional) — Optional - max posts to retrieve (default 50, max 100) ## Get post by ID **TOOL** `FACEBOOK_GET_POST_BY_ID` Fetches a single row from the posts table by its AutessaDB `rowId`, returning content, status, media URL, target details, and the stored Facebook post ID. Use it to inspect one specific draft or published post without paging through the full list. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_GET_POST_BY_ID", "connectorId": "V1StGXR8Z5", "rowId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `rowId` (integer) — AutessaDB row ID of the post to retrieve ## Sync posts **TOOL** `FACEBOOK_SYNC_POSTS` Imports a Page's existing posts from the Graph API into the AutessaDB table as `PUBLISHED` rows, skipping posts that are already present. Results are cached for 24 hours — a second sync inside that window returns the cached summary unless `forceRefresh` is `true`. Run this once after connecting so the table reflects the Page's history, then periodically to pick up posts made outside Autessa. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_SYNC_POSTS", "connectorId": "V1StGXR8Z5", "pageId": "104857361234567", "forceRefresh": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `pageId` (string) — The Facebook Page ID to sync posts from - `forceRefresh` (boolean · optional) — Optional: bypass daily cache if true ## Get Page insights **TOOL** `FACEBOOK_GET_PAGE_INSIGHTS` Fetches Page-level metrics from the Graph API Insights endpoint: unique impressions, post engagements, daily new follows, and video views — the set of metrics Meta still supports after its 2024–2025 insights deprecations. `period` buckets the numbers by `day`, `week`, or `days_28`, and the optional `datePreset` (for example `last_30d`) selects the reporting window. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_GET_PAGE_INSIGHTS", "connectorId": "V1StGXR8Z5", "pageId": "104857361234567", "period": "day", "datePreset": "last_30d" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `pageId` (string) — Facebook Page ID to get insights for - `period` (string) — Period for metrics: day, week, days_28 (default: day) - `datePreset` (string · optional) — Optional: today, yesterday, this_week, last_week, this_month, last_month, last_7d, last_30d, last_90d ## List ad accounts **TOOL** `FACEBOOK_LIST_AD_ACCOUNTS` Lists the ad accounts the connected user can access, including each account's ID (the `act_...` form the other ads tools expect), name, and status. This is the usual first step of an ads-reporting flow: find the account, then list its campaigns or pull insights. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_LIST_AD_ACCOUNTS", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. ## List campaigns **TOOL** `FACEBOOK_LIST_CAMPAIGNS` Lists campaigns in an ad account, optionally filtered by `status` (`ACTIVE`, `PAUSED`, `DELETED`, `ARCHIVED`), up to `limit` (default 50). Each campaign's ID can be passed to the ads-insights tool for per-campaign performance. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_LIST_CAMPAIGNS", "connectorId": "V1StGXR8Z5", "adAccountId": "act_123456789", "status": "ACTIVE", "limit": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `adAccountId` (string) — Ad account ID (e.g., "act_123456789") - `status` (string · optional) — Optional - filter by status: ACTIVE, PAUSED, DELETED, ARCHIVED - `limit` (integer · optional) — Optional - max campaigns to retrieve (default 50) ## Get ads insights **TOOL** `FACEBOOK_GET_ADS_INSIGHTS` Pulls performance metrics from the Marketing API for any level of the ads hierarchy — pass an ad account, campaign, ad set, or ad ID as `objectId`. Returns spend, impressions, clicks, CTR, CPC, CPM, reach, and frequency, plus action breakdowns (conversions and cost per action) when available. `datePreset` selects the window (for example `last_30d` or `lifetime`) and `level` re-aggregates account-level queries by `campaign`, `adset`, or `ad`. ### Example ```json title="Tool config" { "toolType": "FACEBOOK_GET_ADS_INSIGHTS", "connectorId": "V1StGXR8Z5", "objectId": "act_123456789", "datePreset": "last_30d", "level": "campaign" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `objectId` (string) — Ad account ID, campaign ID, ad set ID, or ad ID - `datePreset` (string · optional) — Optional: today, yesterday, this_week, last_week, last_7d, last_14d, last_30d, last_90d, this_month, last_month, lifetime - `level` (string · optional) — Optional: account, campaign, adset, ad (default: derives from objectId type) ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Instagram tool types](/docs/tools/instagram.md) - [Threads tool types](/docs/tools/threads.md) - [Google Analytics tool types](/docs/tools/google-analytics.md) --- # Instagram Source: https://docs.autessa.com/docs/tools/instagram > Tool types for drafting, publishing, syncing, and measuring Instagram posts through an Instagram connector. # Instagram Draft Instagram posts — single images or carousels — into a reviewable AutessaDB table, publish approved drafts to the connected account, sync existing posts, and read per-post engagement. Every tool on this page requires an Instagram connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Posts live as rows in an AutessaDB table the connector provisions: drafts start at `status = DRAFT`, and publishing runs Instagram's container flow (create the media container, wait for processing, then publish) before flipping the row to `PUBLISHED` with the Instagram post ID stored. Image URLs must be publicly accessible — Instagram fetches the media from the URL, not from Autessa. ## Create a draft **TOOL** `INSTAGRAM_CREATE_DRAFT` Inserts a new draft row with `status = DRAFT` — nothing is posted to Instagram yet. `caption` is capped at 2,200 characters, and `mediaUrls` is a comma-separated list of image URLs: one URL publishes as a single image, two to five publish as a carousel. Returns the new `recordId` for the update, publish, and stats tools. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "caption": "@@{CAPTION}", "mediaUrls": "https://example.com/photo-1.jpg,https://example.com/photo-2.jpg" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `caption` (string) — Max 2200 characters - `mediaUrls` (string) — Comma-separated URLs of images (max 5) ## Update a draft **TOOL** `INSTAGRAM_UPDATE_DRAFT` Edits an existing draft row in place; `caption` and `mediaUrls` are both optional and only the provided fields change. This edits the AutessaDB row only — Instagram does not allow editing a post's media after publishing, so get the draft right before you publish it. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_UPDATE_DRAFT", "connectorId": "V1StGXR8Z5", "recordId": 42, "caption": "@@{REVISED_CAPTION}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required integer value. - `caption` (string · optional) — Optional - only updates if provided - `mediaUrls` (string · optional) — Optional - only updates if provided ## Publish a post **TOOL** `INSTAGRAM_PUBLISH_POST` Reads the draft row and runs Instagram's two-step publish: it creates a media container (single image or carousel), polls until Instagram finishes processing the media, then publishes the container. The row must still be in `DRAFT` status — republishing fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` with the Instagram post ID stored. Expect this call to take longer than most tools while Instagram processes the media. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required integer value. ## Get post stats **TOOL** `INSTAGRAM_GET_POST_STATS` Looks up the Instagram post ID on a published row and fetches live engagement metrics for that post from the Instagram API. Only works for rows that have been published (or synced) and therefore carry an Instagram post ID. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_GET_POST_STATS", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required integer value. ## Get all posts **TOOL** `INSTAGRAM_GET_ALL_POSTS` Lists rows from the connector's posts table — drafts and published posts together by default, or filtered with `status` (`DRAFT`, `PUBLISHED`). `pageSize` defaults to 50 and caps at 100. Reads the AutessaDB table, so results include synced posts as well as agent-created drafts. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_GET_ALL_POSTS", "connectorId": "V1StGXR8Z5", "status": "DRAFT", "pageSize": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional filter: DRAFT, PUBLISHED - `pageSize` (integer · optional) — Optional, default 50, max 100 ## Get post by ID **TOOL** `INSTAGRAM_GET_POST_BY_ID` Fetches a single row from the posts table by its AutessaDB `rowId`, returning the caption, media URLs, status, and stored Instagram post ID. Use it to inspect one specific draft or published post without paging through the full list. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_GET_POST_BY_ID", "connectorId": "V1StGXR8Z5", "rowId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `rowId` (integer) — Required integer value. ## Sync posts **TOOL** `INSTAGRAM_SYNC_POSTS` Imports the connected account's existing posts from the Instagram API into the AutessaDB table as `PUBLISHED` rows, skipping posts that are already present. Results are cached for 24 hours — a second sync inside that window returns the cached summary unless `forceRefresh` is `true`. ### Example ```json title="Tool config" { "toolType": "INSTAGRAM_SYNC_POSTS", "connectorId": "V1StGXR8Z5", "forceRefresh": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `forceRefresh` (boolean · optional) — Optional: bypass daily cache if true ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Facebook tool types](/docs/tools/facebook.md) - [Threads tool types](/docs/tools/threads.md) - [TikTok tool types](/docs/tools/tiktok.md) --- # Reddit Source: https://docs.autessa.com/docs/tools/reddit > Tool types for drafting, publishing, replying to, and measuring Reddit posts through a Reddit connector. # Reddit Draft Reddit posts into a reviewable AutessaDB table, publish approved drafts to their subreddit, thread reply drafts off published posts, and read live score and comment stats per post. Every tool on this page requires a Reddit connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Posts live as rows in an AutessaDB table the connector provisions: agent-written drafts start at `status = DRAFT`, and publishing flips the row to `PUBLISHED` and records the Reddit fullname in `reddit_post_id`. Replies are rows in the same table, linked to their parent through `parent_post_id` and `reddit_parent_fullname`, so an entire discussion threads through one table. Subreddit names are stored without the `r/` prefix — the tools strip it if you include it. ## Create a draft **TOOL** `REDDIT_CREATE_DRAFT` Inserts a new draft row with `status = DRAFT` — nothing is posted to Reddit yet. `title`, `body`, and `subreddit` are all required; posts publish as text posts, so the body is the post's full content. Returns the new `recordId` for use with the update, publish, stats, and reply tools. ### Example ```json title="Tool config" { "toolType": "REDDIT_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "title": "@@{POST_TITLE}", "body": "@@{POST_BODY}", "subreddit": "@@{SUBREDDIT}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `title` (string) — Required for posts - `body` (string) — Required - the post content - `subreddit` (string) — Required - subreddit name without r/ prefix ## Update a draft **TOOL** `REDDIT_UPDATE_DRAFT` Edits an existing draft row in place; `title`, `body`, and `subreddit` are all optional and only the provided fields change. Only rows still in `DRAFT` status can be updated — anything else fails with `NOT_A_DRAFT`, and calling it with no fields to change fails with `NO_UPDATES`. ### Example ```json title="Tool config" { "toolType": "REDDIT_UPDATE_DRAFT", "connectorId": "V1StGXR8Z5", "recordId": 42, "body": "@@{REVISED_BODY}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required - AutessaDB record ID - `title` (string · optional) — Optional - new title - `body` (string · optional) — Optional - new body content - `subreddit` (string · optional) — Optional - new subreddit ## Publish a post **TOOL** `REDDIT_PUBLISH_POST` Reads the draft row and submits it to its subreddit as a text post. The row must still be in `DRAFT` status — republishing fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` with the Reddit fullname stored in `reddit_post_id`, and the response includes a direct `postUrl` to the live post. ### Example ```json title="Tool config" { "toolType": "REDDIT_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required - AutessaDB record ID of the draft to publish ## Get post stats **TOOL** `REDDIT_GET_POST_STATS` Looks up the `reddit_post_id` on a published row and fetches live metrics from the Reddit API: `score` (upvotes minus downvotes), `upvoteRatio`, `numComments`, upvote and downvote counts when Reddit exposes them, and `totalAwards`. Only works for rows in `PUBLISHED` status — drafts fail with `NOT_PUBLISHED`. ### Example ```json title="Tool config" { "toolType": "REDDIT_GET_POST_STATS", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required - AutessaDB record ID ## Get all posts **TOOL** `REDDIT_GET_ALL_POSTS` Lists rows from the connector's posts table — drafts and published posts together by default, or filtered with `status` (`DRAFT`, `PUBLISHED`) and `subreddit`. Replies are included unless `includeReplies` is `false`, which restricts results to top-level posts. `pageSize` defaults to 50 and caps at 100. ### Example ```json title="Tool config" { "toolType": "REDDIT_GET_ALL_POSTS", "connectorId": "V1StGXR8Z5", "status": "DRAFT", "subreddit": "startups", "pageSize": 50, "includeReplies": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional - filter by DRAFT/PUBLISHED - `subreddit` (string · optional) — Optional - filter by subreddit - `pageSize` (integer · optional) — Optional - default 50, max 100 - `includeReplies` (boolean · optional) — Optional - default true ## Get post by ID **TOOL** `REDDIT_GET_POST_BY_ID` Fetches a single row from the posts table by its AutessaDB `rowId`, returning the title, body, subreddit, status, and stored Reddit IDs. Works for posts and replies alike — use it to inspect one specific record without paging through the full list. ### Example ```json title="Tool config" { "toolType": "REDDIT_GET_POST_BY_ID", "connectorId": "V1StGXR8Z5", "rowId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `rowId` (integer) — Required - AutessaDB row ID ## Create a reply draft **TOOL** `REDDIT_CREATE_REPLY_DRAFT` Inserts a reply draft under an existing post: `parentPostId` is the AutessaDB row ID of the parent, which must already be published to Reddit — otherwise the call fails with `PARENT_NOT_PUBLISHED`. The reply inherits the parent's subreddit, stores the parent's Reddit fullname on the row, and starts at `status = DRAFT` like any other draft. ### Example ```json title="Tool config" { "toolType": "REDDIT_CREATE_REPLY_DRAFT", "connectorId": "V1StGXR8Z5", "parentPostId": 42, "body": "@@{REPLY_BODY}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `parentPostId` (integer) — Required - AutessaDB row ID of the parent post to reply to - `body` (string) — Required - the reply content ## Publish a reply **TOOL** `REDDIT_PUBLISH_REPLY` Reads a reply draft and posts it to Reddit as a comment on its parent. The row must still be in `DRAFT` status (`ALREADY_PUBLISHED` otherwise) and must actually be a reply — publishing a top-level post this way fails with `NOT_A_REPLY`, which points you to `REDDIT_PUBLISH_POST` instead. On success the row flips to `status = PUBLISHED` with the comment's fullname stored in `reddit_post_id`. ### Example ```json title="Tool config" { "toolType": "REDDIT_PUBLISH_REPLY", "connectorId": "V1StGXR8Z5", "recordId": 43 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — Required - AutessaDB record ID of the reply draft to publish ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Twitter / X tool types](/docs/tools/twitter.md) - [Threads tool types](/docs/tools/threads.md) - [Bluesky tool types](/docs/tools/bluesky.md) --- # Threads Source: https://docs.autessa.com/docs/tools/threads > Tool types for drafting, publishing, syncing, and measuring Threads posts through a Threads connector. # Threads Draft Threads posts — up to 500 characters with an optional image — into a reviewable AutessaDB table, publish approved drafts through Threads' container flow, sync the account's existing posts, and read per-post engagement. Every tool on this page requires a Threads connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Posts live as rows in an AutessaDB table the connector provisions: drafts start at `status = DRAFT`, and publishing runs Threads' container flow (create the media container, wait for processing, then publish) before flipping the row to `PUBLISHED` with the `threads_post_id` stored. The sync tool imports posts published outside Autessa so the table stays the single picture of the account. ## Create a draft **TOOL** `THREADS_CREATE_DRAFT` Inserts a new draft row with `status = DRAFT` — nothing is posted to Threads yet. `postContent` is capped at 500 characters, and the optional `mediaUrl` image is stored on the row and only sent to Threads at publish time. Returns the new `recordId` (plus a `characterCount`) for use with the update, publish, and stats tools. ### Example ```json title="Tool config" { "toolType": "THREADS_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "postContent": "@@{POST_CONTENT}", "mediaUrl": "@@{IMAGE_URL}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postContent` (string) — Max 500 characters - `mediaUrl` (string · optional) — Optional - URL of image to attach ## Update a draft **TOOL** `THREADS_UPDATE_DRAFT` Edits an existing draft row in place; `postContent` (still max 500 characters) and `mediaUrl` are both optional and only the provided fields change. Only rows still in `DRAFT` status can be updated — anything else fails with `NOT_A_DRAFT`, since a post already on Threads cannot be edited from here. ### Example ```json title="Tool config" { "toolType": "THREADS_UPDATE_DRAFT", "connectorId": "V1StGXR8Z5", "recordId": 42, "postContent": "@@{REVISED_POST}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to update - `postContent` (string · optional) — New content (optional - only updates if provided, max 500 chars) - `mediaUrl` (string · optional) — New image URL (optional - only updates if provided) ## Publish a post **TOOL** `THREADS_PUBLISH_POST` Reads the draft row and runs Threads' two-step publish: it creates a media container, polls every 5 seconds (up to 60 seconds — `TIMEOUT` after that, `CONTAINER_ERROR` if Threads rejects the media) until the container is ready, then publishes it. The row must still be in `DRAFT` status — republishing fails with `ALREADY_PUBLISHED`. On success the row is updated to `status = PUBLISHED` with the `threads_post_id` stored, and the response includes a direct `postUrl`. ### Example ```json title="Tool config" { "toolType": "THREADS_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID of the draft to publish ## Get post stats **TOOL** `THREADS_GET_POST_STATS` Looks up the `threads_post_id` on a published row and fetches live insights from the Threads API: views, likes, replies, reposts, and quotes. When views are available the tool also computes `totalEngagements` and an `engagementRate` percentage, and it returns a direct `postUrl` to the post. Only works for rows in `PUBLISHED` status. ### Example ```json title="Tool config" { "toolType": "THREADS_GET_POST_STATS", "connectorId": "V1StGXR8Z5", "recordId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `recordId` (integer) — AutessaDB record ID to get threads_post_id from ## Get all posts **TOOL** `THREADS_GET_ALL_POSTS` Lists rows from the connector's posts table — drafts and published posts together by default, or filtered with `status` (`DRAFT`, `PUBLISHED`, `SCHEDULED`). `pageSize` defaults to 50 and caps at 100. This reads the AutessaDB table, so it reflects synced posts as well as agent-created ones. ### Example ```json title="Tool config" { "toolType": "THREADS_GET_ALL_POSTS", "connectorId": "V1StGXR8Z5", "status": "DRAFT", "pageSize": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional - filter by status: DRAFT, PUBLISHED, SCHEDULED - `pageSize` (integer · optional) — Optional - max posts to retrieve (default 50, max 100) ## Get post by ID **TOOL** `THREADS_GET_POST_BY_ID` Fetches a single row from the posts table by its AutessaDB `rowId`, returning the post's content, status, media URL, and stored `threads_post_id`. Use it to inspect one specific draft or published post without paging through the full list. ### Example ```json title="Tool config" { "toolType": "THREADS_GET_POST_BY_ID", "connectorId": "V1StGXR8Z5", "rowId": 42 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `rowId` (integer) — Required - the AutessaDB row ID of the post to fetch ## Sync posts **TOOL** `THREADS_SYNC_POSTS` Imports the connected account's recent posts (up to 50 per sync) from the Threads API into the AutessaDB table as `PUBLISHED` rows, skipping posts that are already present. Results are cached for 24 hours — a second sync inside that window returns the cached summary unless `forceRefresh` is `true`. The summary reports `syncedCount`, `newPostsCount`, and `skippedCount`. ### Example ```json title="Tool config" { "toolType": "THREADS_SYNC_POSTS", "connectorId": "V1StGXR8Z5", "forceRefresh": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `forceRefresh` (boolean · optional) — Optional: bypass daily cache if true ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Twitter / X tool types](/docs/tools/twitter.md) - [Instagram tool types](/docs/tools/instagram.md) - [Bluesky tool types](/docs/tools/bluesky.md) --- # TikTok Source: https://docs.autessa.com/docs/tools/tiktok > Tool types for uploading draft videos to a TikTok inbox and reading video and channel stats through a TikTok connector. # TikTok Upload a video into the connected TikTok account's drafts inbox, list the account's published videos, and read per-video and channel-level engagement straight from the TikTok API. Every tool on this page requires a TikTok connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Unlike the other social families there is no AutessaDB posts table — every tool calls the TikTok API directly, and the draft upload lands in the account's TikTok inbox for a human to review and publish from the TikTok app (TikTok does not let third-party tools publish outright). TikTok's refresh tokens expire after long inactivity; when that happens tools fail with `REFRESH_TOKEN_EXPIRED` and the connector must be reauthorized. ## Upload a draft video **TOOL** `TIKTOK_CREATE_DRAFT` Downloads the video from the publicly accessible `videoUrl` and uploads it to TikTok, where it lands as a draft in the account's TikTok inbox — the final publish happens manually in the TikTok app. `title` is capped at 150 characters and `description` at 2,200, and the three `disable*` flags control whether comments, duets, and stitches are allowed on the eventual post. The tool streams progress updates during the download and upload phases and returns a `publishId` with `status = PROCESSING`; the video appears in the drafts inbox once TikTok finishes processing it. ### Example ```json title="Tool config" { "toolType": "TIKTOK_CREATE_DRAFT", "connectorId": "V1StGXR8Z5", "videoUrl": "@@{VIDEO_URL}", "title": "@@{VIDEO_TITLE}", "description": "@@{VIDEO_DESCRIPTION}", "disableComment": false, "disableDuet": false, "disableStitch": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the TikTok connection References a connector by ID. - `videoUrl` (string) — The publicly accessible URL of the video to upload (TikTok will fetch from this URL) - `title` (string) — Video title (max 150 characters) - `description` (string) — Video description (max 2200 characters) - `disableComment` (boolean) — If true, disables comments on the video - `disableDuet` (boolean) — If true, disables duet for the video - `disableStitch` (boolean) — If true, disables stitch for the video ## Get all videos **TOOL** `TIKTOK_GET_ALL_VIDEOS` Lists the connected account's published videos — the TikTok API does not return drafts, so videos uploaded with the draft tool only show up here after they are published from the app. `maxCount` defaults to 20, which is also TikTok's per-page maximum; page further by passing the `nextCursor` from a previous response as `cursor`, and check `hasMore`. Set `minimal` to `true` to trim each video to `id`, `title`, `video_description`, and `create_time` — useful when an agent only needs to pick a video. ### Example ```json title="Tool config" { "toolType": "TIKTOK_GET_ALL_VIDEOS", "connectorId": "V1StGXR8Z5", "maxCount": 20, "cursor": 0, "minimal": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the TikTok connection References a connector by ID. - `maxCount` (integer) — Maximum number of videos to return (default 20, max 20 per TikTok API) - `cursor` (integer) — Cursor for pagination from previous response - `minimal` (boolean) — When true, returns minimal video info (id, title, video_description, create_time only) ## Get video by ID **TOOL** `TIKTOK_GET_VIDEO_BY_ID` Fetches one video's full record by its TikTok `videoId`: title, description, duration, cover image, embed link, creation time, and the engagement counts. Use the list tool to discover video IDs first. ### Example ```json title="Tool config" { "toolType": "TIKTOK_GET_VIDEO_BY_ID", "connectorId": "V1StGXR8Z5", "videoId": "@@{VIDEO_ID}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the TikTok connection References a connector by ID. - `videoId` (string) — The TikTok video ID to retrieve ## Get video stats **TOOL** `TIKTOK_GET_VIDEO_STATS` Fetches live engagement metrics for one video by its TikTok `videoId`: `view_count`, `like_count`, `comment_count`, and `share_count`. Note the ID is TikTok's own video ID (as returned by the list tool), not an AutessaDB record — this family has no drafts table. ### Example ```json title="Tool config" { "toolType": "TIKTOK_GET_VIDEO_STATS", "connectorId": "V1StGXR8Z5", "videoId": "@@{VIDEO_ID}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the TikTok connection References a connector by ID. - `videoId` (string) — The TikTok video ID to get stats for ## Get channel stats **TOOL** `TIKTOK_GET_CHANNEL_STATS` Fetches account-level metrics for the connected TikTok account: `follower_count`, `following_count`, `likes_count` (total likes across all videos), and `video_count`, alongside the account's display name. Takes only the `connectorId` — there is nothing else to configure. ### Example ```json title="Tool config" { "toolType": "TIKTOK_GET_CHANNEL_STATS", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the TikTok connection References a connector by ID. ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [YouTube tool types](/docs/tools/youtube.md) - [Instagram tool types](/docs/tools/instagram.md) - [Facebook tool types](/docs/tools/facebook.md) --- # YouTube Source: https://docs.autessa.com/docs/tools/youtube > Read-only tool types for listing a channel's videos and pulling YouTube Analytics through a YouTube connector. # YouTube List the connected channel's uploads, fetch a single video's details, and pull YouTube Analytics metrics for a video over a date range — a fully read-only family. Every tool on this page requires a YouTube connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Listing reads the channel's uploads playlist via the YouTube Data API, and the analytics tool queries the YouTube Analytics API. The connector stores the channel's metadata (channel ID and uploads playlist) at authorization time — if a tool fails with `MISSING_METADATA`, reconnect the YouTube account. ## Get videos **TOOL** `YOUTUBE_GET_VIDEOS` Lists videos from the connected channel's uploads playlist, newest first. `maxResults` defaults to 25 and caps at 50; page further by passing the `nextPageToken` from a previous response as `pageToken`. Set `minimal` to `true` to trim each video to `videoId`, `title`, and `description` — useful when an agent only needs to pick a video. The response also carries the channel's `channelId` and `channelTitle`. ### Example ```json title="Tool config" { "toolType": "YOUTUBE_GET_VIDEOS", "connectorId": "V1StGXR8Z5", "maxResults": 25, "minimal": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the YouTube connection References a connector by ID. - `maxResults` (integer) — Maximum number of videos to return (default 25, max 50) - `pageToken` (string) — Page token for pagination from previous response - `minimal` (boolean) — When true, returns minimal video info (videoId, title, description only) ## Get video by ID **TOOL** `YOUTUBE_GET_VIDEO_BY_ID` Fetches one video's full details by its YouTube `videoId` — the same ID that appears in the video's URL and in the list tool's results. Use it to inspect a specific video without paging through the uploads playlist. ### Example ```json title="Tool config" { "toolType": "YOUTUBE_GET_VIDEO_BY_ID", "connectorId": "V1StGXR8Z5", "videoId": "@@{VIDEO_ID}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the YouTube connection References a connector by ID. - `videoId` (string) — The YouTube video ID to retrieve ## Get video analytics **TOOL** `YOUTUBE_GET_VIDEO_ANALYTICS` Runs a YouTube Analytics report for one video over `startDate` to `endDate` (both `YYYY-MM-DD`; defaults to the last 30 days), returning views, estimated minutes watched, average view duration and percentage, likes, dislikes, comments, shares, and subscribers gained. Set `includeTrafficSources` to `true` to add a views and watch-time breakdown by traffic source. Analytics need the channel metadata stored on the connector, and freshly published videos can lag a day or two behind in YouTube's reporting. ### Example ```json title="Tool config" { "toolType": "YOUTUBE_GET_VIDEO_ANALYTICS", "connectorId": "V1StGXR8Z5", "videoId": "@@{VIDEO_ID}", "startDate": "2026-06-01", "endDate": "2026-06-30", "includeTrafficSources": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the YouTube connection References a connector by ID. - `videoId` (string) — The YouTube video ID to get analytics for - `startDate` (string) — Start date in YYYY-MM-DD format (default: 30 days ago) - `endDate` (string) — End date in YYYY-MM-DD format (default: today) - `includeTrafficSources` (boolean) — Whether to include traffic source breakdown ## Related - [Tool Types overview](/docs/tools.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [TikTok tool types](/docs/tools/tiktok.md) - [Instagram tool types](/docs/tools/instagram.md) - [Google Analytics tool types](/docs/tools/google-analytics.md) --- # Mailchimp Source: https://docs.autessa.com/docs/tools/mailchimp > Tool types for the Mailchimp connector. # Mailchimp Tool types for creating and sending Mailchimp campaigns, managing audience contacts, and reading engagement data — opens, clicks, and per-member activity. Every type on this page requires a [Mailchimp connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Campaign workflow tools follow Mailchimp's own lifecycle: create a campaign, set its content, then send or schedule it — an agent equipped with all three can run the full flow in one conversation. ## Create campaign **TOOL** `MAILCHIMP_CREATE_CAMPAIGN` Creates a new regular email campaign targeted at an audience (list). The campaign is created empty — pair this with `MAILCHIMP_SET_CAMPAIGN_CONTENT` to fill it and `MAILCHIMP_SEND_CAMPAIGN` or `MAILCHIMP_SCHEDULE_CAMPAIGN` to deliver it. If `title` is omitted, the subject line doubles as the campaign title. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_CREATE_CAMPAIGN", "connectorId": "V1StGXR8Z5", "listId": "a1b2c3d4e5", "subjectLine": "@@{subjectLine}", "fromName": "Acme Weekly", "replyTo": "hello@acme.com" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `listId` (string) — Audience/List ID - `subjectLine` (string) — Email subject - `fromName` (string) — Sender name - `replyTo` (string) — Reply-to email - `title` (string · optional) — Campaign title (optional, defaults to subject) ## Set campaign content **TOOL** `MAILCHIMP_SET_CAMPAIGN_CONTENT` Sets the body of an existing campaign. Provide either `html` (full HTML body) or `templateId` — one of the two is required, and `plainText` can accompany either as the text fallback. Setting content replaces whatever the campaign previously contained. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_SET_CAMPAIGN_CONTENT", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4", "html": "@@{emailHtml}", "plainText": "@@{emailPlainText}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string) — Required string value. - `html` (string · optional) — HTML content (optional, use html or templateId) - `plainText` (string · optional) — Plain text content (optional) - `templateId` (string (ID) · optional) — Template ID to use (optional) References an app template by ID. ## Send campaign **TOOL** `MAILCHIMP_SEND_CAMPAIGN` Sends a campaign to its audience immediately. This is irreversible once Mailchimp begins delivery, and it fails if the campaign has no content yet — set content first. Give this tool only to agents that are supposed to actually deliver email. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_SEND_CAMPAIGN", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string) — Required string value. ## Schedule campaign **TOOL** `MAILCHIMP_SCHEDULE_CAMPAIGN` Schedules a campaign for future delivery instead of sending it now. `scheduleTime` is an ISO 8601 UTC timestamp (e.g. `2024-01-15T10:00:00Z`); Mailchimp rounds delivery to 15-minute slots on non-premium plans. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_SCHEDULE_CAMPAIGN", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4", "scheduleTime": "@@{sendAt}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string) — Required string value. - `scheduleTime` (string) — ISO 8601 format: 2024-01-15T10:00:00Z ## Get campaigns **TOOL** `MAILCHIMP_GET_CAMPAIGNS` Lists campaigns in the account, optionally filtered by status (`save`, `paused`, `schedule`, `sending`, `sent`). Use `count` and `offset` to page through large accounts; the default page is 10 campaigns. This is how an agent finds the `campaignId` the other campaign tools need. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_GET_CAMPAIGNS", "connectorId": "V1StGXR8Z5", "status": "sent", "count": 25 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `status` (string · optional) — Optional: save, paused, schedule, sending, sent - `count` (integer · optional) — Optional: number of campaigns to return (default 10, max 1000) - `offset` (integer · optional) — Optional: number of campaigns to skip (for pagination) ## Get campaign report **TOOL** `MAILCHIMP_GET_CAMPAIGN_REPORT` Fetches the performance report for a sent campaign: sends, opens, clicks, bounces, unsubscribes, and rates. Useful for agents that summarize campaign performance or decide what to send next based on past results. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_GET_CAMPAIGN_REPORT", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string) — Required string value. ## Get campaign content **TOOL** `MAILCHIMP_GET_CAMPAIGN_CONTENT` Retrieves the HTML and plain-text body of an existing campaign. Handy when an agent needs to review, quote, or adapt a previous campaign's copy before drafting a new one. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_GET_CAMPAIGN_CONTENT", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string) — Required string value. ## Get lists **TOOL** `MAILCHIMP_GET_LISTS` Lists the audiences (lists) in the account with their IDs and member counts. Agents usually call this first to discover the `listId` that the campaign and contact tools require. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_GET_LISTS", "connectorId": "V1StGXR8Z5", "count": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `count` (integer · optional) — Optional: number of lists to return (default 10, max 1000) - `offset` (integer · optional) — Optional: number of lists to skip (for pagination) ## Add contact **TOOL** `MAILCHIMP_ADD_CONTACT` Adds a contact to an audience. `status` controls the subscription state — use `subscribed` for contacts who already opted in, or `pending` to trigger a double-opt-in confirmation email. Arbitrary merge fields (PHONE, ADDRESS, custom fields) go in the `mergeFields` object. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_ADD_CONTACT", "connectorId": "V1StGXR8Z5", "listId": "a1b2c3d4e5", "email": "@@{contactEmail}", "status": "subscribed", "firstName": "@@{firstName}", "lastName": "@@{lastName}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `listId` (string) — Required string value. - `email` (string) — Required string value. - `status` (string) — subscribed, pending, unsubscribed, cleaned, transactional - `firstName` (string · optional) — Optional - `lastName` (string · optional) — Optional - `mergeFields` (object · optional) — Optional: additional merge fields like PHONE, ADDRESS, etc. ## Update contact **TOOL** `MAILCHIMP_UPDATE_CONTACT` Updates an existing contact in an audience, identified by email address (the MD5 subscriber hash Mailchimp requires is computed for you). Only the fields you supply change — omit `status` to leave the subscription state alone. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_UPDATE_CONTACT", "connectorId": "V1StGXR8Z5", "listId": "a1b2c3d4e5", "email": "@@{contactEmail}", "firstName": "@@{firstName}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `listId` (string) — Required string value. - `email` (string) — Used to identify the contact (MD5 hash computed internally) - `status` (string · optional) — Optional: subscribed, pending, unsubscribed, cleaned, transactional - `firstName` (string · optional) — Optional - `lastName` (string · optional) — Optional - `mergeFields` (object · optional) — Optional: additional merge fields ## Get contacts **TOOL** `MAILCHIMP_GET_CONTACTS` Lists the members of an audience, optionally filtered by subscription status. Pages with `count` (default 10, max 1000) and `offset`, so an agent can walk an entire audience in batches. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_GET_CONTACTS", "connectorId": "V1StGXR8Z5", "listId": "a1b2c3d4e5", "status": "subscribed", "count": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `listId` (string) — Required string value. - `status` (string · optional) — Optional: subscribed, pending, unsubscribed, cleaned, transactional - `count` (integer · optional) — Optional: number of contacts to return (default 10, max 1000) - `offset` (integer · optional) — Optional: number of contacts to skip (for pagination) ## List email clickers **TOOL** `MAILCHIMP_LIST_EMAIL_CLICKERS` Returns the contacts who clicked links in a campaign. With `campaignId` it reports on that single campaign; without it, the tool scans the most recent sent campaigns (up to `count`, default 10) and aggregates clickers across them. Results are cached briefly, so back-to-back calls don't rescan the account. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_LIST_EMAIL_CLICKERS", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string · optional) — Optional: if provided, only get clickers for this campaign - `count` (integer · optional) — Optional: max campaigns when campaignId is null (default 10) ## List contacts with clicked campaigns **TOOL** `MAILCHIMP_LIST_CONTACTS_WITH_CLICKED_CAMPAIGNS` Cross-references an audience against recent campaign click activity, returning each contact together with the campaigns they clicked. `campaignCount` bounds how many recent sent campaigns are scanned (default 10), while `count` and `offset` page through the audience itself. Good for building engaged-segment lists for follow-up. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_LIST_CONTACTS_WITH_CLICKED_CAMPAIGNS", "connectorId": "V1StGXR8Z5", "listId": "a1b2c3d4e5", "campaignCount": 10 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `listId` (string) — Required string value. - `campaignCount` (integer · optional) — Optional: max sent campaigns to scan (default 10) - `offset` (integer · optional) — Optional: pagination offset (default 0) - `count` (integer · optional) — Optional: contacts per page (default 1000) ## Get member click activity **TOOL** `MAILCHIMP_GET_MEMBER_CLICK_ACTIVITY` Looks up the click history of a single contact by email address. Supplying `listId` lets the tool fetch the member's profile and use the richer per-member activity feed; without it, activity is resolved from campaign click reports. Use this when an agent needs to answer "what has this specific person engaged with?" ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_GET_MEMBER_CLICK_ACTIVITY", "connectorId": "V1StGXR8Z5", "email": "@@{contactEmail}", "listId": "a1b2c3d4e5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `email` (string) — Required: email address to look up - `listId` (string · optional) — Optional: if provided, fetches member info and uses activity-feed endpoint ## List email openers **TOOL** `MAILCHIMP_LIST_EMAIL_OPENERS` Returns the contacts who opened a campaign, with open counts. Like the clickers tool, it works in single-campaign mode when `campaignId` is set, or scans the most recent sent campaigns (up to `count`) when it isn't. Note that Apple Mail privacy features inflate open counts — clicks are the more reliable engagement signal. ### Example ```json title="Tool config" { "toolType": "MAILCHIMP_LIST_EMAIL_OPENERS", "connectorId": "V1StGXR8Z5", "campaignId": "abc123def4" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `campaignId` (string · optional) — Optional: if provided, only get openers for this campaign - `count` (integer · optional) — Optional: max campaigns when campaignId is null (default 10) ## Related - [Connectors](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Beehiiv](/docs/tools/beehiiv.md) - [Ghost](/docs/tools/ghost.md) - [Email tools](/docs/tools/email.md) --- # Beehiiv Source: https://docs.autessa.com/docs/tools/beehiiv > Tool types for the Beehiiv connector. # Beehiiv Read-only tool types for Beehiiv newsletters: posts and their performance, subscriber lookups, and publication-level growth stats. Every type on this page requires a [Beehiiv connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. All Beehiiv tools are also scoped to a `publicationId` (Beehiiv's `pub_...` identifier), since one Beehiiv account can run several publications. ## Get posts **TOOL** `BEEHIIV_GET_POSTS` Lists posts in a publication, newest first, with optional filters for status (`draft`, `confirmed`, `archived`), delivery platform (`web`, `email`, `both`), and audience tier (`free`, `premium`). All filters default to `all`. This is the discovery tool — agents call it to find the `postId` values the other post tools take. ### Example ```json title="Tool config" { "toolType": "BEEHIIV_GET_POSTS", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "status": "confirmed", "limit": 25 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication to fetch posts from - `limit` (integer · optional) — Optional, default 10, max 100 - `status` (string · optional) — Optional: "draft", "confirmed", "archived", "all" (default: all) - `platform` (string · optional) — Optional: "web", "email", "both", "all" (default: all) - `audience` (string · optional) — Optional: "free", "premium", "all" (default: all) ## Get post by ID **TOOL** `BEEHIIV_GET_POST_BY_ID` Fetches a single post's full details — title, subtitle, content URLs, publish state, and audience settings. Set `expandStats` to `true` to include the post's engagement stats in the same call instead of making a separate stats request. ### Example ```json title="Tool config" { "toolType": "BEEHIIV_GET_POST_BY_ID", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "postId": "@@{postId}", "expandStats": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication the post belongs to - `postId` (string) — Required - the post ID to fetch - `expandStats` (boolean · optional) — Optional - whether to include stats (default: false) ## Get post stats **TOOL** `BEEHIIV_GET_POST_STATS` Returns engagement stats for one post: email recipients, opens, unique opens, clicks, and web views where available. Use it when the agent already knows which post it cares about and only needs the numbers. ### Example ```json title="Tool config" { "toolType": "BEEHIIV_GET_POST_STATS", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "postId": "@@{postId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication the post belongs to - `postId` (string) — Required - the post ID to get stats for ## Get aggregate stats **TOOL** `BEEHIIV_GET_AGGREGATE_STATS` Returns publication-wide aggregate stats across posts — totals and averages rather than per-post numbers. The `status` and `platform` filters narrow which posts count toward the aggregate, so an agent can compare, say, email-only performance against web. ### Example ```json title="Tool config" { "toolType": "BEEHIIV_GET_AGGREGATE_STATS", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "platform": "email" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication to get aggregate stats for - `status` (string · optional) — Optional: "draft", "confirmed", "archived", "all" (default: all) - `platform` (string · optional) — Optional: "web", "email", "both", "all" (default: all) ## List subscriptions **TOOL** `BEEHIIV_LIST_SUBSCRIPTIONS` Lists a publication's subscriptions with optional filters for status (`validating`, `invalid`, `pending`, `active`, `inactive`), tier (`free`, `premium`), and creation date (`YYYY/MM/DD`). Results are cursor-paginated: pass the `cursor` from one response into the next call to keep walking the list (page size via `limit`, max 100). ### Example ```json title="Tool config" { "toolType": "BEEHIIV_LIST_SUBSCRIPTIONS", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "status": "active", "tier": "premium", "limit": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication to list subscriptions for - `limit` (integer · optional) — Optional, default 10, max 100 - `status` (string · optional) — Optional: "validating", "invalid", "pending", "active", "inactive", "all" (default: all) - `tier` (string · optional) — Optional: "free", "premium", "all" (default: all) - `creationDate` (string · optional) — Optional: Filter by creation date (format: YYYY/MM/DD) - `cursor` (string · optional) — Optional: Cursor for pagination ## Get subscriber by email **TOOL** `BEEHIIV_GET_SUBSCRIBER_BY_EMAIL` Looks up a single subscriber by email address and returns their subscription record — status, tier, signup source, and UTM attribution. The address is URL-encoded for you, so raw emails with `+` tags are fine. ### Example ```json title="Tool config" { "toolType": "BEEHIIV_GET_SUBSCRIBER_BY_EMAIL", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "email": "@@{subscriberEmail}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication to search in - `email` (string) — Required - email address to search for (will be URL encoded) ## Get subscriber stats **TOOL** `BEEHIIV_GET_SUBSCRIBER_STATS` Returns subscriber-count stats for the publication, optionally bounded by `startDate` and `endDate` (`YYYY/MM/DD`) to measure growth over a window. Useful for agents that report on list growth or compare acquisition periods. ### Example ```json title="Tool config" { "toolType": "BEEHIIV_GET_SUBSCRIBER_STATS", "connectorId": "V1StGXR8Z5", "publicationId": "pub_00000000-0000-0000-0000-000000000000", "startDate": "2026/01/01", "endDate": "2026/06/30" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required References a connector by ID. - `publicationId` (string) — Required - publication to get subscriber stats for - `startDate` (string · optional) — Optional: Start date for subscriber count (format: YYYY/MM/DD) - `endDate` (string · optional) — Optional: End date for subscriber count (format: YYYY/MM/DD) ## Related - [Connectors](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Mailchimp](/docs/tools/mailchimp.md) - [Ghost](/docs/tools/ghost.md) --- # Ghost Source: https://docs.autessa.com/docs/tools/ghost > Tool types for the Ghost connector. # Ghost Tool types for running a Ghost publication end to end: browse and read posts, create and edit drafts, publish or schedule them, and read per-post engagement analytics. Every type on this page requires a [Ghost connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Post IDs are Ghost's own hex identifiers — agents typically discover them with `GHOST_GET_POSTS` before calling the single-post tools. ## Get posts **TOOL** `GHOST_GET_POSTS` Lists posts in the publication, optionally filtered by `status` (`draft`, `published`, `scheduled`, or `all`). Returns up to `limit` posts (default 15) with their IDs, titles, and publish state — the starting point for any workflow that edits, publishes, or analyzes existing posts. ### Example ```json title="Tool config" { "toolType": "GHOST_GET_POSTS", "connectorId": "V1StGXR8Z5", "status": "draft", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `limit` (integer · optional) — Optional, default 15 - `status` (string · optional) — Optional: "draft", "published", "scheduled", "all" ## Get post **TOOL** `GHOST_GET_POST` Fetches a single post by ID with its full content and metadata — title, HTML body, tags, feature image, excerpt, and status. Use it before an update so the agent edits the real current content rather than guessing. ### Example ```json title="Tool config" { "toolType": "GHOST_GET_POST", "connectorId": "V1StGXR8Z5", "postId": "@@{postId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required - Ghost connector ID References a connector by ID. - `postId` (string) — Required - The post ID to retrieve ## Create post **TOOL** `GHOST_CREATE_POST` Creates a new post. By default it lands as a `draft`; pass `status: "published"` to go live immediately. The body is HTML, tags are auto-created if they don't already exist, and `featureImage` takes an image URL. Keeping creation as a draft and publishing via `GHOST_PUBLISH_POST` gives a human a review point in between. ### Example ```json title="Tool config" { "toolType": "GHOST_CREATE_POST", "connectorId": "V1StGXR8Z5", "title": "@@{postTitle}", "html": "@@{postHtml}", "status": "draft", "tags": ["newsletter", "product"] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `title` (string) — Required - `html` (string · optional) — Optional - post content in HTML - `status` (string · optional) — Optional: "draft" (default), "published" - `tags` (array · optional) — Optional - tag names (auto-created if not exist) - `featureImage` (string · optional) — Optional - URL - `customExcerpt` (string · optional) — Optional ## Update post **TOOL** `GHOST_UPDATE_POST` Updates an existing post's title, HTML body, tags, feature image, or excerpt. Only the fields you supply change — with one caveat: `tags` replaces the post's entire tag list, so include every tag the post should keep. ### Example ```json title="Tool config" { "toolType": "GHOST_UPDATE_POST", "connectorId": "V1StGXR8Z5", "postId": "@@{postId}", "html": "@@{revisedHtml}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postId` (string) — Required - the post ID to update - `title` (string · optional) — Optional - `html` (string · optional) — Optional - post content in HTML - `tags` (array · optional) — Optional - replaces all tags - `featureImage` (string · optional) — Optional - URL - `customExcerpt` (string · optional) — Optional ## Publish post **TOOL** `GHOST_PUBLISH_POST` Publishes a draft post immediately, making it live on the site. Depending on the publication's newsletter settings, publishing can also trigger the email send — so treat this as the "go" button, not a save. ### Example ```json title="Tool config" { "toolType": "GHOST_PUBLISH_POST", "connectorId": "V1StGXR8Z5", "postId": "@@{postId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postId` (string) — Required - the post ID to publish ## Schedule post **TOOL** `GHOST_SCHEDULE_POST` Schedules a draft post to publish automatically at a future time. `publishAt` is an ISO 8601 datetime (e.g. `2025-12-15T10:00:00.000Z`) and must be in the future; Ghost handles the actual publish when the time arrives. ### Example ```json title="Tool config" { "toolType": "GHOST_SCHEDULE_POST", "connectorId": "V1StGXR8Z5", "postId": "@@{postId}", "publishAt": "@@{publishAt}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `postId` (string) — Required - the post ID to schedule - `publishAt` (string) — Required - ISO 8601 datetime (e.g., "2025-12-15T10:00:00.000Z") ## Get post analytics **TOOL** `GHOST_GET_POST_ANALYTICS` Returns a comprehensive engagement picture for one post: email delivery stats (sent, delivered, opened, failed), click counts, member signups and paid conversions attributed to the post, and positive/negative feedback. Open and click tracking flags are included so an agent knows whether zero means "nobody opened" or "tracking off". ### Example ```json title="Tool config" { "toolType": "GHOST_GET_POST_ANALYTICS", "connectorId": "V1StGXR8Z5", "postId": "@@{postId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required - Ghost connector ID References a connector by ID. - `postId` (string) — Required - The post ID to get analytics for ## Get top posts **TOOL** `GHOST_GET_TOP_POSTS` Ranks the publication's recent posts by engagement. It fetches posts published within the last `days` (default 30, computed in `timezone`, default UTC), sorts them by click count descending, and returns the top `limit` (default 10). Good for "what's working?" summaries and for picking themes worth repeating. ### Example ```json title="Tool config" { "toolType": "GHOST_GET_TOP_POSTS", "connectorId": "V1StGXR8Z5", "days": 30, "limit": 10 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required - Ghost connector ID References a connector by ID. - `days` (integer · optional) — Optional, default 30 - Number of days to look back - `limit` (integer · optional) — Optional, default 10 - Number of posts to return - `timezone` (string · optional) — Optional, default "UTC" - Timezone for date calculations ## Related - [Connectors](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Beehiiv](/docs/tools/beehiiv.md) - [Mailchimp](/docs/tools/mailchimp.md) --- # Google Analytics Source: https://docs.autessa.com/docs/tools/google-analytics > Tool types for the Google Analytics connector. # Google Analytics Read-only tool types over GA4: run standard and realtime reports, pull conversion and event breakdowns, list the properties an account can see, and look up which dimensions and metrics a property supports. Every type on this page requires a [Google Analytics connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. All reporting tools target a GA4 `propertyId` in the `properties/123456789` form — use `GOOGLE_ANALYTICS_LIST_PROPERTIES` to discover it, and `GOOGLE_ANALYTICS_GET_METADATA` to find valid dimension and metric names before building a report. ## Run report **TOOL** `GOOGLE_ANALYTICS_RUN_REPORT` Runs a standard GA4 report: any combination of dimensions and metrics over a date range, ordered and capped as you choose. Dimensions and metrics are comma-separated GA4 API names (`date,pagePath,sessionSource` / `activeUsers,sessions,screenPageViews`), and dates accept both `YYYY-MM-DD` and relative forms like `7daysAgo` or `yesterday`. This is the general-purpose tool — the conversions and events tools below are convenience wrappers over the same reporting API. ### Example ```json title="Tool config" { "toolType": "GOOGLE_ANALYTICS_RUN_REPORT", "connectorId": "V1StGXR8Z5", "propertyId": "properties/123456789", "startDate": "30daysAgo", "endDate": "today", "dimensions": "date,sessionSource", "metrics": "activeUsers,sessions,screenPageViews", "limit": 1000, "orderBy": "sessions", "orderDirection": "DESCENDING" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics connection References a connector by ID. - `propertyId` (string) — The GA4 property ID to query (e.g., "properties/123456789"). Get available properties from the connector's propertiesData. - `startDate` (string) — Start date in YYYY-MM-DD format, or relative date like 'yesterday', '7daysAgo', '30daysAgo' - `endDate` (string) — End date in YYYY-MM-DD format, or relative date like 'today', 'yesterday' - `dimensions` (string) — Comma-separated dimensions to include. Examples: date,pagePath,sessionSource,deviceCategory,country - `metrics` (string) — Comma-separated metrics to include. Examples: activeUsers,sessions,screenPageViews,bounceRate,averageSessionDuration - `limit` (integer) — Maximum number of rows to return (default 10000) - `orderBy` (string) — Field to order results by (must be a metric or dimension in the request) - `orderDirection` (string) — Order direction: ASCENDING or DESCENDING ## Realtime report **TOOL** `GOOGLE_ANALYTICS_REALTIME_REPORT` Reports on activity happening right now (roughly the last 30 minutes) — no date range, just realtime dimensions and metrics like `unifiedScreenName,country` and `activeUsers,screenPageViews`. Note that the realtime API supports a smaller dimension/metric set than standard reports, and the default row limit is 100. ### Example ```json title="Tool config" { "toolType": "GOOGLE_ANALYTICS_REALTIME_REPORT", "connectorId": "V1StGXR8Z5", "propertyId": "properties/123456789", "dimensions": "unifiedScreenName,country", "metrics": "activeUsers", "limit": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics connection References a connector by ID. - `propertyId` (string) — The GA4 property ID to query (e.g., "properties/123456789"). Get available properties from the connector's propertiesData. - `dimensions` (string) — Comma-separated realtime dimensions. Examples: unifiedScreenName,country,city,deviceCategory,platform - `metrics` (string) — Comma-separated realtime metrics. Examples: activeUsers,screenPageViews,conversions,eventCount - `limit` (integer) — Maximum number of rows to return (default 100 for realtime) ## Get conversions **TOOL** `GOOGLE_ANALYTICS_GET_CONVERSIONS` Reports on key (conversion) events — `purchase`, `sign_up`, `generate_lead`, and any custom events marked as conversions — broken down by the dimensions you choose. `conversionEvents` is a comma-separated filter of event names, so an agent can compare specific funnels side by side without pulling every event in the property. ### Example ```json title="Tool config" { "toolType": "GOOGLE_ANALYTICS_GET_CONVERSIONS", "connectorId": "V1StGXR8Z5", "propertyId": "properties/123456789", "startDate": "7daysAgo", "endDate": "today", "conversionEvents": "purchase,sign_up", "dimensions": "date,sessionSource", "limit": 500 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics connection References a connector by ID. - `propertyId` (string) — The GA4 property ID to query (e.g., "properties/123456789"). Get available properties from the connector's propertiesData. - `startDate` (string) — Start date in YYYY-MM-DD format, or relative date like '7daysAgo', '30daysAgo' - `endDate` (string) — End date in YYYY-MM-DD format, or relative date like 'today', 'yesterday' - `conversionEvents` (string) — Comma-separated conversion event names to filter by. Examples: purchase,sign_up,generate_lead,add_to_cart - `dimensions` (string) — Comma-separated dimensions to break down conversions. Examples: date,sessionSource,deviceCategory,country - `limit` (integer) — Maximum number of rows to return ## Get events **TOOL** `GOOGLE_ANALYTICS_GET_EVENTS` Reports event counts over a date range, filtered to specific `eventNames` (comma-separated) or all events when the filter is empty. Break results down with dimensions like `date,pagePath,sessionSource` to see where and when events fire. ### Example ```json title="Tool config" { "toolType": "GOOGLE_ANALYTICS_GET_EVENTS", "connectorId": "V1StGXR8Z5", "propertyId": "properties/123456789", "startDate": "30daysAgo", "endDate": "today", "eventNames": "page_view,video_start", "dimensions": "date,pagePath", "limit": 1000 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics connection References a connector by ID. - `propertyId` (string) — The GA4 property ID to query (e.g., "properties/123456789"). Get available properties from the connector's propertiesData. - `startDate` (string) — Start date in YYYY-MM-DD format, or relative date like '7daysAgo', '30daysAgo' - `endDate` (string) — End date in YYYY-MM-DD format, or relative date like 'today', 'yesterday' - `eventNames` (string) — Comma-separated event names to filter by. If empty, returns all events. Examples: page_view,scroll,click,video_start - `dimensions` (string) — Comma-separated dimensions to break down events. Examples: date,pagePath,sessionSource - `limit` (integer) — Maximum number of rows to return ## List properties **TOOL** `GOOGLE_ANALYTICS_LIST_PROPERTIES` Lists every GA4 property the connected account can access, grouped by Analytics account, with the `properties/...` IDs the reporting tools need. Takes no parameters beyond the connector — agents call it once at the start of an analytics conversation to resolve which property to query. ### Example ```json title="Tool config" { "toolType": "GOOGLE_ANALYTICS_LIST_PROPERTIES", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics connection References a connector by ID. ## Get metadata **TOOL** `GOOGLE_ANALYTICS_GET_METADATA` Returns the dimensions and metrics a property supports, including custom definitions when `includeCustomDefinitions` is true (the default). The optional `query` filters results case-insensitively across API names, UI names, and descriptions — e.g. `"page"` surfaces `pagePath`, `pageTitle`, and `screenPageViews`. Give this tool to any agent that builds reports dynamically, so it can verify field names instead of hallucinating them. ### Example ```json title="Tool config" { "toolType": "GOOGLE_ANALYTICS_GET_METADATA", "connectorId": "V1StGXR8Z5", "propertyId": "properties/123456789", "includeCustomDefinitions": true, "query": "@@{searchTerm}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics connection References a connector by ID. - `propertyId` (string) — The GA4 property ID to get metadata for (e.g., "properties/123456789"). Required if includeCustomDefinitions is true. - `includeCustomDefinitions` (boolean) — If true, includes custom dimensions and metrics specific to the property. If false, only returns universal dimensions and metrics common to all properties. Default: true - `query` (string · optional) — Optional search query to filter dimensions and metrics. Matches against apiName, uiName, and description (case-insensitive). Example: "page" will return dimensions/metrics like pagePath, pageTitle, screenPageViews, etc. ## Related - [Connectors](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [BigQuery](/docs/tools/bigquery.md) - [All tool types](/docs/tools.md) --- # BigQuery Source: https://docs.autessa.com/docs/tools/bigquery > Tool types for the BigQuery connector. # BigQuery Tool types for querying Google BigQuery: browse datasets and tables, inspect schemas, and run SQL with built-in cost controls. Every type on this page requires a [BigQuery connector](/docs/guide/connectors.md) (the same Google connector used for Google Analytics); `connectorId` is the ID of that connector. Each tool also names an explicit GCP `projectId`, since one connected account can span many projects. The three browse tools exist so an agent can discover schema before writing SQL — a `RUN_QUERY`-only agent tends to guess table names. ## List datasets **TOOL** `BIGQUERY_LIST_DATASETS` Lists the datasets in a GCP project, up to `maxResults` (default 100). This is the top of the discovery chain: dataset → tables → schema → query. ### Example ```json title="Tool config" { "toolType": "BIGQUERY_LIST_DATASETS", "connectorId": "V1StGXR8Z5", "projectId": "acme-analytics-prod", "maxResults": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics/BigQuery connection References a connector by ID. - `projectId` (string) — The GCP project ID containing the datasets - `maxResults` (integer) — Maximum number of datasets to return (default 100, max 1000) ## List tables **TOOL** `BIGQUERY_LIST_TABLES` Lists the tables (and views) inside a dataset, up to `maxResults` (default 100). GA4 BigQuery exports show up here as date-sharded `events_YYYYMMDD` tables — useful context when an agent is deciding what to query. ### Example ```json title="Tool config" { "toolType": "BIGQUERY_LIST_TABLES", "connectorId": "V1StGXR8Z5", "projectId": "acme-analytics-prod", "datasetId": "analytics_123456789", "maxResults": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics/BigQuery connection References a connector by ID. - `projectId` (string) — The GCP project ID - `datasetId` (string) — The dataset ID to list tables from - `maxResults` (integer) — Maximum number of tables to return (default 100, max 1000) ## Get table schema **TOOL** `BIGQUERY_GET_TABLE_SCHEMA` Returns a table's schema — column names, types, modes, and nested record structure. Have the agent call this before writing SQL against an unfamiliar table; it is the difference between correct queries and column-name guesswork. ### Example ```json title="Tool config" { "toolType": "BIGQUERY_GET_TABLE_SCHEMA", "connectorId": "V1StGXR8Z5", "projectId": "acme-analytics-prod", "datasetId": "analytics_123456789", "tableId": "@@{tableId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics/BigQuery connection References a connector by ID. - `projectId` (string) — The GCP project ID - `datasetId` (string) — The dataset ID containing the table - `tableId` (string) — The table ID to get schema for ## Run query **TOOL** `BIGQUERY_RUN_QUERY` Executes SQL (Standard SQL unless `useLegacySql` is true) and returns up to `maxRows` rows (default 1000, max 10000). Two guardrails matter for cost: `maxBytesProcessed` makes the query fail fast if it would scan more data than you allow, and `dryRun: true` returns the estimated bytes scanned without executing at all — a cheap way to validate a query before paying for it. `timeoutMs` caps execution at up to 5 minutes. ### Example ```json title="Tool config" { "toolType": "BIGQUERY_RUN_QUERY", "connectorId": "V1StGXR8Z5", "projectId": "acme-analytics-prod", "query": "@@{sqlQuery}", "maxRows": 1000, "timeoutMs": 30000, "maxBytesProcessed": 1000000000, "dryRun": false, "useLegacySql": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — The connector ID for the Google Analytics/BigQuery connection References a connector by ID. - `projectId` (string) — The GCP project ID to run the query in - `query` (string) — The SQL query to execute (Standard SQL by default) - `maxRows` (integer) — Maximum number of rows to return (default 1000, max 10000) - `timeoutMs` (integer) — Query timeout in milliseconds (default 30000, max 300000 = 5 min) - `maxBytesProcessed` (integer) — Maximum bytes the query can process (fails if exceeded, good for cost control) - `dryRun` (boolean) — If true, only estimates cost without executing (returns bytesProcessed estimate) - `useLegacySql` (boolean) — If true, uses legacy SQL. Default false = Standard SQL ## Related - [Connectors](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [Google Analytics](/docs/tools/google-analytics.md) - [AutessaDB tools](/docs/tools/autessadb.md) --- # Zendesk Source: https://docs.autessa.com/docs/tools/zendesk > Tool types for the Zendesk connector. # Zendesk Read-only tool types over Zendesk Support tickets: fetch one ticket by ID, search with Zendesk's query syntax, or list tickets in a date range. Every type on this page requires a [Zendesk connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Ticket IDs are Zendesk's own numeric IDs — the ones agents and customers see in the Zendesk UI and email subjects. ## Get ticket **TOOL** `ZENDESK_GET_TICKET` Fetches a single ticket by its numeric ID, returning subject, description, status, priority, requester and assignee, tags, and timestamps. Use it when the agent already has a ticket number — from a customer message, a search result, or a webhook. ### Example ```json title="Tool config" { "toolType": "ZENDESK_GET_TICKET", "connectorId": "V1StGXR8Z5", "ticketId": "@@{ticketId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `ticketId` (integer) — Required integer value. ## Search tickets **TOOL** `ZENDESK_SEARCH_TICKETS` Searches tickets with Zendesk's full search query syntax — free text plus operators like `status:open`, `priority:high`, `requester:jane@example.com`, or `tags:billing`. Results can be sorted by `updated_at`, `created_at`, `priority`, or `status`, in either direction, and capped with `limit` (default 25, max 100). This is the most flexible of the three tools; reach for it whenever the criteria are anything other than "a date range". ### Example ```json title="Tool config" { "toolType": "ZENDESK_SEARCH_TICKETS", "connectorId": "V1StGXR8Z5", "query": "@@{searchQuery}", "sortBy": "updated_at", "sortOrder": "desc", "limit": 25 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Zendesk search query string (required) - `sortBy` (string · optional) — Optional: "updated_at", "created_at", "priority", "status" - `sortOrder` (string · optional) — Optional: "asc" or "desc" - `limit` (integer · optional) — Optional: number of results (default 25, max 100) ## List tickets **TOOL** `ZENDESK_LIST_TICKETS` Lists tickets created or updated within a date range — `startDate` and `endDate` in `YYYY-MM-DD`, with `dateField` choosing `created` (default) or `updated`. The bounds are exclusive, so widen the range by a day on each side to include the endpoints. An optional `status` filter narrows to one lifecycle state, and results paginate internally up to `limit` (default 100, max 1000). Under the hood this builds a Zendesk search query, so it shares the Search API's behavior and rate limits. ### Example ```json title="Tool config" { "toolType": "ZENDESK_LIST_TICKETS", "connectorId": "V1StGXR8Z5", "startDate": "2026-06-01", "endDate": "2026-07-01", "dateField": "created", "status": "open", "limit": 200 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `startDate` (string) — Required: YYYY-MM-DD format - `endDate` (string) — Required: YYYY-MM-DD format - `dateField` (string · optional) — Optional: "created" or "updated" (default "created") - `status` (string · optional) — Optional: "new", "open", "pending", "hold", "solved", "closed" - `sortBy` (string · optional) — Optional: "updated_at", "created_at", "priority", "status" - `sortOrder` (string · optional) — Optional: "asc" or "desc" - `limit` (integer · optional) — Optional: number of results (default 100, max 1000) ## Related - [Connectors](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [HubSpot](/docs/tools/hubspot.md) - [Email tools](/docs/tools/email.md) --- # HubSpot Source: https://docs.autessa.com/docs/tools/hubspot > Tool types for searching HubSpot CRM objects, reading pipelines and forms, and sending logged emails. # HubSpot Give agents read access to your HubSpot CRM — contacts, companies, deals, meetings, engagements, and forms — plus a send-email tool that delivers through Gmail and logs the message back to the contact's HubSpot timeline. Every tool on this page requires a HubSpot connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. The search tools all use HubSpot's CRM Search API: `query` is a free-text search, the optional filters are combined with AND, results are paginated with `limit` and the `after` cursor from the previous response, and `properties` lets you request extra CRM properties beyond the defaults. ## Search contacts **TOOL** `HUBSPOT_SEARCH_CONTACTS` Searches contacts by free-text query — names, email addresses, and other indexed properties all match. Narrow the results with `lifecycleStage`, `leadStatus`, or a `startDate`/`endDate` window on the contact's create date. This is the usual first step when an agent only has a name or email and needs the HubSpot contact ID for the other tools. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_SEARCH_CONTACTS", "connectorId": "V1StGXR8Z5", "query": "@@{CUSTOMER_EMAIL}", "lifecycleStage": "customer", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Free-text search query - `lifecycleStage` (string · optional) — Optional filter: subscriber, lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, other - `leadStatus` (string · optional) — Optional filter: e.g. NEW, OPEN, IN_PROGRESS, etc. - `startDate` (string · optional) — Optional: YYYY-MM-DD format, filters on createdate - `endDate` (string · optional) — Optional: YYYY-MM-DD format, filters on createdate - `properties` (string · optional) — Optional: comma-separated property names to return - `limit` (integer · optional) — Optional: max results (default 20, max 100) - `after` (string · optional) — Optional: pagination cursor from previous response ## Get a contact **TOOL** `HUBSPOT_GET_CONTACT` Fetches a single contact by its HubSpot contact ID, returning the default property set plus anything you list in `properties`. Set `associations` to pull the IDs of related records — for example `"companies,deals"` — so the agent can chase the rest of the relationship graph. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_GET_CONTACT", "connectorId": "V1StGXR8Z5", "contactId": "@@{HUBSPOT_CONTACT_ID}", "properties": "email,firstname,lastname,phone,lifecyclestage", "associations": "companies,deals" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `contactId` (string) — HubSpot contact ID (required) - `properties` (string · optional) — Optional: comma-separated property names - `associations` (string · optional) — Optional: comma-separated e.g. "companies,deals" ## Search companies **TOOL** `HUBSPOT_SEARCH_COMPANIES` Searches companies by free-text query, with optional filters on `industry` and a create-date window. Returns matching company records with their IDs, ready to feed into `HUBSPOT_GET_COMPANY` for full detail. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_SEARCH_COMPANIES", "connectorId": "V1StGXR8Z5", "query": "@@{COMPANY_NAME}", "industry": "COMPUTER_SOFTWARE", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Free-text search query - `industry` (string · optional) — Optional filter: industry name - `startDate` (string · optional) — Optional: YYYY-MM-DD format, filters on createdate - `endDate` (string · optional) — Optional: YYYY-MM-DD format, filters on createdate - `properties` (string · optional) — Optional: comma-separated property names - `limit` (integer · optional) — Optional: max results (default 20, max 100) - `after` (string · optional) — Optional: pagination cursor ## Get a company **TOOL** `HUBSPOT_GET_COMPANY` Fetches a single company by its HubSpot company ID. Use `properties` for extra fields and `associations` (e.g. `"contacts,deals"`) to list the IDs of people and deals attached to the company. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_GET_COMPANY", "connectorId": "V1StGXR8Z5", "companyId": "@@{HUBSPOT_COMPANY_ID}", "properties": "name,domain,industry,numberofemployees", "associations": "contacts,deals" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `companyId` (string) — HubSpot company ID (required) - `properties` (string · optional) — Optional: comma-separated property names - `associations` (string · optional) — Optional: comma-separated e.g. "contacts,deals" ## Search deals **TOOL** `HUBSPOT_SEARCH_DEALS` Searches deals by free-text query with filters for `pipeline`, `dealStage`, an amount range (`minAmount`/`maxAmount`), and a create-date window. Pipeline and stage filters take HubSpot IDs, not display labels — run `HUBSPOT_GET_PIPELINES` first to map labels to IDs. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_SEARCH_DEALS", "connectorId": "V1StGXR8Z5", "query": "@@{DEAL_KEYWORDS}", "pipeline": "default", "minAmount": "10000", "startDate": "2026-01-01", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Free-text search query - `pipeline` (string · optional) — Optional filter: pipeline ID (e.g. "default") - `dealStage` (string · optional) — Optional filter: deal stage ID - `minAmount` (string · optional) — Optional filter: minimum deal amount - `maxAmount` (string · optional) — Optional filter: maximum deal amount - `startDate` (string · optional) — Optional: YYYY-MM-DD format, filters on createdate - `endDate` (string · optional) — Optional: YYYY-MM-DD format, filters on createdate - `properties` (string · optional) — Optional: comma-separated property names - `limit` (integer · optional) — Optional: max results (default 20, max 100) - `after` (string · optional) — Optional: pagination cursor ## Get a deal **TOOL** `HUBSPOT_GET_DEAL` Fetches a single deal by its HubSpot deal ID, with optional extra `properties` and `associations` (e.g. `"contacts,companies"`) so the agent can see who and what the deal is attached to. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_GET_DEAL", "connectorId": "V1StGXR8Z5", "dealId": "@@{HUBSPOT_DEAL_ID}", "properties": "dealname,amount,dealstage,closedate", "associations": "contacts,companies" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `dealId` (string) — HubSpot deal ID (required) - `properties` (string · optional) — Optional: comma-separated property names - `associations` (string · optional) — Optional: comma-separated e.g. "contacts,companies" ## Get pipelines **TOOL** `HUBSPOT_GET_PIPELINES` Lists every deal pipeline in the portal with its stages, including the stage IDs that `HUBSPOT_SEARCH_DEALS` filters on. It takes no parameters beyond the connector, so agents typically call it once to learn the portal's sales process before filtering or interpreting deal stages. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_GET_PIPELINES", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. ## Search meetings **TOOL** `HUBSPOT_SEARCH_MEETINGS` Searches meeting engagements, filtered by a `startDate`/`endDate` window on the meeting start time and an optional `outcome` (SCHEDULED, COMPLETED, RESCHEDULED, NO_SHOW, CANCELLED). Results come back sorted by meeting start time and simplified to title, start/end time, outcome, and location — useful for "what's on the calendar this week" style questions. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_SEARCH_MEETINGS", "connectorId": "V1StGXR8Z5", "startDate": "@@{WEEK_START}", "endDate": "@@{WEEK_END}", "outcome": "SCHEDULED", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `startDate` (string · optional) — Optional: YYYY-MM-DD format, filters on hs_meeting_start_time - `endDate` (string · optional) — Optional: YYYY-MM-DD format, filters on hs_meeting_start_time - `outcome` (string · optional) — Optional filter: SCHEDULED, COMPLETED, RESCHEDULED, NO_SHOW, CANCELLED - `properties` (string · optional) — Optional: comma-separated property names - `limit` (integer · optional) — Optional: max results (default 20, max 100) - `after` (string · optional) — Optional: pagination cursor ## Search engagements **TOOL** `HUBSPOT_SEARCH_ENGAGEMENTS` Searches CRM activity records of one `engagementType` — `calls`, `emails`, `notes`, or `tasks` — optionally windowed by `startDate`/`endDate` on the engagement timestamp. This is the generalist companion to `HUBSPOT_SEARCH_MEETINGS`: use it to reconstruct recent activity on an account or audit what the sales team has logged. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_SEARCH_ENGAGEMENTS", "connectorId": "V1StGXR8Z5", "engagementType": "calls", "startDate": "@@{REPORT_START_DATE}", "limit": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `engagementType` (string) — Required: one of "calls", "emails", "notes", "tasks" - `startDate` (string · optional) — Optional: YYYY-MM-DD format, filters on hs_timestamp - `endDate` (string · optional) — Optional: YYYY-MM-DD format, filters on hs_timestamp - `properties` (string · optional) — Optional: comma-separated property names - `limit` (integer · optional) — Optional: max results (default 20, max 100) - `after` (string · optional) — Optional: pagination cursor ## List forms **TOOL** `HUBSPOT_LIST_FORMS` Lists the marketing forms in the portal with their names and GUIDs. Agents run this to find the form GUID that `HUBSPOT_GET_FORM_SUBMISSIONS` needs. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_LIST_FORMS", "connectorId": "V1StGXR8Z5", "limit": 50 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `limit` (integer · optional) — Optional: number of forms to return (default 20, max 100) - `after` (string · optional) — Optional: pagination cursor from previous response ## Get form submissions **TOOL** `HUBSPOT_GET_FORM_SUBMISSIONS` Fetches recent submissions for one form, identified by its GUID (from `HUBSPOT_LIST_FORMS`). Each submission includes the submitted field values, so an agent can triage new inbound leads or answer "who filled out the demo request form today". ### Example ```json title="Tool config" { "toolType": "HUBSPOT_GET_FORM_SUBMISSIONS", "connectorId": "V1StGXR8Z5", "formId": "@@{FORM_GUID}", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `formId` (string) — HubSpot form GUID (required) - `limit` (integer · optional) — Optional: number of submissions to return (default 20, max 50) - `after` (string · optional) — Optional: pagination cursor ## Send email to contact **TOOL** `HUBSPOT_SEND_EMAIL` Sends an email to a HubSpot contact and logs it on their CRM timeline. It takes two connectors: the recipient address is looked up from the HubSpot contact (you pass `contactId`, never a raw email address), the message is sent from the connected Gmail account, and the sent email is then recorded in HubSpot as an email engagement associated with the contact. At least one of `body` (plain text) or `bodyHtml` is required. If the HubSpot logging step fails, the email has still been sent — the response carries a `hubspotWarning` instead of failing the whole call. ### Example ```json title="Tool config" { "toolType": "HUBSPOT_SEND_EMAIL", "hubspotConnectorId": "V1StGXR8Z5", "gmailConnectorId": "aB3dE5fG7h", "contactId": "@@{HUBSPOT_CONTACT_ID}", "subject": "Following up on your demo request", "body": "@@{EMAIL_BODY}", "cc": "sales@example.com" } ``` > This tool type requires a connected external account. Pass the connector's ID as `hubspotConnectorId`, `gmailConnectorId` in the tool configuration. **Configuration fields** - `hubspotConnectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `gmailConnectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `contactId` (string) — HubSpot contact ID — recipient email is fetched from HubSpot - `subject` (string) — Required string value. - `body` (string) — Required string value. - `bodyHtml` (string · optional) — Optional - `cc` (string · optional) — Optional - `bcc` (string · optional) — Optional ## Related - [Tools overview](/docs/tools.md) - [Connectors](/docs/guide/connectors.md) - [Agentic tools guide](/docs/guide/agentic/tools.md) - [Zoho CRM tools](/docs/tools/zoho-crm.md) - [Zendesk tools](/docs/tools/zendesk.md) - [Gmail tools](/docs/tools/gmail.md) --- # Zoho CRM Source: https://docs.autessa.com/docs/tools/zoho-crm > Tool types for searching, reading, and upserting Zoho CRM contacts, accounts, and records. # Zoho CRM Let agents work a Zoho CRM: find and upsert contacts and accounts, link them together, attach notes, log task activities, and reach into any other module with the generic record tools. Every tool on this page requires a Zoho CRM connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Zoho identifies everything by module (`Contacts`, `Accounts`, `Deals`, `Leads`, ...) and record ID, and field names use Zoho's API names with underscores (`First_Name`, `Account_Name`). The search tools paginate with `limit` (default 20, max 200 per page) and a 1-based `page` number. ## Search contacts **TOOL** `ZOHO_CRM_SEARCH_CONTACTS` Searches the Contacts module. `query` is a free-text word search across the record; the optional `email`, `phone`, and `lastName` filters do exact criteria matching when the agent already knows a specific attribute. Returns matching contacts with their Zoho record IDs for use in the get, note, and task tools. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_SEARCH_CONTACTS", "connectorId": "V1StGXR8Z5", "query": "@@{CONTACT_NAME}", "email": "@@{CONTACT_EMAIL}", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Free-text search (maps to Zoho criteria word search) - `email` (string · optional) — Optional: filter by email - `phone` (string · optional) — Optional: filter by phone - `lastName` (string · optional) — Optional: filter by last name - `limit` (integer · optional) — Optional: max results per page (default 20, max 200) - `page` (integer · optional) — Optional: page number (1-based) ## Get a contact **TOOL** `ZOHO_CRM_GET_CONTACT` Fetches a single contact by its Zoho record ID. By default the full record comes back; set `fields` to a comma-separated list of API field names to trim the response to just what the agent needs. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_GET_CONTACT", "connectorId": "V1StGXR8Z5", "contactId": "@@{ZOHO_CONTACT_ID}", "fields": "First_Name,Last_Name,Email,Phone,Account_Name" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `contactId` (string) — Zoho record ID of the contact - `fields` (string · optional) — Optional: comma-separated field names to return ## Upsert a contact **TOOL** `ZOHO_CRM_UPSERT_CONTACT` Creates a contact, or updates an existing one when `duplicateCheckFields` is set and a record matches on those fields — checking `["Email"]` is the usual choice. `fields` is an object of Zoho API field names to values. The response reports whether the record was inserted or updated, along with its Zoho record ID. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_UPSERT_CONTACT", "connectorId": "V1StGXR8Z5", "fields": { "First_Name": "@@{FIRST_NAME}", "Last_Name": "@@{LAST_NAME}", "Email": "@@{CONTACT_EMAIL}", "Phone": "@@{CONTACT_PHONE}" }, "duplicateCheckFields": ["Email"] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `fields` (object) — Contact field values (e.g. First_Name, Last_Name, Email, Phone) - `duplicateCheckFields` (array · optional) — Optional: fields to check for duplicates (e.g. ["Email"]) ## Search accounts **TOOL** `ZOHO_CRM_SEARCH_ACCOUNTS` Searches the Accounts module by free-text `query`, with an optional `accountName` filter for matching on the company name specifically. Use it to resolve a company name to the account record ID before linking contacts or logging activity against the account. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_SEARCH_ACCOUNTS", "connectorId": "V1StGXR8Z5", "query": "@@{COMPANY_NAME}", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `query` (string) — Free-text search - `accountName` (string · optional) — Optional: filter by account/company name - `limit` (integer · optional) — Optional: max results per page (default 20, max 200) - `page` (integer · optional) — Optional: page number (1-based) ## Get an account **TOOL** `ZOHO_CRM_GET_ACCOUNT` Fetches a single account by its Zoho record ID, optionally restricted to a comma-separated list of `fields`. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_GET_ACCOUNT", "connectorId": "V1StGXR8Z5", "accountId": "@@{ZOHO_ACCOUNT_ID}", "fields": "Account_Name,Phone,Website,Industry" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `accountId` (string) — Zoho record ID of the account - `fields` (string · optional) — Optional: comma-separated field names to return ## Upsert an account **TOOL** `ZOHO_CRM_UPSERT_ACCOUNT` Creates or updates an account, deduplicating on `duplicateCheckFields` — typically `["Account_Name"]`. Works the same way as the contact upsert: `fields` carries the Zoho API field values, and the response says whether the account was inserted or updated. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_UPSERT_ACCOUNT", "connectorId": "V1StGXR8Z5", "fields": { "Account_Name": "@@{COMPANY_NAME}", "Website": "@@{COMPANY_WEBSITE}", "Phone": "@@{COMPANY_PHONE}" }, "duplicateCheckFields": ["Account_Name"] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `fields` (object) — Account field values (e.g. Account_Name, Phone, Website) - `duplicateCheckFields` (array · optional) — Optional: fields to check for duplicates (e.g. ["Account_Name"]) ## Add contact to account **TOOL** `ZOHO_CRM_ADD_CONTACT_TO_ACCOUNT` Links an existing contact to an existing account by setting the contact's `Account_Name` lookup to the account. Both records must already exist — pair this with the upsert tools when building out a new company and its people. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_ADD_CONTACT_TO_ACCOUNT", "connectorId": "V1StGXR8Z5", "contactId": "@@{ZOHO_CONTACT_ID}", "accountId": "@@{ZOHO_ACCOUNT_ID}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `contactId` (string) — Zoho record ID of the contact - `accountId` (string) — Zoho record ID of the account to link to ## Add a note **TOOL** `ZOHO_CRM_ADD_NOTE` Attaches a text note to any record — pass the `parentModule` (e.g. `"Contacts"`, `"Accounts"`) and the parent's record ID. Handy for agents that summarize a call or conversation and file the summary on the CRM record. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_ADD_NOTE", "connectorId": "V1StGXR8Z5", "parentModule": "Contacts", "parentId": "@@{ZOHO_CONTACT_ID}", "noteTitle": "Call summary", "noteContent": "@@{CALL_SUMMARY}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `parentModule` (string) — Module the note is attached to (e.g. "Contacts", "Accounts") - `parentId` (string) — Zoho record ID of the parent record - `noteTitle` (string · optional) — Optional: title of the note - `noteContent` (string) — Content/body of the note ## Log a task **TOOL** `ZOHO_CRM_LOG_TASK` Creates a Tasks record associated with a contact or account — the CRM-native way to log an activity like "Trade Show Visit" or a follow-up reminder. `status` is a Zoho task status such as `"Not Started"`, `"In Progress"`, or `"Completed"`, and `dueDate` (YYYY-MM-DD) is optional. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_LOG_TASK", "connectorId": "V1StGXR8Z5", "subject": "Follow up after demo", "status": "Not Started", "description": "@@{FOLLOW_UP_NOTES}", "relatedModule": "Contacts", "relatedRecordId": "@@{ZOHO_CONTACT_ID}", "dueDate": "@@{DUE_DATE}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `subject` (string) — Task subject/title (e.g. "Trade Show Visit") - `status` (string) — Task status (e.g. "Not Started", "In Progress", "Completed") - `description` (string · optional) — Optional: task description/notes - `relatedModule` (string) — Module to associate with (e.g. "Contacts", "Accounts") - `relatedRecordId` (string) — Zoho record ID to associate this task with - `dueDate` (string · optional) — Optional: due date in YYYY-MM-DD format ## Get a record **TOOL** `ZOHO_CRM_GET_RECORD` Fetches any record from any module by ID — the escape hatch for modules without a dedicated tool, such as Deals, Leads, or custom modules. Returns the full record. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_GET_RECORD", "connectorId": "V1StGXR8Z5", "module": "Deals", "recordId": "@@{ZOHO_DEAL_ID}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `module` (string) — Zoho CRM module (e.g. "Contacts", "Accounts", "Deals", "Leads") - `recordId` (string) — Zoho record ID ## Search records **TOOL** `ZOHO_CRM_SEARCH_RECORDS` Searches any module with Zoho's criteria syntax — e.g. `(Email:equals:jane@example.com)` or `(Account_Name:starts_with:Acme)` — or plain text, which falls back to a word search. Use it whenever the dedicated contact and account search tools don't cover the module or the filter you need. ### Example ```json title="Tool config" { "toolType": "ZOHO_CRM_SEARCH_RECORDS", "connectorId": "V1StGXR8Z5", "module": "Leads", "criteria": "(Email:equals:@@{LEAD_EMAIL})", "limit": 20 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `module` (string) — Zoho CRM module (e.g. "Contacts", "Accounts", "Deals", "Leads") - `criteria` (string · optional) — Optional: Zoho search criteria (e.g. "(Email:equals:test)") or free-text word search - `limit` (integer · optional) — Optional: max results per page (default 20, max 200) - `page` (integer · optional) — Optional: page number (1-based) ## Related - [Tools overview](/docs/tools.md) - [Connectors](/docs/guide/connectors.md) - [Agentic tools guide](/docs/guide/agentic/tools.md) - [HubSpot tools](/docs/tools/hubspot.md) - [Acumatica tools](/docs/tools/acumatica.md) --- # Acumatica Source: https://docs.autessa.com/docs/tools/acumatica > Tool types for reading and writing Acumatica ERP entities, working with attachments, and invoking business actions. # Acumatica A generic wrapper over Acumatica's contract-based REST API: agents discover what entities and actions your instance exposes, then read, upsert, attach, and act on any of them — sales orders, customers, invoices, or your own customizations — without a per-entity tool. Every tool on this page requires an Acumatica connector (see [Connectors](/docs/guide/connectors.md)); pass its ID as `connectorId`. Unlike most connectors, these tools aren't hard-coded to specific objects. Acumatica publishes its API as *endpoints* (named contract versions like `Default` `24.200.001`) containing *entities* (`SalesOrder`, `Customer`, `Bill`, ...), and every tool takes the same addressing trio: `endpointName` (defaults to `"Default"`), `endpointVersion`, and `entityName`. The intended flow is discovery first: `ACUMATICA_GET_ENTITIES` to see what exists, `ACUMATICA_GET_ENTITY_SCHEMA` to learn an entity's exact field names and shapes, then the read/write/action tools. Record payloads use Acumatica's value-wrapper format — `{"OrderType": {"value": "SO"}}` — which the schema tool's output demonstrates. ## Get entities **TOOL** `ACUMATICA_GET_ENTITIES` Lists every entity exposed by an endpoint, parsed from the endpoint's swagger definition. This is the starting point when an agent doesn't yet know what the instance calls something — run it once, then drill into a specific entity with the schema tool. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_GET_ENTITIES", "connectorId": "V1StGXR8Z5", "endpointVersion": "24.200.001" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") ## Get entity schema **TOOL** `ACUMATICA_GET_ENTITY_SCHEMA` Returns an LLM-friendly template of an entity's fields, built from Acumatica's ad-hoc schema plus (by default) a sample record for realistic example values. Deeply nested structures are truncated at `maxDepth` with a hint telling the agent to re-run with `focusPath` (e.g. `"Details.Allocations"`) to go deeper. Always consult this before `ACUMATICA_UPSERT_RECORD` — field names are instance-specific and case-sensitive. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_GET_ENTITY_SCHEMA", "connectorId": "V1StGXR8Z5", "entityName": "SalesOrder", "endpointVersion": "24.200.001", "maxDepth": 3 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string) — Required: Name of the entity (e.g., "SalesOrder", "Customer") - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `includeSampleData` (boolean · optional) — Optional: Fetch sample record for examples (default: true) - `maxDepth` (integer · optional) — Optional: Max nesting depth for schema output (default: 3) - `focusPath` (string · optional) — Optional: Drill down into nested path (e.g., "Details.Allocations") ## Get records **TOOL** `ACUMATICA_GET_RECORD` Queries an entity with the standard OData toolkit: `filter` (e.g. `"CustomerID eq 'CUST001' and Status eq 'Open'"`), `select` to limit fields, `expand` to include nested detail entities, `orderBy`, and `top`/`skip` pagination (default 10 records). Errors come back with suggestions that steer the agent toward the schema tool when a field name is wrong. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_GET_RECORD", "connectorId": "V1StGXR8Z5", "entityName": "SalesOrder", "endpointVersion": "24.200.001", "filter": "CustomerID eq '@@{CUSTOMER_ID}' and Status eq 'Open'", "select": "OrderNbr,CustomerID,OrderTotal,Status", "orderBy": "OrderNbr desc", "top": 10 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string) — Required: Name of the entity (e.g., "SalesOrder", "Customer") - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `filter` (string · optional) — Optional: OData filter (e.g., "CustomerID eq 'CUST001' and Status eq 'Open'") - `select` (string · optional) — Optional: Fields to return (e.g., "OrderNbr,CustomerID,OrderTotal") - `expand` (string · optional) — Optional: Nested entities to expand (e.g., "Details,Payments") - `orderBy` (string · optional) — Optional: Sort order (e.g., "OrderNbr desc" or "Date asc,CustomerID asc") - `top` (integer · optional) — Optional: Max records to return (default: 10) - `skip` (integer · optional) — Optional: Records to skip for pagination ## Upsert a record **TOOL** `ACUMATICA_UPSERT_RECORD` Creates or updates a record using Acumatica's PUT semantics: if the key fields in `record` match an existing record it's updated, otherwise a new one is created. `record` is a JSON string in Acumatica's value-wrapper format and supports `@@{variable}` replacement inside the string. Get the field names right with `ACUMATICA_GET_ENTITY_SCHEMA` first — validation errors return targeted suggestions so the agent can self-correct. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_UPSERT_RECORD", "connectorId": "V1StGXR8Z5", "entityName": "Customer", "endpointVersion": "24.200.001", "record": "{\"CustomerID\": {\"value\": \"@@{CUSTOMER_ID}\"}, \"CustomerName\": {\"value\": \"@@{CUSTOMER_NAME}\"}, \"CustomerClass\": {\"value\": \"DEFAULT\"}}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string) — Required: Name of the entity (e.g., "SalesOrder", "Customer") - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `record` (string) — Required: JSON string of record data (supports @@{var} replacement) ## Get attachments **TOOL** `ACUMATICA_GET_ATTACHMENTS` Lists the files attached to a specific record. `keyId` is the record's key values joined with `/` — `"SO/000123"` for a sales order (order type then number), `"Normal/000029"` for a bill. Returns filenames the agent can pass to `ACUMATICA_DOWNLOAD_ATTACHMENT`. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_GET_ATTACHMENTS", "connectorId": "V1StGXR8Z5", "entityName": "SalesOrder", "endpointVersion": "24.200.001", "keyId": "SO/@@{ORDER_NUMBER}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string) — Required: Name of the entity (e.g., "SalesOrder", "PurchaseOrder") - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `keyId` (string) — Required: Entity key values (e.g., "SO/000123" or "Normal/000029") ## Download an attachment **TOOL** `ACUMATICA_DOWNLOAD_ATTACHMENT` Downloads one attachment from a record and extracts its content so the agent can read it — a PDF invoice or a spec sheet becomes text the model can work with. `filename` must match the attachment exactly (case-sensitive), so list attachments first with `ACUMATICA_GET_ATTACHMENTS`. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_DOWNLOAD_ATTACHMENT", "connectorId": "V1StGXR8Z5", "entityName": "SalesOrder", "endpointVersion": "24.200.001", "keyId": "SO/@@{ORDER_NUMBER}", "filename": "@@{ATTACHMENT_FILENAME}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string) — Required: Name of the entity (e.g., "SalesOrder", "PurchaseOrder") - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `keyId` (string) — Required: Entity key values (e.g., "SO/000123" or "Normal/000029") - `filename` (string) — Required: Exact filename to download (case-sensitive) ## Get actions **TOOL** `ACUMATICA_GET_ACTIONS` Discovers the business actions an endpoint exposes — release, confirm shipment, email invoice, and so on — parsed from the endpoint's swagger definition. Filter to one entity with `entityName`, or match action names with the case-insensitive `filter`. Run this before `ACUMATICA_INVOKE_ACTION` to get the exact action name and its parameters. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_GET_ACTIONS", "connectorId": "V1StGXR8Z5", "entityName": "SalesOrder", "endpointVersion": "24.200.001" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string · optional) — Optional: Filter to actions for specific entity only - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `filter` (string · optional) — Optional: Filter action names (case-insensitive contains match) ## Invoke an action **TOOL** `ACUMATICA_INVOKE_ACTION` Executes a business action against a record — the write-side of ERP workflows, like `ReleaseSalesOrder`. `entity` is a JSON string holding the record's key fields in value-wrapper format, and `parameters` carries any action parameters the same way. Long-running actions are polled automatically for up to 10 seconds; if the action is still in progress the response returns `status: "PENDING"` with an `operationUrl` for `ACUMATICA_GET_ACTION_STATUS` to poll later. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_INVOKE_ACTION", "connectorId": "V1StGXR8Z5", "entityName": "SalesOrder", "actionName": "ReleaseSalesOrder", "endpointVersion": "24.200.001", "entity": "{\"OrderType\": {\"value\": \"SO\"}, \"OrderNbr\": {\"value\": \"@@{ORDER_NUMBER}\"}}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `entityName` (string) — Required: Name of the entity (e.g., "SalesOrder") - `actionName` (string) — Required: Name of the action (e.g., "ReleaseSalesOrder") - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `endpointVersion` (string) — Required: API version (e.g., "24.200.001") - `entity` (string) — Required: JSON object with entity key fields in Acumatica format - `parameters` (string · optional) — Optional: JSON object with action parameters in Acumatica format ## Get action status **TOOL** `ACUMATICA_GET_ACTION_STATUS` Polls a long-running action that `ACUMATICA_INVOKE_ACTION` left in `PENDING`, using the `operationUrl` from that response. Reports whether the operation has completed, is still running, or failed. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_GET_ACTION_STATUS", "connectorId": "V1StGXR8Z5", "operationUrl": "@@{OPERATION_URL}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `operationUrl` (string) — Required: The Location URL from AcumaticaInvokeAction 202 response ## Raw API call **TOOL** `ACUMATICA_RAW_API_CALL` The escape hatch: calls any Acumatica REST endpoint directly with the connector's authentication and returns the complete response — status, headers, and raw body. `path` can be a short form like `"/entity/SalesOrder"` (completed using `endpointName`/`endpointVersion`) or a full path. Supports GET, POST, PUT, and DELETE, so reserve it for cases the typed tools don't cover. ### Example ```json title="Tool config" { "toolType": "ACUMATICA_RAW_API_CALL", "connectorId": "V1StGXR8Z5", "method": "GET", "path": "/entity/SalesOrder", "endpointVersion": "24.200.001" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required: Reference to the Acumatica connector References a connector by ID. - `method` (string) — Required: HTTP method (GET, POST, PUT, DELETE) - `path` (string) — Required: API path (e.g., "/entity/SalesOrder" or full path like "/entity/Default/24.200.001/SalesOrder") - `endpointVersion` (string · optional) — Optional: API version (e.g., "24.200.001") - used to build path if not fully specified - `endpointName` (string · optional) — Optional: Endpoint name, defaults to "Default" - `body` (string · optional) — Optional: JSON body for POST/PUT requests ## Related - [Tools overview](/docs/tools.md) - [Connectors](/docs/guide/connectors.md) - [Agentic tools guide](/docs/guide/agentic/tools.md) - [QuickBooks tools](/docs/tools/quickbooks.md) - [Zoho CRM tools](/docs/tools/zoho-crm.md) --- # QuickBooks Source: https://docs.autessa.com/docs/tools/quickbooks > Overview of the QuickBooks tool type families: accounting, reports, payments processing, and multi-step workflows. # QuickBooks The QuickBooks Online connector exposes the largest tool family on the platform — 96 tool types covering the full accounting surface (customers, invoices, bills, items, accounts, reports), the QuickBooks Payments API for real card and eCheck processing, and a set of composite workflow tools that collapse common multi-step sequences into a single agent call. ## Connector setup Every QuickBooks tool takes a `connectorId` — the ID of a QuickBooks connector your company has authorized. Connectors are created from the connectors page via Intuit's OAuth flow: you pick the QuickBooks company (realm) to link, grant access, and Autessa stores and refreshes the tokens for you. Tool executions never see credentials; they reference the connector by ID and the platform signs each API call. One connector maps to one QuickBooks company. If your organization runs several QuickBooks companies, create one connector per company and point each tool config at the right `connectorId`. See [Connectors](/docs/guide/connectors.md) for the general connector lifecycle. ## Sandbox vs production A QuickBooks connector is flagged as sandbox or production when it is authorized, based on the Intuit company you connect. The flag switches every call the connector makes: - **Accounting API** — `https://quickbooks.api.intuit.com` in production, `https://sandbox-quickbooks.api.intuit.com` for sandbox companies. - **Payments API** — `https://api.intuit.com/quickbooks/v4/payments` in production, `https://sandbox.api.intuit.com/quickbooks/v4/payments` for sandbox. Tool configs are identical in both modes — swap the `connectorId` to move an agent from a sandbox company to the live one. Build and test agents against an Intuit sandbox company first; sandbox Payments calls accept Intuit's test card and bank numbers without moving real money. ## The tool families | Subpage | What's on it | Types | | --- | --- | --- | | [Sales & AR](/docs/tools/quickbooks/sales-ar.md) | Customers, invoices, accounting-side payments, estimates | 19 | | [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) | Vendors, bills, bill payments, purchase orders | 15 | | [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) | Products/services, chart of accounts, company info, preferences | 9 | | [Reports](/docs/tools/quickbooks/reports.md) | P&L, balance sheet, cash flow, aging, balances, transaction list | 8 | | [Payments](/docs/tools/quickbooks/payments.md) | Payments API: tokenize, charge, refund cards and eChecks, plus payment-method composites | 16 | | [Workflows](/docs/tools/quickbooks/workflows.md) | Composite tools: create-and-send, search, get-or-create, summaries, PDF and attachment operations | 29 | ## Composite workflow tools Most tool types map one-to-one onto a QuickBooks API operation. The composite types on the [Workflows](/docs/tools/quickbooks/workflows.md) and [Payments](/docs/tools/quickbooks/payments.md) pages instead chain several operations behind a single tool call — `QUICKBOOKS_CREATE_AND_SEND_INVOICE` creates the invoice and emails it, `QUICKBOOKS_GET_OR_CREATE_CUSTOMER` searches by email and only creates on a miss, `QUICKBOOKS_CHARGE_AND_RECORD_PAYMENT` charges a card and books the accounting entry. Composites exist because each step an agent has to plan itself is a chance to mis-sequence it. Prefer a composite when one matches the task; reach for the granular types when the agent needs control between steps. ## Accounting records vs the Payments API Two very different things both get called "payments": - **Accounting-side payment records** (`QUICKBOOKS_CREATE_PAYMENT`, `QUICKBOOKS_CREATE_BILL_PAYMENT`, on the [Sales & AR](/docs/tools/quickbooks/sales-ar.md) and [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) pages) record that money moved — they update ledgers and invoice balances but move no funds. - **The [Payments](/docs/tools/quickbooks/payments.md) family** drives the QuickBooks Payments API, which actually processes card charges and ACH eChecks. It requires a QuickBooks Payments merchant account on the connected company, and its operations move real money in production. The `*_AND_RECORD_*` composites bridge the two: process the money movement, then write the matching accounting record in one call. ## Related - [Tools overview](/docs/tools.md) - [Connectors](/docs/guide/connectors.md) - [Sales & AR](/docs/tools/quickbooks/sales-ar.md) - [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) - [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) - [Reports](/docs/tools/quickbooks/reports.md) - [Payments](/docs/tools/quickbooks/payments.md) - [Workflows](/docs/tools/quickbooks/workflows.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Sales & AR Source: https://docs.autessa.com/docs/tools/quickbooks/sales-ar > QuickBooks tool types for customers, invoices, payments, and estimates. # Sales & AR Tool types for the accounts-receivable side of QuickBooks: creating and managing customers, invoices, accounting-side payment records, and estimates. Every type on this page requires a [QuickBooks connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Note that `QUICKBOOKS_CREATE_PAYMENT` records money movement in the ledger without processing anything — to actually charge a card or bank account, see the [Payments](/docs/tools/quickbooks/payments.md) family. See the [QuickBooks overview](/docs/tools/quickbooks.md) for connector setup and sandbox behavior. ## Create customer **TOOL** `QUICKBOOKS_CREATE_CUSTOMER` Creates a customer record. Only `displayName` is required — it must be unique across customers, vendors, and employees in the QuickBooks company, so creation fails on a duplicate name. If the customer may already exist, prefer `QUICKBOOKS_GET_OR_CREATE_CUSTOMER` on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_CUSTOMER", "connectorId": "V1StGXR8Z5", "displayName": "@@{customerName}", "email": "@@{customerEmail}", "phone": "@@{customerPhone}", "companyName": "@@{companyName}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `displayName` (string) — Required - Display name - `givenName` (string) — First name - `familyName` (string) — Last name - `companyName` (string) — Company name - `email` (string) — Primary email - `phone` (string) — Primary phone - `mobile` (string) — Mobile phone - `billingAddress` (object) — Billing address - `shippingAddress` (object) — Shipping address - `notes` (string) — Customer notes - `taxable` (boolean) — Is customer taxable - `balance` (number) — Opening balance ## Update customer **TOOL** `QUICKBOOKS_UPDATE_CUSTOMER` Updates an existing customer identified by `customerId`. Set `sparseUpdate` to true so only the fields you supply change — without it, QuickBooks treats the update as a full replacement and clears omitted fields. Setting `active` to false soft-deletes the customer. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPDATE_CUSTOMER", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "email": "@@{newEmail}", "sparseUpdate": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Required - QuickBooks customer ID - `displayName` (string) — Required string value. - `givenName` (string) — Required string value. - `familyName` (string) — Required string value. - `companyName` (string) — Required string value. - `email` (string) — Required string value. - `phone` (string) — Required string value. - `mobile` (string) — Required string value. - `billingAddress` (object) — Billing address - `shippingAddress` (object) — Shipping address - `notes` (string) — Required string value. - `active` (boolean) — Required boolean value. - `sparseUpdate` (boolean) — If true, only update provided fields ## Get customers **TOOL** `QUICKBOOKS_GET_CUSTOMERS` Lists customers with pagination (`maxResults`, `startPosition`) and an optional SQL-like `whereClause` filter such as `Balance > '0'`. This is how an agent discovers the `customerId` the other Sales & AR tools need; for targeted lookups, the name and email search composites on the [Workflows](/docs/tools/quickbooks/workflows.md) page are simpler. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_CUSTOMERS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "activeOnly": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string · optional) — Optional SQL-like where clause - `orderBy` (string · optional) — Optional order by clause - `activeOnly` (boolean) — Filter to only active customers ## Get customer by ID **TOOL** `QUICKBOOKS_GET_CUSTOMER_BY_ID` Fetches a single customer's full record — contact details, addresses, balance, and the `SyncToken` that update and delete operations require. Use it to confirm details before writing invoices against the customer. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_CUSTOMER_BY_ID", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — QuickBooks customer ID ## Create invoice **TOOL** `QUICKBOOKS_CREATE_INVOICE` Creates an invoice for a customer. `lines` is an array of QuickBooks sales lines — each needs a `DetailType` of `SalesItemLineDetail`, an `Amount`, and a `SalesItemLineDetail` object referencing an item by ID. Creating an invoice does not email it; pair with `QUICKBOOKS_SEND_INVOICE`, or use `QUICKBOOKS_CREATE_AND_SEND_INVOICE` to do both in one call. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_INVOICE", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "lines": [ { "DetailType": "SalesItemLineDetail", "Amount": 450.00, "Description": "Consulting services", "SalesItemLineDetail": { "ItemRef": { "value": "12" }, "Qty": 3, "UnitPrice": 150.00 } } ], "dueDate": "@@{dueDate}", "customerMemo": "Thank you for your business." } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Required - Customer reference - `lines` (array) — Required - Line items array - `txnDate` (string) — Transaction date (YYYY-MM-DD) - `dueDate` (string) — Due date (YYYY-MM-DD) - `docNumber` (string) — Invoice number - `customerMemo` (string) — Memo visible to customer - `privateNote` (string) — Internal note - `billingEmail` (string) — Email for billing - `billingAddress` (object) — Required object value. - `shippingAddress` (object) — Required object value. - `applyTaxAfterDiscount` (boolean) — Required boolean value. - `salesTermId` (string) — Payment terms reference ## Update invoice **TOOL** `QUICKBOOKS_UPDATE_INVOICE` Updates an existing invoice — due date, memo, line items, or customer reference. As with customers, pass `sparseUpdate: true` to change only the supplied fields; supplying `lines` replaces the entire line array, not individual lines. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPDATE_INVOICE", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}", "dueDate": "@@{newDueDate}", "sparseUpdate": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Required - `customerId` (string) — Required string value. - `lines` (array) — Required array value. - `txnDate` (string) — Required string value. - `dueDate` (string) — Required string value. - `docNumber` (string) — Required string value. - `customerMemo` (string) — Required string value. - `privateNote` (string) — Required string value. - `billingEmail` (string) — Required string value. - `sparseUpdate` (boolean) — Required boolean value. ## Send invoice **TOOL** `QUICKBOOKS_SEND_INVOICE` Emails an existing invoice through QuickBooks' own delivery system, using the QuickBooks company's branding and payment links. `emailTo` overrides the recipient; when omitted, the invoice goes to the customer's billing email on file. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SEND_INVOICE", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}", "emailTo": "@@{recipientEmail}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Required - `emailTo` (string · optional) — Email address to send to (optional, uses customer email if not provided) ## Get invoices **TOOL** `QUICKBOOKS_GET_INVOICES` Lists invoices with pagination and filters by customer, transaction-date range, or a raw `whereClause` (e.g. `Balance > '0'` for open invoices). Combine with an `orderBy` such as `TxnDate DESC` to surface the most recent activity first. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_INVOICES", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "customerId": "@@{customerId}", "whereClause": "Balance > '0'" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `orderBy` (string) — Required string value. - `customerId` (string) — Filter by customer ID - `startDate` (string) — Filter by transaction date (YYYY-MM-DD) - `endDate` (string) — Filter by transaction date (YYYY-MM-DD) ## Get invoice by ID **TOOL** `QUICKBOOKS_GET_INVOICE_BY_ID` Fetches one invoice in full: line items, balance, linked payments, and the `SyncToken` needed for void and delete. For the invoice plus its customer's contact details in one call, use `QUICKBOOKS_GET_INVOICE_WITH_CUSTOMER` on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_INVOICE_BY_ID", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Required string value. ## Void invoice **TOOL** `QUICKBOOKS_VOID_INVOICE` Voids an invoice — it stays visible in QuickBooks with a zeroed amount and a Voided status, preserving the audit trail. Requires the invoice's current `syncToken` (fetch it with a get-by-ID call first); a stale token is rejected by QuickBooks' optimistic locking. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_VOID_INVOICE", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}", "syncToken": "@@{syncToken}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Required - `syncToken` (string) — Required for optimistic locking ## Delete invoice **TOOL** `QUICKBOOKS_DELETE_INVOICE` Permanently deletes an invoice from the QuickBooks company. Like void, it requires the current `syncToken`, but unlike void the record is removed entirely — prefer voiding when the paper trail matters. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_DELETE_INVOICE", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}", "syncToken": "@@{syncToken}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Required - `syncToken` (string) — Required for optimistic locking ## Create payment **TOOL** `QUICKBOOKS_CREATE_PAYMENT` Records a customer payment in the ledger — it reduces invoice balances and books the deposit but moves no money. `invoiceLines` links the payment to specific invoices via `LinkedTxn` entries. To actually charge a card and record the payment together, use `QUICKBOOKS_CHARGE_AND_RECORD_PAYMENT` on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_PAYMENT", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "totalAmount": 450.00, "depositToAccountId": "35", "invoiceLines": [ { "Amount": 450.00, "LinkedTxn": [{ "TxnId": "@@{invoiceId}", "TxnType": "Invoice" }] } ], "paymentRefNum": "@@{referenceNumber}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Required - Customer reference - `totalAmount` (number) — Required - Total payment amount - `txnDate` (string) — Transaction date (YYYY-MM-DD) - `paymentMethodId` (string) — Payment method reference - `depositToAccountId` (string) — Deposit to account reference - `invoiceLines` (array) — Invoice references to apply payment - `paymentRefNum` (string) — Payment reference number (check number, etc.) - `privateNote` (string) — Required string value. ## Get payments **TOOL** `QUICKBOOKS_GET_PAYMENTS` Lists received-payment records with pagination, filtered by customer, date range, or a raw `whereClause`. These are the accounting-side records — Payments API charges only appear here if something recorded them (for example the `*_AND_RECORD_PAYMENT` composites). ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_PAYMENTS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "customerId": "@@{customerId}", "startDate": "@@{startDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `customerId` (string) — Required string value. - `startDate` (string) — Required string value. - `endDate` (string) — Required string value. ## Get payment by ID **TOOL** `QUICKBOOKS_GET_PAYMENT_BY_ID` Fetches a single payment record, including the invoices it was applied to and the `SyncToken` a void requires. Useful when reconciling which invoices a specific payment covered. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_PAYMENT_BY_ID", "connectorId": "V1StGXR8Z5", "paymentId": "@@{paymentId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `paymentId` (string) — Required string value. ## Void payment **TOOL** `QUICKBOOKS_VOID_PAYMENT` Voids a payment record, restoring the balances of any invoices it was applied to. Requires the payment's current `syncToken`. This only reverses the ledger entry — refunding money actually charged through the Payments API is `QUICKBOOKS_REFUND_CHARGE` on the [Payments](/docs/tools/quickbooks/payments.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_VOID_PAYMENT", "connectorId": "V1StGXR8Z5", "paymentId": "@@{paymentId}", "syncToken": "@@{syncToken}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `paymentId` (string) — Required string value. - `syncToken` (string) — Required string value. ## Create estimate **TOOL** `QUICKBOOKS_CREATE_ESTIMATE` Creates an estimate (quote) for a customer using the same sales-line shape as invoices. Estimates don't post to the ledger; when the customer accepts, `QUICKBOOKS_CONVERT_ESTIMATE_TO_INVOICE` on the [Workflows](/docs/tools/quickbooks/workflows.md) page turns it into a billable invoice. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_ESTIMATE", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "lines": [ { "DetailType": "SalesItemLineDetail", "Amount": 1200.00, "Description": "@@{lineDescription}", "SalesItemLineDetail": { "ItemRef": { "value": "12" }, "Qty": 8, "UnitPrice": 150.00 } } ], "expirationDate": "@@{expirationDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Required - `lines` (array) — Required - Line items - `txnDate` (string) — Transaction date - `expirationDate` (string) — Expiration date - `docNumber` (string) — Estimate number - `customerMemo` (string) — Required string value. - `privateNote` (string) — Required string value. - `billingEmail` (string) — Required string value. - `billingAddress` (object) — Required object value. - `shippingAddress` (object) — Required object value. ## Send estimate **TOOL** `QUICKBOOKS_SEND_ESTIMATE` Emails an estimate through QuickBooks' delivery system. `emailTo` overrides the recipient; when omitted, the estimate goes to the customer's email on file. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SEND_ESTIMATE", "connectorId": "V1StGXR8Z5", "estimateId": "@@{estimateId}", "emailTo": "@@{recipientEmail}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `estimateId` (string) — Required - `emailTo` (string · optional) — Optional - uses customer email if not provided ## Get estimates **TOOL** `QUICKBOOKS_GET_ESTIMATES` Lists estimates with pagination, filtered by customer, transaction-date range, or a raw `whereClause`. Handy for finding outstanding quotes that haven't converted to invoices yet. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ESTIMATES", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "customerId": "@@{customerId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `customerId` (string) — Required string value. - `startDate` (string) — Required string value. - `endDate` (string) — Required string value. ## Get estimate by ID **TOOL** `QUICKBOOKS_GET_ESTIMATE_BY_ID` Fetches a single estimate in full — line items, status, expiration, and linked transactions. Use it to review a quote's contents before converting or re-sending it. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ESTIMATE_BY_ID", "connectorId": "V1StGXR8Z5", "estimateId": "@@{estimateId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `estimateId` (string) — Required string value. ## Related - [Tools overview](/docs/tools.md) - [QuickBooks overview](/docs/tools/quickbooks.md) - [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) - [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) - [Reports](/docs/tools/quickbooks/reports.md) - [Payments](/docs/tools/quickbooks/payments.md) - [Workflows](/docs/tools/quickbooks/workflows.md) - [Connectors](/docs/guide/connectors.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Purchasing & AP Source: https://docs.autessa.com/docs/tools/quickbooks/purchasing-ap > QuickBooks tool types for vendors, bills, and purchase orders. # Purchasing & AP Tool types for the accounts-payable side of QuickBooks: vendors, bills, bill payments, and purchase orders. Every type on this page requires a [QuickBooks connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Bill payments here are accounting records (check or credit-card entries in the ledger) — QuickBooks' Payments API on the [Payments](/docs/tools/quickbooks/payments.md) page is receivables-only and plays no role in paying vendors. See the [QuickBooks overview](/docs/tools/quickbooks.md) for connector setup and sandbox behavior. ## Create vendor **TOOL** `QUICKBOOKS_CREATE_VENDOR` Creates a vendor (supplier) record. Only `displayName` is required and it must be unique across customers, vendors, and employees. Set `vendor1099` and `taxIdentifier` for contractors that need 1099 reporting; if the vendor may already exist, prefer `QUICKBOOKS_GET_OR_CREATE_VENDOR` on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_VENDOR", "connectorId": "V1StGXR8Z5", "displayName": "@@{vendorName}", "companyName": "@@{vendorCompany}", "primaryEmailAddr": "@@{vendorEmail}", "vendor1099": false } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `displayName` (string) — Required - `givenName` (string) — Required string value. - `familyName` (string) — Required string value. - `companyName` (string) — Required string value. - `primaryEmailAddr` (string) — Required string value. - `primaryPhone` (string) — Required string value. - `mobile` (string) — Required string value. - `billAddr` (object) — Billing address - `webAddr` (string) — Required string value. - `taxIdentifier` (string) — Tax ID for 1099 reporting - `acctNum` (string) — Account number - `vendor1099` (boolean) — Is 1099 vendor ## Update vendor **TOOL** `QUICKBOOKS_UPDATE_VENDOR` Updates an existing vendor identified by `vendorId` — contact details, addresses, 1099 status, or account number. Setting `active` to false soft-deletes the vendor while preserving its transaction history. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPDATE_VENDOR", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "primaryEmailAddr": "@@{newEmail}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - `displayName` (string) — Required string value. - `givenName` (string) — Required string value. - `familyName` (string) — Required string value. - `companyName` (string) — Required string value. - `primaryEmailAddr` (string) — Required string value. - `primaryPhone` (string) — Required string value. - `mobile` (string) — Required string value. - `billAddr` (object) — Billing address - `webAddr` (string) — Required string value. - `taxIdentifier` (string) — Required string value. - `acctNum` (string) — Required string value. - `vendor1099` (boolean) — Required boolean value. - `active` (boolean) — Required boolean value. ## Get vendors **TOOL** `QUICKBOOKS_GET_VENDORS` Lists vendors with pagination, an optional SQL-like `whereClause`, and an `orderBy`. This is how an agent discovers the `vendorId` the bill and purchase-order tools need; `QUICKBOOKS_SEARCH_VENDOR_BY_NAME` on the [Workflows](/docs/tools/quickbooks/workflows.md) page handles fuzzy name lookups. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_VENDORS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "activeOnly": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `orderBy` (string) — Required string value. - `activeOnly` (boolean) — Required boolean value. ## Get vendor by ID **TOOL** `QUICKBOOKS_GET_VENDOR_BY_ID` Fetches a single vendor's full record — contact details, balance owed, 1099 status, and the `SyncToken` updates require. Use it to confirm details before entering bills against the vendor. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_VENDOR_BY_ID", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required string value. ## Create bill **TOOL** `QUICKBOOKS_CREATE_BILL` Enters a bill (a vendor invoice you owe) into accounts payable. `lines` is an array of expense lines — use `DetailType` of `AccountBasedExpenseLineDetail` with an `AccountRef` to book against an expense account, or `ItemBasedExpenseLineDetail` for item purchases. Creating a bill doesn't pay it; that's `QUICKBOOKS_CREATE_BILL_PAYMENT`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_BILL", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "lines": [ { "DetailType": "AccountBasedExpenseLineDetail", "Amount": 1200.00, "Description": "@@{expenseDescription}", "AccountBasedExpenseLineDetail": { "AccountRef": { "value": "7" } } } ], "dueDate": "@@{dueDate}", "docNumber": "@@{billNumber}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - VendorRef - `lines` (array) — Required - Line items - `txnDate` (string) — Transaction date - `dueDate` (string) — Due date - `docNumber` (string) — Bill number/reference - `privateNote` (string) — Internal memo - `apAccountId` (string) — Accounts Payable account ## Update bill **TOOL** `QUICKBOOKS_UPDATE_BILL` Updates an existing bill identified by `billId` — due date, line items, memo, or vendor reference. Supplying `lines` replaces the whole line array, so include every line the bill should end up with. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPDATE_BILL", "connectorId": "V1StGXR8Z5", "billId": "@@{billId}", "dueDate": "@@{newDueDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `billId` (string) — Required - `vendorId` (string) — Required string value. - `lines` (array) — Bill line items - `txnDate` (string) — Required string value. - `dueDate` (string) — Required string value. - `docNumber` (string) — Required string value. - `privateNote` (string) — Required string value. ## Delete bill **TOOL** `QUICKBOOKS_DELETE_BILL` Permanently deletes a bill. Requires the bill's current `syncToken` (fetch it with `QUICKBOOKS_GET_BILL_BY_ID` first) for QuickBooks' optimistic locking, and fails if bill payments are already linked to the bill. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_DELETE_BILL", "connectorId": "V1StGXR8Z5", "billId": "@@{billId}", "syncToken": "@@{syncToken}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `billId` (string) — Required - `syncToken` (string) — Required for delete ## Get bills **TOOL** `QUICKBOOKS_GET_BILLS` Lists bills with pagination, filtered by vendor, transaction-date range, or a raw `whereClause` (e.g. `Balance > '0'` for unpaid bills). Use it to find what's outstanding before scheduling payments. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_BILLS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "whereClause": "Balance > '0'" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `vendorId` (string) — Required string value. - `startDate` (string) — Required string value. - `endDate` (string) — Required string value. ## Get bill by ID **TOOL** `QUICKBOOKS_GET_BILL_BY_ID` Fetches one bill in full: line items, balance, linked payments, and the `SyncToken` a delete requires. For the bill plus its vendor's contact details in one call, use `QUICKBOOKS_GET_BILL_WITH_VENDOR` on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_BILL_BY_ID", "connectorId": "V1StGXR8Z5", "billId": "@@{billId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `billId` (string) — Required string value. ## Create bill payment **TOOL** `QUICKBOOKS_CREATE_BILL_PAYMENT` Records a payment against one or more bills. `payType` is `Check` (requires `bankAccountId`) or `CreditCard` (requires `creditCardAccountId`), and `linkedBills` allocates the total across specific bills by `TxnId`. This books the ledger entry only — it doesn't print checks or move funds. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_BILL_PAYMENT", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "totalAmt": 1200.00, "payType": "Check", "bankAccountId": "35", "linkedBills": [ { "TxnId": "@@{billId}", "Amount": 1200.00 } ] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - VendorRef - `totalAmt` (number) — Required - Total amount - `payType` (string) — Required - "Check" or "CreditCard" - `bankAccountId` (string) — Required for Check payments - `checkNum` (string · optional) — Check number (optional) - `creditCardAccountId` (string) — Required for CreditCard payments - `txnDate` (string) — Transaction date - `privateNote` (string) — Internal memo - `linkedBills` (array) — Bills to pay with amounts ## Get bill payments **TOOL** `QUICKBOOKS_GET_BILL_PAYMENTS` Lists bill-payment records with pagination, filtered by vendor, date range, or a raw `whereClause`. Useful for reconciling what has already been paid to a vendor over a period. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_BILL_PAYMENTS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "vendorId": "@@{vendorId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `vendorId` (string) — Required string value. - `startDate` (string) — Required string value. - `endDate` (string) — Required string value. ## Get bill payment by ID **TOOL** `QUICKBOOKS_GET_BILL_PAYMENT_BY_ID` Fetches a single bill payment, including the bills it was applied to, the payment type, and the account it was drawn from. Use it to answer "how and when was this bill paid?" ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_BILL_PAYMENT_BY_ID", "connectorId": "V1StGXR8Z5", "billPaymentId": "@@{billPaymentId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `billPaymentId` (string) — Required string value. ## Create purchase order **TOOL** `QUICKBOOKS_CREATE_PURCHASE_ORDER` Creates a purchase order to send to a vendor. Line items use the same expense-line shapes as bills (`ItemBasedExpenseLineDetail` for goods, `AccountBasedExpenseLineDetail` for services); `vendorMemo` appears on the PO the vendor sees. POs are non-posting — they don't hit the ledger until converted to a bill. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_PURCHASE_ORDER", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "lines": [ { "DetailType": "ItemBasedExpenseLineDetail", "Amount": 500.00, "Description": "@@{itemDescription}", "ItemBasedExpenseLineDetail": { "ItemRef": { "value": "19" }, "Qty": 10, "UnitPrice": 50.00 } } ], "expectedDate": "@@{expectedDate}", "vendorMemo": "@@{vendorMemo}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - VendorRef - `lines` (array) — Required - Line items - `txnDate` (string) — Transaction date - `expectedDate` (string) — Expected delivery date - `docNumber` (string) — PO number - `vendorMemo` (string) — Memo for vendor - `privateNote` (string) — Internal memo - `shipAddr` (object) — Ship to address - `apAccountId` (string) — Accounts Payable account ## Update purchase order **TOOL** `QUICKBOOKS_UPDATE_PURCHASE_ORDER` Updates an existing purchase order — line items, expected date, memos, or ship-to address. Set `poStatus` to `Closed` once the order is fulfilled so it stops counting as open. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPDATE_PURCHASE_ORDER", "connectorId": "V1StGXR8Z5", "purchaseOrderId": "@@{purchaseOrderId}", "poStatus": "Closed" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `purchaseOrderId` (string) — Required - `vendorId` (string) — Required string value. - `lines` (array) — PO line items - `txnDate` (string) — Required string value. - `expectedDate` (string) — Required string value. - `docNumber` (string) — Required string value. - `vendorMemo` (string) — Required string value. - `privateNote` (string) — Required string value. - `shipAddr` (object) — Ship to address - `poStatus` (string) — Open, Closed ## Get purchase orders **TOOL** `QUICKBOOKS_GET_PURCHASE_ORDERS` Lists purchase orders with pagination, filtered by vendor, date range, `poStatus` (`Open` or `Closed`), or a raw `whereClause`. Use it to review outstanding orders before receiving goods or entering the matching bill. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_PURCHASE_ORDERS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "poStatus": "Open" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `vendorId` (string) — Required string value. - `startDate` (string) — Required string value. - `endDate` (string) — Required string value. - `poStatus` (string) — Open, Closed ## Related - [Tools overview](/docs/tools.md) - [QuickBooks overview](/docs/tools/quickbooks.md) - [Sales & AR](/docs/tools/quickbooks/sales-ar.md) - [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) - [Reports](/docs/tools/quickbooks/reports.md) - [Payments](/docs/tools/quickbooks/payments.md) - [Workflows](/docs/tools/quickbooks/workflows.md) - [Connectors](/docs/guide/connectors.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Items, Accounts & Company Source: https://docs.autessa.com/docs/tools/quickbooks/items-accounts-company > QuickBooks tool types for items, chart of accounts, and company info. # Items, Accounts & Company Tool types for the reference data everything else is built on: products and services (items), the chart of accounts, and read-only company info and preferences. Every type on this page requires a [QuickBooks connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Invoice lines reference items and expense lines reference accounts, so agents that create transactions usually carry one of the lookup tools from this page too. See the [QuickBooks overview](/docs/tools/quickbooks.md) for connector setup and sandbox behavior. ## Create item **TOOL** `QUICKBOOKS_CREATE_ITEM` Creates a product or service item. `type` is `Service`, `NonInventory`, or `Inventory` — sales items need an `incomeAccountId`, purchase items an `expenseAccountId`, and inventory items additionally need an `assetAccountId` plus `trackQtyOnHand` and a starting `qtyOnHand`. The created item's ID is what invoice and PO lines reference. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_ITEM", "connectorId": "V1StGXR8Z5", "name": "@@{itemName}", "type": "Service", "unitPrice": 150.00, "incomeAccountId": "45", "taxable": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `name` (string) — Required - `type` (string) — Required - Inventory, NonInventory, Service - `description` (string) — Required string value. - `unitPrice` (number) — Sales price - `purchaseCost` (number) — Purchase cost - `incomeAccountId` (string) — Required for sales items - `expenseAccountId` (string) — Required for purchase items - `assetAccountId` (string) — Required for inventory items - `trackQtyOnHand` (boolean) — Track quantity - `qtyOnHand` (number) — Starting quantity - `sku` (string) — SKU/Part number - `taxable` (boolean) — Is taxable ## Update item **TOOL** `QUICKBOOKS_UPDATE_ITEM` Updates an existing item's name, description, pricing, SKU, or tax flag. Setting `active` to false retires the item from pick lists without disturbing historical transactions that reference it. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPDATE_ITEM", "connectorId": "V1StGXR8Z5", "itemId": "@@{itemId}", "unitPrice": 175.00 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `itemId` (string) — Required - `name` (string) — Required string value. - `description` (string) — Required string value. - `unitPrice` (number) — Required number value. - `purchaseCost` (number) — Required number value. - `sku` (string) — Required string value. - `taxable` (boolean) — Required boolean value. - `active` (boolean) — Required boolean value. ## Get items **TOOL** `QUICKBOOKS_GET_ITEMS` Lists items with pagination, filtered by `itemType` (`Inventory`, `NonInventory`, `Service`, `Group`, ...) or a raw `whereClause`. This is how an agent finds the item IDs invoice lines need; `QUICKBOOKS_GET_ITEMS_FOR_INVOICE` on the [Workflows](/docs/tools/quickbooks/workflows.md) page adds fuzzy name search on top. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ITEMS", "connectorId": "V1StGXR8Z5", "maxResults": 50, "startPosition": 1, "itemType": "Service", "activeOnly": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `itemType` (string) — Inventory, NonInventory, Service, Group, etc. - `activeOnly` (boolean) — Required boolean value. ## Get item by ID **TOOL** `QUICKBOOKS_GET_ITEM_BY_ID` Fetches a single item's full record — pricing, linked income/expense/asset accounts, SKU, and quantity on hand for inventory items. Use it to verify pricing before quoting or invoicing. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ITEM_BY_ID", "connectorId": "V1StGXR8Z5", "itemId": "@@{itemId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `itemId` (string) — Required string value. ## Create account **TOOL** `QUICKBOOKS_CREATE_ACCOUNT` Adds an account to the chart of accounts. `accountType` (e.g. `Bank`, `Expense`, `Income`, `OtherCurrentAsset`) is required and pairs with an optional `accountSubType` like `Checking` or `AdvertisingPromotional`; `parentAccountId` nests it as a sub-account. Changing the chart of accounts affects reporting, so give this tool only to agents that are meant to restructure books. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_ACCOUNT", "connectorId": "V1StGXR8Z5", "name": "@@{accountName}", "accountType": "Expense", "accountSubType": "AdvertisingPromotional", "description": "@@{accountDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `name` (string) — Required - `accountType` (string) — Required - Bank, Expense, Income, OtherCurrentAsset, etc. - `accountSubType` (string) — Sub-type like Checking, Savings, AdvertisingPromotional, etc. - `description` (string) — Required string value. - `acctNum` (string) — Account number - `parentAccountId` (string) — For sub-accounts ## Get accounts **TOOL** `QUICKBOOKS_GET_ACCOUNTS` Lists the chart of accounts with pagination, filtered by `accountType`, `classification` (`Asset`, `Liability`, `Equity`, `Revenue`, `Expense`), or a raw `whereClause`. Agents use it to find the account IDs that bills, payments, and deposits reference. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ACCOUNTS", "connectorId": "V1StGXR8Z5", "maxResults": 100, "startPosition": 1, "classification": "Expense", "activeOnly": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `maxResults` (integer) — Required integer value. - `startPosition` (integer) — Required integer value. - `whereClause` (string) — Required string value. - `accountType` (string) — Bank, Expense, Income, etc. - `classification` (string) — Asset, Liability, Equity, Revenue, Expense - `activeOnly` (boolean) — Required boolean value. ## Get account by ID **TOOL** `QUICKBOOKS_GET_ACCOUNT_BY_ID` Fetches a single account's details — type, sub-type, current balance, and parent relationship. Handy for confirming an account is the right target before booking transactions against it. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ACCOUNT_BY_ID", "connectorId": "V1StGXR8Z5", "accountId": "@@{accountId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `accountId` (string) — Required string value. ## Get company info **TOOL** `QUICKBOOKS_GET_COMPANY_INFO` Returns the connected QuickBooks company's profile: legal name, addresses, fiscal-year start, and contact details. Takes only the `connectorId`, which also makes it a cheap way to verify a connector is alive and pointed at the right company. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_COMPANY_INFO", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. ## Get preferences **TOOL** `QUICKBOOKS_GET_PREFERENCES` Returns the company's QuickBooks settings — sales-tax configuration, multi-currency, class tracking, default terms, and email preferences. Agents should read this before assuming a feature is on; for example, tax-code references on invoice lines only apply when sales tax is enabled here. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_PREFERENCES", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. ## Related - [Tools overview](/docs/tools.md) - [QuickBooks overview](/docs/tools/quickbooks.md) - [Sales & AR](/docs/tools/quickbooks/sales-ar.md) - [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) - [Reports](/docs/tools/quickbooks/reports.md) - [Payments](/docs/tools/quickbooks/payments.md) - [Workflows](/docs/tools/quickbooks/workflows.md) - [Connectors](/docs/guide/connectors.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Reports Source: https://docs.autessa.com/docs/tools/quickbooks/reports > QuickBooks reporting tool types. # Reports Tool types that run QuickBooks' built-in reports: profit and loss, balance sheet, cash flow, receivables and payables aging, customer and vendor balances, and the raw transaction list. Every type on this page requires a [QuickBooks connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. All dates are `YYYY-MM-DD` strings, and where `accountingMethod` appears it's `Cash` or `Accrual`. Reports come back in QuickBooks' row/column report format rather than as entity lists — for pre-digested aggregates, see the summary composites on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ## Get profit and loss **TOOL** `QUICKBOOKS_GET_PROFIT_AND_LOSS` Runs the P&L (income statement) for a date range: income, cost of goods sold, expenses, and net income. `summarizeColumnsBy` splits the columns by `Month`, `Week`, and so on, or keeps a single `Total` column. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_PROFIT_AND_LOSS", "connectorId": "V1StGXR8Z5", "startDate": "@@{startDate}", "endDate": "@@{endDate}", "accountingMethod": "Accrual", "summarizeColumnsBy": "Month" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `startDate` (string) — Start date (YYYY-MM-DD) - `endDate` (string) — End date (YYYY-MM-DD) - `accountingMethod` (string) — Cash or Accrual - `summarizeColumnsBy` (string) — Total, Month, Week, etc. ## Get balance sheet **TOOL** `QUICKBOOKS_GET_BALANCE_SHEET` Runs the balance sheet as of `endDate`: assets, liabilities, and equity at that point in time. Like the P&L, columns can be summarized by period for trend views. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_BALANCE_SHEET", "connectorId": "V1StGXR8Z5", "endDate": "@@{asOfDate}", "accountingMethod": "Accrual", "summarizeColumnsBy": "Total" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `endDate` (string) — As of date (YYYY-MM-DD) - `accountingMethod` (string) — Cash or Accrual - `summarizeColumnsBy` (string) — Total, Month, Week, etc. ## Get cash flow **TOOL** `QUICKBOOKS_GET_CASH_FLOW` Runs the statement of cash flows for a date range, broken into operating, investing, and financing activities. Useful for agents answering "where did the money actually go?" as opposed to accrual-based P&L questions. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_CASH_FLOW", "connectorId": "V1StGXR8Z5", "startDate": "@@{startDate}", "endDate": "@@{endDate}", "summarizeColumnsBy": "Month" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `startDate` (string) — Start date (YYYY-MM-DD) - `endDate` (string) — End date (YYYY-MM-DD) - `summarizeColumnsBy` (string) — Total, Month, Week, etc. ## Get aged receivables **TOOL** `QUICKBOOKS_GET_AGED_RECEIVABLES` Runs the A/R aging report — who owes money and how overdue it is, bucketed into aging periods (`daysPerAgingPeriod` wide, `numPeriods` of them, defaulting to 4 x 30 days). Pass `customerId` to age a single customer's balance; this is the natural input for collections and dunning agents. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_AGED_RECEIVABLES", "connectorId": "V1StGXR8Z5", "reportDate": "@@{asOfDate}", "agingMethod": "Report_Date", "daysPerAgingPeriod": 30, "numPeriods": 4 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string) — As of date (YYYY-MM-DD) - `agingMethod` (string) — Report_Date or Current - `daysPerAgingPeriod` (integer) — Days per period (default 30) - `numPeriods` (integer) — Number of periods (default 4) - `customerId` (string) — Filter by specific customer ## Get aged payables **TOOL** `QUICKBOOKS_GET_AGED_PAYABLES` The A/P mirror of aged receivables — which vendors the company owes and how overdue each balance is, in the same configurable aging buckets. Pass `vendorId` to focus on one vendor before deciding what to pay first. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_AGED_PAYABLES", "connectorId": "V1StGXR8Z5", "reportDate": "@@{asOfDate}", "agingMethod": "Report_Date", "daysPerAgingPeriod": 30, "numPeriods": 4 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string) — As of date (YYYY-MM-DD) - `agingMethod` (string) — Report_Date or Current - `daysPerAgingPeriod` (integer) — Days per period (default 30) - `numPeriods` (integer) — Number of periods (default 4) - `vendorId` (string) — Filter by specific vendor ## Get customer balance **TOOL** `QUICKBOOKS_GET_CUSTOMER_BALANCE` Runs the customer balance report — each customer's open balance as of `reportDate`, without the aging breakdown. Filter with `customerId` for a quick "how much does this customer owe right now?" answer. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_CUSTOMER_BALANCE", "connectorId": "V1StGXR8Z5", "reportDate": "@@{asOfDate}", "accountingMethod": "Accrual", "customerId": "@@{customerId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string) — As of date (YYYY-MM-DD) - `accountingMethod` (string) — Cash or Accrual - `customerId` (string) — Filter by specific customer ## Get vendor balance **TOOL** `QUICKBOOKS_GET_VENDOR_BALANCE` Runs the vendor balance report — how much is owed to each vendor as of `reportDate`. Filter with `vendorId` to check a single vendor before approving new spend or a payment run. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_VENDOR_BALANCE", "connectorId": "V1StGXR8Z5", "reportDate": "@@{asOfDate}", "accountingMethod": "Accrual", "vendorId": "@@{vendorId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string) — As of date (YYYY-MM-DD) - `accountingMethod` (string) — Cash or Accrual - `vendorId` (string) — Filter by specific vendor ## Get transaction list **TOOL** `QUICKBOOKS_GET_TRANSACTION_LIST` Runs the transaction list report — every transaction in a date range, filterable by comma-separated `transactionType` values (`Invoice`, `Payment`, `Bill`, ...), groupable by customer, vendor, or account, with selectable `columns`. It's the most flexible read on this page and the engine behind `QUICKBOOKS_GET_RECENT_ACTIVITY` on the [Workflows](/docs/tools/quickbooks/workflows.md) page. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_TRANSACTION_LIST", "connectorId": "V1StGXR8Z5", "startDate": "@@{startDate}", "endDate": "@@{endDate}", "transactionType": "Invoice,Payment", "groupBy": "Customer" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `startDate` (string) — Start date (YYYY-MM-DD) - `endDate` (string) — End date (YYYY-MM-DD) - `transactionType` (string) — Invoice, Payment, Bill, etc. (comma-separated) - `sourceAccountType` (string) — Filter by account type - `groupBy` (string) — None, Customer, Vendor, Account, etc. - `columns` (string) — Comma-separated column names to include ## Related - [Tools overview](/docs/tools.md) - [QuickBooks overview](/docs/tools/quickbooks.md) - [Sales & AR](/docs/tools/quickbooks/sales-ar.md) - [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) - [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) - [Payments](/docs/tools/quickbooks/payments.md) - [Workflows](/docs/tools/quickbooks/workflows.md) - [Connectors](/docs/guide/connectors.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Payments Source: https://docs.autessa.com/docs/tools/quickbooks/payments > QuickBooks Payments API tool types. # Payments Tool types that drive the QuickBooks Payments API: tokenizing cards and bank accounts, charging and refunding them, storing payment methods on customers, and composites that collapse tokenize-store-charge sequences into one call. Every type on this page requires a [QuickBooks connector](/docs/guide/connectors.md) whose Intuit company has a QuickBooks Payments merchant account; `connectorId` is the ID of that connector. Unlike the accounting families, these tools move real money in production — sandbox connectors hit Intuit's sandbox Payments endpoint and accept test card and bank numbers. Charges and refunds here do **not** write accounting entries by themselves; the `*_AND_RECORD_*` composites here and on the [Workflows](/docs/tools/quickbooks/workflows.md) page bridge the two sides. ## Tokenize card **TOOL** `QUICKBOOKS_TOKENIZE_CARD` Exchanges raw card details for a single-use payment token, so downstream calls never carry the card number. Tokens are short-lived — spend the token promptly with `QUICKBOOKS_CHARGE_CARD` or store it with `QUICKBOOKS_CREATE_CARD`, or use the tokenize-and-* composites to do both steps atomically. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_TOKENIZE_CARD", "connectorId": "V1StGXR8Z5", "cardNumber": "@@{cardNumber}", "expMonth": "@@{expMonth}", "expYear": "@@{expYear}", "cvc": "@@{cvc}", "cardHolderName": "@@{cardHolderName}", "addressPostalCode": "@@{billingZip}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `cardNumber` (string) — Credit card number - `expMonth` (string) — Expiration month (MM) - `expYear` (string) — Expiration year (YYYY) - `cvc` (string) — Card verification code - `cardHolderName` (string) — Name on card - `addressStreet` (string) — Billing address street - `addressCity` (string) — Billing address city - `addressRegion` (string) — Billing address state/region - `addressPostalCode` (string) — Billing address postal code - `addressCountry` (string) — Billing address country (ISO code) ## Tokenize bank account **TOOL** `QUICKBOOKS_TOKENIZE_BANK_ACCOUNT` Exchanges bank account details (routing and account number) for a single-use token for ACH eCheck processing. `accountType` is one of `PERSONAL_CHECKING`, `PERSONAL_SAVINGS`, `BUSINESS_CHECKING`, or `BUSINESS_SAVINGS`. Pair with `QUICKBOOKS_PROCESS_ECHECK` or the tokenize-and-* composites. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_TOKENIZE_BANK_ACCOUNT", "connectorId": "V1StGXR8Z5", "routingNumber": "@@{routingNumber}", "accountNumber": "@@{accountNumber}", "accountType": "PERSONAL_CHECKING", "accountHolderName": "@@{accountHolderName}", "phone": "@@{phone}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `routingNumber` (string) — Bank routing number (9 digits) - `accountNumber` (string) — Bank account number - `accountType` (string) — PERSONAL_CHECKING, PERSONAL_SAVINGS, BUSINESS_CHECKING, BUSINESS_SAVINGS - `accountHolderName` (string) — Name on account - `phone` (string) — Account holder phone ## Create card **TOOL** `QUICKBOOKS_CREATE_CARD` Stores a tokenized card on a QuickBooks customer as a reusable payment method, returning a permanent `cardId`. The `cardToken` must come from a fresh `QUICKBOOKS_TOKENIZE_CARD` call; `isDefault` makes it the customer's default method. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_CARD", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "cardToken": "@@{cardToken}", "isDefault": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — QuickBooks customer ID - `cardToken` (string) — Token from tokenizeCard - `isDefault` (boolean) — Set as default payment method ## Charge card **TOOL** `QUICKBOOKS_CHARGE_CARD` Charges a card for `amount` — pass either a one-time `cardToken` or a stored `cardId` (with its owning `customerId`), never both. `capture` defaults to true; set it false to authorize only. This processes money but books nothing; use `QUICKBOOKS_CHARGE_AND_RECORD_PAYMENT` on the [Workflows](/docs/tools/quickbooks/workflows.md) page to also write the accounting entry. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CHARGE_CARD", "connectorId": "V1StGXR8Z5", "amount": 450.00, "currency": "USD", "cardToken": "@@{cardToken}", "capture": true, "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `amount` (number) — Charge amount - `currency` (string) — Currency code (USD, etc.) - `cardToken` (string) — Token for one-time payment (use this OR cardId) - `cardId` (string) — Stored card ID (use this OR cardToken) - `customerId` (string) — Customer ID (required if using cardId) - `capture` (boolean) — Whether to capture immediately (default true) - `description` (string) — Payment description ## Process eCheck **TOOL** `QUICKBOOKS_PROCESS_ECHECK` Debits a bank account via ACH — pass either a one-time `bankAccountToken` or a stored `bankAccountId` (with its `customerId`). `paymentMode` is the ACH authorization class (`WEB` default, `TEL`, `CCD`, `PPD`). ACH settles over days rather than instantly, so treat the result as initiated, not cleared. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_PROCESS_ECHECK", "connectorId": "V1StGXR8Z5", "amount": 450.00, "bankAccountToken": "@@{bankAccountToken}", "paymentMode": "WEB", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `amount` (number) — Payment amount - `bankAccountToken` (string) — Token for one-time payment (use this OR bankAccountId) - `bankAccountId` (string) — Stored bank account ID (use this OR bankAccountToken) - `customerId` (string) — Customer ID (required if using bankAccountId) - `paymentMode` (string) — WEB (default), TEL, CCD, PPD - `description` (string) — Payment description ## Refund charge **TOOL** `QUICKBOOKS_REFUND_CHARGE` Refunds a card charge by `chargeId`, back to the original card. Omit `amount` for a full refund or set it for a partial one. The refund is Payments-API-only; `QUICKBOOKS_REFUND_CHARGE_AND_RECORD_CREDIT` below also books the credit memo. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_REFUND_CHARGE", "connectorId": "V1StGXR8Z5", "chargeId": "@@{chargeId}", "amount": 450.00, "description": "@@{refundReason}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `chargeId` (string) — ID of the charge to refund - `amount` (number · optional) — Refund amount (optional for partial refund) - `description` (string) — Refund description/reason ## Refund eCheck **TOOL** `QUICKBOOKS_REFUND_ECHECK` Refunds an ACH eCheck payment by `echeckId`, in full (omit `amount`) or partially. eCheck refunds can only be issued once the original debit has settled, so a refund immediately after processing may be rejected. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_REFUND_ECHECK", "connectorId": "V1StGXR8Z5", "echeckId": "@@{echeckId}", "description": "@@{refundReason}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `echeckId` (string) — ID of the eCheck to refund - `amount` (number · optional) — Refund amount (optional for partial refund) - `description` (string) — Refund description/reason ## Tokenize and charge **TOOL** `QUICKBOOKS_TOKENIZE_AND_CHARGE` Tokenizes raw card details and charges the token in one call — the standard way to take a one-off card payment without juggling short-lived tokens across tool calls. The card is not stored; for repeat billing use `QUICKBOOKS_TOKENIZE_AND_STORE_CARD` instead. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_TOKENIZE_AND_CHARGE", "connectorId": "V1StGXR8Z5", "amount": 450.00, "currency": "USD", "cardNumber": "@@{cardNumber}", "expMonth": "@@{expMonth}", "expYear": "@@{expYear}", "cvc": "@@{cvc}", "cardHolderName": "@@{cardHolderName}", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `amount` (number) — Charge amount - `currency` (string) — Currency code (USD, etc.) - `cardNumber` (string) — Credit card number - `expMonth` (string) — Expiration month (MM) - `expYear` (string) — Expiration year (YYYY) - `cvc` (string) — Card verification code - `cardHolderName` (string) — Name on card - `description` (string) — Payment description ## Tokenize and process eCheck **TOOL** `QUICKBOOKS_TOKENIZE_AND_PROCESS_ECHECK` Tokenizes bank account details and initiates the ACH debit in one call — the eCheck counterpart of tokenize-and-charge. The bank account is not stored for reuse; that's `QUICKBOOKS_TOKENIZE_AND_STORE_BANK_ACCOUNT`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_TOKENIZE_AND_PROCESS_ECHECK", "connectorId": "V1StGXR8Z5", "amount": 450.00, "routingNumber": "@@{routingNumber}", "accountNumber": "@@{accountNumber}", "accountType": "PERSONAL_CHECKING", "accountHolderName": "@@{accountHolderName}", "paymentMode": "WEB", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `amount` (number) — Payment amount - `routingNumber` (string) — Bank routing number - `accountNumber` (string) — Bank account number - `accountType` (string) — PERSONAL_CHECKING, PERSONAL_SAVINGS, etc. - `accountHolderName` (string) — Name on account - `paymentMode` (string) — WEB, TEL, CCD, PPD - `description` (string) — Payment description ## Tokenize and store card **TOOL** `QUICKBOOKS_TOKENIZE_AND_STORE_CARD` Tokenizes a card and stores it on an existing customer in one call, returning a permanent `cardId` for later `QUICKBOOKS_CHARGE_STORED_CARD` calls. Nothing is charged. If the customer record may not exist yet, `QUICKBOOKS_SETUP_CUSTOMER_PAYMENT_METHOD` below handles that too. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_TOKENIZE_AND_STORE_CARD", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "cardNumber": "@@{cardNumber}", "expMonth": "@@{expMonth}", "expYear": "@@{expYear}", "cvc": "@@{cvc}", "cardHolderName": "@@{cardHolderName}", "isDefault": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Customer to store card for - `cardNumber` (string) — Credit card number - `expMonth` (string) — Expiration month (MM) - `expYear` (string) — Expiration year (YYYY) - `cvc` (string) — Card verification code - `cardHolderName` (string) — Name on card - `isDefault` (boolean) — Set as default payment method ## Tokenize and store bank account **TOOL** `QUICKBOOKS_TOKENIZE_AND_STORE_BANK_ACCOUNT` Tokenizes a bank account and stores it on an existing customer in one call, returning a permanent `bankAccountId` for later `QUICKBOOKS_CHARGE_STORED_BANK_ACCOUNT` calls. Nothing is debited at store time. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_TOKENIZE_AND_STORE_BANK_ACCOUNT", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "routingNumber": "@@{routingNumber}", "accountNumber": "@@{accountNumber}", "accountType": "PERSONAL_CHECKING", "accountHolderName": "@@{accountHolderName}", "isDefault": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Customer to store bank account for - `routingNumber` (string) — Bank routing number - `accountNumber` (string) — Bank account number - `accountType` (string) — PERSONAL_CHECKING, PERSONAL_SAVINGS, etc. - `accountHolderName` (string) — Name on account - `phone` (string) — Account holder phone - `isDefault` (boolean) — Set as default payment method ## Charge stored card **TOOL** `QUICKBOOKS_CHARGE_STORED_CARD` Charges a card previously stored on a customer, identified by `customerId` plus `cardId` — no card details or fresh token needed. This is the repeat-billing workhorse for customers set up via the store composites. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CHARGE_STORED_CARD", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "cardId": "@@{cardId}", "amount": 450.00, "currency": "USD", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Customer ID who owns the card - `cardId` (string) — Stored card ID - `amount` (number) — Charge amount - `currency` (string) — Currency code (USD, etc.) - `description` (string) — Payment description ## Charge stored bank account **TOOL** `QUICKBOOKS_CHARGE_STORED_BANK_ACCOUNT` Debits a bank account previously stored on a customer via ACH, identified by `customerId` plus `bankAccountId`. `paymentMode` carries the ACH authorization class, and the same settlement-delay caveat as `QUICKBOOKS_PROCESS_ECHECK` applies. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CHARGE_STORED_BANK_ACCOUNT", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "bankAccountId": "@@{bankAccountId}", "amount": 450.00, "paymentMode": "WEB", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Customer ID who owns the bank account - `bankAccountId` (string) — Stored bank account ID - `amount` (number) — Payment amount - `paymentMode` (string) — WEB, TEL, CCD, PPD - `description` (string) — Payment description ## Setup customer payment method **TOOL** `QUICKBOOKS_SETUP_CUSTOMER_PAYMENT_METHOD` The full onboarding composite: finds the customer by email (creating one if missing), then tokenizes and stores the payment method on them. `paymentMethodType` selects `CARD` (card fields required) or `BANK_ACCOUNT` (bank fields required), so one tool config can onboard either kind. Returns the customer, whether it was created, and the stored method. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SETUP_CUSTOMER_PAYMENT_METHOD", "connectorId": "V1StGXR8Z5", "email": "@@{customerEmail}", "displayName": "@@{customerName}", "paymentMethodType": "CARD", "cardNumber": "@@{cardNumber}", "expMonth": "@@{expMonth}", "expYear": "@@{expYear}", "cvc": "@@{cvc}", "cardHolderName": "@@{cardHolderName}", "setAsDefault": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `email` (string) — Customer email (primary search key) - `displayName` (string) — Customer display name - `givenName` (string) — First name - `familyName` (string) — Last name - `paymentMethodType` (string) — CARD or BANK_ACCOUNT - `cardNumber` (string) — Required string value. - `expMonth` (string) — Required string value. - `expYear` (string) — Required string value. - `cvc` (string) — Required string value. - `cardHolderName` (string) — Required string value. - `routingNumber` (string) — Required string value. - `accountNumber` (string) — Required string value. - `accountType` (string) — Required string value. - `accountHolderName` (string) — Required string value. - `phone` (string) — Required string value. - `setAsDefault` (boolean) — Required boolean value. ## Refund charge and record credit **TOOL** `QUICKBOOKS_REFUND_CHARGE_AND_RECORD_CREDIT` Refunds a card charge and books a matching credit memo against the customer in the same call, keeping the Payments side and the ledger in sync. Omit `amount` for a full refund; `reason` lands on both the refund and the credit memo. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_REFUND_CHARGE_AND_RECORD_CREDIT", "connectorId": "V1StGXR8Z5", "chargeId": "@@{chargeId}", "customerId": "@@{customerId}", "reason": "@@{refundReason}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `chargeId` (string) — Charge ID to refund - `customerId` (string) — Customer ID for credit memo - `amount` (number · optional) — Refund amount (optional for full refund) - `reason` (string) — Refund reason/description ## Refund eCheck and record credit **TOOL** `QUICKBOOKS_REFUND_ECHECK_AND_RECORD_CREDIT` The ACH counterpart: refunds an eCheck by `echeckId` and books the matching credit memo against the customer in one call. The eCheck must have settled before it can be refunded, same as the plain refund tool. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_REFUND_ECHECK_AND_RECORD_CREDIT", "connectorId": "V1StGXR8Z5", "echeckId": "@@{echeckId}", "customerId": "@@{customerId}", "reason": "@@{refundReason}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `echeckId` (string) — eCheck ID to refund - `customerId` (string) — Customer ID for credit memo - `amount` (number · optional) — Refund amount (optional for full refund) - `reason` (string) — Refund reason/description ## Related - [Tools overview](/docs/tools.md) - [QuickBooks overview](/docs/tools/quickbooks.md) - [Sales & AR](/docs/tools/quickbooks/sales-ar.md) - [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) - [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) - [Reports](/docs/tools/quickbooks/reports.md) - [Workflows](/docs/tools/quickbooks/workflows.md) - [Connectors](/docs/guide/connectors.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Workflows Source: https://docs.autessa.com/docs/tools/quickbooks/workflows > QuickBooks composite workflow tool types. # Workflows Composite tool types that chain several QuickBooks operations behind one call: create-and-send, get-or-create, search helpers, payment-status checks, financial summaries, and PDF/attachment flows. Every type on this page requires a [QuickBooks connector](/docs/guide/connectors.md); `connectorId` is the ID of that connector. Each composite exists so an agent doesn't have to plan a multi-step sequence itself — prefer these when one matches the task, and fall back to the granular types on the [Sales & AR](/docs/tools/quickbooks/sales-ar.md) and [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) pages when the agent needs control between steps. ## Create and send invoice **TOOL** `QUICKBOOKS_CREATE_AND_SEND_INVOICE` Creates an invoice and emails it through QuickBooks in one call — the single most common billing flow. `lineItems` uses the same sales-line shape as `QUICKBOOKS_CREATE_INVOICE`, and `sendToEmail` overrides the recipient (the customer's email on file is used when omitted). ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_AND_SEND_INVOICE", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "lineItems": [ { "DetailType": "SalesItemLineDetail", "Amount": 450.00, "Description": "@@{lineDescription}", "SalesItemLineDetail": { "ItemRef": { "value": "12" }, "Qty": 3, "UnitPrice": 150.00 } } ], "dueDate": "@@{dueDate}", "customerMemo": "Thank you for your business." } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — QuickBooks customer ID - `lineItems` (array) — Invoice line items - `dueDate` (string) — Invoice due date (YYYY-MM-DD) - `customerMemo` (string) — Memo visible to customer - `privateNote` (string) — Internal note - `sendToEmail` (string) — Email to send invoice to (uses customer email if not provided) ## Record payment for invoice **TOOL** `QUICKBOOKS_RECORD_PAYMENT_FOR_INVOICE` Records a ledger payment against a specific invoice without the agent having to look up the customer or balance first — the tool fetches the invoice, resolves its customer, and defaults `amount` to the open balance when omitted. Moves no money; it's the composite version of `QUICKBOOKS_CREATE_PAYMENT`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_RECORD_PAYMENT_FOR_INVOICE", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}", "depositToAccountId": "35", "paymentRefNum": "@@{referenceNumber}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Invoice to apply payment to - `amount` (number · optional) — Payment amount (optional - uses invoice balance if not provided) - `paymentMethodId` (string) — Payment method reference - `depositToAccountId` (string) — Account to deposit to - `paymentRefNum` (string) — Reference number - `privateNote` (string) — Internal note ## Charge and record payment **TOOL** `QUICKBOOKS_CHARGE_AND_RECORD_PAYMENT` Charges a card through the Payments API and books the matching payment record — optionally applied to an invoice via `invoiceId` — in one call. Pass either a one-time `cardToken` or a stored `cardId`; this is the bridge between the [Payments](/docs/tools/quickbooks/payments.md) family and the ledger. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CHARGE_AND_RECORD_PAYMENT", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "invoiceId": "@@{invoiceId}", "amount": 450.00, "cardId": "@@{cardId}", "depositToAccountId": "35", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — QuickBooks customer ID - `invoiceId` (string · optional) — Invoice to apply payment to (optional) - `amount` (number) — Charge amount - `cardToken` (string) — Card token for one-time payment - `cardId` (string) — Stored card ID (alternative to cardToken) - `depositToAccountId` (string) — Account to deposit to - `description` (string) — Payment description ## Process eCheck and record payment **TOOL** `QUICKBOOKS_PROCESS_ECHECK_AND_RECORD_PAYMENT` The ACH counterpart of charge-and-record: initiates an eCheck debit (one-time `bankAccountToken` or stored `bankAccountId`) and books the payment record, optionally against an invoice. Remember ACH settles over days — the ledger entry is written when the debit is accepted, not when it clears. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_PROCESS_ECHECK_AND_RECORD_PAYMENT", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "invoiceId": "@@{invoiceId}", "amount": 450.00, "bankAccountId": "@@{bankAccountId}", "depositToAccountId": "35", "description": "@@{paymentDescription}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — QuickBooks customer ID - `invoiceId` (string · optional) — Invoice to apply payment to (optional) - `amount` (number) — Payment amount - `bankAccountToken` (string) — Bank account token for one-time payment - `bankAccountId` (string) — Stored bank account ID (alternative to token) - `depositToAccountId` (string) — Account to deposit to - `description` (string) — Payment description ## Check invoice payment status **TOOL** `QUICKBOOKS_CHECK_INVOICE_PAYMENT_STATUS` Answers "has this invoice been paid?" in one call: returns a `status` of `PAID`, `PARTIALLY_PAID`, or `UNPAID`, plus the open balance and the linked payment transactions. The natural polling tool for collections and follow-up agents. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CHECK_INVOICE_PAYMENT_STATUS", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — Required string value. ## Convert estimate to invoice **TOOL** `QUICKBOOKS_CONVERT_ESTIMATE_TO_INVOICE` Fetches an accepted estimate, creates an invoice from its customer and line items, and — when `sendToCustomer` is true — emails it, all in one call. `sendToEmail` overrides the recipient; the original estimate is left in place. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CONVERT_ESTIMATE_TO_INVOICE", "connectorId": "V1StGXR8Z5", "estimateId": "@@{estimateId}", "dueDate": "@@{dueDate}", "sendToCustomer": true } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `estimateId` (string) — Estimate ID to convert - `dueDate` (string) — Invoice due date (YYYY-MM-DD) - `sendToCustomer` (boolean) — Whether to email the invoice - `sendToEmail` (string) — Email to send to (uses customer email if not provided) ## Get invoice with customer **TOOL** `QUICKBOOKS_GET_INVOICE_WITH_CUSTOMER` Fetches an invoice and its customer's full record in one call — the join an agent otherwise needs two lookups for. Useful when drafting a payment reminder that needs both the balance and the contact details. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_INVOICE_WITH_CUSTOMER", "connectorId": "V1StGXR8Z5", "invoiceId": "@@{invoiceId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `invoiceId` (string) — QuickBooks invoice ID ## Get or create customer **TOOL** `QUICKBOOKS_GET_OR_CREATE_CUSTOMER` Idempotent customer resolution: searches by exact `email` first, falls back to exact `displayName`, and only creates a new customer on a miss (`displayName` is required for the create). The response's `wasCreated` flag says which path ran — this is the safe default for agents ingesting orders or signups. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_OR_CREATE_CUSTOMER", "connectorId": "V1StGXR8Z5", "email": "@@{customerEmail}", "displayName": "@@{customerName}", "phone": "@@{customerPhone}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `email` (string) — Primary search key - customer email - `displayName` (string) — Display name (required for create, used as fallback search) - `givenName` (string) — First name - `familyName` (string) — Last name - `companyName` (string) — Company name - `phone` (string) — Phone number - `addressLine1` (string) — Billing address line 1 - `addressCity` (string) — Billing address city - `addressState` (string) — Billing address state - `addressPostalCode` (string) — Billing address postal code - `addressCountry` (string) — Billing address country ## Search customer by email **TOOL** `QUICKBOOKS_SEARCH_CUSTOMER_BY_EMAIL` Looks up customers by exact primary email address — the most reliable way to resolve an inbound email sender to a `customerId` without paging through `QUICKBOOKS_GET_CUSTOMERS`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SEARCH_CUSTOMER_BY_EMAIL", "connectorId": "V1StGXR8Z5", "email": "@@{customerEmail}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `email` (string) — Email address to search for ## Search customer by name **TOOL** `QUICKBOOKS_SEARCH_CUSTOMER_BY_NAME` Searches customers by display name with partial (contains) matching, returning up to 25 candidates. Use it when the agent only has a rough name; expect multiple hits and disambiguate before writing transactions. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SEARCH_CUSTOMER_BY_NAME", "connectorId": "V1StGXR8Z5", "displayName": "@@{customerName}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `displayName` (string) — Display name to search for (supports partial match) ## Get or create vendor **TOOL** `QUICKBOOKS_GET_OR_CREATE_VENDOR` Idempotent vendor resolution keyed on `displayName`: finds an existing vendor by name or creates one with the supplied contact and address details. The go-to for AP agents ingesting bills from vendors that may or may not be on file yet. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_OR_CREATE_VENDOR", "connectorId": "V1StGXR8Z5", "displayName": "@@{vendorName}", "email": "@@{vendorEmail}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `displayName` (string) — Primary search key - vendor display name (required) - `email` (string) — Vendor email - `companyName` (string) — Company name - `phone` (string) — Phone number - `addressLine1` (string) — Billing address line 1 - `addressCity` (string) — Billing address city - `addressState` (string) — Billing address state - `addressPostalCode` (string) — Billing address postal code - `addressCountry` (string) — Billing address country ## Search vendor by name **TOOL** `QUICKBOOKS_SEARCH_VENDOR_BY_NAME` Searches vendors by display name with partial (contains) matching, returning up to 25 candidates. The vendor mirror of the customer name search — use it to resolve a bill's sender to a `vendorId`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SEARCH_VENDOR_BY_NAME", "connectorId": "V1StGXR8Z5", "displayName": "@@{vendorName}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `displayName` (string) — Display name to search for (supports partial match) ## Create and pay bill **TOOL** `QUICKBOOKS_CREATE_AND_PAY_BILL` Enters a bill and immediately records its payment in one call — `lineItems` uses the bill expense-line shape, `paymentType` is `Check` or `CreditCard`, and the payment is linked back to the created bill automatically. Both steps are ledger records; no funds move. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_AND_PAY_BILL", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "lineItems": [ { "DetailType": "AccountBasedExpenseLineDetail", "Amount": 1200.00, "Description": "@@{expenseDescription}", "AccountBasedExpenseLineDetail": { "AccountRef": { "value": "7" } } } ], "paymentType": "Check", "bankAccountId": "35" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — QuickBooks vendor ID - `lineItems` (array) — Bill line items - `dueDate` (string) — Bill due date (YYYY-MM-DD) - `paymentType` (string) — Check or CreditCard - `bankAccountId` (string) — Bank account for payment - `checkNumber` (string) — Check number (if paying by check) - `privateNote` (string) — Internal note ## Schedule bill payment **TOOL** `QUICKBOOKS_SCHEDULE_BILL_PAYMENT` Creates a bill payment dated in the future — `txnDate` must be a future date or the call is rejected. The payment record is written into QuickBooks immediately with the future transaction date; it is not held back and released later, so treat it as pre-booking a planned payment run. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_SCHEDULE_BILL_PAYMENT", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "totalAmt": 1200.00, "payType": "Check", "bankAccountId": "35", "txnDate": "@@{scheduledDate}", "linkedBills": [ { "TxnId": "@@{billId}", "Amount": 1200.00 } ] } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - VendorRef - `totalAmt` (number) — Required - Total amount - `payType` (string) — Required - "Check" or "CreditCard" - `bankAccountId` (string) — Required for Check payments - `checkNum` (string · optional) — Check number (optional) - `creditCardAccountId` (string) — Required for CreditCard payments - `txnDate` (string) — Required - Future date for scheduling (YYYY-MM-DD) - `privateNote` (string) — Internal memo - `linkedBills` (array) — Bills to pay with amounts ## Check bill payment status **TOOL** `QUICKBOOKS_CHECK_BILL_PAYMENT_STATUS` The AP mirror of the invoice status check: returns whether a bill is `PAID`, `PARTIALLY_PAID`, or `UNPAID`, plus its open balance and linked payment transactions. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CHECK_BILL_PAYMENT_STATUS", "connectorId": "V1StGXR8Z5", "billId": "@@{billId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `billId` (string) — Required string value. ## Get bill with vendor **TOOL** `QUICKBOOKS_GET_BILL_WITH_VENDOR` Fetches a bill and its vendor's full record in one call, saving the second lookup when an agent needs both the amount owed and the vendor's contact details. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_BILL_WITH_VENDOR", "connectorId": "V1StGXR8Z5", "billId": "@@{billId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `billId` (string) — QuickBooks bill ID ## Get items for invoice **TOOL** `QUICKBOOKS_GET_ITEMS_FOR_INVOICE` Searches active items by partial name match and returns them pre-formatted for building invoice lines (ID, name, price), alongside the raw records. Give it to invoicing agents so "add a line for consulting" resolves to the right `ItemRef` without a full catalog dump. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ITEMS_FOR_INVOICE", "connectorId": "V1StGXR8Z5", "searchTerm": "@@{itemSearch}", "maxResults": 25 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `searchTerm` (string) — Search term for item name - `itemType` (string) — Filter by item type (Service, Inventory, NonInventory) - `maxResults` (integer) — Maximum results (default 50) ## Get accounts for expense **TOOL** `QUICKBOOKS_GET_ACCOUNTS_FOR_EXPENSE` Searches expense accounts by partial name match, returning candidates formatted for building bill lines. The account-side companion to the item search — use it to resolve "book this under advertising" to the right `AccountRef`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ACCOUNTS_FOR_EXPENSE", "connectorId": "V1StGXR8Z5", "searchTerm": "@@{accountSearch}", "maxResults": 25 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `searchTerm` (string) — Search term for account name - `maxResults` (integer) — Maximum results (default 50) ## Get AR summary **TOOL** `QUICKBOOKS_GET_AR_SUMMARY` Runs the aged-receivables and customer-balance reports together and returns both as one accounts-receivable summary as of `reportDate` (defaults to today). One call for "what are we owed and how overdue is it?" ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_AR_SUMMARY", "connectorId": "V1StGXR8Z5", "reportDate": "@@{asOfDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string) — As of date (YYYY-MM-DD), defaults to today ## Get AP summary **TOOL** `QUICKBOOKS_GET_AP_SUMMARY` The payables mirror: runs aged payables and vendor balances together for one accounts-payable summary as of `reportDate`. The starting point for a payment-run planning agent. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_AP_SUMMARY", "connectorId": "V1StGXR8Z5", "reportDate": "@@{asOfDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string) — As of date (YYYY-MM-DD), defaults to today ## Get account balance overview **TOOL** `QUICKBOOKS_GET_ACCOUNT_BALANCE_OVERVIEW` A cash-position snapshot in one call: active bank accounts with balances, open invoices (largest first) as accounts receivable, and open bills as accounts payable. Ideal for a daily "how are we doing?" briefing without running formal reports. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ACCOUNT_BALANCE_OVERVIEW", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `reportDate` (string · optional) — Optional, defaults to today (YYYY-MM-DD) ## Get financial snapshot **TOOL** `QUICKBOOKS_GET_FINANCIAL_SNAPSHOT` Runs the three core statements — profit and loss over the date range, balance sheet as of `endDate`, and cash flow — and returns them together. The one-call input for an agent writing a monthly financial summary. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_FINANCIAL_SNAPSHOT", "connectorId": "V1StGXR8Z5", "startDate": "@@{startDate}", "endDate": "@@{endDate}", "accountingMethod": "Accrual" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `startDate` (string) — Start date for P&L (YYYY-MM-DD) - `endDate` (string) — End date for reports (YYYY-MM-DD) - `accountingMethod` (string) — Cash or Accrual ## Get recent activity **TOOL** `QUICKBOOKS_GET_RECENT_ACTIVITY` Returns all transactions from the last `days` days (default 7) via the transaction-list report, optionally limited to comma-separated `transactionTypes`. The easy feed for "what happened in the books this week?" digests. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_RECENT_ACTIVITY", "connectorId": "V1StGXR8Z5", "days": 7, "transactionTypes": "Invoice,Payment,Bill" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `days` (integer) — Number of days to look back (default 7) - `transactionTypes` (string) — Comma-separated transaction types to include ## Query multiple entities **TOOL** `QUICKBOOKS_QUERY_MULTIPLE_ENTITIES` Runs the same SQL-like `whereClause` across several entity types (e.g. `Customer,Invoice,Payment`) in one call, returning results grouped per type with `maxResultsPerEntity` each. A power tool for cross-entity questions like "everything touching this date range" — each type must actually support the fields in the clause. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_QUERY_MULTIPLE_ENTITIES", "connectorId": "V1StGXR8Z5", "entityTypes": "Invoice,Payment", "whereClause": "TxnDate >= '@@{startDate}'", "maxResultsPerEntity": 100 } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `entityTypes` (string) — Comma-separated entity types (Customer,Invoice,Payment) - `whereClause` (string) — Common where clause to apply - `maxResultsPerEntity` (integer) — Max results per entity type (default 100) ## Get entity PDF **TOOL** `QUICKBOOKS_GET_ENTITY_PDF` Downloads QuickBooks' rendered PDF for an entity (`Invoice`, `Estimate`, and other printable types), stores it, and returns a short-lived download URL — valid for about 10 minutes, so pass it along promptly. This is how an agent gets a customer-ready document out of QuickBooks. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_GET_ENTITY_PDF", "connectorId": "V1StGXR8Z5", "entityType": "Invoice", "entityId": "@@{invoiceId}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `entityType` (string) — "Invoice", "Bill", "PurchaseOrder", "Estimate", etc. - `entityId` (string) — Required string value. ## Upload attachment **TOOL** `QUICKBOOKS_UPLOAD_ATTACHMENT` Downloads a file from `fileUrl` (a public or presigned URL) and attaches it to a QuickBooks entity — receipts on bills, signed POs on invoices, and so on. `contentType` must match the file, e.g. `application/pdf`. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_UPLOAD_ATTACHMENT", "connectorId": "V1StGXR8Z5", "entityType": "Bill", "entityId": "@@{billId}", "fileUrl": "@@{receiptUrl}", "fileName": "receipt.pdf", "contentType": "application/pdf" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `entityType` (string) — "Invoice", "Bill", "PurchaseOrder", etc. - `entityId` (string) — Required string value. - `fileUrl` (string) — URL to download the file from (S3 presigned, public URL, etc.) - `fileName` (string) — e.g., "purchase-order-123.pdf" - `contentType` (string) — e.g., "application/pdf" ## Create invoice and attach PDF **TOOL** `QUICKBOOKS_CREATE_INVOICE_AND_ATTACH_PDF` Creates an invoice (same fields as `QUICKBOOKS_CREATE_INVOICE`) and attaches a PDF fetched from `pdfUrl` — for example the purchase order that triggered the invoice. If the PDF download fails, the invoice is still created and the response says the attachment step failed. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_INVOICE_AND_ATTACH_PDF", "connectorId": "V1StGXR8Z5", "customerId": "@@{customerId}", "lines": [ { "DetailType": "SalesItemLineDetail", "Amount": 450.00, "SalesItemLineDetail": { "ItemRef": { "value": "12" } } } ], "pdfUrl": "@@{sourcePdfUrl}", "pdfFileName": "PO-1234.pdf" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerId` (string) — Required - Customer reference - `lines` (array) — Required - Line items array - `txnDate` (string) — Transaction date (YYYY-MM-DD) - `dueDate` (string) — Due date (YYYY-MM-DD) - `docNumber` (string) — Invoice number - `customerMemo` (string) — Memo visible to customer - `privateNote` (string) — Internal note - `billingEmail` (string) — Email for billing - `billingAddress` (object) — Required object value. - `shippingAddress` (object) — Required object value. - `applyTaxAfterDiscount` (boolean) — Required boolean value. - `salesTermId` (string) — Payment terms reference - `pdfUrl` (string) — Required - URL of the PDF to attach (e.g., PO PDF URL) - `pdfFileName` (string) — Required - Filename for the attachment (e.g., "PO-1234.pdf") ## Create bill and get PDF **TOOL** `QUICKBOOKS_CREATE_BILL_AND_GET_PDF` Creates a bill (same fields as `QUICKBOOKS_CREATE_BILL`) and immediately returns a short-lived download URL for its rendered PDF. Useful when the entered bill needs to go straight into an approval email or archive. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_BILL_AND_GET_PDF", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "lines": [ { "DetailType": "AccountBasedExpenseLineDetail", "Amount": 1200.00, "AccountBasedExpenseLineDetail": { "AccountRef": { "value": "7" } } } ], "dueDate": "@@{dueDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - VendorRef - `lines` (array) — Required - Line items - `txnDate` (string) — Transaction date - `dueDate` (string) — Due date - `docNumber` (string) — Bill number/reference - `privateNote` (string) — Internal memo - `apAccountId` (string) — Accounts Payable account ## Create purchase order and get PDF **TOOL** `QUICKBOOKS_CREATE_PURCHASE_ORDER_AND_GET_PDF` Creates a purchase order (same fields as `QUICKBOOKS_CREATE_PURCHASE_ORDER`) and returns a short-lived download URL for its PDF — ready to email to the vendor in the next tool call. ### Example ```json title="Tool config" { "toolType": "QUICKBOOKS_CREATE_PURCHASE_ORDER_AND_GET_PDF", "connectorId": "V1StGXR8Z5", "vendorId": "@@{vendorId}", "lines": [ { "DetailType": "ItemBasedExpenseLineDetail", "Amount": 500.00, "ItemBasedExpenseLineDetail": { "ItemRef": { "value": "19" }, "Qty": 10, "UnitPrice": 50.00 } } ], "expectedDate": "@@{expectedDate}" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `vendorId` (string) — Required - VendorRef - `lines` (array) — Required - Line items - `txnDate` (string) — Transaction date - `expectedDate` (string) — Expected delivery date - `docNumber` (string) — PO number - `vendorMemo` (string) — Memo for vendor - `privateNote` (string) — Internal memo - `shipAddr` (object) — Ship to address - `apAccountId` (string) — Accounts Payable account ## Related - [Tools overview](/docs/tools.md) - [QuickBooks overview](/docs/tools/quickbooks.md) - [Sales & AR](/docs/tools/quickbooks/sales-ar.md) - [Purchasing & AP](/docs/tools/quickbooks/purchasing-ap.md) - [Items, Accounts & Company](/docs/tools/quickbooks/items-accounts-company.md) - [Reports](/docs/tools/quickbooks/reports.md) - [Payments](/docs/tools/quickbooks/payments.md) - [Connectors](/docs/guide/connectors.md) - [Autessa Payments](/docs/tools/autessa-payments.md) --- # Autessa Payments Source: https://docs.autessa.com/docs/tools/autessa-payments > Tool types for white-label payments: invoices, charges, refunds, and payout status via the Autessa Payments connector. # Autessa Payments Let agents move real money on your behalf: invoice customers, take payments, issue refunds, and check when your payouts land. All four types require an [Autessa Payments connector](/docs/guide/connectors.md) (`connectorId`) whose merchant onboarding is complete — a connector that has not finished onboarding fails with `NOT_ONBOARDED`. Charges run on your merchant account as the merchant of record: sales tax is calculated from the customer address fields you pass (`customerCountry` is the minimum; `customerTaxId` enables B2B reverse-charge handling) and added on top of `amountCents`, while Autessa's platform fee is taken on the pre-tax base. All amounts are integer cents; `currency` defaults to `usd`. ## Create Invoice **TOOL** `AUTESSA_PAYMENTS_CREATE_INVOICE` Bills a customer for an invoice amount: tax is computed from the customer address, the total is charged on your merchant account, and the charge is recorded on your ledger as an invoice. Note that this executes as an immediate charge — it does not email the customer a payable invoice link. `description` defaults to `"Invoice"` (plus the customer email) when omitted. The result is `{"success": true, "invoiceChargeId": ..., "amountCents": ..., "taxCents": ..., "totalAmountCents": ..., "customerEmail": ...}` — keep `invoiceChargeId` if you may need to [refund](#refund-) later. ### Example ```json title="Tool config" { "toolType": "AUTESSA_PAYMENTS_CREATE_INVOICE", "connectorId": "V1StGXR8Z5", "customerEmail": "@@{CUSTOMER_EMAIL}", "amountCents": 25000, "currency": "usd", "description": "@@{INVOICE_DESCRIPTION}", "customerCountry": "US", "customerState": "@@{CUSTOMER_STATE}", "customerPostalCode": "@@{CUSTOMER_POSTAL_CODE}", "customerCity": "@@{CUSTOMER_CITY}", "customerTaxId": "" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `customerEmail` (string) — Required string value. - `amountCents` (integer) — Required integer value. - `currency` (string) — Required string value. - `description` (string) — Required string value. - `customerCountry` (string) — Required string value. - `customerState` (string) — Required string value. - `customerPostalCode` (string) — Required string value. - `customerCity` (string) — Required string value. - `customerTaxId` (string) — Required string value. ## Charge Customer **TOOL** `AUTESSA_PAYMENTS_CHARGE_CUSTOMER` Creates a payment for `amountCents` plus computed tax on your merchant account, identified to your ledger by `customerEmail`. `amountCents` must be a positive integer, and repeated invocations are idempotent per tool invocation, so a retried agent turn cannot double-charge. The result itemizes the money movement: `{"success": true, "chargeId": ..., "amountCents": ..., "taxCents": ..., "taxJurisdiction": ..., "totalAmountCents": ..., "platformFeeCents": ...}`. ### Example ```json title="Tool config" { "toolType": "AUTESSA_PAYMENTS_CHARGE_CUSTOMER", "connectorId": "V1StGXR8Z5", "amountCents": 4999, "currency": "usd", "description": "Monthly service fee", "customerEmail": "@@{CUSTOMER_EMAIL}", "customerCountry": "US", "customerState": "WA", "customerPostalCode": "98101", "customerCity": "Seattle", "customerTaxId": "" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `amountCents` (integer) — Required integer value. - `currency` (string) — Required string value. - `description` (string) — Required string value. - `customerEmail` (string) — Required string value. - `customerCountry` (string) — Required string value. - `customerState` (string) — Required string value. - `customerPostalCode` (string) — Required string value. - `customerCity` (string) — Required string value. - `customerTaxId` (string) — Required string value. ## Refund **TOOL** `AUTESSA_PAYMENTS_REFUND` Refunds a previous charge by its `chargeId` (the `chargeId` or `invoiceChargeId` returned by the charge tools). Leave `amountCents` null for a full refund, or set it for a partial refund — partial refunds prorate the base amount and tax, and any tax recorded on the original charge is reversed proportionally. The refund is written to your ledger as a negative entry against the original charge. The result is `{"success": true, "chargeId": ..., "refundedAmountCents": ...}`. ### Example ```json title="Tool config" { "toolType": "AUTESSA_PAYMENTS_REFUND", "connectorId": "V1StGXR8Z5", "chargeId": "@@{CHARGE_ID}", "amountCents": null } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. - `chargeId` (string) — Required string value. - `amountCents` (integer) — null = full refund ## Get Payout Status **TOOL** `AUTESSA_PAYMENTS_GET_PAYOUT_STATUS` Checks the status of your merchant account's most recent payout — useful for agents that answer "has my money arrived?" questions. It returns a single status string, not a payout history. The result is `{"success": true, "payoutStatus": ...}` where `payoutStatus` is a processor status such as `paid`, `pending`, `in_transit`, `failed`, or `canceled`; `NONE` means no payouts exist yet, and `UNKNOWN` means the status could not be retrieved. ### Example ```json title="Tool config" { "toolType": "AUTESSA_PAYMENTS_GET_PAYOUT_STATUS", "connectorId": "V1StGXR8Z5" } ``` > This tool type requires a connected external account. Pass the connector's ID as `connectorId` in the tool configuration. **Configuration fields** - `connectorId` (string (ID)) — Required string (ID) value. References a connector by ID. ## Related - [Tool Types overview](/docs/tools.md) - [Connectors guide](/docs/guide/connectors.md) - [Tools guide](/docs/guide/agentic/tools.md) - [QuickBooks tool types](/docs/tools/quickbooks.md) — accounting-side payments and invoicing --- # Meeting Recorder Source: https://docs.autessa.com/docs/tools/meeting-recorder > Tool types backing the Meeting Recorder feature: bot dispatch and the system-provisioned notify tool. # Meeting Recorder The two purpose-built tool types behind the Meeting Recorder feature: send a recording bot to a meeting, and notify participants when the recap is ready. When you create a Meeting Recorder bot, the feature provisions its processing pipeline as regular tools in your company — audio extraction, transcription, the summarizer LLM call, retention cleanup, and semantic search over past meetings all use the generic types documented elsewhere ([Extract Audio](/docs/tools/media-web.md#extract-audio-), [Transcribe](/docs/tools/media-web.md#transcribe-), [LLM Call](/docs/tools/core.md#llm-call-), [AutessaDB tools](/docs/tools/autessadb.md), [AutessaScript](/docs/tools/core.md#autessascript-) orchestrators). These are first-class tools: they appear in your tools list and you can edit them — tune the summary prompt, adjust retention filters, extend the orchestration scripts. Only the two types below are specific to this feature. ## Meeting Bot Dispatch **TOOL** `MEETING_BOT_DISPATCH` Sends one of your Meeting Recorder bots (`botId`) to a Zoom, Google Meet, or Teams meeting. The platform is detected from `meetingUrl`, a `SCHEDULED` row is written to the bot's meetings table (with `title`, `origin: MANUAL_DISPATCH`, `start_time`, and `organizer_email` when provided), and the recording bot is dispatched — `startTime` (UTC ISO-8601) makes the bot wait and join at that moment; omit it to join now. From there the recording flows through the bot's normal pipeline: transcription, summary, and notification, all visible on the same row. The result is `{"meeting_id": ..., "platform": ..., "start_time": ..., "success": true}`. ### Example ```json title="Tool config" { "toolType": "MEETING_BOT_DISPATCH", "botId": "b3a1f2c4-8d5e-4f6a-9b7c-1d2e3f4a5b6c", "meetingUrl": "@@{MEETING_URL}", "startTime": "@@{START_TIME_UTC}", "title": "@@{MEETING_TITLE}", "organizerEmail": "@@{ORGANIZER_EMAIL}" } ``` **Configuration fields** - `botId` (string) — Bot connector id (UUID). Required. - `meetingUrl` (string) — Meet/Zoom/Teams URL. Required. - `startTime` (string · optional) — UTC ISO-8601 start time. Optional; defaults to now. Recall.ai waits until this time on its side. - `title` (string · optional) — Optional meeting title for the AutessaDB row. - `organizerEmail` (string · optional) — Optional organizer email recorded on the row. ## Meeting Recorder Notify **TOOL** `MEETING_RECORDER_NOTIFY` _System-provisioned tool type._ Emails meeting participants their recap: it mints a per-recipient share token for the meeting identified by `meetingId` and sends each participant a link to the recording, summary, and transcript from `meetings@autessa.com`. The provisioned tool stores `meetingId` as the `@@{meeting_id}` placeholder so the pipeline's summarize-and-notify step can invoke it for whichever meeting just finished. This is a system-only type: because it carries Autessa's privileged sender identity, you cannot create tools of this type or edit the provisioned one — but it is visible in your tools list and your triggers and scripts can invoke it. The result is `{"meeting_id": ..., "result": ..., "success": true}`. ### Example ```json title="Tool config" { "toolType": "MEETING_RECORDER_NOTIFY", "meetingId": "@@{meeting_id}" } ``` > This tool type is system-provisioned: it is created by Autessa and cannot be created through the tool APIs or the builder UI. **Configuration fields** - `meetingId` (string) — Required string value. ## Related - [Tool Types overview](/docs/tools.md) - [Meeting Recorder poweruser API](/docs/api/poweruser/meeting-recorder.md) - [Media & Web tool types](/docs/tools/media-web.md) - [AutessaDB tool types](/docs/tools/autessadb.md) - [Core tool types](/docs/tools/core.md) # AutessaScript --- # AutessaScript Source: https://docs.autessa.com/docs/autessascript > AutessaScript is sandboxed JavaScript that runs server-side inside script tools and AutessaDB computed columns. # AutessaScript AutessaScript is sandboxed JavaScript that runs server-side on the Autessa platform. It powers the AUTESSA_SCRIPT tool type and AutessaDB computed columns, letting you write custom logic — transforms, validations, HTTP calls, LLM invocations, and multi-tool orchestration — without deploying any infrastructure of your own. ## What it is AutessaScript is standard ECMAScript executed by a hardened, sandboxed JavaScript engine. Your script receives its inputs through a `variables` binding, does its work synchronously, and hands back a single result via `response.setResponse(...)`. Alongside the four global bindings (`console`, `response`, `variables`, `responseStream`), a set of helper libraries — `api`, `llm`, `connector`, `tools`, `mathUtils`, `waitUtils` — provide the only doors out of the sandbox: HTTP requests, LLM calls, connector credentials, and invocations of your other published tools. There is no filesystem, no network access outside the helpers, no Java interop, and no async runtime. Scripts are plain, synchronous JavaScript from top to bottom. See [The sandbox](/docs/autessascript/sandbox.md) for exactly what is and isn't available. ## When to use it Agents call tools, and most tools are declarative configs — an [API_CALL](/docs/tools/core.md) hits one endpoint, an LLM_CALL runs one prompt. Reach for AUTESSA_SCRIPT when a single declarative tool isn't enough: - **Custom logic** — validate inputs, branch on values, compute derived data before deciding what to do. - **Chaining** — call an API, feed the result to an LLM, then write the outcome to AutessaDB, all inside one tool invocation via [`tools.invokeById`](/docs/autessascript/helpers/tools.md). - **Transforms** — reshape a third-party API response into exactly the JSON your agent or table expects. - **Direct third-party access** — pull an OAuth token from a [connector](/docs/autessascript/helpers/connector.md) and call the vendor's API in whatever shape it demands. If one HTTP request or one LLM prompt does the job, prefer the plain API_CALL or LLM_CALL tool type — less code to maintain, and the config is inspectable at a glance. ## A minimal working example A script tool declares arguments the same way every tool does — as `@@{name}` placeholders — and those arrive in the `variables` binding. The one hard rule: **you must call `response.setResponse(...)`**, or the script produces no output at all. ```javascript // Tool argument "customerName" arrives as a variable. const name = variables.get("customerName"); if (!name || name.trim() === "") { response.setResponse(JSON.stringify({ error: "customerName is required" })); } else { const greeting = "Hello, " + name.trim() + "!"; console.log("Built greeting for", name); response.setResponse(JSON.stringify({ greeting: greeting })); } ``` `console.log` output lands in the execution logs, and the string passed to `response.setResponse` becomes the tool's result. > Values returned by `variables.get(...)` in a tool execution are always strings. Parse numbers with `parseInt`/`parseFloat` and JSON payloads with `JSON.parse` yourself. ## Where you write it - **Script tools** — create a tool with type `AUTESSA_SCRIPT` in the tool builder. The editor autocompletes helper methods and your declared variables, and [Script Copilot](/docs/guide/autobots/script-copilot.md) can write and debug the script with you. See [Tools](/docs/guide/agentic/tools.md) for the general tool workflow. - **Computed columns** — attach a script to an AutessaDB column and it computes the column's value from the other columns of the row on every insert and update. The execution context is slimmer there; see [Computed columns](/docs/autessascript/computed-columns.md). ## In this section - [The sandbox](/docs/autessascript/sandbox.md) — execution model, what's blocked, synchronous-only semantics, log capture. - [Global bindings](/docs/autessascript/bindings.md) — `console`, `response`, `variables`, and `responseStream` in full. - [API helper](/docs/autessascript/helpers/api.md) — `api.get/post/put/patch/delete` with SSRF protection. - [LLM helper](/docs/autessascript/helpers/llm.md) — `llm.invoke` for in-script model calls. - [Connector helper](/docs/autessascript/helpers/connector.md) — OAuth tokens and metadata from your connected accounts. - [Math and wait utilities](/docs/autessascript/helpers/utilities.md) — `mathUtils` and `waitUtils`. - [Tool invocation helper](/docs/autessascript/helpers/tools.md) — `tools.invokeById` for scripting other tools. - [Computed columns](/docs/autessascript/computed-columns.md) — the AutessaDB execution context. - [Examples](/docs/autessascript/examples.md) — complete, copy-pasteable recipes. - [Limits and errors](/docs/autessascript/limits-errors.md) — error taxonomy, timeouts, debugging workflow. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Core tool types](/docs/tools/core.md) - [Script Copilot](/docs/guide/autobots/script-copilot.md) - [AutessaDB Tables](/docs/guide/autessadb/tables.md) --- # The Sandbox Source: https://docs.autessa.com/docs/autessascript/sandbox > AutessaScript's execution model: synchronous-only, no I/O outside helpers, no Java interop, and captured console output. # The Sandbox Every AutessaScript execution runs in a fresh context inside a hardened, sandboxed JavaScript engine. The sandbox gives you the full modern ECMAScript language — and deliberately nothing else. This page describes exactly what the sandbox allows, what fails, and how output is captured. ## Execution model: synchronous only A script is evaluated once, top to bottom, on a single thread. When the last statement finishes, the platform reads whatever you passed to `response.setResponse(...)` and the execution is over. There is no event loop, no timers, and no background work: - **`setTimeout`, `setInterval`, `fetch`, `XMLHttpRequest` are not defined.** Calling any of them throws a `ReferenceError` — they are browser/Node APIs and no host provides them here. - **`require`, `process`, `fs`, `path`, `window`, `document` are not defined** either. There is no module system; everything you need is already bound as a global. - **Top-level `await` is a syntax error.** The engine reports `await is only valid in async functions`, and the script fails before running at all. - **`Promise` exists as ECMAScript syntax, but nothing can resolve one asynchronously.** With no timers and no async I/O, a `.then(...)` callback has nothing to wait on — promise-style code either never settles or adds confusion for no benefit. Write straight-line code. The helpers are the flip side of this design: `api.get(...)`, `llm.invoke(...)`, and `tools.invokeById(...)` are **blocking** Java calls. They pause the script until the HTTP response, LLM completion, or tool result comes back, then return it as a plain string. No callbacks, no `await` — just assignment: ```javascript // This blocks until the request completes, then continues. const raw = api.get(JSON.stringify({ endpoint: "https://api.example.com/status" })); const result = JSON.parse(raw); console.log("status code:", result.statusCode); response.setResponse(result.body); ``` ## What the sandbox blocks The sandbox context is created with every escape hatch disabled: - **No Java interop.** Host class lookup is off, so `Java.type(...)` and any reflection into the JVM fail. Only methods explicitly exported on the helper objects are callable. - **No filesystem.** File I/O is disabled at the engine level. - **No process or thread creation.** Scripts cannot spawn processes or threads. - **No native code.** Native access is disabled. - **No environment variables.** The engine cannot read the host's environment. The platform passes context to your script exclusively through the `variables` binding (including platform values like `AUTESSA_COMPANY_ID`). - **No network — except through `api.*`.** The [API helper](/docs/autessascript/helpers/api.md) is the only way to make HTTP requests, and every URL is validated before the request is sent: requests to private networks, internal infrastructure, and non-HTTP(S) schemes are rejected with a validation error. ## ECMAScript surface The *language* itself is fully modern: `let`/`const`, arrow functions, template literals, destructuring, spread, classes, `Map`/`Set`, `JSON`, `RegExp`, `Math`, `Date`, and the standard string/array methods all work as you'd expect. What's missing is the *host environment* (browser or Node APIs), not the language. ```javascript const rows = JSON.parse(variables.get("payload")) .filter(r => r.active) .map(({ id, name }) => `${id}: ${name.toUpperCase()}`); response.setResponse(rows.join("\n")); ``` ## Determinism Prefer deterministic scripts: same inputs, same output. `Date.now()`, `Math.random()`, and `mathUtils.random()` are available but make results irreproducible, which matters most in [computed columns](/docs/autessascript/computed-columns.md) — a column recomputed on the next row update should not silently change value when its inputs didn't. Nondeterminism is fine when it's the point (jitter, sampling, request IDs); just keep it out of logic that other columns or tools depend on. ## Console output and execution logs Everything you write via `console.log/info/warn/error/debug` is captured into the execution's log list with a timestamp and level, e.g. `[2026-07-01 14:02:11.482] [INFO] parsed 12 rows`. Logs are returned alongside the result as the execution's debugging info, and they are your primary debugging tool — see [Limits and errors](/docs/autessascript/limits-errors.md) for the workflow. Two extra capture channels exist for completeness: anything written directly to the engine's stdout/stderr streams is appended to the logs as `[OUTPUT] ...` or `[ERROR] ...` entries. Stick to `console.*`; it's timestamped and leveled. > If your script throws, the platform's wrapper logs the error's stack trace via `console.error` before failing the execution — so the logs always contain the failure point, even for uncaught exceptions. ## Related - [Global bindings](/docs/autessascript/bindings.md) - [Limits and errors](/docs/autessascript/limits-errors.md) - [API helper](/docs/autessascript/helpers/api.md) - [AutessaScript overview](/docs/autessascript.md) --- # Global Bindings Source: https://docs.autessa.com/docs/autessascript/bindings > The four global objects every AutessaScript sees: console, response, variables, and responseStream. # Global Bindings Four objects are injected into every script's global scope before it runs: console for logging, response for returning the result, variables for reading inputs, and responseStream for streaming progress (streaming executions only). Everything else — api, llm, connector, tools, mathUtils, waitUtils — is a helper library covered in the helper pages. ## console Captured logging. Nothing goes to a server console — every call is recorded into the execution's log list with a timestamp and level, in the format `[yyyy-MM-dd HH:mm:ss.SSS] [LEVEL] message`. All five methods accept any number of arguments of any type; arguments are stringified and joined with single spaces. - `console.log` — void"> Records a `LOG`-level entry. - `console.info` — void"> Records an `INFO`-level entry. - `console.warn` — void"> Records a `WARN`-level entry. - `console.error` — void"> Records an `ERROR`-level entry. - `console.debug` — void"> Records a `DEBUG`-level entry. ```javascript console.log("processing", 3, "items"); // -> [2026-07-01 14:02:11.482] [LOG] processing 3 items ``` > Arguments are converted with plain string coercion, not JSON serialization — a JS object logs as an unhelpful opaque string. Log `JSON.stringify(obj)` when you want to see the contents. ## response How your script returns its result. **Calling `response.setResponse(...)` is required.** If the script finishes without calling it, the execution's final response is empty: a script tool returns no result to the agent, and a computed column writes no value. There is no implicit "last expression" return. - `response.setResponse` — void"> Sets the script's final output. Pass a string. Non-string values are coerced with string conversion — so for structured data, always pass `JSON.stringify(obj)` rather than the object itself. Calling it again replaces the previous value; the last call wins. - `response.getFinalResponse` — string"> Returns the value set so far (or nothing if `setResponse` hasn't been called). Occasionally useful for append-style patterns. ```javascript const result = { status: "ok", count: 42 }; response.setResponse(JSON.stringify(result)); // correct // response.setResponse(result); // wrong: coerced to a string, not JSON ``` ## variables Read-only access to the script's inputs. In a script tool this map contains every `@@{name}` argument the tool declares — the same placeholder mechanism all Autessa tools use — plus platform context values such as `AUTESSA_COMPANY_ID`, `AUTESSA_TOOL_NAME`, and `AUTESSA_AGENT_NAME`. A tool config containing `@@{customerName}` means your script reads `variables.get("customerName")`. - `variables.get` — string"> Returns the value for `key`, or `null` if absent. In tool executions every value is a string — parse numbers and JSON yourself. (In computed columns, values keep their native column types; see [Computed columns](/docs/autessascript/computed-columns.md).) - `variables.has` — boolean"> Whether `key` is present. - `variables.keys` — string[]"> All variable names. Iterable from JavaScript. - `variables.size` — number"> Number of variables. ```javascript if (!variables.has("orderId")) { response.setResponse(JSON.stringify({ error: "orderId is required" })); } else { const orderId = variables.get("orderId"); // string, e.g. "1042" const qty = parseInt(variables.get("quantity")); // parse it yourself console.log("keys:", Array.from(variables.keys()).join(", ")); // ... work ... response.setResponse(JSON.stringify({ orderId, qty })); } ``` > `variables` is a method-based accessor, not a plain object — `variables.orderId` and `variables["orderId"]` do not work. Always go through `variables.get("orderId")`. ## responseStream Intermediate progress messages for **streaming executions only** — for example a script tool invoked inside a live agent conversation. Each message is emitted to the caller immediately, before the script finishes; the final result still comes from `response.setResponse(...)`. - `responseStream.addStreamMessage` — void"> Emits one streaming message tagged with the tool name and invocation id. ```javascript responseStream.addStreamMessage("Fetching orders..."); const orders = JSON.parse(api.get(JSON.stringify({ endpoint: url }))); responseStream.addStreamMessage("Fetched " + orders.length + " orders, summarizing..."); ``` **When the execution is not streaming, `responseStream` is not bound at all** — referencing it throws `ReferenceError: responseStream is not defined`. That includes computed columns and non-streaming tool runs. Guard if your script must work in both modes: ```javascript if (typeof responseStream !== "undefined") { responseStream.addStreamMessage("working..."); } ``` ## Related - [The sandbox](/docs/autessascript/sandbox.md) - [Computed columns](/docs/autessascript/computed-columns.md) - [Examples](/docs/autessascript/examples.md) - [AutessaScript overview](/docs/autessascript.md) --- # API Helper Source: https://docs.autessa.com/docs/autessascript/helpers/api > The api helper: SSRF-protected HTTP requests from AutessaScript with get, post, put, patch, and delete. # API Helper The api helper is the only way to make HTTP requests from AutessaScript. Every method takes a JSON string describing the request, blocks until the response arrives, and returns a JSON string envelope containing the status code, body, and headers. All requests pass through the platform's SSRF protection before leaving the box. ## Methods Requests and responses are JSON *strings* on both sides — build the request with `JSON.stringify(...)` and unpack the result with `JSON.parse(...)`. - `api.get` — string"> Sends a GET request. No body. - `api.post` — string"> Sends a POST request. The optional second argument is the raw request body; if omitted, a `body` field inside `requestJson` is used instead. - `api.put` — string"> Sends a PUT request. Body handling is identical to `post`. - `api.patch` — string"> Sends a PATCH request. Body handling is identical to `post`. - `api.delete` — string"> Sends a DELETE request. No body variant — only the `body` field inside `requestJson` is honored. ## The request JSON - `endpoint` (string, required, required) — Full URL including scheme. Only `http` and `https` are allowed. - `headers` (object, optional) — Header name to string value. Set your own `Authorization` header here for authenticated calls. - `body` (any, optional) — Request body, serialized as JSON and sent verbatim. Ignored when the separate `body` argument to `post`/`put`/`patch` is provided (the argument wins). Body rules worth knowing: - Whenever a body is present (argument or field), `Content-Type: application/json` is set automatically. - `POST`, `PUT`, and `PATCH` with no body at all send an empty body. - Redirects are **never** followed — a `301`/`302` comes back to your script as-is, with the `Location` header intact. ```javascript // GET with headers const raw = api.get(JSON.stringify({ endpoint: "https://api.example.com/v1/orders?status=open", headers: { "Authorization": "Bearer " + variables.get("apiToken") } })); // POST with a body field const created = api.post(JSON.stringify({ endpoint: "https://api.example.com/v1/orders", headers: { "Authorization": "Bearer " + variables.get("apiToken") }, body: { sku: "A-100", quantity: 2 } })); // POST with a separate body argument (handy for pre-serialized payloads) const alsoCreated = api.post( JSON.stringify({ endpoint: "https://api.example.com/v1/orders" }), JSON.stringify({ sku: "A-100", quantity: 2 }) ); ``` ## The response envelope Every call returns a JSON string with the same shape — parse it and check `statusCode`: ```json { "statusCode": 200, "body": "{\"orders\":[...]}", "headers": { "content-type": "application/json", "x-request-id": "abc123" } } ``` - `statusCode` (number) — The HTTP status code, or `-1` when the request never completed (see errors below). - `body` (string) — The raw response body. If the endpoint returns JSON, `JSON.parse` it yourself — it arrives as a string. - `headers` (object) — Response headers; multi-valued headers are joined with `", "`. ```javascript const res = JSON.parse(raw); if (res.statusCode >= 200 && res.statusCode < 300) { const data = JSON.parse(res.body); // second parse: body is a string response.setResponse(JSON.stringify({ count: data.orders.length })); } else { response.setResponse(JSON.stringify({ error: "upstream returned " + res.statusCode })); } ``` > Two parses are always needed for JSON APIs: one for the envelope, one for `body`. Forgetting the second parse is the most common `api.*` mistake. ## Error shape When the request fails before an HTTP response exists (blocked URL, network error, interrupt), the envelope carries error fields instead, with `statusCode: -1`: ```json { "error": true, "errorCode": "SSRF_BLOCKED", "errorMessage": "Requests to private addresses are not allowed", "statusCode": -1, "body": "", "headers": {} } ``` - `SSRF_BLOCKED` (errorCode) — The URL failed SSRF validation (see below). - `SECURITY_ERROR` (errorCode) — The SSRF protection service wasn't available, so the request was refused outright. - `IO_ERROR` (errorCode) — Network-level failure: DNS, connection refused, timeout, TLS. - `INTERRUPTED` (errorCode) — The request thread was interrupted mid-flight. - `UNKNOWN_ERROR` (errorCode) — Anything else — malformed request JSON, unexpected runtime failure. Check `res.error === true` (or `statusCode === -1`) before treating the envelope as an HTTP response. ## SSRF protection Every `endpoint` is validated before the request is sent; requests to private networks, internal infrastructure, and non-HTTP(S) schemes are rejected with `SSRF_BLOCKED`. Timeouts: 30 seconds to establish a connection, 60 seconds for the full request. A timeout surfaces as `IO_ERROR`. ## Related - [Connector helper](/docs/autessascript/helpers/connector.md) — get OAuth tokens to use in `Authorization` headers - [The sandbox](/docs/autessascript/sandbox.md) - [Examples](/docs/autessascript/examples.md) - [AutessaScript overview](/docs/autessascript.md) --- # LLM Helper Source: https://docs.autessa.com/docs/autessascript/helpers/llm > The llm helper: call a platform LLM from inside AutessaScript with llm.invoke and parse the JSON result. # LLM Helper The llm helper runs a single, synchronous LLM completion from inside your script — extract fields from messy text, classify an input, draft a reply — and returns the result as a JSON string. Metering and model access are handled by the platform; there are no keys to manage. ## llm.invoke - `llm.invoke` — string"> Runs one completion. `systemPrompt` sets behavior and output format (pass `""` to skip it); `userPrompt` is the user-turn content. Blocks until the model responds, up to 120 seconds. - `llm.invoke` — string"> Same, with an explicit cap on completion tokens. Values of `0` or less are ignored (the model default applies). The model is chosen by the platform: scripts run against Autessa's small instruction-following tier — fast and inexpensive, suited to extraction, classification, and short rewrites rather than long-form reasoning. Model and temperature are not configurable from the script. For heavyweight generation, use a dedicated LLM_CALL tool and chain it via [`tools.invokeById`](/docs/autessascript/helpers/tools.md). ## Return format Always a JSON string — `JSON.parse` it and check `success`: ```json { "success": true, "id": "chatcmpl-abc123", "content": "{\"sentiment\": \"positive\", \"topics\": [\"billing\"]}", "usage": { "promptTokens": 184, "completionTokens": 31, "totalTokens": 215 } } ``` - `success` (boolean) — `true` when the completion succeeded. - `id` (string) — Provider-side completion id. - `content` (string) — The model's text output. If you asked for JSON, parse this string as a second step. - `usage` (object, optional) — `promptTokens`, `completionTokens`, `totalTokens` when the provider reports usage. On failure the envelope is `{ "success": false, "errorCode": "...", "errorMessage": "..." }` with `errorCode` one of: - `CLIENT_NOT_AVAILABLE` (errorCode) — The LLM client wasn't initialized in this environment. - `INVOCATION_ERROR` (errorCode) — The call failed or timed out (120-second limit); `errorMessage` carries the cause. ## Prompt tips - **Pin the output format in the system prompt.** "Respond with only a JSON object, no prose, no code fences" makes the double-parse pattern below reliable. - **Keep it single-shot.** There is no conversation state between `invoke` calls; include everything the model needs in each call. - **Set `maxTokens` for classification tasks.** A tight cap keeps latency and cost down when you only need a word or a small object. - **Expect occasional format drift.** Wrap the inner `JSON.parse` in a `try`/`catch` and fall back gracefully. ## Example: extract, then use ```javascript const ticket = variables.get("ticketBody"); const raw = llm.invoke( "You extract structured data from support tickets. " + "Respond with only a JSON object: {\"sentiment\": \"positive|neutral|negative\", " + "\"product\": string, \"needsHuman\": boolean}. No prose, no code fences.", ticket, 200 ); const result = JSON.parse(raw); if (!result.success) { response.setResponse(JSON.stringify({ error: result.errorCode + ": " + result.errorMessage })); } else { let extracted; try { extracted = JSON.parse(result.content); // parse the model's JSON } catch (e) { extracted = { sentiment: "neutral", product: "unknown", needsHuman: true }; console.warn("model returned non-JSON, using fallback:", result.content); } console.log("tokens used:", result.usage ? result.usage.totalTokens : "n/a"); response.setResponse(JSON.stringify(extracted)); } ``` > `llm.invoke` returns a JSON *envelope string*, and `content` inside it is *another* string. Getting to your extracted object takes two parses — the same double-parse pattern as the [API helper](/docs/autessascript/helpers/api.md). ## Related - [Tool invocation helper](/docs/autessascript/helpers/tools.md) — chain a configured LLM_CALL tool instead - [API helper](/docs/autessascript/helpers/api.md) - [Examples](/docs/autessascript/examples.md) - [AutessaScript overview](/docs/autessascript.md) --- # Connector Helper Source: https://docs.autessa.com/docs/autessascript/helpers/connector > The connector helper: pull OAuth tokens, API keys, and metadata from your connected accounts to call third-party APIs directly. # Connector Helper The connector helper hands your script the live credentials behind a connected account — Gmail, HubSpot, QuickBooks, or any other connector your company has linked — so you can call the vendor's API directly with api.*. Token refresh is handled for you: if the access token is stale, the helper refreshes it before returning. All methods are company-scoped: a script can only see connectors belonging to the company it runs in, and the helper requires the company context that tool execution provides. In contexts without it (such as [computed columns](/docs/autessascript/computed-columns.md)), calls return a `NO_COMPANY_CONTEXT` error. :::note Connector ids are sqid strings Pass the connector id as its **sqid string** — the `connectorId` returned by `connector.listConnectors()` (for example `"MrG4pjkPqiic"`). Raw numeric ids are **deprecated**: they still work for now but log a deprecation warning and will stop being accepted in a future release. ::: ## Methods - `connector.getAuth` — string"> Everything needed for an authenticated call: access token, base URL (when the connector type has one), connector metadata, and any type-specific extra properties. Refreshes the token first if needed. - `connector.getAccessToken` — string"> Just the access token (or API key), refreshed if needed. Lighter envelope when you already know the endpoint. - `connector.getMetadata` — string"> The connector's type-specific metadata (account info, base URL, realm ids, and so on) without touching credentials. - `connector.listConnectors` — string"> All active connectors for the current company — use this to discover connector ids from inside a script. The `connectorId` it returns is the sqid to pass back into the other methods. ## Response shapes Every method returns a JSON string with `success` plus method-specific fields. `connector.getAuth(connectorId)`: ```json { "success": true, "connectorType": "GMAIL", "connectorName": "Support Inbox", "accessToken": "ya29.a0Af...", "baseUrl": "https://gmail.googleapis.com", "additionalProperties": { "...": "type-specific extras when present" }, "metadata": { "...": "type-specific connector metadata when present" } } ``` `baseUrl`, `additionalProperties`, and `metadata` are present only when the connector type provides them — check before using. `connector.getAccessToken(connectorId)`: ```json { "success": true, "accessToken": "ya29.a0Af..." } ``` `connector.getMetadata(connectorId)`: ```json { "success": true, "connectorType": "QUICKBOOKS", "metadata": { "...": "..." } } ``` `connector.listConnectors()`: ```json { "success": true, "connectors": [ { "connectorId": "MrG4pjkPqiic", "connectorSqid": "MrG4pjkPqiic", "connectorName": "Support Inbox", "connectorType": "GMAIL", "isActive": true }, { "connectorId": "9xTbQ2vWz0Ai", "connectorSqid": "9xTbQ2vWz0Ai", "connectorName": "Main CRM", "connectorType": "HUBSPOT", "isActive": true } ] } ``` ## Error codes Failures return `{ "success": false, "errorCode": "...", "errorMessage": "..." }`: - `CONNECTOR_NOT_FOUND` (errorCode) — No connector with that id in this company. Call `connector.listConnectors()` to see what exists. - `CONNECTOR_INACTIVE` (errorCode) — The connector is disabled — re-enable it in settings. - `REFRESH_TOKEN_EXPIRED` (errorCode) — The refresh token was revoked upstream; the connector must be re-authenticated by a user. - `TOKEN_REFRESH_FAILED` (errorCode) — The refresh attempt failed for another reason (transient upstream errors included). - `NO_CREDENTIALS` (errorCode) — The connector exists but holds no usable access token for this type. - `NO_COMPANY_CONTEXT` (errorCode) — The script isn't running with a company context (e.g. a computed column) — connector access requires tool execution. - `SERVICE_NOT_INITIALIZED` (errorCode) — Connector services weren't available yet; retry later. - `INTERNAL_ERROR` (errorCode) — Unexpected failure; `errorMessage` carries details. ## Example: call the Gmail API with a connector token `getAuth` plus `api.get` is the whole pattern — the token goes straight into an `Authorization` header: ```javascript const auth = JSON.parse(connector.getAuth(12)); // your Gmail connector's id if (!auth.success) { response.setResponse(JSON.stringify({ error: auth.errorCode + ": " + auth.errorMessage })); } else { const res = JSON.parse(api.get(JSON.stringify({ endpoint: "https://gmail.googleapis.com/gmail/v1/users/me/messages?q=is:unread&maxResults=5", headers: { "Authorization": "Bearer " + auth.accessToken } }))); if (res.statusCode !== 200) { response.setResponse(JSON.stringify({ error: "Gmail returned " + res.statusCode })); } else { const inbox = JSON.parse(res.body); console.log("unread messages:", inbox.resultSizeEstimate); response.setResponse(JSON.stringify({ unread: inbox.resultSizeEstimate, messageIds: (inbox.messages || []).map(m => m.id) })); } } ``` > Prefer a dedicated connector tool type when one exists for your operation — it handles the vendor's API shape for you. The `connector` helper is for the gaps: endpoints no tool type covers yet, or vendor-specific parameters a config can't express. ## Related - [API helper](/docs/autessascript/helpers/api.md) - [Tool invocation helper](/docs/autessascript/helpers/tools.md) - [Examples](/docs/autessascript/examples.md) - [AutessaScript overview](/docs/autessascript.md) --- # Math and Wait Utilities Source: https://docs.autessa.com/docs/autessascript/helpers/utilities > The mathUtils and waitUtils helpers: arithmetic, random numbers, and pausing script execution. # Math and Wait Utilities Two small helpers round out the library: mathUtils for arithmetic and random numbers, and waitUtils for pausing execution. Unlike the other helpers, these return plain values — no JSON envelopes to parse. ## mathUtils Arithmetic that returns numbers directly. JavaScript's own `Math` and operators cover most of this too; `mathUtils` is useful when inputs arrive as strings (as [tool variables always do](/docs/autessascript/bindings.md)) and for its explicit failure modes. - `mathUtils.add` — number"> Adds two values. The string-accepting overload parses each argument as a number first — handy for `variables.get(...)` values without an explicit `parseFloat`. Non-numeric strings throw. - `mathUtils.subtract` — number"> Returns `a - b`. - `mathUtils.multiply` — number"> Returns `a * b`. - `mathUtils.divide` — number"> Returns `a / b`. Throws an error on division by zero (unlike JavaScript's `/` operator, which returns `Infinity`). - `mathUtils.power` — number"> Returns `base` raised to `exponent`. - `mathUtils.sqrt` — number"> Square root. Throws an error for negative input (unlike `Math.sqrt`, which returns `NaN`). - `mathUtils.random` — number"> Random number in `[0, 1)`. - `mathUtils.randomBetween` — number"> Random number in `[min, max)`. ```javascript const price = mathUtils.add(variables.get("basePrice"), variables.get("surcharge")); // strings ok const perUnit = mathUtils.divide(price, parseInt(variables.get("quantity"))); // throws if quantity is "0" response.setResponse(JSON.stringify({ perUnit: perUnit.toFixed(2) })); ``` > `mathUtils.divide` and `mathUtils.sqrt` throw real errors instead of returning `Infinity`/`NaN`. That's a feature — a thrown error fails the execution loudly instead of letting a silent `NaN` propagate into your result — but wrap them in `try`/`catch` if you want to handle the case yourself. ## waitUtils - `waitUtils.sleep` — void"> Blocks the script for the given number of milliseconds. Throws an error if the wait is interrupted by the platform. There is no cap on the argument itself, but sleeping is never free: the script is fully blocked while it sleeps, and the time counts against the surrounding execution's overall time budget. Keep sleeps short — polling backoff between `api.*` calls is the intended use: ```javascript let status = "pending"; for (let attempt = 0; attempt < 5 && status === "pending"; attempt++) { if (attempt > 0) waitUtils.sleep(2000); // 2s between polls const res = JSON.parse(api.get(JSON.stringify({ endpoint: jobUrl }))); status = JSON.parse(res.body).status; console.log("attempt", attempt + 1, "status:", status); } response.setResponse(JSON.stringify({ status: status })); ``` Remember that `setTimeout` does not exist in the [sandbox](/docs/autessascript/sandbox.md) — `waitUtils.sleep` is the only way to pause, and it pauses everything. ## Related - [The sandbox](/docs/autessascript/sandbox.md) - [API helper](/docs/autessascript/helpers/api.md) - [Limits and errors](/docs/autessascript/limits-errors.md) - [AutessaScript overview](/docs/autessascript.md) --- # Tool Invocation Helper Source: https://docs.autessa.com/docs/autessascript/helpers/tools > The tools helper: invoke any published tool from AutessaScript with tools.invokeById, making scripts the orchestrator. # Tool Invocation Helper The tools helper lets a script invoke any published tool in its company by id — the same tools your agents call, executed through the same config, permission, and variable-substitution pipeline. It turns a script into an orchestrator: fetch with one tool, decide in code, write with another. ## tools.invokeById - `tools.invokeById` — string"> Invokes the tool with the given id and returns its result as a JSON string. `toolId` is the tool's ID string (`"V1StGXR8Z5"`). `variablesJson` is an optional JSON object string supplying values for the tool's declared `@@{name}` placeholders; pass `null` or omit it for none. There is deliberately **no invoke-by-type**: scripts reference concrete, published tools that users can see, edit, and revoke in the tool builder — never anonymous tool types. See [Tools](/docs/guide/agentic/tools.md) for how tools are published. ## How variables merge into the tool The invoked tool's stored config is the source of truth — you never override its shape, you only fill in the `@@{name}` placeholders it declares: - **Strings, numbers, booleans** become string substitutions for matching `@@{name}` placeholders anywhere in the config. `null` substitutes an empty string. - **Objects and arrays** can't survive string substitution into typed config fields, so they are spliced in raw — but only where a config field's value is *exactly* `"@@{name}"`. A placeholder embedded inside a longer string only accepts scalar values. - The enclosing script's own variables (including platform context like `AUTESSA_COMPANY_ID` and private variables) are inherited by the invoked tool automatically — you don't forward them yourself. ```javascript // Tool V1StGXR8Z5 is an AutessaDB update tool whose config contains // "values": "@@{values}" and "rowId": "@@{rowId}" const result = tools.invokeById("V1StGXR8Z5", JSON.stringify({ rowId: 99, // scalar -> string substitution values: { summary: { textValue: "hello" } } // object -> spliced raw })); ``` ## Access rules - **Same company only.** The tool must belong to the company the script runs in; anything else returns `CROSS_COMPANY`. - **Permissions apply.** Tools private to another user, without an explicit grant, are refused — the invocation goes through the same permission pipeline as agent and API calls. ## Return envelope On success you get the invoked tool's result as a JSON string, unwrapped so a single `JSON.parse` works regardless of how the executor produced it: - A structured result (object/array) arrives as that JSON document. - A result that was already a JSON string arrives verbatim — no double-encoding, one `JSON.parse` is enough. - A plain-text or numeric result arrives as a valid JSON literal (e.g. `"done"` or `123`), so `JSON.parse` never fails. On failure you get an error envelope instead: ```json { "error": true, "errorCode": "TOOL_NOT_FOUND", "errorMessage": "No tool with id V1StGXR8Z5" } ``` Always check for `error` before using the result: ```javascript const parsed = JSON.parse(tools.invokeById("V1StGXR8Z5")); if (parsed && parsed.error) { console.error("tool failed:", parsed.errorCode, parsed.errorMessage); } ``` ## Error codes - `BAD_TOOL_ID` (errorCode) — `toolId` couldn't be resolved — null, empty, not a valid tool ID, or an unsupported type. - `NO_SPRING_CONTEXT` (errorCode) — The platform application context wasn't available yet; retry later. - `TOOL_NOT_FOUND` (errorCode) — No tool exists with that id. - `MISSING_COMPANY_ID` (errorCode) — The script has no company id in context (e.g. a computed column) — company-scoped dispatch is refused. - `BAD_COMPANY_ID` (errorCode) — The company id in context wasn't a valid number. - `CROSS_COMPANY` (errorCode) — The tool belongs to a different company. - `BAD_TOOL_CONFIG` (errorCode) — The tool exists but its stored config is unreadable or has no tool type. - `INVALID_CONFIG` (errorCode) — Building the executable config failed — usually a missing or wrongly-typed variable for a required placeholder. - `NULL_RESULT` (errorCode) — The executor ran but produced no result. - `EXECUTION_ERROR` (errorCode) — The tool executed and failed; `errorMessage` carries the underlying cause. ## Composition pattern: script as orchestrator The canonical use is read → decide → write, with each side effect going through a published, user-visible tool: ```javascript // 1. Read: a query tool returns matching rows. const rows = JSON.parse(tools.invokeById("kA9mB2xQ7p", JSON.stringify({ status: "overdue" }))); if (rows.error) { response.setResponse(JSON.stringify({ error: rows.errorMessage })); } else { // 2. Decide: pure JavaScript in between. const toEscalate = rows.filter(r => parseInt(r.daysOverdue) > 30); console.log("escalating", toEscalate.length, "of", rows.length, "rows"); // 3. Write: an update tool per row. const failures = []; for (const row of toEscalate) { const res = JSON.parse(tools.invokeById("pQ4rT8nW2c", JSON.stringify({ rowId: row.id, values: { priority: { textValue: "high" } } }))); if (res && res.error) failures.push({ rowId: row.id, error: res.errorCode }); } response.setResponse(JSON.stringify({ escalated: toEscalate.length - failures.length, failures: failures })); } ``` > Each `invokeById` call is synchronous and runs the full tool pipeline. Loops over many rows work, but budget accordingly — prefer a tool that operates on batches when one exists. ## Related - [Tools](/docs/guide/agentic/tools.md) - [Global bindings](/docs/autessascript/bindings.md) — how `@@{name}` placeholders map to `variables` - [Examples](/docs/autessascript/examples.md) - [AutessaScript overview](/docs/autessascript.md) --- # Computed Columns Source: https://docs.autessa.com/docs/autessascript/computed-columns > Running AutessaScript inside AutessaDB computed columns: the slimmer execution context, native-typed variables, and type conversion rules. # Computed Columns A computed column is an AutessaDB column whose value is produced by an AutessaScript instead of being written directly. The script reads the row's other columns as variables, returns one value with response.setResponse(...), and the platform converts that value to the column's type and merges it into the row. Same language, same sandbox — but a slimmer execution context than a script tool. See [AutessaDB Tables](/docs/guide/autessadb/tables.md) for creating tables and columns; this page covers what your script sees once it's attached to a column. ## When the script runs Computed columns are recomputed **asynchronously, in the background**, after the row write that triggers them: - **On insert**, every computed column in the table runs. - **On update**, a computed column runs only when one of its declared dependencies (see below) is among the updated columns. If an update writes a computed column directly, that column is *not* recomputed over your explicit value in the same update. Because computation is asynchronous, the insert or update returns immediately — with the row's computation status reported as `COMPUTING` — and the computed value appears in the row shortly after, once the script finishes. Scripts read a snapshot of the row as it was written; they never block the write itself. When several computed columns exist, they run in dependency order: a computed column that depends on another computed column sees the freshly computed value, not the stale one. A circular dependency between computed columns is rejected and none of them compute. ## Variables: the row, natively typed The `variables` binding contains the row's columns, keyed by column name. Unlike tool executions — where every variable is a string — computed columns receive **native types** per the column's type: | Column type | JavaScript value | | --- | --- | | `SHORT_TEXT`, `LONG_TEXT`, `SINGLE_SELECT` | string | | `MULTI_SELECT` | string (the selections in their combined string form) | | `INTEGER` | number | | `FLOAT` | number | | `BOOLEAN` | boolean | | `FILE`, `IMAGE`, `VIDEO`, `AUDIO` | string (the stored file path) | Details that matter in practice: - **Empty columns are absent, not null.** A column with no value doesn't appear in `variables` at all — guard with `variables.has("name")` before reading. - **Built-in fields are included**: `id` (number), `created_at` and `updated_at` (strings). - **`DATETIME` columns don't cross the boundary** — they are neither passed in as variables nor writable as a computed result. Store timestamps as `SHORT_TEXT` UTC strings if a computed column needs them. - Other computed columns' values are available too: previously stored values on update, and freshly computed values for columns earlier in the dependency order. ## Declaring dependencies A computed column's configuration includes a `dependsOn` list — the column names your script reads. It does three jobs: 1. **Update triggering.** After an update, only computed columns whose `dependsOn` intersects the updated columns are recomputed. A script that reads a column it doesn't declare will silently go stale on updates. 2. **Ordering.** Dependencies on other computed columns put your column later in the computation order. 3. **Validation.** A missing dependency is logged; a dependency cycle fails the whole computation pass. Declare every column the script reads. There is no automatic detection. ## Returning the value Call `response.setResponse(...)` with a **string**; the platform converts it to the column's type: | Column type | Conversion | | --- | --- | | `SHORT_TEXT`, `LONG_TEXT`, `SINGLE_SELECT` | stored as-is | | `MULTI_SELECT` | parsed from the combined multi-select string form | | `INTEGER` | parsed as an integer — must be a plain integer string like `"42"` | | `FLOAT` | parsed as a float | | `BOOLEAN` | `"true"` (case-insensitive) becomes `true`; anything else `false` | | `FILE`, `IMAGE`, `VIDEO`, `AUDIO` | stored as the file path | If the string doesn't parse (say, `"42.5"` into an `INTEGER` column), the conversion fails and **no value is written**. The same happens when the script throws or never calls `response.setResponse` — the column is simply left unset. One failing computed column doesn't stop the others. > Failures are silent from the table's point of view: there is no error cell, just an empty column and a server-side log you can't see. Develop and debug the script as a script tool first — where the [test window shows you logs and errors](/docs/autessascript/limits-errors.md) — then attach it to the column. ## What's missing compared to tool execution The script runs in the same sandbox with the same helpers bound, but the context is slimmer: - **No tool arguments.** There are no `@@{name}` placeholders — the variables are the row's columns, nothing else. - **No platform context.** `AUTESSA_COMPANY_ID`, `AUTESSA_TOOL_NAME`, and the other `AUTESSA_*` variables from tool execution are absent. As a consequence, company-scoped helpers refuse to run: [`tools.invokeById`](/docs/autessascript/helpers/tools.md) returns `MISSING_COMPANY_ID` and [`connector.*`](/docs/autessascript/helpers/connector.md) returns `NO_COMPANY_CONTEXT`. - **No `responseStream`.** Computed columns never stream; referencing `responseStream` throws a `ReferenceError`. See [Global bindings](/docs/autessascript/bindings.md). - `api`, `llm`, `mathUtils`, and `waitUtils` work normally. Keep computed-column scripts small, fast, and [deterministic](/docs/autessascript/sandbox.md) — derive from the row, return a value. Side effects and orchestration belong in script tools. ## Example: derive a display name A `SHORT_TEXT` column `full_name` with `dependsOn: ["first_name", "last_name"]`: ```javascript const first = variables.has("first_name") ? variables.get("first_name") : ""; const last = variables.has("last_name") ? variables.get("last_name") : ""; const full = (first + " " + last).trim(); if (full !== "") { response.setResponse(full); } // If both inputs are empty, setResponse is never called and the column stays unset. ``` ## Example: numeric derivation A `FLOAT` column `margin_percent` with `dependsOn: ["revenue", "cost"]`. Numeric columns arrive as real numbers — no `parseFloat` needed: ```javascript if (variables.has("revenue") && variables.has("cost")) { const revenue = variables.get("revenue"); // number const cost = variables.get("cost"); // number if (revenue > 0) { const margin = ((revenue - cost) / revenue) * 100; response.setResponse(margin.toFixed(2)); // "23.50" parses cleanly as FLOAT } } ``` ## Related - [AutessaDB Tables](/docs/guide/autessadb/tables.md) - [Global bindings](/docs/autessascript/bindings.md) - [Limits and errors](/docs/autessascript/limits-errors.md) - [AutessaScript overview](/docs/autessascript.md) --- # Examples Source: https://docs.autessa.com/docs/autessascript/examples > Complete, copy-pasteable AutessaScript recipes: API transforms, LLM chains, tool orchestration, connector calls, streaming, and validation. # Examples Six complete scripts you can paste into an AUTESSA_SCRIPT tool and adapt. Each one follows the house rules: inputs come in as strings through variables.get, helper results are JSON strings that need parsing, and the result always leaves through response.setResponse(JSON.stringify(...)). All recipes assume a script tool execution context. In [computed columns](/docs/autessascript/computed-columns.md) the context is slimmer — no connector or tool helpers, no `responseStream`. ## Validate inputs and return a structured error Validate first, fail with a machine-readable error the agent can relay or retry on, and only do the work when the inputs hold up. Declared variables: `email`, `amount`. ```javascript const errors = []; const email = variables.get("email"); if (!email || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { errors.push("email must be a valid email address"); } const amount = parseFloat(variables.get("amount")); // tool variables are strings if (isNaN(amount) || amount <= 0) { errors.push("amount must be a positive number"); } if (errors.length > 0) { console.warn("validation failed:", errors.join("; ")); response.setResponse(JSON.stringify({ ok: false, errors: errors })); } else { response.setResponse(JSON.stringify({ ok: true, email: email.trim().toLowerCase(), amountCents: Math.round(amount * 100) })); } ``` Expected output: `{"ok":true,"email":"ada@example.com","amountCents":4999}` on good input; `{"ok":false,"errors":["amount must be a positive number"]}` when validation fails — either way the script succeeds and the agent gets something it can act on. ## Fetch an API and reshape the response The classic transform tool: one `api.get`, two `JSON.parse` calls (envelope, then body), and a reshape so the agent sees only what it needs. Declared variables: `apiToken`. ```javascript const raw = api.get(JSON.stringify({ endpoint: "https://api.example.com/v1/orders?status=open", headers: { "Authorization": "Bearer " + variables.get("apiToken") } })); const res = JSON.parse(raw); // parse 1: the envelope if (res.error || res.statusCode !== 200) { response.setResponse(JSON.stringify({ error: res.error ? res.errorMessage : "upstream returned " + res.statusCode })); } else { const data = JSON.parse(res.body); // parse 2: the body string const summary = data.orders.map(o => ({ id: o.id, customer: o.customer.name, total: o.line_items.reduce((sum, li) => sum + li.price * li.quantity, 0) })); console.log("summarized", summary.length, "open orders"); response.setResponse(JSON.stringify({ count: summary.length, orders: summary })); } ``` Expected output: `{"count":2,"orders":[{"id":"ord_1","customer":"Acme","total":129.5}, ...]}` — a compact shape instead of the vendor's full payload. ## Extract with the LLM, then act on the result A two-step LLM chain: extract structured fields from free text, then branch on them — here, drafting a reply only for cancellation requests. Declared variables: `emailBody`. ```javascript const body = variables.get("emailBody"); // Step 1: extract structured fields. const extractRaw = llm.invoke( "You extract fields from customer emails. Respond with only a JSON object: " + "{\"intent\": \"cancellation|billing|question|other\", \"urgency\": \"low|high\"}. " + "No prose, no code fences.", body, 100 ); const extract = JSON.parse(extractRaw); if (!extract.success) { response.setResponse(JSON.stringify({ error: extract.errorCode + ": " + extract.errorMessage })); } else { let fields; try { fields = JSON.parse(extract.content); } catch (e) { fields = { intent: "other", urgency: "high" }; // format drift fallback } console.log("extracted:", JSON.stringify(fields)); // Step 2: branch — only cancellations get a drafted reply. let draft = null; if (fields.intent === "cancellation") { const draftRaw = llm.invoke( "You draft short, empathetic customer-service replies. Plain text only.", "Draft a reply acknowledging this cancellation request and asking for feedback:\n\n" + body, 300 ); const draftRes = JSON.parse(draftRaw); if (draftRes.success) draft = draftRes.content; } response.setResponse(JSON.stringify({ intent: fields.intent, urgency: fields.urgency, draft: draft })); } ``` Expected output: `{"intent":"cancellation","urgency":"high","draft":"Hi Sam, we're sorry to see you go..."}` — or `"draft": null` for non-cancellations. ## Read-modify-write AutessaDB rows through tools The script-as-orchestrator pattern from the [tools helper](/docs/autessascript/helpers/tools.md): read a row with a published get tool, decide in JavaScript, write back with an update tool. Here, tool `aB3dE5fGh1` fetches a ticket row and `J9kL2mNpQ4` updates it. Declared variables: `rowId`. ```javascript const row = JSON.parse(tools.invokeById("aB3dE5fGh1", JSON.stringify({ rowId: variables.get("rowId") }))); if (row && row.error) { response.setResponse(JSON.stringify({ error: row.errorCode + ": " + row.errorMessage })); } else { // Decide: escalate tickets with more than 3 replies that aren't already high priority. const replyCount = parseInt(row.reply_count) || 0; if (replyCount > 3 && row.priority !== "high") { const update = JSON.parse(tools.invokeById("J9kL2mNpQ4", JSON.stringify({ rowId: variables.get("rowId"), values: { priority: { textValue: "high" } } }))); if (update && update.error) { response.setResponse(JSON.stringify({ error: update.errorCode + ": " + update.errorMessage })); } else { console.log("escalated ticket", variables.get("rowId")); response.setResponse(JSON.stringify({ escalated: true, replyCount: replyCount })); } } else { response.setResponse(JSON.stringify({ escalated: false, replyCount: replyCount })); } } ``` Expected output: `{"escalated":true,"replyCount":5}` when the threshold is crossed, `{"escalated":false,...}` otherwise. Every side effect goes through a published, user-visible tool. ## Call a vendor API with a connector token When you already know the vendor endpoint, [`connector.getAccessToken`](/docs/autessascript/helpers/connector.md) plus `api.get` is the whole pattern — the platform refreshes the OAuth token for you. Here, connector `15` is a HubSpot connection. ```javascript const tok = JSON.parse(connector.getAccessToken(15)); if (!tok.success) { response.setResponse(JSON.stringify({ error: tok.errorCode + ": " + tok.errorMessage })); } else { const res = JSON.parse(api.get(JSON.stringify({ endpoint: "https://api.hubapi.com/crm/v3/objects/contacts?limit=10&properties=email,firstname", headers: { "Authorization": "Bearer " + tok.accessToken } }))); if (res.statusCode !== 200) { response.setResponse(JSON.stringify({ error: "HubSpot returned " + res.statusCode })); } else { const page = JSON.parse(res.body); response.setResponse(JSON.stringify({ contacts: page.results.map(c => ({ id: c.id, email: c.properties.email, firstName: c.properties.firstname })) })); } } ``` Expected output: `{"contacts":[{"id":"51","email":"ada@example.com","firstName":"Ada"}, ...]}`. Use `connector.listConnectors()` first if you don't know the connector id. ## Stream progress from a long-running script For scripts that poll or loop, `responseStream` keeps the caller informed while the work runs. It only exists in streaming executions, so guard every use — the same script then also works in non-streaming runs. Declared variables: `jobUrl`. ```javascript function progress(msg) { if (typeof responseStream !== "undefined") { responseStream.addStreamMessage(msg); } console.log(msg); // always land it in the logs too } progress("Starting export job..."); const start = JSON.parse(api.post(JSON.stringify({ endpoint: variables.get("jobUrl") }))); if (start.statusCode !== 202) { response.setResponse(JSON.stringify({ error: "job start returned " + start.statusCode })); } else { const statusUrl = JSON.parse(start.body).statusUrl; let status = "pending"; for (let attempt = 1; attempt <= 10 && status === "pending"; attempt++) { waitUtils.sleep(3000); // 3s between polls const poll = JSON.parse(api.get(JSON.stringify({ endpoint: statusUrl }))); status = JSON.parse(poll.body).status; progress("Poll " + attempt + "/10: " + status); } response.setResponse(JSON.stringify({ status: status, done: status === "complete" })); } ``` Expected output: the caller sees `Starting export job...`, `Poll 1/10: pending`, ... as they happen, then the final result `{"status":"complete","done":true}` (or `{"status":"pending","done":false}` if ten polls weren't enough). ## Related - [Global bindings](/docs/autessascript/bindings.md) - [Tool invocation helper](/docs/autessascript/helpers/tools.md) - [Limits and errors](/docs/autessascript/limits-errors.md) - [AutessaScript overview](/docs/autessascript.md) --- # Limits and Errors Source: https://docs.autessa.com/docs/autessascript/limits-errors > How AutessaScript failures are categorized and surfaced, what limits actually exist, and the debugging workflow. # Limits and Errors When a script fails, the platform categorizes the error, writes it into the execution logs, and returns the error message as the tool's result. This page covers the error taxonomy, how failures surface to agents and to you, what execution limits actually exist, and the workflow for debugging a misbehaving script. ## Error taxonomy Every failed execution is categorized into one of five types, based on the error the engine reported: - `JS_SYNTAX_ERROR` (category) — The script didn't parse — the engine reported a `SyntaxError` (including the `await is only valid in async functions` case from top-level `await`). Nothing ran; there are no logs from your code, only the error itself. - `JS_REFERENCE_ERROR` (category) — A `ReferenceError` — some identifier `is not defined`. The usual suspects: browser/Node APIs that don't exist in the [sandbox](/docs/autessascript/sandbox.md) (`fetch`, `setTimeout`, `require`), a typo'd helper name, or `responseStream` in a non-streaming execution. - `JS_TYPE_ERROR` (category) — A `TypeError` — calling something that isn't a function, reading a property of `undefined`. Frequently the downstream symptom of a missed `JSON.parse` (treating an envelope string as an object). - `JAVA_RUNTIME_ERROR` (category) — The failure happened in platform code around the script rather than inside the JavaScript engine — e.g. the execution environment itself failed. Rare; not something your script logic caused. - `UNKNOWN_ERROR` (category) — Anything else the engine threw that doesn't match the patterns above — including your own `throw new Error(...)`, other JS error types like `RangeError`, and errors thrown out of helper methods (say, `mathUtils.divide` by zero or an interrupted `waitUtils.sleep`). For the three `JS_*` categories, the categorized message is appended to the execution logs as an entry like `[JS_REFERENCE_ERROR] ReferenceError: fetchOrders is not defined`, so the logs always name the failure even when you logged nothing yourself. ## How errors surface Three layers work together: 1. **The wrapper logs the failure point.** Every script runs inside a platform-added `try`/`catch` that calls `console.error` with the error's stack trace (or name and message) before re-throwing. Runtime failures therefore always leave a trace in the logs, even if your code has no `try`/`catch` at all. 2. **The tool result becomes the error message.** For a script tool, a failed execution returns the raw error text (e.g. `TypeError: Cannot read property 'orders' of undefined`) as the tool's result — that is what the agent sees, and what it will react to or retry on. 3. **The debugging info carries everything.** Each execution's debugging info is the full execution result: the success flag, the final response, the error, and the complete log list (your `console.*` output, the wrapper's stack trace, the categorized error line). This is what the test window shows you. In [computed columns](/docs/autessascript/computed-columns.md) there is no tool response to carry the error: a failed script simply writes no value, and the column stays empty. Errors *inside* helpers are different from script errors: `api.*`, `llm.invoke`, `connector.*`, and `tools.invokeById` report their failures as **error envelopes in their return values** (`errorCode`/`errorMessage` fields), not as thrown exceptions. Your script keeps running — which is why every helper page tells you to check the envelope before using the result. An unchecked envelope usually resurfaces two lines later as a `JS_TYPE_ERROR`. ## Forgetting response.setResponse A script that finishes without calling `response.setResponse(...)` is **not an error** — the execution succeeds with an empty result: - A script tool returns nothing to the agent, which typically leaves it confused about whether the tool worked. - A computed column treats the empty result as "no value" and writes nothing (occasionally useful — see [Computed columns](/docs/autessascript/computed-columns.md) for the leave-unset pattern). Make `setResponse` unconditional: every branch, including error branches, should set *something*, even if it's `JSON.stringify({ error: "..." })`. ## Limits The sandbox itself imposes **no CPU, memory, or wall-clock cap on your script's own code** — a plain infinite loop is bounded only by the platform's surrounding task limits, not by an in-engine timer. The practical limits live in the helpers and the platform around the execution: - **`api.*`** — 30 seconds to establish a connection, 60 seconds for the full request; a timeout comes back as an `IO_ERROR` envelope. See [API helper](/docs/autessascript/helpers/api.md). - **`llm.invoke`** — 120 seconds per call; a timeout is an `INVOCATION_ERROR` envelope. See [LLM helper](/docs/autessascript/helpers/llm.md). - **`tools.invokeById`** — no timeout of its own, but each call synchronously runs the invoked tool's full pipeline, inheriting whatever limits that tool's type carries. - **`waitUtils.sleep`** — uncapped, but it blocks the whole script and burns the surrounding execution's time budget. - **The surrounding execution** — script tools run inside an agent task or API call, and the platform's task-level limits bound the total time. A script that outlives its caller gets its threads interrupted, which surfaces as `INTERRUPTED`-style helper errors or an `UNKNOWN_ERROR`. There are no enforced limits on script length, log volume, or response size beyond those platform task limits. ## Debugging workflow 1. **Log liberally with `console.*`.** Log inputs on entry, intermediate values after each helper call, and decisions at each branch. Remember plain object logging coerces to an opaque string — log `JSON.stringify(obj)`. Every entry lands timestamped and leveled in the execution logs. 2. **Run it in the test window.** The tool builder's test window executes the script with variables you supply and shows the full debugging info — result, error, and the complete log list, including the wrapper's stack trace and the categorized `[JS_*]` line for failures. Iterate here, not inside a live agent conversation. 3. **Hand it to Script Copilot.** [Script Copilot](/docs/guide/autobots/script-copilot.md) sits in the script editor, sees your script and helpers, and can diagnose a failing run or rewrite the script against the error you paste in. Two habits prevent most failures in the first place: parse every helper return (envelope first, body second) and check its error fields before touching the data; and validate `variables` up front, returning a structured error early — see the [validation recipe](/docs/autessascript/examples.md) for the pattern. ## Related - [The sandbox](/docs/autessascript/sandbox.md) - [Global bindings](/docs/autessascript/bindings.md) - [Script Copilot](/docs/guide/autobots/script-copilot.md) - [Examples](/docs/autessascript/examples.md) - [AutessaScript overview](/docs/autessascript.md) --- # Where your data lives Source: https://docs.autessa.com/docs/autessascript/guides/data-placement > Decide where records belong: an AutessaDB table you own, or the data a feature already provisions — and never mirror user-facing data in a second place. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # Where your data lives Decide where records belong: an AutessaDB table you own, or the data a feature already provisions — and never mirror user-facing data in a second place. **The decision:** you have data to store — customer records, extracted fields, a log of what a workflow did. Where does it go? ## The systems - **AutessaDB tables** — your own structured tables, created and owned through `autessa.autessadb.*`. Rows are user-editable, secured by row/column rules (see the *sharing-permissions* guide), and can drive automation through triggers (see the *automations* guide). This is the home for any data your workflow produces or curates. - **The data a feature already provisions** — many features keep their own authoritative records: meeting recordings live under `autessa.meetingRecorder.*`, e-signature envelopes under `autessa.eSignature.*`, agent conversations under `autessa.agents.*`. You read from those surfaces; you do not re-store their contents. ## Decision rules 1. **New structured data you own → an AutessaDB table.** Create it with `autessa.autessadb.tables.create`, then write rows with `autessa.autessadb.query.insert` and read them with `autessa.autessadb.query.query`. 2. **Data a feature already owns → leave it there; reference it.** If a meeting recording already exists, read it via `autessa.meetingRecorder.recordings.get` — do not copy its transcript into a table "to be safe". Store a *reference* (its id) plus only the derived fields you actually add. 3. **Never mirror user-facing data.** AutessaDB is the source of truth for the rows it holds. Do not keep a parallel "system of record" elsewhere; a second copy drifts and confuses the user who owns the row. 4. **Pick column types deliberately.** AutessaDB has twelve creatable column types: SHORT_TEXT, LONG_TEXT, ULTRA_LONG_TEXT, FILE, AUDIO, IMAGE, VIDEO, INTEGER, FLOAT, BOOLEAN, SINGLE_SELECT, MULTI_SELECT. ULTRA_LONG_TEXT has **no character limit** — values past a threshold are stored in S3 and re-inflated on read — but it cannot be vectorized (no semantic search) or indexed, so reach for it only when the value may exceed LONG_TEXT's cap. There is **no JSON column type** — serialize structured blobs into a LONG_TEXT column. There is **no DATETIME column** for your own timestamps — store times as SHORT_TEXT holding a UTC ISO-8601 string (`yyyy-MM-ddTHH:mm:ssZ`) so they sort and compare correctly. ## Worked examples **A. Summaries of meetings into a table.** Read each recording with `autessa.meetingRecorder.recordings.list`, then for the derived summary create a table once with `autessa.autessadb.tables.create` (a SHORT_TEXT `recordingId`, a LONG_TEXT `summary`, a SHORT_TEXT `summarizedAt`), and append rows with `autessa.autessadb.query.insert`. The transcript stays in the recorder; only your summary is new. **B. A structured blob with no native column.** You have a JSON payload to keep alongside a row. Add a LONG_TEXT column and write the JSON string into it — AutessaDB has no JSON type, so LONG_TEXT is the intended home. **C. An event timestamp.** Store `2026-07-16T09:30:00Z` in a SHORT_TEXT column, not a DATETIME column (there isn't one), so string comparison gives correct chronological order. ## See also - *automations* — how a table's rows trigger downstream work. - *sharing-permissions* — who can read and edit the rows you create. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # Making things happen automatically Source: https://docs.autessa.com/docs/autessascript/guides/automations > Choose between AutessaDB triggers, scheduled events, and custom webhooks — the three ways to run work without a person pressing a button. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # Making things happen automatically Choose between AutessaDB triggers, scheduled events, and custom webhooks — the three ways to run work without a person pressing a button. **The decision:** something should run on its own. What makes it fire — a row changing, a clock, or an outside system calling in? ## The systems - **AutessaDB triggers** (`autessa.autessadb.trigger.*`) — fire **per row** when a table row is inserted, updated, or deleted. A trigger runs a tool, an agent, a manager, or a write to another table. User-editable, and the natural way to build processing *chains*: a write into table A fires a trigger that writes table B, whose trigger writes table C. - **Scheduled events** (`autessa.scheduledEvents.*`) — fire on a **clock**: a recurring rate, a cron expression, or a one-time future moment. Each event invokes an agent, executes a tool, or writes AutessaDB rows. This is for "every Monday", "in 24 hours", "nightly". - **Custom webhooks** (`autessa.customWebhooks.*`) — fire on an **inbound event**: an outside system calls the webhook's public URL, and that runs a tool, agent, manager, or AutessaDB operation. The webhook can transform the incoming payload before it runs. ## Decision rules 1. **Reacting to your own data changing → a trigger.** If the cue is "a row was added/edited", add a trigger with `autessa.autessadb.trigger.add`. Key the trigger on the artifact columns that mean "ready to process", not on a status flag you flip by hand. 2. **Reacting to time → a scheduled event.** If the cue is a date or a cadence, use `autessa.scheduledEvents.create`. Toggle it on/off with `autessa.scheduledEvents.toggle` rather than deleting and recreating. 3. **Reacting to the outside world → a custom webhook.** If a third-party system needs to push an event in, create one with `autessa.customWebhooks.create` (its API key is returned exactly once — capture it then). 4. **Chain steps as triggers, not queues.** When a feature's records live in AutessaDB, a multi-step pipeline is a *chain of triggers on the tables*, each step firing the next — not a set of background queues. 5. **Retention belongs in triggers too.** To expire old rows, drive deletion from a trigger or a scheduled event that removes rows past their age, keeping the cleanup user-visible and user-editable rather than hidden in backend code. ## Worked examples **A. Enrich every new lead.** A trigger on the `leads` table (`autessa.autessadb.trigger.add`, on INSERT) runs a tool that fills in company data and writes it back to the same row. **B. Weekly digest.** A scheduled event (`autessa.scheduledEvents.create`, cron for Monday 09:00) executes a tool that queries the week's rows with `autessa.autessadb.query.query` and emails a summary. **C. Inbound form submissions.** A custom webhook (`autessa.customWebhooks.create`) receives a third-party form POST, transforms it, and inserts a row via the AutessaDB operation it runs — which then fires the enrichment trigger from example A, chaining the two. ## See also - *data-placement* — where the rows these automations read and write should live. - *agents-tools-managers* — what a trigger, event, or webhook actually invokes. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # Tools, agents, and managers Source: https://docs.autessa.com/docs/autessascript/guides/agents-tools-managers > Choose the right unit of work: a single-capability tool, a conversational agent, a multi-agent manager — or no LLM at all, just a script. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # Tools, agents, and managers Choose the right unit of work: a single-capability tool, a conversational agent, a multi-agent manager — or no LLM at all, just a script. **The decision:** you have a capability to build. Is it one deterministic action, one conversational assistant, or a team of assistants — and does it even need an LLM? ## The systems - **Tools** (`autessa.tools.*`) — a single capability. A tool is backed by a script, an LLM call, or a connector, takes typed inputs, and returns a result. Tools are the reusable verbs of the platform; triggers, scheduled events, and webhooks all invoke tools. - **Agents** (`autessa.agents.*`) — a conversational assistant with a persona, optional knowledge, and its own tools. Agents have a draft → publish lifecycle (`autessa.agents.update`, then `autessa.agents.publish`) and hold multi-turn conversations (`autessa.agents.execute`). Running an agent bills agent execution. - **Managers** (`autessa.agenticManagers.*`) — an orchestrator that runs a team of sub-agents on a task. Same draft → publish lifecycle. A manager conversation bills the *configured number of sub-agents* (the sub-agents do not bill separately) — see the *metering-billing* guide. ## Decision rules 1. **One deterministic action → a tool.** If the work is "do X to these inputs" with no need for conversation, build a tool with `autessa.tools.create` and configure it with `autessa.tools.update`. **Make it dynamic with arguments:** any value that changes per run must be declared under the tool's `arguments` (or `privateArguments` for secrets), then referenced in the config with the `@@{argumentName}` placeholder — it is substituted at execution time (an AUTESSA_SCRIPT tool reads them with `variables.get('name')` instead). **Declare each argument with its full shape** — an object with `description` (string), `isRequired` (boolean), and `argumentSchema` (a JSON-schema object that MUST contain a `type`, e.g. `{ "type": "string" }`). An argument written as just `{ description }` with no `argumentSchema` cannot be rendered or edited in the tool UI and cannot run — set `argumentSchema.type` (use `"string"` when unsure). **Then test it:** call `autessa.tools.execute` with sample values and check the result, so you confirm every argument you referenced is declared and resolves. A mistyped `@@{name}` is left unsubstituted rather than erroring, so executing is the only reliable way to catch a missing argument. Iterate update → publish → execute until it's right before attaching it to an agent. 2. **No judgement needed → skip the LLM entirely; just script it.** If plain code can do it, write an AutessaScript that calls the `autessa.*` SDK directly. Not every automation needs an agent; a script that queries a table and sends an email is cheaper and more predictable. 3. **A conversation with a user or a persona → an agent.** If the work is open-ended dialog, needs to search over documents, or should feel like an assistant, build a SIMPLE agent with `autessa.agents.create`, publish it with `autessa.agents.publish`, and talk to it with `autessa.agents.execute`. SIMPLE is the agent type you build — give it document search by attaching a semantic-query tool over a basic vectorized table (see the grounding-agent-on-documents guide). Other agent types are out of scope; don't reach for them. **An agent uses only the tools attached to it.** Building a tool does not connect it to any agent — you attach it by adding the tool to the agent's `tools` array via `autessa.agents.update`, then `autessa.agents.publish`. So the full flow to give an agent a capability is: build + test the tool (`tools.create` → `tools.update` → `tools.publish` → `tools.execute` to verify), then attach it (`agents.get` → add to `tools` → `agents.update` → `agents.publish`). 4. **A task too big for one agent → a manager.** If the job needs several specialists coordinated (research, then draft, then review), build a manager with `autessa.agenticManagers.create`. Budget for it deliberately: it bills its configured sub-agent count. ## Worked examples **A. Format a phone number.** Pure logic — no LLM. Write a script, or publish it once as a tool with `autessa.tools.create` so a trigger can reuse it. **B. A support assistant over your documents.** Create a SIMPLE agent with `autessa.agents.create`, attach a semantic-query tool over a basic AutessaDB table with a vectorized column (build it per the grounding-agent-on-documents guide), `autessa.agents.publish` it, then serve chats with `autessa.agents.execute`. **C. Draft-and-review a report.** A manager (`autessa.agenticManagers.create`) coordinating a writer and a reviewer sub-agent produces a better result than one agent — at the cost of its configured sub-agent count per conversation. ## See also - *scripting-model* — how a plain AutessaScript runs when you skip the LLM. - *metering-billing* — what agent and manager execution costs. - *automations* — the triggers/events/webhooks that invoke tools and agents. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # Building a custom app Source: https://docs.autessa.com/docs/autessascript/guides/custom-apps > The Custom Apps V2 lifecycle: create, check out a workspace, edit with autosave, flush, publish, and poll the build — plus rollback and the read-only kit. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # Building a custom app The Custom Apps V2 lifecycle: create, check out a workspace, edit with autosave, flush, publish, and poll the build — plus rollback and the read-only kit. **The decision:** you are building or editing a hosted custom app from a script or an agent. What is the safe edit-to-live loop? ## The lifecycle 1. **Create** the app with `autessa.customAppsV2.create` (or `autessa.customAppsV2.clone` from an existing one). Create hands you back a `workspaceId` — a ready-to-edit workspace (handle `ws-1`) already checked out — so you can start editing immediately. Workspace ids are friendly handles like `ws-1` (the raw UUID also works everywhere). 2. **Check out** an *additional* private server-side workspace with `autessa.customAppsV2.workspace.checkout` — only if you need a second/fresh working copy; create already gave you the first. Checkout returns a `workspaceId` handle (e.g. `ws-2`) plus the editable files and the read-only files. A workspace is a copy-on-write working copy of the draft, isolated from the browser editor. 3. **Edit** inside the workspace. Every write, delete, and rename autosaves immediately into the workspace tree — there is no separate save step. Use `autessa.customAppsV2.workspace.files.write`, `.delete`, `.rename`, or the atomic `autessa.customAppsV2.workspace.applyChangeSet` for a multi-file change. 4. **Check your work** with `autessa.customAppsV2.workspace.status` — it reports what you added, modified, deleted, or renamed, and whether the shared draft drifted underneath you. 5. **Flush** with `autessa.customAppsV2.workspace.flush` to project your changes onto the app's shared draft and tear the workspace down. Conflicts (the draft changed while you edited) surface here; the default is to fail on conflict rather than clobber. 6. **Publish** with `autessa.customAppsV2.workspace.publish` (flush + build in one step) or `autessa.customAppsV2.publish` on the already-flushed draft. 7. **Poll the build** with `autessa.customAppsV2.buildStatus`. Polling is **mandatory**: the first poll that sees SUCCESS finalizes the publish. On FAILED, the status carries a build log excerpt so you can read the error and fix the file — closing the edit→publish→fix loop. ## Decision rules - **Agents and scripts edit through a workspace; the browser editor edits the draft directly.** Checkout keeps your automated edits isolated until you deliberately flush. - **`src/autessa/**` and the lockfile are read-only.** Those files are the app's SDK kit and its pinned dependencies — reading them teaches you the app's available surface, but writes are refused. - **Discard, don't leave it open.** If you abandon an edit, call `autessa.customAppsV2.workspace.discard` — it deletes the working copy and leaves the draft untouched. Idle workspaces expire on their own, but discard is immediate and explicit. - **Roll back a bad publish** with `autessa.customAppsV2.rollback`, which restores an earlier version's source onto the draft. ## Worked example Check out the app (`autessa.customAppsV2.workspace.checkout`), write the changed files (`autessa.customAppsV2.workspace.files.write`), confirm the diff (`autessa.customAppsV2.workspace.status`), then `autessa.customAppsV2.workspace.publish` and loop on `autessa.customAppsV2.buildStatus` until SUCCESS. If it returns FAILED, read the log excerpt, re-check-out, fix the file, and publish again. ## See also - *data-placement* — the AutessaDB tables a custom app reads and writes. - *sharing-permissions* — who can edit and publish an app, and the app's table security. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # Connectors and authentication Source: https://docs.autessa.com/docs/autessascript/guides/connectors > How connectors to outside systems authenticate — OAuth (browser-connected, not scriptable) versus credential-based — and the read/post surface a script gets. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # Connectors and authentication How connectors to outside systems authenticate — OAuth (browser-connected, not scriptable) versus credential-based — and the read/post surface a script gets. **The decision:** your workflow needs to reach an outside system (Gmail, a calendar, a CRM). How does that connection get authorized, and what can a script do with it? ## The two auth patterns - **OAuth connectors** (Gmail, Google Calendar, Outlook, most social platforms). These are **connected in the browser** — a person clicks through the provider's consent screen once. A script **cannot create** an OAuth connection; it can only *use* one that already exists. Token refresh is owned by the connector's API client, not by your script — you never hand-refresh a token before calling. - **Credential-based connectors** (many ERP/CRM/API integrations). These authenticate with keys or credentials the user supplies. Depending on the connector, these can be configured without a browser round-trip. ## Decision rules 1. **Need an OAuth integration? Assume it is already connected.** List the existing connections and use one — do not try to script the OAuth handshake. For Gmail, that's `autessa.gmail.connectors.all` to find a connection and `autessa.gmail.connectors.get` to inspect it. 2. **Don't re-check tokens.** The connector's client refreshes tokens itself; your script just calls the read/post surface and trusts it to be authenticated. 3. **Respect the share/access model.** A connector is shared with users and teams at an access level, exactly like other resources. Inspect it with the connector's `getSharingDetails` and adjust with `share` — e.g. `autessa.gmail.connectors.getSharingDetails` and `autessa.gmail.connectors.share`. See the *sharing-permissions* guide for what the access levels mean. 4. **Know the typical surface.** Most connectors expose a small, consistent set: list/get the connection, update its settings, view and update which sub-resources are available (labels, calendars), and share it. Calendar connectors, for instance, add `autessa.googleCalendar.connectors.syncCalendars` and `autessa.googleCalendar.connectors.viewAvailableCalendars`. ## Calendar tools and time zones When you build a calendar tool (the `GOOGLE_CALENDAR_*` and `OUTLOOK_CALENDAR_*` tool types), its config has a `timeZone` key. **Leave `timeZone` empty to use the calendar's own default time zone** — the connector's configured zone is applied automatically when you don't set one. Only set `timeZone` (to an IANA identifier like `America/New_York`) when the user explicitly asks for a specific zone. **Do not invent or hard-code a time zone**: guessing one silently shifts every event. Dates in these configs are `yyyy-MM-dd` and times are 24-hour `HH:mm` (kept as separate `startDate`/`startTime` and `endDate`/`endTime` keys, not one timestamp). ## Worked examples **A. Read mail from an existing Gmail connection.** Find the connection with `autessa.gmail.connectors.all`, confirm which labels are available via `autessa.gmail.connectors.viewAvailableLabels`, then use it — no OAuth handshake in the script. **B. Schedule against a calendar.** Use an already-connected calendar: list with `autessa.googleCalendar.connectors.all`, sync its calendars with `autessa.googleCalendar.connectors.syncCalendars`, and read events from it. ## See also - *sharing-permissions* — the access levels that govern who can use a connector. - *automations* — scheduled events and triggers that drive connector-backed tools. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # Sharing and permissions Source: https://docs.autessa.com/docs/autessascript/guides/sharing-permissions > How access works: company roles, per-resource access levels, sharing, and AutessaDB row/column security — and what the SDK deliberately does not expose. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # Sharing and permissions How access works: company roles, per-resource access levels, sharing, and AutessaDB row/column security — and what the SDK deliberately does not expose. **The decision:** you built something. Who can see it, edit it, or run it — and how do you control that from a script? ## The layers - **Company roles.** Every member holds a role in the company. There are six built-in roles; **"User" is the base role** granted on auto-join. List them with `autessa.company.role.all`. Roles gate what a member can do at the company level. - **Per-resource access levels.** Resources (agents, tools, tables, connectors, apps, scheduled events, e-signature templates, …) are shared at an access level: **READ**, **WRITE**, **PUBLISH**, or **ADMIN**, in increasing power. Most resource namespaces expose a matching pair — a `getSharingDetails` to see who has what, and a `share` to grant or change it, e.g. `autessa.autessadb.tables.getSharingDetails` and `autessa.autessadb.tables.share`. - **AutessaDB row/column security (RLS/FLS).** Within a table, roles decide which *rows* a user sees and which *columns* are masked. Table security is **default-open**: a table with no role rules is fully visible, and a masked column needs an explicit role rule to hide it. Manage table roles with `autessa.autessadb.roles.create`, `.update`, and `.list`. Every query runs under an *acting identity*, which is what the row/column rules are evaluated against. ## Decision rules 1. **Grant the least level that works.** READ to look, WRITE to edit rows/config, PUBLISH to publish a new version, ADMIN to manage sharing itself. Don't hand out ADMIN to let someone merely edit. 2. **Auto-join grants "User", never "Admin".** When wiring up membership, the base "User" role is the default; elevate deliberately. 3. **Table security is opt-in.** If a table holds sensitive rows or columns, add explicit role rules with `autessa.autessadb.roles.create` — otherwise it is open by default. 4. **Check before you assume.** Use the resource's `getSharingDetails` to read the current grants before changing them. ## Worked examples **A. Share a table read-only with a team.** `autessa.autessadb.tables.share` at READ, then confirm with `autessa.autessadb.tables.getSharingDetails`. **B. Hide a salary column.** Add a table role via `autessa.autessadb.roles.create` that masks the `salary` column for the base role, leaving it visible only to a privileged role — because without a rule the column is open. ## What the SDK deliberately does NOT expose Some administrative actions are intentionally out of scope for scripts — for example transferring ownership of a resource is not a scriptable operation. Sharing and access-level changes are scriptable; wholesale ownership transfer is not. ## See also - *data-placement* — the tables these rules protect. - *connectors* — connectors follow the same share/access-level model. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # What costs money Source: https://docs.autessa.com/docs/autessascript/guides/metering-billing > Which SDK calls produce billable AI tasks, how manager billing works, how work inside one conversation folds into a single task, and the plan at a behavior level. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # What costs money Which SDK calls produce billable AI tasks, how manager billing works, how work inside one conversation folds into a single task, and the plan at a behavior level. **The decision:** you are building something that runs AI work. What will it bill, and how do you keep it predictable? ## What bills - **AI execution bills; plumbing does not.** Reading a table, listing agents, or sharing a resource is not a billable AI task. Running the models is: sending a message to an agent (`autessa.agents.execute`) bills agent execution, and holding a manager conversation bills for its work. - **Manager billing = the configured sub-agent count.** A manager conversation bills the number of sub-agents it is configured to run — the sub-agents do **not** bill separately on top. Choosing how many specialists a manager coordinates is a direct cost decision. - **Work inside one conversation folds into one task.** When an agent or manager turn makes several model/RAG calls to answer, those fold together into that conversation's single billable task rather than each becoming its own charge — so a multi-step answer is metered as one unit of work, not many. ## Seeing the bill Read-only usage against every limit dimension is available from `autessa.company.usageStatus` — its AI-task block reports this period's task counts, the resolved per-task rate, and the projected month cost. Invoices, the per-task cost drill-down, payment methods, and commitment changes are billing administration: they live in the browser billing UI, not the script SDK. ## The plan, behaviorally Billing is pay-as-you-go with an optional monthly commitment: you pay per AI task, and committing to a monthly minimum lowers the per-task rate. There is no free tier. The exact rates and thresholds are shown to the user in the billing UI and the published pricing docs. A script that needs the current rate should read the live figure from the AI-task block of `autessa.company.usageStatus` rather than hardcode any number, because the rate depends on the company's commitment tier. ## Decision rules 1. **Budget managers by sub-agent count.** More specialists means a better answer and a higher per-conversation bill; pick the smallest team that does the job. 2. **Prefer a plain script when no judgement is needed.** If deterministic code can do the work, it costs nothing in AI tasks — see the *agents-tools-managers* guide. 3. **Read rates live; never hardcode prices.** Pull the current per-task rate from `autessa.company.usageStatus`, because rates depend on the company's commitment tier. ## Worked example A nightly report that runs a manager to summarize the day will bill that manager's configured sub-agent count each night. To estimate the monthly cost, multiply by the schedule and read the per-task rate from the AI-task block of `autessa.company.usageStatus`. ## See also - *agents-tools-managers* — the units of work that do and don't bill. - *automations* — the schedules and triggers that decide how often billable work runs. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # How AutessaScript runs Source: https://docs.autessa.com/docs/autessascript/guides/scripting-model > The execution model: the autessa.* global, the ok/data/errors response envelope, dispatch guards (depth, budget, read-only, strict validation), and scripts calling scripts. {/* GENERATED by scripts/generate-sdk-reference.mjs from AutessaBrainJava — do not edit by hand. */} # How AutessaScript runs The execution model: the autessa.* global, the ok/data/errors response envelope, dispatch guards (depth, budget, read-only, strict validation), and scripts calling scripts. **The decision:** you are writing an AutessaScript. What can you rely on about how it runs, and how do you read the result of every `autessa.*` call? ## The globals Every script runs with a `globalThis.autessa` object already in scope — the whole SDK tree, one function per method. You never import or construct it; `autessa.agents.create({...})` is just there. Namespaces nest (`autessa.autessadb.query.query`), and a call takes a body object and an optional options argument. ## The response contract: the ok/data/errors envelope **Every `autessa.*` call returns an envelope**, not a bare value: ```javascript const { ok, data, errors } = autessa.agents.create({ agentType: 'SIMPLE', agentName: 'Bot' }); if (ok) { console.log('created', data.agent.agentId); } else { console.error('failed', errors); } ``` - `ok` is a boolean — always check it before using `data`. - `data` holds the response shape documented for that method (the response schema in its docs). - `errors` is a non-empty array of human-readable reason strings whenever `ok` is false — so `errors` (and `errors[0]`) always tells you what went wrong. It is absent on success. A script's own final result follows the same idea: return the value you want the caller to receive; failures surface through the envelope rather than by throwing raw exceptions. ## Dispatch guards Each `autessa.*` call passes a guard chain before it reaches the backing endpoint: 1. **Depth** — a script may call other scripts/tools, which may call more; the runtime caps how deep that recursion can go, so a runaway call tree is stopped. 2. **Budget** — there is a per-run ceiling on how many SDK calls a single run may make (a read-then-write counts as more than one). Exceeding it fails the call rather than the run spinning forever. 3. **Read-only policy** — a run can be marked read-only; under it, any MUTATE-classified call is refused. Read calls still work. This is how a plan/preview run is kept side-effect-free. 4. **Strict validation** — mutations are validated against the authored request shape, and an **undeclared field is rejected** rather than silently forwarded. The documented request schema *is* the contract; if a field isn't in the docs, it won't reach the endpoint. ## Created-resource tracking and calling scripts from scripts - When a run creates resources (a table, an agent, a row), the runtime tracks what was created, so a run's side effects are attributable. - Scripts can invoke other scripts and tools through the SDK; the depth and budget guards above govern that fan-out. Design chains to stay well within the caps — prefer a trigger chain (see the *automations* guide) over one script that recurses deeply. ## Decision rules 1. **Always destructure and check `ok`.** Never use `data` before confirming `ok` is true. 2. **Send only documented fields on mutations.** Strict validation rejects the rest. 3. **Mark preview runs read-only.** Rely on the read-only policy to guarantee no writes during a dry run. 4. **Respect the budget.** Batch where the SDK offers a batch method (e.g. `autessa.autessadb.query.batchUpdate`) instead of a call per row. ## Worked example ```javascript const { ok, data } = autessa.autessadb.query.query({ tableId: 'tbl_leads', whereGroups: [{ logicalOperator: 'AND', conditions: [ { columnName: 'status', operator: 'EQUALS', value: 'new' } ] }], page: 1, pageSize: 50 }); if (!ok) return { processed: 0 }; // one batched write instead of one call per row — stays within the SDK budget autessa.autessadb.query.batchUpdate({ tableId: 'tbl_leads', rowIds: data.rows.map(r => r.id), columnData: { status: { textValue: 'queued' } } }); ``` ## See also - *agents-tools-managers* — when to script directly versus configure an agent. - *automations* — trigger chains as the alternative to deep script recursion. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # Autessa SDK Source: https://docs.autessa.com/docs/autessascript/sdk > The autessa.* SDK lets AutessaScript drive the whole platform synchronously, acting as the invoking user with their exact permissions. # Autessa SDK The autessa global gives AutessaScript synchronous access to everything a logged-in user can do on the platform — creating and running agents, querying and writing AutessaDB tables, managing tools, scheduled jobs, connectors, e-signature envelopes, custom apps, and more — organized into namespaces like autessa.agents and autessa.autessadb. ## Acts as you Every `autessa.*` call runs as the script's invoking user, through the exact same permission checks, resource-sharing rules, and audit logging as if that user had clicked the equivalent button in the product. A script can never do more than its user, and never less — there is no separate "script identity" with elevated or reduced access. ## Calls are synchronous Unlike a browser, which fires requests and updates the UI when a response arrives, every `autessa.*` call blocks until it completes and returns its result directly: ```javascript const result = autessa.agents.create({ agentName: "Support Triage", agentType: "CONVERSATIONAL", }); if (result.ok) { console.log("Created agent:", result.data.agentId); } ``` There is no `await`, no callback, and no promise — AutessaScript itself is synchronous end to end (see [The sandbox](/docs/autessascript/sandbox.md)), and the SDK follows the same model. ## The result envelope Every call returns one of two shapes: ```javascript // Success { ok: true, data: { /* the method's response fields */ } } // Failure { ok: false, errorCode: "...", errors: ["human-readable message", ...] } ``` Always check `ok` before reading `data` — a failed call never throws, it returns `ok: false` with `errorCode` and `errors` describing what went wrong. This is the one failure signal for every method: validation problems, permission denials, and downstream errors all normalize to the same shape. ## Updates are safe-merged by default Update-style methods (`autessa..update`, and similarly named methods across namespaces) treat the fields you pass as a **patch**, not a full replacement. Only the fields you include are changed; every other field on the entity keeps its current value: ```javascript // Only agentName changes. Every other field on the agent — its type, tools, // prompt, sharing settings — is left exactly as it was. autessa.agents.update({ agentId: existingAgentId, agentName: "New Name", }); ``` Pass `{ replace: true }` as part of the call to opt out and replace the entity wholesale with exactly the fields you provide: ```javascript autessa.agents.update({ agentId: existingAgentId, agentName: "New Name", replace: true, }); ``` Prefer the default safe-merge behavior unless you specifically intend a full replacement — it is much harder to accidentally clobber a field you didn't mean to touch. ## Ids are opaque strings Every resource id in the SDK — `agentId`, `tableId`, `connectorId`, and so on — is an opaque string. Treat ids as values to pass back to the SDK, not as something to parse, decode, or construct yourself. They are stable for the life of the resource but carry no meaning beyond identifying it. ## Discovering the surface The SDK is large — dozens of namespaces, hundreds of methods — so it isn't dumped into this page. Instead: - **In the copilot / an LLM context**, use the progressive-discovery tools: `list_sdk_namespaces` to see every namespace, `search_sdk_capabilities(query)` to search by keyword, `get_sdk_namespace_overview(namespace)` for one namespace's full method list, and `get_sdk_method_details(names)` for the request/response fields of specific methods. - **In these docs**, each namespace below has its own reference page listing every method, its call signature, and its request/response fields. - **In the script editor**, typing `autessa.` triggers autocomplete for namespaces and methods, with inline signatures and field lists — the same data these docs and the discovery tools are generated from. ## Namespaces - [autessa.agents](/docs/autessascript/sdk/agents.md) — create, configure, publish, and run agent conversations. - [autessa.autessadb](/docs/autessascript/sdk/autessadb.md) — tables, rows, queries, triggers, and computed columns. - [autessa.tools](/docs/autessascript/sdk/tools.md) — create and execute tools, including other scripts. - [autessa.scheduledEvents](/docs/autessascript/sdk/scheduledEvents.md) — scheduled and recurring jobs. - [autessa.mcp](/docs/autessascript/sdk/mcp.md) — MCP server configuration. - [autessa.customAppsV2](/docs/autessascript/sdk/customAppsV2.md) — Custom App source files, builds, and publishing. - [autessa.eSignature](/docs/autessascript/sdk/eSignature.md) — e-signature envelopes and templates. - [autessa.meetingRecorder](/docs/autessascript/sdk/meetingRecorder.md) — meeting recordings and transcripts. The full namespace list — including per-connector namespaces like `autessa.gmail`, `autessa.hubspot`, and `autessa.quickbooks` — is available via `list_sdk_namespaces` or the editor's autocomplete. ## Related - [AutessaScript overview](/docs/autessascript.md) - [The sandbox](/docs/autessascript/sandbox.md) - [Tool invocation helper](/docs/autessascript/helpers/tools.md) --- # autessa.acumatica Source: https://docs.autessa.com/docs/autessascript/sdk/acumatica > 8 methods in the acumatica namespace. # autessa.acumatica 8 methods in the acumatica namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.agentic Source: https://docs.autessa.com/docs/autessascript/sdk/agentic > Cross-cutting agentic-copilot capabilities: manager conversations, shared context, and copilot session plumbing. # autessa.agentic Cross-cutting agentic-copilot capabilities: manager conversations, shared context, and copilot session plumbing. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.agents Source: https://docs.autessa.com/docs/autessascript/sdk/agents > Create, configure, publish, and run agent conversations. # autessa.agents Create, configure, publish, and run agent conversations. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.autessadb Source: https://docs.autessa.com/docs/autessascript/sdk/autessadb > AutessaDB tables, rows, queries, joins, triggers, access logs, Drives, and analytics tables. # autessa.autessadb AutessaDB tables, rows, queries, joins, triggers, access logs, Drives, and analytics tables. Alongside single-row CRUD, this namespace covers **bulk writes** (`bulkInsert`, `bulkUpdateByIds`, `bulkDeleteByIds` — many rows per call, with per-row results and modes that control atomicity and whether triggers/indexing/computation run), **aggregation** (`aggregate` — grouped `COUNT`/`SUM`/`AVG`/`MIN`/`MAX`), and **keyset (cursor) pagination** on reads (`orderBy` plus a `cursor` from the prior response's `nextCursor`, staying fast on large tables). See each method's signature below for the exact request and response shapes. Two table-shape options appear here as well. A table can be **append-only** (immutable / audit-grade): rows can be inserted and read but never updated or deleted, computed columns are not allowed, and column removal is blocked — the setting is a one-way latch chosen at table creation, and each returned schema carries an `appendOnly` flag. Columns can also be **indexed** for faster equality filtering: `INTEGER`, `FLOAT`, `BOOLEAN`, `SINGLE_SELECT`, and foreign-key columns are always indexed, `SHORT_TEXT` can opt in (its values are length-capped to 2000 characters once indexed), and `LONG_TEXT` / `MULTI_SELECT` / media columns cannot be indexed. On a large table a newly requested index builds in the background and becomes usable when its build finishes. ## Drives `autessa.autessadb.drive` works with [Drives](/docs/guide/autessadb/drive.md), tables of files and folders. Calls take the Drive's `tableId` and absolute paths, run as the invoking user, and only reach the files and folders that user can open. | Group | Methods | | --- | --- | | `drive` | `create`, `ls`, `tree`, `find`, `stat`, `grep`, `read`, `search`, `mkdir`, `move`, `trash`, `restore`, `purge`, `downloadUrl`, `status`, `usage`, `transferOwner` | | `drive.uploads` | `create`, `partUrls`, `completeMultipart`, `commit`, `abort` | | `drive.versions` | `list`, `restore` | | `drive.shares` | `list`, `upsert`, `remove`, `sharedWithMe`, `whoHasAccess` | | `drive.links` | `create`, `list`, `revoke` | Listings and searches return `nextCursor` and `truncated`; pass the cursor back with the same arguments to continue. A `move` that would change who has access returns `requiresConfirmation` and moves nothing until you repeat it with `confirm: true`. Request and response shapes match the [Drive API](/docs/api/autessadb-drive.md). ```javascript const DRIVE = "V1StGXR8Z5"; // Find a clause, then read the page it is on before quoting it. const hits = autessa.autessadb.drive.grep({ tableId: DRIVE, path: "/Shared/Legal/Contracts", pattern: "termination for convenience", caseInsensitive: true, }); if (hits.ok && hits.data.matches.length > 0) { const m = hits.data.matches[0]; const page = autessa.autessadb.drive.read({ tableId: DRIVE, id: m.id, pages: String(m.page) }); console.log(`${m.path} p.${m.page} L.${m.pageLine}`, page.data.lines.map((l) => l.text).join("\n")); } // Share a folder with a team as Viewer. autessa.autessadb.drive.shares.upsert({ tableId: DRIVE, path: "/Shared/Legal", principals: [{ type: "TEAM", teamId: "3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903" }], role: "VIEWER", }); ``` Uploading from a script follows the same three steps as the API (`uploads.create`, send the bytes, `uploads.commit`); from a terminal, `autessa autessadb drive push` does all of it for a whole folder. ## Analytics tables `autessa.autessadb.analytics` works with [analytics tables](/docs/guide/autessadb/analytics.md): columnar tables queried with read-only ANSI SQL (analytical functions such as `date_trunc`, `approx_distinct`, and `format_datetime` are available). | Purpose | Methods | | --- | --- | | Tables | `create`, `inferSchema`, `describe`, `addColumns`, `updateSemantics`, `createAgentTools` | | Data | `loadFromS3`, `loadFromTable`, `appendRows`, `preview` | | Queries | `query`, `estimate`, `jobStatus`, `jobResults`, `jobDownloadUrl`, `cancel`, `jobs` | Queries run asynchronously as jobs and typically take a few seconds. `query` waits up to `waitMs` (at most 20 seconds) and, if the job finished, returns the first rows in `preview`; otherwise poll `jobStatus` and page through `jobResults`. `DECIMAL` values and very large integers come back as strings, and timestamps as ISO-8601 UTC strings. Request and response shapes match the [Analytics API](/docs/api/autessadb-analytics.md). ```javascript const ORDERS = "V1StGXR8Z5"; let res = autessa.autessadb.analytics.query({ tableId: ORDERS, sql: `SELECT region, SUM(net_amount) AS revenue FROM order_lines WHERE order_date >= DATE '2026-01-01' GROUP BY region`, waitMs: 20000, }); let job = res.data; while (job.state === "QUEUED" || job.state === "RUNNING") { job = autessa.autessadb.analytics.jobStatus({ tableId: ORDERS, jobId: job.jobId, waitMs: 20000 }).data; } if (job.state === "SUCCEEDED") { const page = autessa.autessadb.analytics.jobResults({ tableId: ORDERS, jobId: job.jobId, page: 0, pageSize: 1000 }); page.data.rows.forEach((r) => console.log(r.region, r.revenue)); } else { console.log(job.error.errorCode, job.error.message, job.error.hint); } ``` The analytics methods do not support row-level calls: `autessa.autessadb.query.*` on an analytics table fails with a message pointing to `autessa.autessadb.analytics.query`. On a Drive, write files through `drive.*` rather than `query.insert`, which cannot create files or folders. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) - [AutessaDB Drive](/docs/guide/autessadb/drive.md) and the [Drive API](/docs/api/autessadb-drive.md) - [Analytics tables](/docs/guide/autessadb/analytics.md) and the [Analytics API](/docs/api/autessadb-analytics.md) --- # autessa.autessascript Source: https://docs.autessa.com/docs/autessascript/sdk/autessascript > Script tool documentation and copilot passthroughs (the surface these very docs describe). # autessa.autessascript Script tool documentation and copilot passthroughs (the surface these very docs describe). ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.beehiiv Source: https://docs.autessa.com/docs/autessascript/sdk/beehiiv > 6 methods in the beehiiv namespace. # autessa.beehiiv 6 methods in the beehiiv namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.bluesky Source: https://docs.autessa.com/docs/autessascript/sdk/bluesky > 8 methods in the bluesky namespace. # autessa.bluesky 8 methods in the bluesky namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.box Source: https://docs.autessa.com/docs/autessascript/sdk/box > 11 methods in the box namespace. # autessa.box 11 methods in the box namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.company Source: https://docs.autessa.com/docs/autessascript/sdk/company > Company settings, team membership, roles, billing, and usage limits. # autessa.company Company settings, team membership, roles, billing, and usage limits. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.conversationSync Source: https://docs.autessa.com/docs/autessascript/sdk/conversationSync > 5 methods in the conversationSync namespace. # autessa.conversationSync 5 methods in the conversationSync namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.customAppsV2 Source: https://docs.autessa.com/docs/autessascript/sdk/customAppsV2 > Custom App source files, builds, environment variables, and publishing. # autessa.customAppsV2 Custom App source files, builds, environment variables, and publishing. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.customWebhooks Source: https://docs.autessa.com/docs/autessascript/sdk/customWebhooks > 8 methods in the customWebhooks namespace. # autessa.customWebhooks 8 methods in the customWebhooks namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.eSignature Source: https://docs.autessa.com/docs/autessascript/sdk/eSignature > E-signature envelopes, templates, and signer flows. # autessa.eSignature E-signature envelopes, templates, and signer flows. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.facebook Source: https://docs.autessa.com/docs/autessascript/sdk/facebook > 7 methods in the facebook namespace. # autessa.facebook 7 methods in the facebook namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.ghost Source: https://docs.autessa.com/docs/autessascript/sdk/ghost > 9 methods in the ghost namespace. # autessa.ghost 9 methods in the ghost namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.gmail Source: https://docs.autessa.com/docs/autessascript/sdk/gmail > 9 methods in the gmail namespace. # autessa.gmail 9 methods in the gmail namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.googleAnalytics Source: https://docs.autessa.com/docs/autessascript/sdk/googleAnalytics > 8 methods in the googleAnalytics namespace. # autessa.googleAnalytics 8 methods in the googleAnalytics namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.googleCalendar Source: https://docs.autessa.com/docs/autessascript/sdk/googleCalendar > 10 methods in the googleCalendar namespace. # autessa.googleCalendar 10 methods in the googleCalendar namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.hubspot Source: https://docs.autessa.com/docs/autessascript/sdk/hubspot > 7 methods in the hubspot namespace. # autessa.hubspot 7 methods in the hubspot namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.instagram Source: https://docs.autessa.com/docs/autessascript/sdk/instagram > 7 methods in the instagram namespace. # autessa.instagram 7 methods in the instagram namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.keys Source: https://docs.autessa.com/docs/autessascript/sdk/keys > API keys and poweruser key management. # autessa.keys API keys and poweruser key management. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.linkedin Source: https://docs.autessa.com/docs/autessascript/sdk/linkedin > 7 methods in the linkedin namespace. # autessa.linkedin 7 methods in the linkedin namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.mailchimp Source: https://docs.autessa.com/docs/autessascript/sdk/mailchimp > 7 methods in the mailchimp namespace. # autessa.mailchimp 7 methods in the mailchimp namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.mcp Source: https://docs.autessa.com/docs/autessascript/sdk/mcp > MCP server configuration. # autessa.mcp MCP server configuration. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.meetingBot Source: https://docs.autessa.com/docs/autessascript/sdk/meetingBot > Meeting bot dispatch for live call recording. # autessa.meetingBot Meeting bot dispatch for live call recording. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.meetingRecorder Source: https://docs.autessa.com/docs/autessascript/sdk/meetingRecorder > Meeting recordings, transcripts, and recording configuration. # autessa.meetingRecorder Meeting recordings, transcripts, and recording configuration. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.notion Source: https://docs.autessa.com/docs/autessascript/sdk/notion > 10 methods in the notion namespace. # autessa.notion 10 methods in the notion namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.outlook Source: https://docs.autessa.com/docs/autessascript/sdk/outlook > 8 methods in the outlook namespace. # autessa.outlook 8 methods in the outlook namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.outlookCalendar Source: https://docs.autessa.com/docs/autessascript/sdk/outlookCalendar > 8 methods in the outlookCalendar namespace. # autessa.outlookCalendar 8 methods in the outlookCalendar namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.phoneReminder Source: https://docs.autessa.com/docs/autessascript/sdk/phoneReminder > 8 methods in the phoneReminder namespace. # autessa.phoneReminder 8 methods in the phoneReminder namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.quickbooks Source: https://docs.autessa.com/docs/autessascript/sdk/quickbooks > QuickBooks connector operations. # autessa.quickbooks QuickBooks connector operations. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.reddit Source: https://docs.autessa.com/docs/autessascript/sdk/reddit > 7 methods in the reddit namespace. # autessa.reddit 7 methods in the reddit namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.scheduledEvents Source: https://docs.autessa.com/docs/autessascript/sdk/scheduledEvents > Scheduled and recurring jobs. # autessa.scheduledEvents Scheduled and recurring jobs. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.threads Source: https://docs.autessa.com/docs/autessascript/sdk/threads > 7 methods in the threads namespace. # autessa.threads 7 methods in the threads namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.tiktok Source: https://docs.autessa.com/docs/autessascript/sdk/tiktok > 4 methods in the tiktok namespace. # autessa.tiktok 4 methods in the tiktok namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.tools Source: https://docs.autessa.com/docs/autessascript/sdk/tools > Create tools of any type and execute them. # autessa.tools Create tools of any type and execute them. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.twilio Source: https://docs.autessa.com/docs/autessascript/sdk/twilio > Phone numbers, call flows, call settings, call history, text handling, the shared inbox, and text messages from AutessaScript. # autessa.twilio Twilio voice and SMS connector operations. ## Texting from a script Scripts can find numbers to buy, send texts, and read a number's text history. Sending is checked exactly like the [Send SMS tool](/docs/tools/phone-sms.md#send-sms-): your company needs a finished [Text Messaging registration](/docs/guide/agentic/sms-setup.md) (or the number comes from your own connected Twilio account), and the recipient needs an approved [consent record](/docs/guide/agentic/sms-consent.md) or a text to you within the last year. Opted-out numbers are never texted. ```javascript title="Send a text" const { ok, data } = autessa.twilio.sendSms({ configId: 'pH9nR3vC6b', to: '+14155550100', body: 'Hi Sam, your order has shipped. Reply STOP to opt out.' }); if (data.success) console.log('sent', data.messageId, data.status, data.segments); else console.log('not sent', data.errorCode, data.errorMessage); ``` A refused text isn't an exception: `success` is `false`, `errorCode` says why, and `errorMessage` explains it in plain language. ```javascript title="Read a number's text history" let cursor; do { const { ok, data } = autessa.twilio.listSmsMessages({ configId: 'pH9nR3vC6b', pageSize: 25, cursor }); if (!ok) break; data.messages.forEach(m => console.log(m.direction, m.counterparty, m.status, m.body)); cursor = data.nextCursor; } while (cursor); ``` Messages come newest first, with the other party's number masked. Blocked sends are included with their reason. ```javascript title="Find numbers to buy" const { ok, data } = autessa.twilio.searchNumbers({ areaCode: '206', limit: 10 }); data.numbers.forEach(n => console.log(n.phoneNumber, n.locality, n.region)); ``` > `sendSms`, `listSmsMessages`, and `searchNumbers` ship with the Text Messaging release. The reference below is generated from the platform and lists them once the release is live. ## Calls from a script Scripts can set who answers a number, change its recording and call-limit settings, read its call history, and turn its quick automations on or off. What each setting does is explained in the [Phone Numbers guide](/docs/guide/agentic/phone-numbers.md). ```javascript title="Ring people, with voicemail as the fallback" const { ok, data } = autessa.twilio.updateCallFlow({ configId: 'pH9nR3vC6b', callFlow: { greeting: 'Thanks for calling Acme Dental.', destination: { type: 'RING', ring: { targets: [ { to: '+14155550100', label: 'Front desk' }, { to: '+14155550101', label: 'Office manager', ringSeconds: 30 } ], strategy: 'SEQUENTIAL', ringSeconds: 20, maxTotalSeconds: 45, requireKeyPress: true, whisper: 'Call from the Acme Dental line.', noAnswer: { type: 'VOICEMAIL', voicemail: { maxSeconds: 120, finishOnKey: '#', transcribe: true } } } } } }); if (ok) console.log(data.routingType); // "FLOW" ``` Pass `callFlow: null` to go back to sending every call straight to the number's agent or multi-agent. An invalid flow (an unknown agent, a bad time, a phone number not in E.164 format) comes back with `ok` false and a plain-language message. Every flow field, including holiday `closedDates`, menu timing and messages, and ring options like `callerId` and `waitAudio`, is described in the [call flow object](/docs/api/poweruser/phone-numbers.md#the-call-flow-object) reference. ```javascript title="Upload a recorded greeting" // 1. get an upload link const link = autessa.twilio.audioUploadLink({ configId: 'pH9nR3vC6b', fileType: 'mp3' }).data; console.log(link.uploadUrl, link.contentType, link.audioUri); // 2. PUT the file to link.uploadUrl with the header "Content-Type: ", // from curl or any HTTP client that can send a file's raw bytes // 3. once it's uploaded, save link.audioUri into an audio field autessa.twilio.updateCallFlow({ configId: 'pH9nR3vC6b', callFlow: { greeting: 'Thanks for calling Acme Dental.', greetingAudio: link.audioUri, destination: { type: 'AI_AGENT', resourceId: 'V1StGXR8Z5' } } }); ``` The upload must use exactly the `contentType` the link came with, and must happen before `expiresInSeconds` runs out. The [`api` helper](/docs/autessascript/helpers/api.md) sends text bodies, so upload the audio file itself from outside the script. Audio fields (`greetingAudio`, `messageAudio`, `promptAudio`, a voicemail's `greetingAudio`, a ring's `waitAudio`) also accept an `https://` link to a file you host. When one is set, it plays instead of the matching text. ```javascript title="Record calls and remove card numbers" const { ok, data } = autessa.twilio.updateCallSettings({ configId: 'pH9nR3vC6b', callSettings: { recording: { save: true, announcement: 'This call may be recorded.' }, redaction: { enabled: true, redact: ['CARD_NUMBER', 'CARD_EXPIRY', 'CARD_CVV'], audioRedaction: 'TONE' }, limits: { maxDurationMinutes: 30, silenceTimeoutSeconds: 20 } } }); ``` Settings sections are `speech` (voice and language), `transcript` and `recording` (each with an optional `retentionDays`), `redaction`, `limits`, `callerInfo`, `screening` (blocked callers), `ai` (interruptions, end-of-turn speed, hang-up phrases, key presses), and `answeringMachine` (detection on outgoing calls). Call Redaction costs $0.01 per call. Every field and range is in the [call settings object](/docs/api/poweruser/phone-numbers.md#the-call-settings-object) reference. ```javascript title="Answer in Spanish, block a number, and keep recordings for 90 days" const { data: options } = autessa.twilio.voiceOptions({}); options.languages.forEach(l => console.log(l.code, l.label)); autessa.twilio.updateCallSettings({ configId: 'pH9nR3vC6b', callSettings: { speech: { language: 'es-US' }, recording: { save: true, announcement: 'Esta llamada puede ser grabada.', retentionDays: 90 }, screening: { blockedNumbers: ['+15555550123'], rejectAnonymous: true }, ai: { endOfTurn: 'PATIENT', hangUpPhrases: ['Que tenga un buen día'] } } }); ``` `voiceOptions` lists the languages and voices you can use in `speech`. ```javascript title="Read call history" let after; do { const { ok, data } = autessa.twilio.getCalls({ configId: 'pH9nR3vC6b', limit: 50, after }); if (!ok) break; data.calls.forEach(c => console.log(c.startedAt, c.direction, c.otherNumber, c.status, c.durationSeconds)); after = data.next; } while (after); ``` Calls come newest first from your company's [Phone calls table](/docs/guide/agentic/phone-numbers.md#the-phone-calls-table). Each call includes its caller info, status, who handled it, the transcript, and links to the recording and voicemail that stay valid for 10 minutes. `data.tableId` is the Phone calls table, so you can also query it directly with [`autessa.autessadb`](/docs/autessascript/sdk/autessadb.md). ```javascript title="Email new voicemails to the front desk" autessa.twilio.setAutomation({ configId: 'pH9nR3vC6b', automation: 'EMAIL_VOICEMAILS', enabled: true, emails: ['frontdesk@example.com'] }); ``` `automation` is `TEXT_BACK_MISSED_CALLS` (with `smsMessage`; the number must be able to text) or `EMAIL_VOICEMAILS` (with up to 5 `emails`). `enabled: false` turns it off without deleting the trigger behind it. > `updateCallFlow`, `updateCallSettings`, `getCalls`, `setAutomation`, `audioUploadLink`, and `voiceOptions` ship with the phone call controls release. The reference below lists them once the release is live. ## Texts without AI from a script Scripts can decide who answers a number's texts, read and reply to its shared inbox, and forward texts to your team. What each setting does is explained in [Texts](/docs/guide/agentic/phone-numbers.md#texts). ```javascript title="Team answers texts by day, an AI agent after hours" const { ok, data } = autessa.twilio.updateTextFlow({ configId: 'pH9nR3vC6b', textFlow: { keywordReplies: [{ keyword: 'HOURS', reply: 'We are open Monday to Friday, 9 to 5.' }], useCallFlowHours: true, open: { type: 'NO_AI', autoReply: { message: 'Thanks for your message. We will text you back shortly.', onceEveryHours: 24 } }, closed: { type: 'AI_AGENT', resourceId: 'V1StGXR8Z5' } } }); if (ok) console.log(data.config.textFlow); ``` Pass `textFlow: null` to go back to the default: the agent the number sends calls to directly answers texts, or, without one, texts get no reply and wait in the inbox. STOP, START, and HELP are always handled first. Every field is in the [text flow object](/docs/api/poweruser/phone-numbers.md#the-text-flow-object) reference. ```javascript title="Read the inbox and reply" const threads = autessa.twilio.listSmsThreads({ configId: 'pH9nR3vC6b', limit: 20 }).data.threads; threads.forEach(t => console.log(t.otherNumber, t.lastDirection, t.lastBody, t.messageCount)); const first = threads[0]; const { data } = autessa.twilio.getSmsThread({ configId: 'pH9nR3vC6b', otherNumber: first.otherNumber, limit: 50 }); data.messages.forEach(m => console.log(m.sentAt, m.direction, m.handledBy, m.body)); if (first.lastDirection === 'INBOUND') { autessa.twilio.sendSms({ configId: 'pH9nR3vC6b', to: first.otherNumber, body: 'Thanks, we got your message.' }); } ``` Pass `next` as `after` for more threads, or as `before` for older messages in a thread. Replies go out as the business number and are checked like any other text. ```javascript title="Forward incoming texts to the front desk" autessa.twilio.setAutomation({ configId: 'pH9nR3vC6b', automation: 'FORWARD_TEXTS_SMS', enabled: true, phones: ['+14155550100'] }); ``` `FORWARD_TEXTS_SMS` takes up to 5 `phones`, and `FORWARD_TEXTS_EMAIL` up to 5 `emails`. Forwarded texts are sent from the business number, so each phone must be allowed to receive texts from it (texting the number once from each phone is enough). Every text is also a row in your company's [Text messages table](/docs/guide/agentic/phone-numbers.md#the-text-messages-table); `textLogTableId` from `getConfig` is its id, so you can query it with [`autessa.autessadb`](/docs/autessascript/sdk/autessadb.md). > `updateTextFlow`, `listSmsThreads`, `getSmsThread`, and the forwarding automations ship with the texting-without-AI release. The reference below lists them once the release is live. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) - [Text Messaging setup](/docs/guide/agentic/sms-setup.md) - [Phone and SMS tool types](/docs/tools/phone-sms.md) --- # autessa.twitter Source: https://docs.autessa.com/docs/autessascript/sdk/twitter > 7 methods in the twitter namespace. # autessa.twitter 7 methods in the twitter namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.user Source: https://docs.autessa.com/docs/autessascript/sdk/user > The current user’s profile and account settings. # autessa.user The current user’s profile and account settings. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.voice Source: https://docs.autessa.com/docs/autessascript/sdk/voice > Voice cloning and voice client configuration. # autessa.voice Voice cloning and voice client configuration. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.youtube Source: https://docs.autessa.com/docs/autessascript/sdk/youtube > 4 methods in the youtube namespace. # autessa.youtube 4 methods in the youtube namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.zendesk Source: https://docs.autessa.com/docs/autessascript/sdk/zendesk > 7 methods in the zendesk namespace. # autessa.zendesk 7 methods in the zendesk namespace. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) --- # autessa.zohoCrm Source: https://docs.autessa.com/docs/autessascript/sdk/zohoCrm > Zoho CRM connector operations. # autessa.zohoCrm Zoho CRM connector operations. ## Related - [Autessa SDK overview](/docs/autessascript/sdk.md) # CLI --- # Autessa CLI Source: https://docs.autessa.com/docs/cli > Build the whole platform from a terminal with a command surface generated from the live SDK — and inspect, audit and hand off every piece of it in the web app the moment it exists. # Autessa CLI `autessa` is a model-driven command line interface over the Autessa SDK. The builder UI and the CLI are two front ends over one platform: everything you would normally click together — tools, agents, tables, triggers, scheduled jobs, webhooks, custom apps — can be built entirely from a terminal, with no browser in the loop. And because it is the same SDK writing the same records, anything you build this way is in the web app the instant it is written: same ids, same versions, same history, same permissions. ## What it's for - **Reproducible builds.** A shell script that creates a table, a tool, an agent and a schedule is a deployable artifact. Clicking through a UI is not. - **Agent-built systems.** A coding agent can read the CLI's own help and assemble a working Autessa workflow without a human driving a mouse, either from its shell or through the hosted Autessa connector in any AI app that supports MCP. See [Coding agents](/docs/cli/coding-agents.md). - **Environment promotion.** The same script, run against a different [profile](/docs/cli/install.md), builds the same thing in a different company. - **Scripting and automation.** Every command reads and writes JSON, so the CLI composes with `jq`, shell scripts, and CI steps. - **Review and handoff.** Build from the terminal, then send a teammate a link to the resource in the builder. They see exactly what you made, in the UI they already know. ## Key concepts ### The surface is generated, not written The CLI ships as a single zero-dependency Node file that contains **no** command list. On first use it fetches the SDK manifest from your server and caches it at `~/.autessa/manifest.json`. Command names, argument names and types, help text, response shapes and grouping all come from that manifest. Two consequences worth internalizing: - When the platform gains a command, your CLI gains it too — no upgrade needed, just `--refresh`. - When a command name changes, your cached list goes stale. The CLI detects this (`SDK_UNKNOWN_ENDPOINT`), refreshes automatically, and tells you to re-run. That is not a missing command; it is an out-of-date cache. ### Namespace, command, flags Every invocation is `autessa [--flags]`, with names in hyphen-case: ```bash autessa agents all autessa tools publish --toolId gPdJwp5jbr9eN autessa autessadb tables create --tableName todos --schema '{"columnDefinitions":[...]}' ``` Namespaces nest where the SDK nests them — `autessa autessadb query insert`, `autessa tools history list`, `autessa app domains status`. ### You discover, you don't memorize Three commands get you anywhere: ```bash autessa help # every namespace autessa help # a namespace's commands autessa help # a command's arguments, types and response ``` Running a command that still needs required arguments **prints its help instead of failing** — the drill-down is the interface. See [Discovering commands](/docs/cli/discovery.md). ### One SDK, two front ends There is no "CLI API" separate from the platform. Every command posts a `{methodId, args}` envelope to a single dispatcher, which invokes the same SDK method the web builder invokes, behind the same guard chain: company scope, permission checks, read-only policy, argument validation, call budget. ``` your terminal the browser │ │ │ autessa │ clicks ▼ ▼ ┌────────────────────────────────────────┐ │ the Autessa SDK │ │ scope · permissions · validation · │ │ budget · versioning · audit │ └────────────────────────────────────────┘ │ ▼ one set of records, one history ``` The practical guarantee: **the CLI cannot do anything your account could not do in the browser, and the browser cannot see anything the CLI did not really do.** There is no shadow state and no sync step. ### Immediately auditable "Auditable in the web app" is not a promise about eventual replication — it is a consequence of there being one store. A tool you create from the terminal has a `toolId` you can paste into a builder URL right away. An agent you publish from a script shows up in the agent list at version 1. A row you insert lands in the table grid. The reverse holds too. Runs you trigger from the CLI are recorded in the same execution history the UI reads, tagged with where they came from: ```bash autessa tools history list --toolId gPdJwp5jbr9eN ``` ```json { "executions": [ { "eventId": "a02e8438-9032-46f3-a875-41d051f2c013", "toolId": "gPdJwp5jbr9eN", "toolName": "docs_scratch_summarize", "invocationId": "df4fd983-9021-43c8-b42c-cb02c8f0105c", "source": "API", "success": true, "durationMs": 3001, "createdAt": "2026-09-10T09:24:45Z", "requestVariables": "{\"text\":\"...\"}", "result": "{\"success\":true,\"content\":\"...\"}", "requestTruncated": false, "resultTruncated": false } ], "totalElements": 1, "hasNext": false } ``` The `source` field distinguishes `API`, `TRIGGER`, `WORK_ITEM`, `AGENT`, `SCHEDULED`, `WEBHOOK` and `SCRIPT` — so a terminal-driven run is auditable *as* a terminal-driven run, not disguised as something else. > Nothing you build from the terminal is private to the terminal. Sharing, ownership and row-level security apply exactly as they do in the UI — a resource you create from the CLI starts private to you and becomes visible to teammates only when you share it. ### The audit surfaces Every one of these is readable from the CLI and viewable in the web app: | What | Command | In the web app | | --- | --- | --- | | Tool executions, with decrypted inputs and outputs | `autessa tools history list` / `history detail` | Tool history | | Closed agent conversations, with full message and tool logs | `autessa agents conversations history` / `conversation` | [Agent history](/docs/guide/agentic/agent-history.md) | | Manager conversations and evaluations | `autessa managers conversations` | [Manager history](/docs/guide/agentic/manager-history.md) | | Table trigger firings, with event input and response | `autessa trigger-history list` / `detail` | [Triggers](/docs/guide/autessadb/triggers.md) | | Row-level data access | `autessa autessadb access-log list` | AutessaDB access log | | Scheduled job runs | `autessa scheduled-events history` | [Scheduled jobs](/docs/guide/scheduled-jobs.md) | | Inbound webhook calls | `autessa custom-webhooks get-execution-history` | [Webhook handlers](/docs/guide/api-management/webhook-handlers.md) | Payloads are capped in list responses — `requestTruncated` and `resultTruncated` tell you when — and the matching `detail` command returns the full, uncapped value. > History that depends on a resource reaching a terminal state is written asynchronously. An agent conversation is logged after it is closed, not while it is open, so `conversations history` may lag a moment behind `execute-close`. ### Jumping from a terminal to the page Ids are the bridge. Any resource you create prints an opaque id you can hand straight to the web app. For custom apps the CLI composes the URLs for you: ```bash autessa app visible-urls --appId GTBhswTJ6a9r ``` ```json { "editorUrl": "https://builder.autessa.com/app-builder/editor/GTBhswTJ6a9r", "publishedUrl": "https://builder.autessa.com/customappsv2/view/GTBhswTJ6a9r" } ``` `editorUrl` is where a human opens what you built; `publishedUrl` is where end users reach it once you publish. A build script that ends by printing `editorUrl` gives whoever runs it a one-click path into the UI. ## What stays in the browser Working from a terminal does not make the browser obsolete. A number of operations are deliberately web-app-only, and they fall into recognizable groups: - **OAuth connector setup.** Connecting Gmail, HubSpot, QuickBooks, Notion and the rest requires a human granting consent to a third party. The CLI lists, reads, shares, uses and deletes connectors — it just cannot create the ones that need a consent screen. A few that authenticate with an API key rather than OAuth (beehiiv, Ghost, Bluesky) can be connected without a browser. - **Money.** Paying an invoice, prepaying, changing a plan commitment and managing payment methods are UI-only. Reading your balance, ledger, budgets, invoices and per-task usage is not. - **Identity and org.** SSO configuration, ownership transfer, and poweruser API-key management are administered in the browser. - **Human-confirmation flows.** Sending and voiding e-signature envelopes, dispatching meeting recorder bots, publishing marketplace templates, and recording SMS consent. - **App source editing in the visual builder.** You can pull, edit and push an app's files from the CLI — see [Custom apps](/docs/cli/apps.md) — but the vibecode/workspace editing experience itself is a browser feature. These commands still appear in the help tree, and running one tells you where to go: ```bash autessa payments add-bank-account ``` ``` Payout bank accounts are linked from your Autessa Payments settings in the web app. ``` That is a design decision rather than an omission: discovery should tell you a capability exists and where it lives, instead of leaving you unsure whether you mistyped. ## How to build a workflow end to end A complete, reproducible build — table, tool, agent — with no browser: ```bash #!/usr/bin/env bash set -euo pipefail # 1. A table to work against TABLE_ID=$(autessa autessadb tables create \ --tableName support_tickets \ --schema '{"columnDefinitions":[ {"columnName":"subject","columnType":"SHORT_TEXT","required":true}, {"columnName":"body","columnType":"LONG_TEXT"}, {"columnName":"status","columnType":"SINGLE_SELECT","allowedValues":["open","triaged","closed"]} ]}' | jq -r '.tableId') # 2. A tool the agent can call TOOL_ID=$(autessa tools create \ --toolName triage_ticket \ --toolDescription "Classify a support ticket's urgency in one word." \ | jq -r '.tool.toolId') autessa tools update --toolId "$TOOL_ID" --updatedTool "$(jq -n --arg id "$TOOL_ID" '{ toolId: $id, name: "triage_ticket", description: "Classify a support ticket'"'"'s urgency in one word.", toolConfig: { toolType: "LLM_CALL", config: { promptTemplate: "Reply with exactly one of: low, medium, high.\n\n@@{body}", model: "SMALL_INSTRUCTION_FOLLOWING" } }, arguments: { body: { description: "The ticket body.", isRequired: true, argumentSchema: { type: "string" } } } }')" autessa tools publish --toolId "$TOOL_ID" # 3. An agent that uses it AGENT_ID=$(autessa agents create \ --agentType SIMPLE \ --agentName "Ticket Triage" \ --agentDescription "Triages inbound support tickets." \ | jq -r '.agent.agentId') autessa agents update --agentId "$AGENT_ID" --updatedAgent "$(jq -n --arg id "$AGENT_ID" --arg tool "$TOOL_ID" '{ agentId: $id, agentType: "SIMPLE", name: "Ticket Triage", agentInstructions: "Triage the ticket with the triage_ticket tool and reply with just the urgency.", model: "SMALL_INSTRUCTION_FOLLOWING", maxIterations: 10, tools: [{ toolId: $tool }] }')" autessa agents publish --agentId "$AGENT_ID" echo "table $TABLE_ID" echo "tool $TOOL_ID" echo "agent $AGENT_ID" ``` Run it and every id it prints is live in the builder before the script exits. Delete everything just as reproducibly: ```bash autessa agents delete --agentId "$AGENT_ID" autessa tools delete --toolId "$TOOL_ID" autessa autessadb tables cli-delete --tableId "$TABLE_ID" ``` > Table deletion is a soft delete with a 14-day grace period — `autessa autessadb tables list-deleted` shows your trash and `restore` brings a table back. Agent and tool deletion is permanent. ## How to run unattended `autessa login` needs a browser once, and the credential it mints is tenant-scoped and self-expiring with no silent refresh. That is a security property, not an oversight — there is no long-lived CLI token to leak from a build machine. For work that must run without a human present: - **Long-running or scheduled work belongs on the platform, not on a build box.** [Scheduled jobs](/docs/cli/scheduled-jobs.md), [table triggers](/docs/cli/autessadb.md) and [inbound webhooks](/docs/cli/webhooks-and-queues.md) run server-side with no session to expire. Use the CLI to *create* them, and let the platform run them. - **For genuinely unattended HTTP automation, use a [poweruser key](/docs/guide/api-management/poweruser-keys.md)** — the [Poweruser API](/docs/api/poweruser.md) covers the same operations the CLI exposes, and every CLI command has a poweruser equivalent. - **Keep environments in separate [profiles](/docs/cli/install.md)** and select per-invocation, so a promotion script never has to guess which tenant it is pointed at: ```bash AUTESSA_PROFILE=staging ./build-workflow.sh AUTESSA_PROFILE=prod ./build-workflow.sh ``` ## Where to go next | If you want to… | Read | | --- | --- | | Install, sign in, manage several tenants | [Install and sign in](/docs/cli/install.md) | | Learn the help tree, flags, output formats, errors | [Discovering commands](/docs/cli/discovery.md) | | Sync a custom app's source to disk | [Custom apps](/docs/cli/apps.md) | | Create tables and read/write rows | [AutessaDB](/docs/cli/autessadb.md) | | Build a capability an agent can call | [Tools](/docs/cli/tools.md) | | Create, configure and run an agent | [Agents](/docs/cli/agents.md) | | Orchestrate several agents | [Managers](/docs/cli/managers.md) | | Run work on a cron, interval or timer | [Scheduled jobs](/docs/cli/scheduled-jobs.md) | | Point a coding agent at Autessa | [Coding agents](/docs/cli/coding-agents.md) | | See every namespace and command | [All commands](/docs/cli/all-commands.md) | ## Related - [Discovering commands](/docs/cli/discovery.md) - [Coding agents](/docs/cli/coding-agents.md) - [Command surface](/docs/cli/command-surface.md) - [Poweruser API](/docs/api/poweruser.md) — the same operations over HTTP - [Platform concepts](/docs/guide/concepts.md) --- # Install and Sign In Source: https://docs.autessa.com/docs/cli/install > Install the Autessa CLI, sign in through the browser, and keep several tenants side by side with profiles. # Install and Sign In The CLI is a single zero-dependency Node script. Installing it takes one command, and signing in takes one more — a browser round-trip that mints a tenant-scoped credential without you ever pasting a key. ## Requirements Node 18 or newer. The CLI uses only the Node standard library (`fetch`, `fs`, `http`, `crypto`), so there is nothing else to install and no dependency tree to audit. ## Install ```bash npm install -g @autessa/cli autessa version ``` ``` autessa 0.3.1 ``` ## Sign in ```bash autessa login ``` ``` Opening your browser to sign in and choose a company… Waiting for you to finish in the browser… Successfully signed in. you@example.com · tenant Acme Inc (544 commands) Try: autessa help • check later: autessa status ``` What happens: 1. The CLI binds an ephemeral loopback port on `127.0.0.1` and opens `/user/cli-login` in your default browser with that port and a random `state` value. 2. You sign in normally and pick which company the session should be scoped to. 3. The server mints a tenant-scoped CLI credential and redirects back to the loopback with it. The `state` is verified before anything is stored. 4. The credential is written to `~/.autessa/config`, and the SDK manifest is fetched and cached. Nothing is copied, pasted, or shown in the terminal. If the browser does not open on its own, the URL is printed for you to open manually. > The credential is **tenant-scoped and self-expiring** — roughly an eight-hour session, with no silent refresh. When it lapses, commands fail with "Session expired" and you run `autessa login` again. There is no long-lived CLI token to leak. ## Check your session ```bash autessa status ``` ``` Profile: default Endpoint: https://api.autessa.com Status: signed in ✓ User: you@example.com Tenant: Acme Inc Session: valid until 2026-09-10T17:19:43 Live: credential OK ✓ ``` `Live:` is an actual round-trip against the server, so it distinguishes "my stored token has not expired yet" from "my token still works". `autessa whoami` is an alias for the same command. ## Sign out ```bash autessa logout ``` The credential is revoked server-side — it stops working immediately rather than lingering until its natural expiry — and the local profile is removed. If the server is unreachable the local profile is still cleared. ## Profiles A profile is one saved sign-in: an endpoint, a tenant, and a credential. Profiles let you keep several companies or environments side by side without re-authenticating each time you switch. ```bash autessa login --profile prod # create or refresh a named profile autessa login --profile staging autessa profiles # list them ``` ``` PROFILE ENDPOINT COMPANY STATUS default * https://api.autessa.com Acme Personal signed in prod https://api.autessa.com Acme Inc signed in staging https://api.autessa.com Acme Staging session expired * = default (used when --profile / $AUTESSA_PROFILE aren't set). Change default: autessa profiles use Per command: autessa --profile (or: export AUTESSA_PROFILE=) ``` ### Selecting a profile Resolution order, highest priority first: 1. `--profile ` on the command 2. `$AUTESSA_PROFILE` 3. The persisted default profile (`autessa profiles use `), falling back to `default` ```bash autessa agents all --profile prod # one command autessa profiles use prod # persistently ``` > `--profile` is the only flag that selects a profile. `--name` is deliberately left free so it can be an ordinary SDK argument — `autessa qr-codes create --name "Launch poster"` passes a name to the command, not to the profile system. ### Removing a profile ```bash autessa logout --profile staging ``` Revokes that profile's credential and deletes it from the config. If it was the default, the default reverts to `default`. > Using an AI app? You don’t need the CLI for that: add the hosted Autessa connector by URL. See [Coding agents](/docs/cli/coding-agents.md#autessa-connector-mcp). ## Where things are stored | Path | Contents | | --- | --- | | `~/.autessa/config` | Profiles: endpoint, tenant, identity, and session credential per profile | | `~/.autessa/manifest.json` | Cached SDK manifest — the entire command surface | | `~/.autessa/update-check.json` | Last update check, so the version nudge costs no network call | The config is JSON and safe to inspect. It contains a live credential, so treat it like any other credential file — do not commit it, and do not copy it between machines. ## Staying current ```bash autessa update --check # compare installed vs latest, change nothing autessa update # reinstall the latest autessa update --version 0.3.0 # pin or roll back to a specific version ``` ``` Installed: autessa 0.3.1 Latest: autessa 0.3.1 (up to date) ``` The CLI also prints a one-line "update available" nudge, at most once a day, when the server advertises a newer version than the one you have. Because the command surface comes from the manifest rather than the binary, an out-of-date CLI is usually a cosmetic problem — but the server can gate clients that fall too far behind. ## Refreshing the command surface The manifest is cached, so a command added on the server is not visible until you refresh: ```bash autessa --refresh help # re-fetch the manifest, then show help autessa agents all --refresh # re-fetch, then run ``` You rarely need this by hand. If you run a command the server no longer recognizes, the CLI refreshes automatically and asks you to re-run. ## Related - [Autessa CLI](/docs/cli.md) - [Discovering commands](/docs/cli/discovery.md) - [Authentication](/docs/guide/authentication.md) --- # Discovering Commands Source: https://docs.autessa.com/docs/cli/discovery > The help tree, argument shapes, JSON arguments, global flags, output formats, exit codes and error handling in the Autessa CLI. # Discovering Commands The CLI has no manual to memorize because it generates its own. Command names, argument types, response fields and prose descriptions all come from the SDK manifest, and three help commands walk you from "I have no idea what exists" to "here is the exact flag I need". ## What it's for You will not find a printed list of every flag in these docs, and that is intentional — it would be stale within a release. Instead, learn the four moves on this page and the CLI will answer every "what can I do here" question itself. ## The help tree ```bash autessa help # every namespace, with a paragraph on each autessa help # that namespace's commands, one line each autessa help # arguments, types, requiredness, and the response shape ``` Nesting works the same way at every depth: ```bash autessa autessadb help autessa autessadb query help autessa autessadb query insert help ``` ### Namespace help ```bash autessa tools help ``` ``` Create, configure, run, and share tools — the capabilities you attach to agents. A tool is drafted with create, configured (its type and arguments) with update, made runnable with publish, and can be run directly with execute to test it. Creating and publishing a tool does NOT attach it to any agent — attach it by adding it to an agent's tools array via the agents update command. [...] * * * * * * * * * * * * * * * * * * COMMANDS in 'tools' * * * * * * * * * * * * * * * * * * all List every tool you can access in the current company, split into three buckets — tools you own, tools shared company-wide, and tools shared specifically with you. create Create a new bare draft tool with a name and description; set its type and configuration afterward with update. [...] history: detail Fetch one tool execution with its FULL (uncapped) decrypted request input and result output. list List tool executions newest-first, scoped to your company, optionally filtered by tool and/or outcome, paginated. Run 'autessa tools help' for a command's arguments. ``` The namespace paragraph is worth reading rather than skimming — it carries the intended lifecycle ("draft with create, configure with update, publish to make runnable") and the traps ("publishing a tool does NOT attach it to an agent"). ### Command help ```bash autessa tools create help ``` ``` Create a new bare draft tool with a name and description; set its type and configuration afterward with update. usage: autessa tools create [--flags] * * * * * * * * * * * * * * * * * * ARGUMENTS * * * * * * * * * * * * * * * * * * --toolName (required) Display name; also the function name presented to the LLM. --toolDescription (required) What the tool does; presented to the LLM to decide when to call it. Object/array args take JSON, e.g. --schema '{"columnDefinitions":[...]}'. Whole body: --json '{...}'. * * * * * * * * * * * * * * * * * * RESPONSE * * * * * * * * * * * * * * * * * * tool The newly created draft tool (no type/arguments set yet). toolId Opaque id of the tool. name Display name shown in the tool list; also the callable function name presented to the LLM. [...] ``` Nested object arguments expand inline, so you can see the shape of a `--schema` or `--updatedTool` without guessing. ## Drill-down: running an incomplete command is safe Invoking a command that still needs required arguments does **not** error and does **not** execute. It prints the argument list: ```bash autessa tools create ``` ``` This command needs arguments — add the flags below to run it. [...the same help as above...] ``` So exploring is free. Type the command you think you want, read what it asks for, add the flags. Commands with no required arguments (`autessa agents all`) run straight away. ## Passing arguments ### Scalars ```bash autessa tools publish --toolId gPdJwp5jbr9eN autessa agents create --agentType SIMPLE --agentName "Digest bot" ``` `--flag value` and `--flag=value` both work. A flag with no following value is a bare `true`: ```bash autessa app push --appId lCHdKACw4tpE --files '{}' --addOnly ``` Values are coerced to the type the manifest declares — integers, numbers and booleans are parsed rather than sent as strings. ### Objects and arrays Any argument the help shows as an object, an array, or a named type takes JSON: ```bash autessa autessadb tables create \ --tableName todos \ --schema '{"columnDefinitions":[{"columnName":"task","columnType":"SHORT_TEXT","required":true}]}' ``` If you pass a bare string where an object is expected, the CLI says so before sending anything: ``` error: value for this argument must be JSON: doing ``` ### The whole body at once `--json` replaces every other argument flag with one request body. This is the form to reach for when you are generating calls programmatically, or when a payload is long enough that shell quoting becomes the enemy: ```bash autessa agents update --json '{ "agentId": "K2UYSwk3Yy4i", "updatedAgent": { "agentId": "K2UYSwk3Yy4i", "agentType": "SIMPLE", "name": "Summarizer", "model": "SMALL_INSTRUCTION_FOLLOWING", "tools": [{ "toolId": "gPdJwp5jbr9eN" }] } }' ``` From a file: ```bash autessa agents update --json "$(cat agent.json)" ``` ## Global flags These are handled by the CLI itself and are never passed through as SDK arguments. | Flag | Effect | | --- | --- | | `--output json\|table\|csv\|text` | Response format. JSON is the default. `csv` writes the first array in the response as CSV; `text` is a line-oriented format for commands that define one, such as `autessadb drive grep`. | | `--no-wait` | On commands that start a job (such as `autessadb analytics query`), return the job ID immediately instead of waiting for the result. | | `--json '{...}'` | Send this object as the entire request body. | | `--profile ` | Run against a specific saved sign-in. | | `--refresh` | Re-fetch the SDK manifest before running. | | `--full` | On list commands that support a `summary` argument, return full records instead of trimmed ones. | | `--replace` | Skip the SDK's read-merge-write on replace-semantics methods — send the value as authoritative. | ### `--output table` ```bash autessa agents all --output table ``` ``` agentId agentType name model maxIterations ------------ --------- ------------------- --------------------------- ------------- 4fyAZFoKALmC SIMPLE HiOnceEmailAgent SMALL_INSTRUCTION_FOLLOWING 1 vI9tkBVAtMce SIMPLE Hi Emailer BIG_INSTRUCTION_FOLLOWING 30 bMR35XZD37Ol SIMPLE Message Digest BIG_INSTRUCTION_FOLLOWING 6 ``` Table mode finds the first array in the response and flattens it, dropping nested-object columns. It is a reading convenience — when a response has no array, or has nested structure that matters, it falls back to JSON. Never parse table output in a script; parse the JSON. ### `--full` List commands that accept a `summary` argument default to the trimmed form, so a company with hundreds of agents still returns a response under the size limit. `--full` opts out: ```bash autessa tools all # trimmed records autessa tools all --full # complete records ``` An explicit `--summary true|false` always wins over `--full`. ### `--replace` Several update methods read the current value, merge your changes into it, and write the result — so omitting a field leaves it alone. `--replace` skips the merge and treats what you send as the complete new value. Use it when you are pushing an authoritative snapshot (a full schema, a full file set) and want fields you omitted to actually disappear. ## Reading the output Successful commands print JSON to **stdout**; errors go to **stderr**. That split is what makes the CLI composable: ```bash autessa agents all | jq -r '.ownedAgents[].agentId' autessa tools history list --status error 2>/dev/null | jq '.executions | length' ``` A useful pattern for scripts — capture an id from a create: ```bash TOOL_ID=$(autessa tools create \ --toolName summarize \ --toolDescription "Summarize text in one sentence." \ | jq -r '.tool.toolId') ``` ## Exit codes | Code | Meaning | | --- | --- | | `0` | Command ran and succeeded | | `1` | Anything that failed: unknown command, validation error, application error, expired session | | `2` | You named a namespace instead of a command — its help was printed | Exit code `2` is the one to special-case in scripts: it means "you stopped one level short", not "something broke". ## Errors ### Validation errors Missing or malformed arguments come back with the SDK's own field table, not a generic message: ```bash autessa agents create --agentName "Digest bot" ``` ``` error [SDK_VALIDATION_ERROR]: Missing required field(s): agentType Request fields: | Field | Type | Required | Description | |---|---|---|---| | agentType | one of: SIMPLE | yes | The kind of agent to create. The SDK only creates SIMPLE agents; must be SIMPLE. | | agentName | string | yes | Display name shown in the agent list. | | agentDescription | string | - | Human-facing description of what the agent does. | ``` ### Type errors ``` error [ENDPOINT_ERROR]: The request body did not match this endpoint's schema. Field $.values. expected an object but got a string — send an object of that field's type (e.g. { "columnType": ..., "...Value": ... }), not a bare value. ``` The error names the JSON path, so `$.values.` points at the exact argument to fix. ### Stale command list ``` error [SDK_UNKNOWN_ENDPOINT]: No SDK endpoint named 'agenticManagers.all' note: your cached command list was out of date — refreshed it. Re-run the command (a name may have changed, e.g. agenticManagers → managers; 'autessa help' shows the current ones). ``` This almost never means the command is gone. Your cached manifest predates a rename or addition. The CLI has already refreshed by the time you read the message — re-run, and use `autessa help` if the name did change. ### Expired session ``` error: Session expired. Run 'autessa login' to sign in again. ``` There is no silent refresh by design. Run `autessa login` (with `--profile` if you are not on the default). ### Unknown command ``` error: Unknown command 'agents runit'. Try 'autessa help'. ``` Walk the help tree — you are almost always off by one segment. ## Commands that live in the web app Some operations are listed but not runnable from the CLI: OAuth connector setup, billing payments, SSO configuration, payment-method management, and similar flows that need a browser or a human confirmation step. Running one prints where to go instead of failing: ```bash autessa sms-consent create ``` ``` SMS consent records are created from your company's SMS consent page in the web app. ``` They remain in the help tree deliberately, so discovery tells you the capability exists and where it lives, rather than leaving you to wonder whether you typed the name wrong. See [Autessa CLI](/docs/cli.md). ## Related - [Autessa CLI](/docs/cli.md) - [Install and sign in](/docs/cli/install.md) - [Command surface](/docs/cli/command-surface.md) - [Coding agents](/docs/cli/coding-agents.md) --- # Custom Apps Source: https://docs.autessa.com/docs/cli/apps > Pull a custom app's React source to disk, edit it locally, push it back to the draft, and publish when you're ready to go live. # Custom Apps Custom apps are full Vite/React/TypeScript projects hosted by Autessa. The CLI can pull an app's draft source to your machine, push edits back, and run a production deploy — which makes an Autessa app editable with whatever editor, diff tool, or coding agent you already use. ## Key concepts ### Draft and published An app has a **draft** — the private working copy — and a **published version**, which is what end users actually reach. `push` writes to the draft. Nothing you push is live. **Publishing is a production deploy, not a save.** It snapshots the draft, builds it, and once the build succeeds serves it to everyone with access — its public URL and any custom domains — replacing whatever was live before. Treat it as a deliberate step, not a reflex after every edit. > If you are driving the CLI from a coding agent, this is the rule to encode: stay in the draft unless the user explicitly asked to launch, ship, deploy or go live. See [Coding agents](/docs/cli/coding-agents.md). ### What the file API covers `pull` and `push` handle **text source files only**. The read-only `src/autessa/` library — the generated bindings an app uses to reach agents, tools and tables — is never returned by `pull` and cannot be pushed. `index.html` is Vite's entry shell: a `` and a module script tag. Page content belongs in the React files under `src/`. Overwriting `index.html` with static markup breaks the app. ### `autessa.config.json` Every app carries a config file binding it to the platform resources it uses: ```json { "version": "1.0.0", "agents": { "TODO_MANAGER": "Lo59cuYy9d8T" }, "tools": {}, "tables": { "TODO_LIST": "U49RQBUdAaRq", "TODO_COMMENTS": "mcjl7XmDfKCI" }, "managers": {}, "scheduledJobs": {} } ``` This is the seam between an app and everything else you build headless. A script that creates a table and an agent can write their ids straight into this file and push it, and the app picks them up. ## How to find an app ```bash autessa app all --output table ``` ``` appId appName sharingLevel userAccessLevel hostingMode latestVersion publishedVersion ------------ ----------- ------------ --------------- ----------- ------------- ---------------- GTBhswTJ6a9r BasicTodo PRIVATE ADMIN DEFAULT 1 1 lCHdKACw4tpE BasicTodo PRIVATE ADMIN DEFAULT ``` An app with no `publishedVersion` has never been deployed — it exists only as a draft. Get the URLs a human needs: ```bash autessa app visible-urls --appId GTBhswTJ6a9r ``` ```json { "editorUrl": "https://builder.autessa.com/app-builder/editor/GTBhswTJ6a9r", "publishedUrl": "https://builder.autessa.com/customappsv2/view/GTBhswTJ6a9r" } ``` `autessa app get` returns full details plus the draft file listing; `get-app-info` is a lighter read that skips the file listing. ## How to pull the source ```bash autessa app pull --appId GTBhswTJ6a9r ``` Returns a path→content map of the draft's editable files: ```json { "files": { "autessa.config.json": "{\n \"version\": \"1.0.0\", ... }", "index.html": "\n ... ", "package.json": "{\"private\":true,\"devDependencies\":{ ... }}", "src/App.tsx": "...", "src/components/ui/Button.tsx": "...", "src/pages/Todos.tsx": "..." } } ``` ### Paging Large apps come back in pages. When `nextPageToken` is present, call `pull` again with it until it is absent: ```bash autessa app pull --appId GTBhswTJ6a9r --pageToken ``` A file larger than the transfer budget is reported in `skipped` rather than dropped silently — check that array, don't assume an empty response means an empty app. ### Writing the files to disk `pull` returns JSON, so unpacking it is one `jq` step: ```bash mkdir -p ./myapp && cd ./myapp autessa app pull --appId GTBhswTJ6a9r \ | jq -r '.files | to_entries[] | @base64' \ | while read -r row; do entry=$(echo "$row" | base64 --decode) path=$(echo "$entry" | jq -r '.key') mkdir -p "$(dirname "$path")" echo "$entry" | jq -r '.value' > "$path" done ``` Now edit with anything — your editor, a formatter, a coding agent, a codemod. ## How to push edits back `push` takes the same path→content map: ```bash autessa app push --appId GTBhswTJ6a9r --files '{ "src/pages/Todos.tsx": "import React from \"react\";\n\nexport default function Todos() {\n return …;\n}\n" }' ``` ```json { "savedPaths": ["src/pages/Todos.tsx"], "deletedPaths": [] } ``` Rebuild the map from a local directory: ```bash FILES=$(find . -type f \ ! -path './node_modules/*' ! -path './.git/*' ! -path './dist/*' ! -path './src/autessa/*' \ -print0 | xargs -0 jq -Rs 'input_filename as $f | {($f | ltrimstr("./")): .}' | jq -s 'add') autessa app push --appId GTBhswTJ6a9r --files "$FILES" ``` Skip `node_modules`, `.git`, `dist`/`build`, lockfiles, `src/autessa/`, and any binary files — the file API is text-only. ### Mirroring vs. adding By default `push` **mirrors**: draft files absent from your payload are deleted, so a partial push removes everything you left out. That is what you want when pushing a complete local tree. `--addOnly` changes it to add-and-update, never delete: ```bash autessa app push --appId GTBhswTJ6a9r --files '{"src/pages/Todos.tsx":"..."}' --addOnly ``` Use `--addOnly` for a targeted single-file edit. Use the default mirror when your local directory is the authoritative copy. The response tells you exactly what happened — `savedPaths`, `deletedPaths`, and `binaryContentPaths` for anything containing NUL bytes (still saved, but worth a look). ## How to publish Only when the app is genuinely meant to go live: ```bash autessa app publish --appId GTBhswTJ6a9r ``` Publishing is **asynchronous**. It returns a `publishJobId` and a `version` and starts a build; the app is live only once that build reaches `SUCCESS`: ```bash autessa app publish-status --publishJobId ``` Poll while `terminal` is `false`, and watch `phase`. A `FAILED` build carries the error tail — fix the draft and publish again. You can also publish as part of a push: ```bash autessa app push --appId GTBhswTJ6a9r --files "$FILES" --publish ``` which returns `version`, `buildId`, `publishJobId` and `buildStatus: BUILDING` — still asynchronous, still needing `publish-status`. > Do not report an app as live until the build has actually succeeded. `publish` returning cleanly means the build *started*. ## How to create, clone and roll back ```bash # start from a template autessa app create --appName "My Site" --templateType SAAS_APP # copy an existing app's draft into a new private app you own autessa app clone --appId GTBhswTJ6a9r --appName "My Site (fork)" # roll the DRAFT back to a previously published version's source autessa app rollback --appId GTBhswTJ6a9r --version 1 # list versions with build status autessa app versions --appId GTBhswTJ6a9r ``` Templates are `BOOTSTRAP`, `SAAS_APP`, `LANDING_PAGE` and `DASHBOARD`. Note what `rollback` does: it restores the **draft** from an old version's source. It does not itself change what is live — publish afterwards for that. ## How to control access and domains ```bash autessa app get-sharing-details --appId GTBhswTJ6a9r autessa app share --appId GTBhswTJ6a9r \ --sharingLevel COMPANY \ --companyPermissionLevel WRITE \ --hostingMode AUTH_CODE \ --publicSlug my-site \ --authCode s3cr3t ``` `share` configures two independent things in one ADMIN-only call: who can **edit** the source (code sharing), and how **end users** reach the deployed app (hosting mode). `get-sharing-details` is the read-only view of exactly what it would modify. Custom domains: ```bash autessa app check-slug --appId GTBhswTJ6a9r --publicSlug my-site autessa app domains add --appId GTBhswTJ6a9r --domainName app.example.com autessa app domains status --appId GTBhswTJ6a9r autessa app domains verify --appId GTBhswTJ6a9r --domainId autessa app domains remove --appId GTBhswTJ6a9r --domainId ``` `domains add` returns the CNAME — and, when required, the TXT verification record — the domain owner must create. `domains status` lists each domain's state, its CNAME target, the live URL once `ACTIVE`, and the TXT record while `PENDING_TXT`. `domains verify` runs one provisioning step synchronously instead of waiting for the next scheduled poll, and also resets a `FAILED` domain for retry. ## What stays in the web builder The visual app builder — the workspace, the file tree UI, and the vibecode assistant — is a browser experience. The CLI covers the file round-trip and the lifecycle; it does not replace the editor. See [App Builder](/docs/guide/platform/app-builder.md) and [Vibecode](/docs/guide/autobots/vibecode.md). ## Full command reference ## Related - [Coding agents](/docs/cli/coding-agents.md) - [Autessa CLI](/docs/cli.md) - [App Builder](/docs/guide/platform/app-builder.md) - [Custom apps API](/docs/api/poweruser/custom-apps.md) --- # AutessaDB Source: https://docs.autessa.com/docs/cli/autessadb > Create tables, write and query rows with typed column values, wire up triggers, and read the access log — all from the terminal. # AutessaDB AutessaDB is the platform's system of record, and the `autessadb` namespace is the largest in the CLI. Tables, rows, security roles, triggers and the audit trail are all reachable from the terminal, and every read and write goes through the same row-level and field-level security the web app enforces. ## Key concepts ### The sub-namespaces | Namespace | What it does | | --- | --- | | `autessadb tables` | Create and administer tables — schema, security, sharing, ownership, soft-delete | | `autessadb query` | Read and write rows, including bulk operations and semantic search | | `autessadb trigger` | Per-row automation that fires on insert, update or delete | | `autessadb roles` | The security roles that scope access | | `autessadb access-log` | The row-level data access audit trail | | `autessadb drive` | Files and folders in a [Drive](/docs/guide/autessadb/drive.md): browse, push and pull folders, grep, search, read, and share | | `autessadb analytics` | [Analytics tables](/docs/guide/autessadb/analytics.md): load files and run read-only SQL | ### Typed column values This is the one thing that trips people up. Rows are **not** flat maps of column name to scalar. Each value is an `AutessaDbColumnValue` object carrying a `columnType` and the matching typed field: ```json { "task": { "columnType": "SHORT_TEXT", "textValue": "Write the CLI docs" }, "status": { "columnType": "SINGLE_SELECT", "textValue": "doing" }, "id": { "columnType": "INTEGER", "integerValue": 1 } } ``` Sending a bare value gets you a clear error rather than a silent misparse: ``` error [ENDPOINT_ERROR]: The request body did not match this endpoint's schema. Field $.values. expected an object but got a string — send an object of that field's type (e.g. { "columnType": ..., "...Value": ... }), not a bare value. ``` The reliable way to learn a column's typed field name is to read one row back with `query get-data` and copy the shape. > Filters are the exception. `whereGroups` conditions take plain scalars in `value` — the typed-object form is only for writing rows and reading them back. ### Built-in columns Every table gets `id`, `created_at` and `updated_at` automatically. Do not declare them in a schema, and omit them when inserting — they are set for you. They *are* valid in filters and sort terms. ## How to create a table ```bash autessa autessadb tables create \ --tableName docs_cli_scratch \ --schema '{ "columnDefinitions": [ { "columnName": "task", "columnType": "SHORT_TEXT", "required": true, "columnComment": "What needs doing." }, { "columnName": "status", "columnType": "SINGLE_SELECT", "allowedValues": ["todo", "doing", "done"] } ] }' ``` ```json { "tableId": "ayXduiaLeFKh" } ``` Columns live in `schema.columnDefinitions` — not a field called `columns`. Per-column options worth knowing: | Field | Effect | | --- | --- | | `required` | Every row must carry a value | | `columnComment` | A human- and model-facing description of what the column holds | | `allowedValues` | The option set for `SINGLE_SELECT` / `MULTI_SELECT` | | `vectorizeField` | Index the column for semantic search (`SHORT_TEXT`, `LONG_TEXT`, `FILE`, `AUDIO` only) | | `masked` | Store encrypted at rest (`SHORT_TEXT` / `LONG_TEXT` only) | Two schema-level flags matter at create time: - **`appendOnly`** makes the table write-once — rows can be inserted and read but never updated or deleted. It is a one-way latch: it can be turned on later but never off, so set it at creation for ledger and audit tables. Computed columns are not allowed on one, and column removal is rejected because it would destroy history. - **`tags`** are free-form organizational labels. > `DATETIME` is legacy and rejected on create. Use `SHORT_TEXT` holding a UTC ISO-8601 string instead. Read a table's full configuration back with `autessa autessadb tables get --tableId `, and list what you can reach with `tables all` — which splits results by how you got access: owned, shared company-wide, shared directly with you. ## How to write rows ### One row ```bash autessa autessadb query insert --tableId ayXduiaLeFKh --values '{ "task": { "columnType": "SHORT_TEXT", "textValue": "Write the CLI docs" }, "status": { "columnType": "SINGLE_SELECT", "textValue": "doing" } }' ``` ```json { "insertedId": 1, "newRow": { "id": { "columnType": "INTEGER", "integerValue": 1 }, "task": { "columnType": "SHORT_TEXT", "textValue": "Write the CLI docs" }, "status": { "columnType": "SINGLE_SELECT", "textValue": "doing" }, "created_at": { "columnType": "SHORT_TEXT", "textValue": "2026-09-10 09:26:50.497812" }, "updated_at": { "columnType": "SHORT_TEXT", "textValue": "2026-09-10 09:26:50.497812" } } } ``` Required columns and select-option membership are validated, and writing a column your field-level security forbids is rejected. Insert triggers and computed columns fire asynchronously afterwards — a `computationStatus` of `COMPUTING` in the response means computed columns are still being calculated. ### Updating ```bash autessa autessadb query update --tableId ayXduiaLeFKh --rowId 1 --values '{ "status": { "columnType": "SINGLE_SELECT", "textValue": "done" } }' ``` Only the columns you send change. Omitted columns keep their current values, which is what lets a script update one field safely while a person edits another in the UI. ### Many rows at once | Command | Use | | --- | --- | | `query bulk-insert` | Insert many rows in one call | | `query bulk-update-by-ids` | Update many rows by numeric id | | `query bulk-delete-by-ids` | Delete many rows by numeric id | | `query batch-update` | Update every row matching a filter | | `query batch-delete` | Delete every row matching a filter | The `bulk-*` family addresses rows by id; the `batch-*` family addresses them by `whereGroups`. Prefer either over a loop of single-row calls — they are one round trip and one unit of work. ## How to query rows ### Everything, paged ```bash autessa autessadb query get-data --tableId ayXduiaLeFKh --output table ``` Returns the schema plus a page of rows, sorted newest-updated first. ### Filtered `whereGroups` is an array of groups; conditions inside a group combine with the group's `logicalOperator`, and the groups themselves combine with `--groupsLogicalOperator`: ```bash autessa autessadb query query --tableId ayXduiaLeFKh --whereGroups '[ { "logicalOperator": "AND", "conditions": [ { "columnName": "status", "operator": "EQUALS", "value": "doing" }, { "columnName": "created_at", "operator": "GREATER_THAN", "value": "2026-09-01" } ] } ]' ``` ```json { "rows": [ { "id": 1, "columnData": { "task": { "columnType": "SHORT_TEXT", "textValue": "Write the CLI docs" }, "status": { "columnType": "SINGLE_SELECT", "textValue": "doing" } }, "createdAt": "2026-09-10T09:26:50.497812", "updatedAt": "2026-09-10T09:26:50.497812" } ] } ``` Available operators: `EQUALS`, `NOT_EQUALS`, `GREATER_THAN`, `LESS_THAN`, `GREATER_THAN_OR_EQUAL`, `LESS_THAN_OR_EQUAL`, `CONTAINS`, `ICONTAINS`, `STARTS_WITH`, `ENDS_WITH`, `LIKE`, `ILIKE`, `IN`, `NOT_IN`, `BETWEEN`, `IS_NULL`, `IS_NOT_NULL`. Omitting `operator` defaults to `EQUALS`. A few rules that save debugging time: - `IN` / `NOT_IN` take an array; `BETWEEN` takes exactly two values `[low, high]`; `IS_NULL` / `IS_NOT_NULL` ignore `value`. - `ICONTAINS` is the right operator for a user-typed search box — it is case-insensitive `CONTAINS`. - **`LIKE` and `ILIKE` pass `%` and `_` through unescaped.** Never hand them raw user input. - Media columns support only `IS_NULL` / `IS_NOT_NULL`. Masked columns support equality operators only. Sorting and paging: ```bash autessa autessadb query query --tableId ayXduiaLeFKh \ --orderBy '[{"columnName":"created_at","direction":"ASC"}]' \ --page 1 --pageSize 25 ``` `orderBy` defaults to `updated_at DESC` then `id DESC`. Direction defaults to `DESC` — only an explicit `ASC` ascends. Page numbers here are **1-based**, unlike the 0-based paging used by the history commands. ### Joins, aggregates and semantic search ```bash # follow foreign keys autessa autessadb query join-query --tableId ayXduiaLeFKh --json '{...}' # group-by rollups computed server-side autessa autessadb query aggregate --tableId ayXduiaLeFKh --json '{...}' # vector search over a vectorized column autessa autessadb query semantic-search --tableId ayXduiaLeFKh --json '{...}' ``` `semantic-search` needs a column created with `vectorizeField: true`. Run each command's `help` for its exact argument shape. ## How to change a schema ```bash autessa autessadb tables update-schema --tableId ayXduiaLeFKh --json '{...}' ``` `update-schema` adds columns, removes columns, and adds options to `SINGLE_SELECT` / `MULTI_SELECT` columns. **There is no rename and no retype** — changing a column's name or type means adding a new one and migrating. This is a replace-semantics method, so `--replace` applies if you are sending an authoritative schema rather than a delta. See [Global flags](/docs/cli/discovery.md). ## How to add a trigger Triggers fire per row on insert, update or delete, and run a tool, an agent, a manager, or a write to another table: ```bash autessa autessadb trigger add --tableId ayXduiaLeFKh --json '{...}' autessa autessadb trigger all --tableId ayXduiaLeFKh autessa autessadb trigger disable --tableId ayXduiaLeFKh --triggerId autessa autessadb trigger enable --tableId ayXduiaLeFKh --triggerId ``` `disable` stops a trigger firing without deleting it — the right move when you are debugging a loop, since `delete` is permanent. Read what fired, with decrypted event input and response output: ```bash autessa autessadb trigger history --tableId ayXduiaLeFKh --year 2026 --month 9 autessa trigger-history list --tableId ayXduiaLeFKh autessa trigger-history detail --eventId a02e8438-9032-46f3-a875-41d051f2c013 ``` `autessadb trigger history` is per-table and per-month; the top-level `trigger-history` namespace lists across the whole company newest-first, with `detail` returning full uncapped payloads. Both back the same views described in [Triggers](/docs/guide/autessadb/triggers.md). ## How to review access ```bash autessa autessadb access-log list --tableId ayXduiaLeFKh ``` The row-level data access log, newest first, filterable by table, row, date range and action. This is the record of who read and wrote which rows — the audit surface the web app renders, readable from the terminal. ## How to manage security ```bash autessa autessadb roles list autessa autessadb roles create --json '{...}' autessa autessadb tables update-settings --tableId ayXduiaLeFKh --json '{"rlsEnforced":true}' autessa autessadb tables share --tableId ayXduiaLeFKh --level COMPANY --accessLevel READ ``` `update-settings` is owner-or-ADMIN only. It toggles row-level security enforcement and sets the default role auto-assigned to grantees when the table is shared. `get-sharing-details` shows exactly what `share` and `update-settings` would modify, without changing anything. ## How to delete and restore ```bash autessa autessadb tables cli-delete --tableId ayXduiaLeFKh autessa autessadb tables list-deleted autessa autessadb tables restore --tableId ayXduiaLeFKh ``` Table deletion is a **soft delete** with a 14-day grace period. `list-deleted` is your trash, showing the time left before each table is purged, and `restore` brings one back within that window. ## Shorthand: `autessa drive` Short commands for Drives (CLI 0.6.0 and later). Pick the Drive once with `autessa drive use `; it is remembered for the profile. Paths are relative to the Drive root (a leading `/` is optional; `/` alone is the whole Drive). Output is plain text; add `--json` for the raw response, or `--drive ` to use another Drive for one command. | Command | What it does | | --- | --- | | `autessa drive use [name]` | Pick the Drive (by name or ID); without a name, list your Drives | | `autessa drive ls [path]` | List a folder | | `autessa drive tree [path]` | Show the folder structure | | `autessa drive stat ` | Size, version, text status and your access | | `autessa drive cat ` | Print a file's text (`--numbers` keeps line numbers; `--pages 2-3` or `--lines 1-50` for a range) | | `autessa drive read --pages 3-5` | Print one range of a file's text | | `autessa drive grep [folder]` | Find exact text: one `path:page:line: text` line per match | | `autessa drive search "" [folder]` | Ranked keyword search; meaning-based when semantic search is enabled | | `autessa drive touch ` | Create an empty file (no change if it exists) | | `autessa drive write [text]` | Create or replace a text file; text from the argument or stdin | | `autessa drive append [text]` | Add text to the end of a file; text from the argument or stdin | | `autessa drive edit --old --new [--all]` | Replace exact text | | `autessa drive mkdir ...` | Create folders | | `autessa drive mv ` | Move into a folder, or rename by giving a new path (`--confirm` when access changes) | | `autessa drive rm ... [--forever]` | Move to the trash, or delete for good | | `autessa drive comment "text" [--page N] [--reply ]` | Add a comment, optionally on a page or as a reply | | `autessa drive comments [--includeResolved]` | List a file's comment threads | | `autessa drive share --team \| --user \| --company \| --members --role viewer\|commenter\|editor` | Share a file or folder (`--members`: with every Drive member), or `/` to add Drive members | | `autessa drive link ` | Create a view link (`--audience ANYONE` for a public one) | | `autessa drive open ` | Print a 10-minute download link | | `autessa drive push [drive folder]` | Upload a local folder (resumable) | | `autessa drive pull ` | Download a Drive folder | ```bash autessa drive use Docs echo "- Acme prefers Net 45" | autessa drive append notes/memory.md autessa drive edit notes/memory.md --old "Net 45" --new "Net 60" autessa drive grep "Net 60" notes/ ``` Errors show the server's code and a hint, for example `error [EDIT_AMBIGUOUS]: oldText matches 2 places` followed by `hint: Add surrounding words…`. Run `autessa drive help ` for a command's arguments and the full command it runs. ## Shorthand: `autessa analytics` | Command | What it does | | --- | --- | | `autessa analytics use [table]` | Pick the analytics table; without a name, list them | | `autessa analytics describe` | Columns, partition columns and size | | `autessa analytics load [--mode REPLACE]` | Upload a CSV, Parquet or JSON-lines file and load it | | `autessa analytics query ""` | Run SQL and print the rows (waits for the job; Ctrl-C cancels) | | `autessa analytics jobs` | Recent query and load jobs | | `autessa analytics suggest [--focus measures] [--apply]` | Draft the semantic layer with AI; `--apply` saves it | `--table ` uses another table for one command. ## How to work with a Drive A [Drive](/docs/guide/autessadb/drive.md) is a table of files and folders, and `autessadb drive` treats it like a remote file system. Every command takes the Drive's `--tableId`, and paths are absolute (`/Shared/Legal/Contracts`). Commands run as you, so you see and change exactly what you could in the explorer. ### Browse ```bash autessa autessadb drive ls --tableId V1StGXR8Z5 --path /Shared/Legal --output table autessa autessadb drive tree --tableId V1StGXR8Z5 --path /Shared/Legal --maxDepth 2 autessa autessadb drive find --tableId V1StGXR8Z5 --path /Shared --glob '*.pdf' --modifiedAfter 2026-09-01T00:00:00Z autessa autessadb drive stat --tableId V1StGXR8Z5 --path /Shared/Legal/Contracts/acme-msa.pdf ``` ``` id type name sizeBytes version extractionStatus ---- ------ ------------- --------- ------- ---------------- 4190 FOLDER Archive 4182 FILE acme-msa.pdf 482113 2 EXTRACTED ``` Listings are paged. When a response carries a `nextCursor`, pass it back as `--cursor` with the same other flags. `truncated` tells you whether a response is complete; nothing is dropped silently. ### Upload a folder ```bash autessa autessadb drive push --tableId V1StGXR8Z5 --localDir ./contracts --path /Shared/Legal/Contracts ``` ``` scanning ./contracts ... 214 files, 1.8 GB uploading [=============> ] 131/214 1.1 GB 4 parallel committed 214 files, created 9 folders ``` `push` walks the local folder, creates the matching folders, uploads files four at a time (large files in parts), and commits them. Progress goes to stderr, so stdout stays clean JSON. Add a `.autessaignore` file (same syntax as `.gitignore`) to the local folder to skip files. | Flag | Effect | | --- | --- | | `--onConflict FAIL\|RENAME\|NEW_VERSION` | What to do when a file already exists. `FAIL` (default) skips and reports that file; `RENAME` keeps both; `NEW_VERSION` replaces it and keeps the old content as a version. | | `--mirror` | Also move Drive files that are no longer in the local folder to the trash. Without it, `push` only adds and updates. | To upload a single file: ```bash autessa autessadb drive upload --tableId V1StGXR8Z5 --localFile ./initech-msa.pdf --path /Shared/Legal/Contracts/initech-msa.pdf ``` ### Download a folder ```bash autessa autessadb drive pull --tableId V1StGXR8Z5 --path /Shared/Legal/Contracts --localDir ./contracts ``` `pull` recreates the folder structure locally and verifies each file's checksum after download. ### Search and read ```bash # exact text, one line per match autessa autessadb drive grep --tableId V1StGXR8Z5 --path /Shared/Legal \ --pattern 'termination for convenience' --caseInsensitive true --output text # ranked search (meaning-based when the Drive's semantic search is on) autessa autessadb drive search --tableId V1StGXR8Z5 --query 'terminate contract early' # read a page or a line range autessa autessadb drive read --tableId V1StGXR8Z5 --path /Shared/Legal/Contracts/acme-msa.pdf --pages 4 ``` ``` /Shared/Legal/Contracts/acme-msa.pdf:4:12: 12.2 Termination for Convenience. Either party may terminate this Agreement /Shared/Legal/Contracts/globex-msa.pdf:7:3: 9.1 Termination for Convenience. Customer may terminate upon thirty (30) days ``` `--output text` prints `path:page:line: text`, ready for `grep`, `sort`, or an editor's quickfix list. For text files the page is omitted and the line is the file's real line number. Add `--regex true` for regular expressions and `--context 2` for surrounding lines. ### Organize and share ```bash autessa autessadb drive mkdir --tableId V1StGXR8Z5 --path /Shared/Legal/Contracts/2026 autessa autessadb drive move --tableId V1StGXR8Z5 --path /Drafts/sow.docx --destination /Shared/Legal/Contracts autessa autessadb drive trash --tableId V1StGXR8Z5 --path /Shared/Legal/Contracts/old autessa autessadb drive shares upsert --tableId V1StGXR8Z5 --path /Shared/Legal \ --principals '[{"type":"TEAM","teamId":"3b1f6a2e-7c41-4d55-9a0e-58c2b7f1d903"}]' --role VIEWER autessa autessadb drive shares list --tableId V1StGXR8Z5 --path /Shared/Legal/Contracts/acme-msa.pdf ``` To add Drive members, use `--path /` (or the shorthand `autessa drive share / --team Finance --role editor`). Members see the Drive but no folder or file in it until that item is shared with them; `autessa drive share Legal --members --role viewer` shares a folder with every current member. A `move` that would change who has access returns `requiresConfirmation` with the list of who would gain or lose access, and moves nothing. Re-run with `--confirm true` to go ahead. ## How to load and query analytics tables [Analytics tables](/docs/guide/autessadb/analytics.md) are queried with read-only ANSI SQL. Loads and queries run as asynchronous jobs, and queries typically take a few seconds; by default the CLI waits for the job and prints the result. ### Load a file ```bash autessa autessadb analytics load --tableId V1StGXR8Z5 --localFile ./orders-2026-09.parquet autessa autessadb analytics load --tableId V1StGXR8Z5 --localFile ./orders.csv --mode REPLACE ``` `load` uploads the local CSV or Parquet file in parts, starts the load, and waits for it to finish, printing rows loaded and any rejected rows. `--mode` is `APPEND` (default) or `REPLACE`. ### Run a query ```bash autessa autessadb analytics query --tableId V1StGXR8Z5 --output table --sql " SELECT region, SUM(net_amount) AS revenue FROM order_lines WHERE order_date >= DATE '2026-01-01' GROUP BY region ORDER BY revenue DESC" ``` ``` region revenue ------ ----------- NA 28411093.20 EMEA 15002118.45 APAC 6120774.00 ``` | Flag | Effect | | --- | --- | | `--output table\|csv\|json` | `table` for reading, `csv` to save or pipe (`> revenue.csv`), `json` (default) for scripts. | | `--maxRows` | Cap the rows kept in the result. | | `--no-wait` | Return the `jobId` immediately instead of waiting. | With `--no-wait`, check on the job and fetch its result later: ```bash autessa autessadb analytics query --tableId V1StGXR8Z5 --sql "SELECT ..." --no-wait autessa autessadb analytics job-status --tableId V1StGXR8Z5 --jobId 1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54 autessa autessadb analytics job-results --tableId V1StGXR8Z5 --jobId 1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54 --output csv autessa autessadb analytics cancel --tableId V1StGXR8Z5 --jobId 1f8e5c3a-9b2d-4e7f-a6c1-3d0b9e2f7a54 ``` ### Draft the semantic layer with AI ``` autessa analytics suggest # print the AI draft (JSON) autessa analytics suggest --focus measures # only measures autessa analytics suggest --apply # print, then save it ``` `--apply` saves through `update-semantics` at the version the draft was made from, so a concurrent edit is refused rather than overwritten. Needs CLI 0.7.0 or later. ### Describe before you query ```bash autessa autessadb analytics describe --tableId V1StGXR8Z5 autessa autessadb analytics estimate --tableId V1StGXR8Z5 --sql "SELECT COUNT(*) FROM order_lines" ``` `describe` returns the SQL name, columns, partition columns, measures, joins, and verified queries. `estimate` reports how much data a query would scan without running it. Filter on partition columns to keep scans small. > `drive push`, `drive pull`, `drive upload`, and `analytics load` read and write local files, which needs a recent CLI. If the command is missing, update with `npm install -g @autessa/cli` and run `autessa --refresh help`. ## Full command reference ## Related - [Tools](/docs/cli/tools.md) and [Agents](/docs/cli/agents.md) - [Autessa CLI](/docs/cli.md) - [Tables](/docs/guide/autessadb/tables.md) and [Triggers](/docs/guide/autessadb/triggers.md) in the builder - [AutessaDB tool types](/docs/tools/autessadb.md) - [AutessaDB API](/docs/api/autessadb.md), [Drive API](/docs/api/autessadb-drive.md), and [Analytics API](/docs/api/autessadb-analytics.md) - [Drive](/docs/guide/autessadb/drive.md) and [Analytics tables](/docs/guide/autessadb/analytics.md) in the builder --- # Tools Source: https://docs.autessa.com/docs/cli/tools > Pick a tool type, create and configure a tool, publish it, run it directly to test, and read its execution history — all from the terminal. # Tools A tool is a single capability an agent can call — send an email, query a table, scrape a page, run a script. Building one from the terminal is a four-step loop: create the draft, set its type and arguments, publish, then run it directly to check your work. Every command and response on this page is real output from a live session. ## Key concepts ### Draft and published are different things A tool has a **draft** (your working copy) and a **published version** (what actually runs). `create` and `update` write the draft; `publish` snapshots it as a new live version. A tool must be published before an [agent](/docs/cli/agents.md), a [scheduled job](/docs/cli/scheduled-jobs.md), or a [table trigger](/docs/cli/autessadb.md) can run it. A draft-only tool will attach fine and then fail at call time. ### Publishing a tool does not attach it to an agent This is the single most common mistake. `tools create` + `tools publish` gives you a runnable tool that no agent knows about. Attaching is a separate step on the agent side — see [Agents](/docs/cli/agents.md). ### Prefer a purpose-built tool type over a script The platform ships hundreds of native tool types grouped by category — `API_CALL` and `LLM_CALL` as general building blocks, the `AUTESSADB_*` family for data, email and calendar types, media types, web types, MCP, and a large connector library. A native type arrives pre-wired with the right config fields and authentication. `AUTESSA_SCRIPT` is the general-purpose escape hatch. A new draft tool starts as an `AUTESSA_SCRIPT` hello-world, which makes it tempting to just write code there. Resist that unless nothing fits — reach for a native type first. ## How to pick a tool type Browse the catalog by category: ```bash autessa tool-types categories ``` ```json { "categories": [ { "category": "platform", "title": "Platform", "description": "Autessa's own building blocks — the tool families that need no external connector.", "count": 28, "toolTypes": [ "AUTESSA_SCRIPT", "API_CALL", "LLM_CALL", "SCHEDULE_EVENT", "AUTESSADB_INSERT", "AUTESSADB_UPDATE", "AUTESSADB_QUERY_TABLE", "AUTESSADB_SEMANTIC_QUERY", "MCP", "TRANSCRIBE", "SCRAPE_URL", "PERPLEXITY_SEARCH", "..." ] } ] } ``` Then read one type's exact configuration fields, output shape and example: ```bash autessa tool-types LLM_CALL ``` ``` LLM Call (LLM_CALL) Sends promptTemplate — with its @@{...} placeholders already resolved — to a model as a single prompt and returns the completion. model picks a capability tier by alias rather than a vendor model name: SMALL_INSTRUCTION_FOLLOWING (the default), BIG_INSTRUCTION_FOLLOWING, [...] * * * * * * * * * * * * * * * * * * CONFIG (inputs) * * * * * * * * * * * * * * * * * * promptTemplate Required string value. model Required object value. timeoutSeconds Per-call timeout (seconds). Null = current default behavior (120s). [...] * * * * * * * * * * * * * * * * * * OUTPUT * * * * * * * * * * * * * * * * * * success Whether the LLM call completed successfully content The text content generated by the LLM usage Token usage counts (promptTokens, completionTokens, totalTokens) ``` `tool-types` is a read-only reference surface — it does not create anything. The full catalog is also documented under [Tool Types](/docs/tools.md). ## How to create and publish a tool ### 1. Create the draft ```bash autessa tools create \ --toolName "summarize_text" \ --toolDescription "Summarizes a block of text in one sentence." ``` ```json { "tool": { "toolId": "gPdJwp5jbr9eN", "companyId": "y8mRwXwboDVu", "toolConfig": { "toolType": "AUTESSA_SCRIPT", "config": { "script": "responseStream.addStreamMessage('Starting script execution');\nconsole.log(\"Hello world\")\n..." } }, "name": "summarize_text", "description": "Summarizes a block of text in one sentence.", "arguments": {}, "privateArguments": {}, "tags": [] } } ``` Note the placeholder `AUTESSA_SCRIPT` config — that is the starting point, not the destination. > `toolName` is the function name the model sees, and `toolDescription` is what it reads to decide whether to call the tool. Write both for the model, not for a human browsing a list — precision here does more for reliability than anything in the agent's instructions. ### 2. Set its type and arguments `tools update` takes the **complete desired configuration** in `--updatedTool`, so read-modify-submit rather than sending a patch: ```bash autessa tools update --toolId gPdJwp5jbr9eN --updatedTool '{ "toolId": "gPdJwp5jbr9eN", "name": "summarize_text", "description": "Summarizes a block of text in one sentence.", "toolConfig": { "toolType": "LLM_CALL", "config": { "promptTemplate": "Summarize the following in exactly one sentence:\n\n@@{text}", "model": "SMALL_INSTRUCTION_FOLLOWING" } }, "arguments": { "text": { "description": "The text to summarize.", "isRequired": true, "argumentSchema": { "type": "string" } } } }' ``` ```json { "updatedTool": { "toolId": "gPdJwp5jbr9eN", "toolConfig": { "toolType": "LLM_CALL", "config": { "promptTemplate": "Summarize the following in exactly one sentence:\n\n@@{text}", "model": "SMALL_INSTRUCTION_FOLLOWING" } }, "name": "summarize_text", "description": "Summarizes a block of text in one sentence.", "arguments": { "text": { "description": "The text to summarize.", "isRequired": true, "argumentSchema": { "type": "string" } } }, "tags": [] } } ``` Two details worth noting: - **`@@{name}` placeholders** in a tool's config are substituted from its arguments at run time. `@@{text}` in the prompt template is filled from the `text` argument. - **Argument definitions normalize.** The stored shape is `isRequired` and `argumentSchema`; the response tells you what was actually saved, so read it rather than assuming your input round-tripped verbatim. ### 3. Publish it ```bash autessa tools publish --toolId gPdJwp5jbr9eN ``` ```json { "publishedToolVersion": 1 } ``` ### 4. Test it directly You can run a tool without an agent, which is the fastest way to check its configuration: ```bash autessa tools execute --toolId gPdJwp5jbr9eN --variables '{ "text": "The Autessa CLI is a model-driven command line interface generated from the live SDK manifest, so every command and flag is discovered rather than hand-maintained." }' ``` ```json { "responseType": "EXECUTE_TOOL", "toolName": "summarize_text", "result": "{\"success\":true,\"content\":\"The Autessa CLI is dynamically generated from the live SDK manifest, allowing commands and flags to be discovered automatically instead of maintained manually.\",\"usage\":{\"promptTokens\":47,\"completionTokens\":44,\"totalTokens\":91}}", "invocationId": "df4fd983-9021-43c8-b42c-cb02c8f0105c" } ``` `--variables` fills public arguments. Two related flags exist for values the model must never see: - `--privateVariables` — secrets and configuration declared as private arguments. - `--environmentVariables` — per-run values injected straight into tool execution, never declared to the model. See [Environment variables](/docs/guide/agentic/environment-variables.md). Each execution bills one tool execution and is recorded in history. ## How to import tools from an MCP server `create-from-mcp` bulk-creates tools from every tool a connected MCP server advertises: ```bash autessa tools create-from-mcp --json '{...}' ``` Imported tools are tagged `mcp` plus the server host, so they are easy to find later with `tools all`. Connect and manage the servers themselves with the `mcp` namespace — see [MCP servers](/docs/guide/agentic/mcp-servers.md). ## How to read tool execution history ```bash autessa tools history list --toolId gPdJwp5jbr9eN ``` ```json { "executions": [ { "eventId": "a02e8438-9032-46f3-a875-41d051f2c013", "toolId": "gPdJwp5jbr9eN", "toolName": "summarize_text", "invocationId": "df4fd983-9021-43c8-b42c-cb02c8f0105c", "source": "API", "success": true, "durationMs": 3001, "createdAt": "2026-09-10T09:24:45Z", "requestVariables": "{\"text\":\"The Autessa CLI is a model-driven...\"}", "result": "{\"success\":true,\"content\":\"...\",\"usage\":{...}}", "requestTruncated": false, "resultTruncated": false } ], "totalElements": 1, "totalPages": 1, "page": 0, "pageSize": 50, "hasNext": false, "hasPrevious": false } ``` `source` tells you what triggered the run — `API` for a direct CLI call, plus `AGENT`, `TRIGGER`, `SCHEDULED`, `WEBHOOK`, `WORK_ITEM` and `SCRIPT`. It is the fastest way to tell a test run from production traffic. Filter by outcome and page through results: ```bash autessa tools history list --status error --page 0 --size 20 autessa tools history detail --eventId a02e8438-9032-46f3-a875-41d051f2c013 ``` `detail` returns the full, uncapped request and result — reach for it whenever `requestTruncated` or `resultTruncated` is `true`. ## How to version, share and delete ```bash autessa tools versions --toolId gPdJwp5jbr9eN autessa tools rollback --toolId gPdJwp5jbr9eN --json '{...}' ``` `versions` lists the published version (null if never published) and every archived one; `rollback` re-publishes an archived version, archiving the current one in its place. See [Versions](/docs/guide/agentic/versions.md). ```bash autessa tools get-sharing-details --toolId gPdJwp5jbr9eN # company-wide, read-only autessa tools share --toolId gPdJwp5jbr9eN --level COMPANY --accessLevel READ # or grant specific teammates autessa tools share --toolId gPdJwp5jbr9eN --level SPECIFIC_MEMBER \ --usersToAdd '[{"userEmail":"teammate@example.com","accessLevel":"WRITE"}]' ``` Sharing modes are `PRIVATE`, `COMPANY` at one access level, or `SPECIFIC_MEMBER` with per-user grants via `usersToAdd`/`usersToRemove`. Agents and tables share the same shape. ```bash autessa tools delete --toolId gPdJwp5jbr9eN ``` Deletion is permanent — the draft and every version go with it. Agents still referencing the tool will fail at call time, so check `tools all` before removing something shared. ## Next: give it to an agent A published tool does nothing on its own. Attach it to an agent and the model can call it: ```bash autessa agents update --agentId K2UYSwk3Yy4i --updatedAgent '{ "...": "...", "tools": [{ "toolId": "gPdJwp5jbr9eN" }] }' ``` The full walkthrough — creating the agent, attaching tools, publishing, and running it one-shot or multi-turn — is in [Agents](/docs/cli/agents.md). ## Full command reference ## Related - [Agents](/docs/cli/agents.md) — attach this tool and run it - [Managers](/docs/cli/managers.md) — orchestrate several agents - [Tool Types](/docs/tools.md) — the full catalog of native types - [Tools](/docs/guide/agentic/tools.md) in the builder - [AutessaDB](/docs/cli/autessadb.md) - [Autessa CLI](/docs/cli.md) --- # Agents Source: https://docs.autessa.com/docs/cli/agents > Create an agent, attach tools to it, publish it, run it one-shot or multi-turn, and read its conversation history and evaluations from the terminal. # Agents An agent is instructions, a model, and a set of tools it may call. Building one from the terminal is create → configure → publish, and running one is either a single-pass `execute` or a multi-turn conversation you open, drive and close. Every command and response on this page is real output from a live session. > An agent's capabilities are its tools, and a tool has to exist and be **published** before you can attach it. If you have not built one yet, start with [Tools](/docs/cli/tools.md) — that page covers picking a tool type, configuring it, and testing it in isolation. ## Key concepts ### Draft and published are different things An agent has a **draft** (your working copy) and a **published version** (what actually runs). `create` and `update` write the draft; `publish` snapshots it as a new live version. Publish before the agent serves anything beyond your own test executions — [scheduled jobs](/docs/cli/scheduled-jobs.md), hosted apps and end-user clients all run the published version. ### The SDK creates SIMPLE agents only `agentType` is required on create and must be `SIMPLE`. The CLI can read, update, run, share and manage agents of **any** type that already exists — the restriction is only on what it creates. ### Tools attach by id Adding a capability means putting a tool's id in the agent's `tools` array. The tool must already be published; see [Tools](/docs/cli/tools.md). Creating and publishing a tool does **not** attach it to anything on its own. ## How to create an agent ```bash autessa agents create \ --agentType SIMPLE \ --agentName "Summarizer" \ --agentDescription "Summarizes text on request." ``` ```json { "agent": { "agentId": "K2UYSwk3Yy4i", "agentType": "SIMPLE", "name": "Summarizer", "description": "Summarizes text on request.", "agentInstructions": "You are a professional agent who does as asked.", "model": "SMALL_INSTRUCTION_FOLLOWING", "maxIterations": 30, "tools": [], "voiceConfig": { "voiceModel": "SMALL", "voiceName": "FEMALE" }, "promptTemplateVariables": {}, "evaluationNotes": "", "tags": [], "enableBrowserTools": false } } ``` A fresh agent arrives with placeholder instructions, a default model, no tools, and `maxIterations: 30`. All of that is yours to change with `update`. ## How to attach tools and write the instructions Like `tools update`, `agents update` takes the **full desired configuration** in `--updatedAgent`, so read-modify-submit rather than sending a patch. Attaching a tool needs only its id — the server hydrates the rest: ```bash autessa agents update --agentId K2UYSwk3Yy4i --updatedAgent '{ "agentId": "K2UYSwk3Yy4i", "agentType": "SIMPLE", "name": "Summarizer", "description": "Summarizes text on request.", "agentInstructions": "You summarize text for the user. Always call the summarize_text tool to produce the summary, then reply with just that sentence.", "model": "SMALL_INSTRUCTION_FOLLOWING", "maxIterations": 10, "tools": [{ "toolId": "gPdJwp5jbr9eN" }] }' ``` The response echoes the agent with each tool fully expanded — its type, config and arguments — so you can confirm you attached the thing you meant to. The fields worth deliberate thought: | Field | What it controls | | --- | --- | | `agentInstructions` | The system prompt. Name the tools you expect it to call and when. | | `model` | Capability tier by alias — `SMALL_INSTRUCTION_FOLLOWING`, `BIG_INSTRUCTION_FOLLOWING`, `BIG_INSTRUCTION_FOLLOWING_REASONING`, `VIBE_CODING`, `AUTESSA_120B` | | `maxIterations` | How many tool-calling rounds before the agent must answer. Too low truncates multi-step work; too high lets a confused agent burn executions. | | `tools` | The capabilities it may call, by id | | `promptTemplateVariables` | Named slots you fill per run | | `enableBrowserTools` | Whether it gets browser capabilities | > A tool's own `toolName` and `toolDescription` do more for reliability than the agent's instructions — the model reads those to decide when to call it. If an agent picks the wrong tool, fix the tool's description before rewriting the prompt. ## How to publish ```bash autessa agents publish --agentId K2UYSwk3Yy4i ``` ```json { "publishedAgentVersion": 1 } ``` ## How to run an agent ### One-shot Omit `conversationId` for a single-pass run: ```bash autessa agents execute \ --agentId K2UYSwk3Yy4i \ --messageType MULTIMODAL \ --input '[{"inputType":"TEXT","content":"Summarize: the Autessa CLI is generated from the live SDK manifest, so commands are discovered with help rather than memorized, and everything it does is visible in the web builder immediately."}]' ``` ```json { "output": [ { "content": "The Autessa CLI is generated from the live SDK manifest, making commands discoverable through help instead of memorization, with all actions instantly visible in the web builder.", "outputType": "TEXT" } ] } ``` `--messageType MULTIMODAL` is the ordinary text execution. The other message types do not run the agent at all: `VOICE` is the handshake that opens a live voice session, and `PLAYBACK_COMPLETE` is a telephony client reporting audio playback progress. `--input` is an array of parts, so text, images, files and audio can be mixed. `TEXT` carries `content`; `IMAGE`/`FILE` carry `s3Uri` and `mimeType`; `AUDIO` carries `base64EncodedData` and `audioFormat`. Presigned upload URLs come from `agents generate-image-upload-link`, `generate-file-upload-link` and `generate-audio-upload-link`. ### Multi-turn Open a conversation, drive it, then close it: ```bash CID=$(autessa agents execute-create --agentId K2UYSwk3Yy4i | jq -r '.conversationId') autessa agents execute \ --agentId K2UYSwk3Yy4i \ --conversationId "$CID" \ --messageType MULTIMODAL \ --input '[{"inputType":"TEXT","content":"Summarize: autessa login opens a browser, you pick a company, and the CLI stores a tenant-scoped session credential."}]' ``` ```json { "output": [ { "content": "`autessa login` opens a browser for company selection, then stores a tenant-scoped session credential in the CLI.", "outputType": "TEXT" } ] } ``` ```bash autessa agents execute-close --agentId K2UYSwk3Yy4i --conversationId "$CID" ``` > Closing a conversation is what commits it to history. An open conversation is not yet in `conversations history`, and logging happens asynchronously after the close — so allow a moment before reading it back. ### Hidden inputs `--environmentVariables` passes values into the agent's tools for a turn without ever showing them to the model — secrets, API keys, or per-caller context a tool needs but the model must not see or invent. `--promptTemplateVariables` fills the agent's declared template slots. See [Environment variables](/docs/guide/agentic/environment-variables.md). ### Running a specific version By default `execute` runs the published version. Target another with `--version`: ```bash autessa agents execute --agentId K2UYSwk3Yy4i \ --version '{"versionState":"PUBLISHED","versionNumber":2}' \ --messageType MULTIMODAL \ --input '[{"inputType":"TEXT","content":"..."}]' ``` `autessa agents versions --agentId ` lists the published and archived version numbers, and `agents rollback` re-publishes an archived one. See [Versions](/docs/guide/agentic/versions.md). ## How to read conversation history ```bash autessa agents conversations history --agentId K2UYSwk3Yy4i --page 0 autessa agents conversations conversation \ --agentId K2UYSwk3Yy4i \ --conversationId f719af72-2e49-462e-a98c-63011a5a5671 ``` `conversation` returns the full message and tool log for one closed conversation, plus its version and any evaluations. The same data backs [Agent history](/docs/guide/agentic/agent-history.md) in the web app. To see the individual tool calls an agent made — with their decrypted inputs and outputs — read the tool side instead, where agent-driven runs are tagged `source: "AGENT"`: ```bash autessa tools history list --toolId gPdJwp5jbr9eN ``` See [Tools](/docs/cli/tools.md). ## How to evaluate ```bash autessa agents submit-evaluation \ --agentId K2UYSwk3Yy4i \ --conversationId f719af72-2e49-462e-a98c-63011a5a5671 \ --humanEvaluation '{"status":"SUCCESSFUL","notes":"Correct summary, one tool call."}' autessa agents statistics \ --agentId K2UYSwk3Yy4i \ --startDate 2026-09-01T00:00:00 \ --endDate 2026-09-30T23:59:59 ``` `submit-evaluation` records a human verdict and notes on a stored conversation — run `autessa agents submit-evaluation help` for the full list of verdict values, which distinguish "succeeded" from "succeeded inefficiently". `statistics` aggregates success and failure counts across a date range, broken down by version. > The date form is strict: `yyyy-MM-ddTHH:mm:ss` with **no** zone. A trailing `Z`, a numeric offset, or a bare date are all rejected with a schema error. The value is interpreted as UTC. ## How to share and delete ```bash autessa agents get-sharing-details --agentId K2UYSwk3Yy4i autessa agents share --agentId K2UYSwk3Yy4i --level COMPANY --accessLevel READ autessa agents share --agentId K2UYSwk3Yy4i --level SPECIFIC_MEMBER \ --usersToAdd '[{"userEmail":"teammate@example.com","accessLevel":"WRITE"}]' ``` Sharing modes are `PRIVATE`, `COMPANY` at one access level, or `SPECIFIC_MEMBER` with per-user grants — the same shape tools and tables use. ```bash autessa agents delete --agentId K2UYSwk3Yy4i ``` Deletion is permanent: the agent, its draft and every version go with it. The tools it referenced are untouched. ## Full command reference ## Related - [Tools](/docs/cli/tools.md) — build the capabilities you attach here - [Managers](/docs/cli/managers.md) — orchestrate several agents - [Scheduled jobs](/docs/cli/scheduled-jobs.md) — run an agent on a timer - [Agents](/docs/guide/agentic/agents.md) in the builder - [Agent history](/docs/guide/agentic/agent-history.md) - [Autessa CLI](/docs/cli.md) --- # Managers Source: https://docs.autessa.com/docs/cli/managers > Create a manager, attach sub-agents, publish it, and read the orchestration logs and evaluations from conversations run by apps and end-user clients. # Managers A manager is a multi-agent orchestrator: instructions, a model, and a set of sub-agents it delegates to. Where an [agent](/docs/cli/agents.md) calls [tools](/docs/cli/tools.md), a manager calls agents. You configure and publish one from the terminal exactly as you would an agent — but you cannot run one here, which is the single most important thing to know about this namespace. > **There is no `managers execute`.** Managers are run only by hosted apps and end-user clients. The conversations and statistics you read here reflect those external runs, never a run you started from the CLI. Attempting it fails with `Unknown command 'managers execute'`. ## Key concepts ### The delegation hierarchy ``` manager instructions + model + sub-agents │ delegates to ▼ agent instructions + model + tools │ calls ▼ tool one capability (LLM_CALL, AUTESSADB_QUERY_TABLE, …) ``` Each layer attaches the one below it by id, and each has its own draft/published lifecycle. Build bottom-up: [tools](/docs/cli/tools.md) first, then the [agents](/docs/cli/agents.md) that use them, then the manager that routes between those agents. ### Draft and published A manager has a **draft** (your working copy) and a **published version**. `create` and `update` write the draft; `publish` snapshots it as a new live version. Because managers are only ever invoked externally, publishing is what makes your changes reachable at all — an unpublished manager is inert. ### The SDK creates SIMPLE managers only `managerType` is required on create and must be `SIMPLE`. As with agents, the CLI can read, update, share and manage managers of any type that already exists. ### Sub-agents must exist and be published Every agent you reference must exist in the same company. Changing the `agents` list rebuilds the manager's agent set wholesale — it is not additive. ## How to create a manager ```bash autessa managers create \ --managerType SIMPLE \ --managerName "Support Router" \ --managerDescription "Routes an inbound support request to the right specialist agent." ``` ```json { "manager": { "managerId": "oXfsm9U2z4a", "managerType": "SIMPLE", "name": "Support Router", "description": "Routes an inbound support request to the right specialist agent.", "instructions": "Coordinate and manage the agents to accomplish the task", "model": "SMALL_INSTRUCTION_FOLLOWING", "maxIterations": 30, "agents": [], "voiceConfig": { "voiceModel": "SMALL", "voiceName": "FEMALE" }, "promptTemplateVariables": {}, "evaluationNotes": "", "tags": [] } } ``` Like an agent, a fresh manager arrives with placeholder instructions, a default model, no sub-agents, and `maxIterations: 30`. > `managerDescription` is required, unlike an agent's description. Write it as a real statement of what the manager routes — it is what a human picks it by in the manager list. ## How to attach sub-agents `managers update` takes the **complete desired configuration** in `--updatedManager`, so read-modify-submit rather than sending a patch. Sub-agents attach by id alone — the server hydrates the rest: ```bash autessa managers update --managerId oXfsm9U2z4a --updatedManager '{ "managerId": "oXfsm9U2z4a", "managerType": "SIMPLE", "name": "Support Router", "description": "Routes an inbound support request to the right specialist agent.", "instructions": "Read the request and delegate: billing questions to billing_agent, technical faults to triage_agent. Return the specialist'"'"'s answer without rewriting it.", "model": "SMALL_INSTRUCTION_FOLLOWING", "maxIterations": 10, "agents": [ { "agentId": "ykWFTe6UF56h" }, { "agentId": "K2UYSwk3Yy4i" } ] }' ``` The response echoes each sub-agent fully expanded — its instructions, model, and its own attached tools — so you can confirm the routing set is what you intended: ```json { "updatedManager": { "managerId": "oXfsm9U2z4a", "maxIterations": 10, "agents": [ { "agentId": "ykWFTe6UF56h", "agentType": "SIMPLE", "name": "billing_agent", "description": "Answers billing questions.", "agentInstructions": "You are a professional agent who does as asked.", "model": "SMALL_INSTRUCTION_FOLLOWING", "tools": [], "maxIterations": 30 } ], "instructions": "Read the request and delegate: billing questions to billing_agent, ...", "model": "SMALL_INSTRUCTION_FOLLOWING" } } ``` The fields worth deliberate thought: | Field | What it controls | | --- | --- | | `instructions` | The orchestration prompt — how it decides which sub-agent handles what. Name the agents explicitly. | | `agents` | The sub-agents it may delegate to, by id. Replaces the whole set. | | `model` | The tier the manager *reasons* with when routing. Sub-agents keep their own models. | | `maxIterations` | Orchestration rounds per turn. A router that hands off once needs far fewer than 30. | | `promptTemplateVariables` | Named slots the instructions reference | | `voiceConfig` | Voice model and name, when voice-enabled | > A sub-agent's own `name` and `description` drive routing quality the way a tool's name and description drive an agent's tool choice. If a manager delegates to the wrong agent, fix that agent's description before rewriting the manager's instructions. ### Voice options `managers get` returns the voice choices valid for the manager's model alongside the configuration, so you do not have to guess a `voiceName`: ```bash autessa managers get --managerId oXfsm9U2z4a ``` ```json { "manager": { "...": "..." }, "permission": "ADMIN", "voiceOptionsList": [ { "voiceModel": "SMALL", "voices": { "builtInVoiceNames": ["FEMALE", "MALE"], "customVoiceNames": [] } } ] } ``` `customVoiceNames` lists your company's voice clones — see [Custom voices](/docs/guide/custom-voices.md). ## How to publish ```bash autessa managers publish --managerId oXfsm9U2z4a ``` ```json { "publishedManagerVersion": 1 } ``` ```bash autessa managers versions --managerId oXfsm9U2z4a autessa managers rollback --managerId oXfsm9U2z4a --json '{...}' ``` `versions` lists the published version (null if never published) and every archived one; `rollback` re-publishes an archived version, archiving the current one in its place. See [Versions](/docs/guide/agentic/versions.md). ## How to read what happened Since you cannot start a run here, this is where most of your time in the namespace goes. ```bash autessa managers conversations --managerId oXfsm9U2z4a --page 0 ``` ```json { "conversations": [], "totalElements": 0, "totalPages": 0, "currentPage": 0, "pageSize": 50, "hasNext": false, "hasPrevious": false } ``` An empty result on a working manager usually means one of two things: nobody has run it yet through an app or client, or the runs that exist are your own sandbox sessions from the web UI. Two flags matter here: | Flag | Effect | | --- | --- | | `--includeMySandboxConversations` | Include your own UI/sandbox conversations alongside API-key ones. **Reach for this first when the list looks empty.** | | `--expandAgents` | Inline each sub-agent's full context into the orchestration log, so you can see what the delegate actually did rather than just that it was called. | Listings are trimmed by default so a page stays under the response size limit — `--full` returns complete transcripts instead of metadata. ```bash autessa managers conversations --managerId oXfsm9U2z4a \ --includeMySandboxConversations --expandAgents --full ``` For one conversation with its complete orchestration log: ```bash autessa managers conversation --managerId oXfsm9U2z4a --conversationId ``` The same data backs [Manager history](/docs/guide/agentic/manager-history.md) in the web app. Tool calls made by sub-agents during a manager run also land in tool history tagged `source: "AGENT"` — see [Tools](/docs/cli/tools.md). ## How to evaluate ```bash autessa managers submit-evaluation \ --managerId oXfsm9U2z4a \ --conversationId \ --humanEvaluation '{"status":"SUCCESSFUL","notes":"Routed to the right specialist."}' autessa managers statistics \ --managerId oXfsm9U2z4a \ --startDate 2026-09-01T00:00:00 \ --endDate 2026-09-30T23:59:59 ``` > The date form is strict: `yyyy-MM-ddTHH:mm:ss` with **no** zone. A trailing `Z`, a numeric offset, or a bare date are all rejected with a schema error. The value is interpreted as UTC. Agent statistics take the same form. ## How to share and delete ```bash autessa managers get-sharing-details --managerId oXfsm9U2z4a autessa managers share --managerId oXfsm9U2z4a --level COMPANY --accessLevel READ autessa managers share --managerId oXfsm9U2z4a --level SPECIFIC_MEMBER \ --usersToAdd '[{"userEmail":"teammate@example.com","accessLevel":"WRITE"}]' ``` Same sharing model as agents, tools and tables. ```bash autessa managers delete --managerId oXfsm9U2z4a ``` Deletion is permanent and **also revokes the manager's API-key permission** — any app or client invoking it stops working immediately. The sub-agents themselves are untouched. ## Full command reference ## Related - [Agents](/docs/cli/agents.md) — the sub-agents a manager delegates to - [Tools](/docs/cli/tools.md) — what those agents call - [Managers](/docs/guide/agentic/managers.md) in the builder - [Manager history](/docs/guide/agentic/manager-history.md) - [Autessa CLI](/docs/cli.md) --- # Scheduled Jobs Source: https://docs.autessa.com/docs/cli/scheduled-jobs > Create cron, interval and one-time scheduled events that invoke an agent, run a tool, or write to AutessaDB — and read their execution history. # Scheduled Jobs Scheduled events run work on a timer without a session attached — invoke an agent, run a tool, or write to an AutessaDB table, once or on a recurring schedule. They are the right home for anything that must keep running after your terminal closes, which makes them the natural counterpart to [building headless](/docs/cli.md). ## Key concepts ### Two halves: when and what Every scheduled event is a `scheduleSpec` (when it fires) plus an `actionType` and its one matching action config (what it does). Get either half's shape wrong and the create is rejected — these are the two things worth reading carefully before your first call. ```bash autessa scheduled-events create \ --eventName "..." \ --scheduleSpec '{...}' \ --actionType INVOKE_TOOL \ --toolConfig '{...}' ``` ### Schedules are tagged objects `scheduleSpec` sets a `pattern` plus the **one** nested object matching it. There are three: | Pattern | Shape | Use | | --- | --- | --- | | `CRON` | `{"pattern":"CRON","cron":{"expression":"0 9 * * ? *","timezone":"America/Los_Angeles"}}` | A recurring wall-clock schedule | | `EVERY` | `{"pattern":"EVERY","every":{"value":6,"unit":"HOURS"}}` | A fixed interval — `MINUTES`, `HOURS` or `DAYS` | | `ONCE` | `{"pattern":"ONCE","once":{"at":"2026-12-01T07:00:00","timezone":"America/Los_Angeles"}}` | A single future run | Three rules that cause most first-attempt failures: - **`cron` is a nested object, not a bare string.** Passing `"cron":"0 9 * * ? *"` fails. - **The expression is a 6-field Quartz cron** — `minute hour day-of-month month day-of-week year` — with **no** `cron(...)` wrapper, and `?` in exactly one of day-of-month or day-of-week. - **`ONCE` takes local wall-clock ISO-8601** with no `Z` and no offset, and it must be in the future. > `EVERY` needs no timezone — it is interval-based. `CRON` and `ONCE` are evaluated in the IANA timezone you name, so always set one rather than relying on a default. ### Action configs nest their request This is the second common trap. The agent or tool id does **not** go directly on the action config — it goes inside a nested request object: ```json // INVOKE_AGENT { "agentExecutionRequest": { "agentId": "K2UYSwk3Yy4i", "input": [{ "inputType": "TEXT", "content": "Summarize yesterday's tickets." }] } } // INVOKE_TOOL { "toolExecutionRequest": { "toolId": "8rkuLgfJaGFuG", "variables": { "window": "24h" } } } ``` Both accept `continueOnError` (default `false`) and `description` as siblings of the nested request. Note that an agent's `input` is a list of multimodal-input parts — one `TEXT` element for a plain prompt, not a bare string. > Scheduled runs use **static** values. Unlike [AutessaDB triggers](/docs/cli/autessadb.md), there is no old/new-record substitution available — a scheduled event has no row that changed. ## How to schedule a tool ```bash autessa scheduled-events create \ --eventName "docs_nightly_scratch" \ --description "Scratch event for CLI docs." \ --scheduleSpec '{"pattern":"CRON","cron":{"expression":"0 9 * * ? *","timezone":"America/Los_Angeles"}}' \ --actionType INVOKE_TOOL \ --toolConfig '{"toolExecutionRequest":{"toolId":"8rkuLgfJaGFuG","variables":{}}}' ``` ```json { "scheduledEvent": { "eventId": "f2126a21-22ed-4ef6-98c7-215b3d46b099", "eventName": "docs_nightly_scratch", "resourceType": "TOOL", "resourceId": "8rkuLgfJaGFuG", "isRecurring": true, "enabled": true, "completed": false, "cronExpression": "cron(0 9 * * ? *)", "scheduleSpec": { "pattern": "CRON", "cron": { "expression": "0 9 * * ? *", "timezone": "America/Los_Angeles" } }, "timezone": "America/Los_Angeles", "nextExecutionAt": "2026-09-11T09:00:00", "executionCount": 0, "sharingLevel": "PRIVATE", "userPermission": "ADMIN", "actionType": "INVOKE_TOOL", "toolConfig": { "toolExecutionRequest": { "variables": {}, "toolId": "8rkuLgfJaGFuG" }, "continueOnError": false } }, "eventId": "f2126a21-22ed-4ef6-98c7-215b3d46b099", "scheduleArn": "..." } ``` `nextExecutionAt` is the field to sanity-check — if it is not when you expected, your cron expression or timezone is wrong, and it is much cheaper to catch that here than after a missed run. > The tool must be **published** before a scheduled event can run it. A draft-only tool creates the event fine and then fails at fire time. ## How to schedule an agent ```bash autessa scheduled-events create \ --eventName "Daily ticket digest" \ --description "Emails a digest of yesterday's tickets each morning." \ --scheduleSpec '{"pattern":"CRON","cron":{"expression":"0 16 * * ? *","timezone":"America/Los_Angeles"}}' \ --actionType INVOKE_AGENT \ --agentConfig '{ "agentExecutionRequest": { "agentId": "bMR35XZD37Ol", "input": [{"inputType":"TEXT","content":"Summarize the tickets filed today and email me the digest."}] }, "continueOnError": false }' ``` The agent runs its published version, exactly as a one-shot `agents execute` would — but with no session and no caller, so anything it needs must come from its instructions, its tools, or the prompt you set here. ## How to schedule a database write `UPDATE_AUTESSADB` performs a static write to one table when the event fires: ```bash autessa scheduled-events create \ --eventName "Archive stale tickets" \ --scheduleSpec '{"pattern":"EVERY","every":{"value":1,"unit":"DAYS"}}' \ --actionType UPDATE_AUTESSADB \ --autessaDbConfig '{ "operation": "UPDATE", "targetTableId": "ayXduiaLeFKh", "values": { "status": { "columnType": "SINGLE_SELECT", "textValue": "archived" } }, "whereGroups": [ { "logicalOperator": "AND", "conditions": [{ "columnName": "status", "operator": "EQUALS", "value": "closed" }] } ], "groupsLogicalOperator": "AND" }' ``` - `operation` is one of `INSERT`, `BULK_INSERT`, `UPDATE`, `UPSERT`. - `values` uses the same [typed column values](/docs/cli/autessadb.md) as every other AutessaDB write — `columnType` plus the one matching field (`textValue`, `integerValue`, `floatValue`, `booleanValue`, `multiSelectValue`, `filePath`, or `variableValue`). - For `UPDATE`, choose rows with **either** `recordId` (a single row) **or** `whereGroups` — they are mutually exclusive, and `INSERT` accepts neither. - `skipIfTargetTableMissing` turns a missing target table into a logged warning instead of an error. ## How to list, inspect and change events ```bash autessa scheduled-events all --output table ``` ``` eventId eventName resourceType isRecurring enabled cronExpression nextExecutionAt executionCount actionType ------------------------------------ -------------------- ------------ ----------- ------- ------------------ ------------------- -------------- ------------ ee33c922-d174-47f0-93a9-ee23777423bd Daily message digest AGENT true true cron(0 16 * * ? *) 2026-09-10T16:00:00 4 INVOKE_AGENT ``` Results are grouped by owned, company-shared and shared-with-you. `--full` opts out of the trimmed listing summary. ```bash autessa scheduled-events get --scheduledEventId autessa scheduled-events update --scheduledEventId --scheduleSpec '{...}' autessa scheduled-events delete --scheduledEventId ``` `update` takes the same fields as `create` — send only what changes, but if you change `actionType` you must supply the matching action config alongside it. ### Pausing instead of deleting ```bash autessa scheduled-events toggle --scheduledEventId --enabled false ``` ```json { "eventId": "f2126a21-22ed-4ef6-98c7-215b3d46b099", "enabled": false } ``` Reach for `toggle` while debugging. `delete` is permanent, and re-creating an event loses its execution history. ### Sharing ```bash autessa scheduled-events get-sharing-details --scheduledEventId autessa scheduled-events share --scheduledEventId --level COMPANY autessa scheduled-events share --scheduledEventId --level SPECIFIC_MEMBER \ --usersToAdd '[{"userEmail":"teammate@example.com","accessLevel":"READ"}]' ``` Same sharing model as tools, agents and tables, though the flags differ slightly — this command takes `--level` plus per-user `accessLevel` inside `usersToAdd`, with no top-level `--accessLevel`. Ownership transfer is done in the web app. ## How to read execution history ```bash autessa scheduled-events history --scheduledEventId --year 2026 --month 9 ``` ```json { "executions": [ { "id": "b45b2f61-853e-4ab6-b319-898f921e1577", "scheduledEventId": "f2126a21-22ed-4ef6-98c7-215b3d46b099", "eventName": "docs_nightly_scratch", "resourceType": "TOOL", "resourceId": "8rkuLgfJaGFuG", "startedAt": "2026-09-10T09:56:26.005409", "endedAt": "2026-09-10T09:56:28.810326", "durationMs": 2804, "status": "SUCCESS", "scheduledFireTime": "2026-09-10T09:56:25.515", "actualFireTime": "2026-09-10T09:56:26.005", "attemptNumber": 1, "payloadJson": "{\"metadata\":{...},\"payload\":{\"toolExecutionRequest\":{...}}}", "responseJson": "{\"status\":\"success\",\"responseData\":{...}}" } ], "totalElements": 1 } ``` History is paginated by month and year, with a 0-based `--page`. The fields that matter when something went wrong: - **`scheduledFireTime` vs `actualFireTime`** — the gap is dispatch lag. A large one points at queue pressure, not at your action. - **`attemptNumber`** — greater than 1 means the run was retried. - **`payloadJson`** — exactly what was dispatched, so you can confirm the event carried the config you meant. - **`responseJson`** — what the action returned, including the error on a failure. Because the action itself also records a run, a scheduled tool invocation appears in `autessa tools history list` too — tagged `source: "SCHEDULED"`, which is how you tell a timer-driven run from an interactive one. The same data backs the History page described in [Scheduled jobs](/docs/guide/scheduled-jobs.md). ## Why this matters for headless work `autessa login` mints a tenant-scoped credential that expires in roughly eight hours, with no silent refresh. That makes the CLI a poor place to *host* recurring work — a cron job wrapping `autessa` will start failing the moment the session lapses. Scheduled events sidestep that entirely: they run server-side with no session to expire. The durable pattern is to use the CLI to **create** the schedule and let the platform **run** it: ```bash #!/usr/bin/env bash set -euo pipefail TOOL_ID=$(autessa tools create --toolName nightly_rollup --toolDescription "..." | jq -r '.tool.toolId') autessa tools update --toolId "$TOOL_ID" --updatedTool '{...}' autessa tools publish --toolId "$TOOL_ID" EVENT_ID=$(autessa scheduled-events create \ --eventName "Nightly rollup" \ --scheduleSpec '{"pattern":"CRON","cron":{"expression":"0 2 * * ? *","timezone":"UTC"}}' \ --actionType INVOKE_TOOL \ --toolConfig "{\"toolExecutionRequest\":{\"toolId\":\"$TOOL_ID\"}}" \ | jq -r '.eventId') echo "scheduled $EVENT_ID" ``` The script exits, your session eventually expires, and the job keeps running. [Table triggers](/docs/cli/autessadb.md) and [inbound webhooks](/docs/guide/api-management/webhook-handlers.md) fill the same role for event-driven rather than time-driven work. ## Full command reference ## Related - [Autessa CLI](/docs/cli.md) - [Tools](/docs/cli/tools.md) and [Agents](/docs/cli/agents.md) - [AutessaDB](/docs/cli/autessadb.md) - [Scheduled jobs](/docs/guide/scheduled-jobs.md) in the builder - [Scheduled events API](/docs/api/scheduled-events.md) --- # Webhooks and Queues Source: https://docs.autessa.com/docs/cli/webhooks-and-queues > Inbound webhooks that let external systems trigger Autessa, durable work queues for background batches, and rules that archive closed agent conversations into AutessaDB. # Webhooks and Queues Three namespaces for work that happens without you watching. `custom-webhooks` lets an outside system trigger Autessa; `workqueues` drains a large batch through the worker fleet; `conversation-sync` archives closed agent conversations into a table. All three run server-side with no session attached, which makes them the durable half of [building headless](/docs/cli.md). ## Inbound webhooks A webhook exposes a public URL. An external system POSTs to it; the payload is transformed into named variables — via JSONPath mappings or an AutessaScript transform — and those variables are substituted into a target resource that runs **asynchronously**. The target can be a tool, an agent, a manager, or a direct AutessaDB operation. ### How to create one ```bash autessa custom-webhooks create --webhookConfig '{ "name": "Inbound lead", "...": "..." }' ``` > `create` mints the webhook's API key and returns it **exactly once**. Capture it from that response — it is never returned by any read. If you lose it, `refresh-api-key` rotates to a new one, which means updating every caller. Run `autessa custom-webhooks create help` for the full `webhookConfig` shape; `name` is the only required field inside it. ### How to manage and debug one ```bash autessa custom-webhooks get-all autessa custom-webhooks get --json '{...}' autessa custom-webhooks update --json '{...}' autessa custom-webhooks refresh-api-key --json '{...}' autessa custom-webhooks delete --json '{...}' ``` `update` changes the configuration and/or the enabled flag — disable rather than delete while you are debugging, since `delete` is a hard delete. The command that earns its keep: ```bash autessa custom-webhooks get-execution-history --json '{...}' ``` Pages through inbound calls newest-first with status, timing, attempt number, and **decrypted execution details** — so you can see the payload that actually arrived rather than what the caller believes it sent. That is almost always where a "the webhook isn't working" investigation ends. Sharing a webhook is web-app-only. ## Work queues Durable background processing. You create a queue, enqueue a batch, and the worker fleet drains it — `enqueue` returns immediately, so you poll progress rather than waiting. ### How to create a queue ```bash autessa workqueues create \ --name "Nightly enrichment" \ --description "Enriches new leads." \ --maxConcurrency 10 \ --defaultMaxAttempts 3 \ --defaultTimeoutSeconds 120 ``` | Setting | Controls | | --- | --- | | `maxConcurrency` | How many items are leased and running at once | | `defaultMaxAttempts` | Retries for items that don't override it | | `defaultTimeoutSeconds` | Per-item lease timeout — an item that exceeds it is retried | | `stagePrecedence` | Ordered stage names, so earlier stages lease first. Omit for plain FIFO-by-priority | | `runtime` | Where items run; defaults to `WORKER` | Time fields are plain integers in seconds, not durations. ### How to enqueue work Each item names the **published tool** that processes it, plus a free-form payload: ```bash autessa workqueues enqueue --queueId --items '[ { "groupKey": "run-2026-09-10", "actionToolId": "gPdJwp5jbr9eN", "payload": {"leadId": "1"} }, { "groupKey": "run-2026-09-10", "actionToolId": "gPdJwp5jbr9eN", "payload": {"leadId": "2"} } ]' ``` `groupKey` is what makes a batch addressable afterwards — progress, cancellation and dead-item retries all operate on a group. Set it deliberately rather than leaving items ungrouped. Build the action tool the usual way, preferring a purpose-built type over a script — see [Tools](/docs/cli/tools.md). ### How to watch it drain ```bash autessa workqueues progress --queueId --groupKey run-2026-09-10 ``` Returns counts by state — `pending`, `running`, `succeeded`, `failed`, `dead`, `cancelled` — plus per-stage counts. This is your polling command. ```bash autessa workqueues items-list --queueId --json '{...}' autessa workqueues items-get --json '{...}' ``` `items-list` filters by state, stage or `groupKey`; `items-get` returns one item's state, attempts, payload, result and last error. ### How to intervene ```bash autessa workqueues pause --queueId # stop leasing autessa workqueues resume --queueId autessa workqueues retry-dead --json '{...}' # requeue a group's DEAD items autessa workqueues cancel-group --json '{...}' ``` **Dead** means an item exhausted its attempts. `retry-dead` requeues every dead item in a group for another run — the right move after you fix the underlying tool. `cancel-group` stops every not-yet-terminal item in a group being leased. > `pause` is the emergency brake: it stops leasing without losing queued work, so a runaway batch can be halted while you investigate and then resumed. ## Conversation sync Company-scoped rules that copy a **closed** agent conversation's message log into an AutessaDB table row, as JSON, for reporting and analysis. ```bash autessa conversation-sync create \ --name "Support transcripts" \ --agentIds '["K2UYSwk3Yy4i"]' \ --tableUpdates '[{...}]' \ --enabled true ``` ```bash autessa conversation-sync all autessa conversation-sync get --json '{...}' autessa conversation-sync update --json '{...}' autessa conversation-sync delete --json '{...}' ``` Three things determine whether this does what you expect: - **It never syncs retroactively.** A config applies only to conversations closed *after* it is written. Creating one does not backfill. - **It runs off the conversation-logged queue**, asynchronously — not at config-write time. A conversation appears in the table some time after it closes. - **The destination is always an AutessaDB table.** Never an external connector. > This is **not** how an agent is deployed or made reachable to end users. It only archives conversations that already happened. If you are looking for how people reach an agent, see [Custom apps](/docs/cli/apps.md) or [Phone and SMS](/docs/cli/phone.md). `requiresEvaluation` limits syncing to evaluated conversations, and `allowSandboxConversations` decides whether your own UI/sandbox runs are included — off by default, which is usually why a test conversation does not show up. ## Related - [Autessa CLI](/docs/cli.md) — why durable work belongs on the platform - [Scheduled jobs](/docs/cli/scheduled-jobs.md) — the time-driven counterpart - [Tools](/docs/cli/tools.md) — what a webhook or queue item runs - [AutessaDB](/docs/cli/autessadb.md) — triggers, the row-change counterpart - [Webhook handlers](/docs/guide/api-management/webhook-handlers.md) in the builder --- # Connectors Source: https://docs.autessa.com/docs/cli/connectors > List, inspect, share and manage third-party service connectors across 24 providers — and understand which ones need a browser and which don't. # Connectors Connectors hold your company's authenticated access to third-party services — Gmail, HubSpot, QuickBooks, Notion, Box, the social platforms and more. At 156 commands across 24 providers, `connectors` is the largest namespace in the CLI, but it is also the most regular: nearly every provider exposes the same six-command lifecycle, plus a handful of provider-specific extras. ## What it's for - **Finding what's connected** before you build a tool that depends on it. - **Sharing a connector** so teammates' agents can use it. - **Managing scope** — which Gmail labels, Box folders or calendars a connector exposes to tools. - **Auditing and cleanup** — renaming, deactivating, and deleting connectors. ## Key concepts ### Use the connector, never the vendor API If a task touches a third-party service, the Autessa connector already holds the credential. Reaching for `curl`, `gcloud` or a vendor CLI instead will either fail outright or route around the credential store — there is no path by which an external call picks up the stored token. This matters most when you are driving the CLI from a coding agent, where the temptation to improvise is highest. See [Coding agents](/docs/cli/coding-agents.md). ### Most connectors are created in the browser A connector that authenticates with OAuth needs a human to grant consent on the provider's own consent screen. There is no `create` command for those — the CLI picks up an existing connector by id and manages it from there. Three providers authenticate with an API key or app password instead, and **can** be created without a browser: | Provider | How it authenticates | CLI can create it | | --- | --- | --- | | **beehiiv** | API key | Yes — `connectors beehiiv create` | | **Ghost** | Admin API key + site URL | Connected in the app; `test-connection` verifies it | | **Bluesky** | Handle + app password | Connected in the app | ```bash # validate a key before storing it autessa connectors beehiiv test-connection --apiKey "$BEEHIIV_KEY" # then create the connector autessa connectors beehiiv create --connectorName "Newsletter" --apiKey "$BEEHIIV_KEY" ``` If no connector exists for a provider your task needs, the answer is to tell whoever is driving to connect it in the app — not to work around it. ### Credentials are never returned Stored tokens, API keys and app passwords are write-only. Reads report metadata and, where relevant, whether auth is configured — never the secret itself. OAuth tokens are refreshed automatically in the background, so a connector that has sat unused still works. ### Access levels Reads (`all`, `get`, `get-sharing-details`) need at least READ; `update` needs WRITE; `share` needs ADMIN; and `delete` is restricted to the connector's owner. ## How to find what's connected List the provider families: ```bash autessa connectors ``` ``` Third-party connectors. Most are connected in the browser (OAuth) — the CLI lists, shares, and manages them; a few (beehiiv, ghost, bluesky) can be connected from the CLI directly. * * * * * * * * * * * * * * * * * * CONNECTORS * * * * * * * * * * * * * * * * * * acumatica Manage Acumatica (cloud ERP) connectors — list and fetch the connectors your company has connected, rename or toggle them, change their sharing, delete them, read a connector's sharing configuration, and probe a connected instance for the contract API versions it supports. [...] ``` Then list one provider's connectors: ```bash autessa connectors gmail all ``` ```json { "connectors": [ { "connectorId": "JNuAlnxY9eRrL5H", "companyId": "y8mRwXwboDVu", "ownerId": "g1G8cnasH8Od", "connectorName": "gmail", "ownerType": "USER", "gmailAccountEmail": "you@example.com", "sharingLevel": "PRIVATE", "isActive": true, "createdAt": "2026-08-28T14:09:27.887588" } ] } ``` The `connectorId` is what you feed to every other command in the family — and what a connector-backed [tool type](/docs/tools.md) needs in its config. > `isActive: false` is a connector that has been toggled off rather than deleted. Tools pointing at it will fail — check this first when a previously working tool stops. ## The common lifecycle Nearly every provider exposes these six commands, with `connectorId` as the only required argument on all but `all`: | Command | What it does | | --- | --- | | `all` | List every connector of this provider you can access | | `get` | Fetch one connector by id | | `update` | Rename it, or toggle whether it is active | | `share` | Change who in your company can use it | | `get-sharing-details` | Read the current sharing configuration | | `delete` | Permanently delete it and its stored credential | ```bash autessa connectors hubspot all autessa connectors hubspot get --connectorId autessa connectors hubspot share --connectorId --level COMPANY autessa connectors hubspot update --connectorId --connectorName "Sales HubSpot" ``` Because the shape is uniform, once you have driven one provider you have driven all of them. Run `autessa connectors help` for the exact arguments — a few differ in details. ## Provider-specific commands Beyond the common six, some providers expose extras. These are the ones worth knowing about: | Provider | Extra commands | Why | | --- | --- | --- | | **gmail** | `view-available-labels`, `update-available-labels` | Control which labels tools may touch | | **box** | `view-available-folders`, `update-available-folders`, `sync-folders` | Mirror and scope the folder tree | | **google-calendar** | `view-available-calendars`, `update-available-calendars`, `sync-calendars` | Scope which calendars are exposed | | **outlook** | `list-folders` | Enumerate mail folders | | **outlook-calendar** | `list-calendars` | Enumerate calendars | | **notion** | `lookup-database-schema`, `lookup-database` | Inspect a Notion database before wiring a tool to it | | **google-analytics** | `bigquery-projects`, `bigquery-datasets`, `bigquery-tables` | Browse the linked BigQuery export | | **acumatica** | `api-versions` | Probe the instance for supported contract API versions | | **quickbooks** | `revoke-access` | Revoke the QuickBooks grant | | **beehiiv** | `create`, `test-connection` | Key-based auth — creatable from the CLI | | **ghost** | `test-connection` | Verify the Admin API key still authenticates | The scoping commands are the ones people miss. A Gmail connector exposes only the labels you have enabled, so a tool that "can't see" a label usually needs `update-available-labels`, not a new connector: ```bash autessa connectors gmail view-available-labels --connectorId autessa connectors gmail update-available-labels --connectorId --json '{...}' ``` Box and Google Calendar work the same way, with `sync-folders` / `sync-calendars` refreshing the mirror from the provider when something new appears upstream. ## Full command reference Every command in the namespace, grouped by provider. Run `autessa connectors help` for arguments. ## Related - [Connectors](/docs/guide/connectors.md) in the builder - [Coding agents](/docs/cli/coding-agents.md) - [Command surface](/docs/cli/command-surface.md) - [Tool Types](/docs/tools.md) — the connector-backed tool families - [Connectors API](/docs/api/poweruser/connectors.md) --- # MCP Servers Source: https://docs.autessa.com/docs/cli/mcp > Connect external Model Context Protocol servers, probe what they expose, and import their tools into Autessa. # MCP Servers MCP (Model Context Protocol) servers are external services that expose tools, resources and prompts. Connect one to your company and its tools become importable as Autessa tools your agents can call. The whole lifecycle is scriptable — this namespace has no web-app-only commands. ## Key concepts ### Commands are `server-*`, not nested Unlike most namespaces, MCP flattens its verbs into the command name: `server-create`, `server-get`, `server-list-tools`. The one plural is `servers-list` — note the `s` moves. ### Auth is write-only `authConfig` can be set but is **never returned**. Reads only report whether auth is configured, via `hasAuthConfig`. If you lose a credential you re-set it; you cannot read it back. ### Listing is live `server-list-tools`, `server-list-resources` and `server-list-prompts` query the server itself at call time rather than returning a cached snapshot. They tell you what the server exposes *now*, which also makes them a connectivity check with detail. ## How to connect a server ```bash autessa mcp server-create \ --name "Internal docs" \ --serverUrl "https://mcp.example.com/sse" \ --description "Company documentation search." \ --authType BEARER \ --authConfig '{"token":"..."}' ``` `authType` defaults to `NONE`; `authConfig` is required unless it is `NONE`, and its shape depends on the type — an API key, a bearer token, basic-auth credentials, or custom headers. It is passed as a **JSON string**. Verify before you rely on it: ```bash autessa mcp server-test-connection --json '{...}' ``` Probes the server to check it is reachable and authenticates — the first thing to run when an imported tool starts failing. ## How to see what a server offers ```bash autessa mcp servers-list autessa mcp server-get --json '{...}' autessa mcp server-list-tools --json '{...}' autessa mcp server-list-resources --json '{...}' autessa mcp server-list-prompts --json '{...}' ``` Read `server-list-tools` before importing — it is the definitive list of what you are about to pull in. ## How to import its tools Importing happens from the [tools](/docs/cli/tools.md) namespace, not here: ```bash autessa tools create-from-mcp --json '{...}' ``` This bulk-creates an Autessa tool for every tool the server advertises. Imported tools are tagged `mcp` plus the server host, so `autessa tools all` lets you find them again — and tell them apart from hand-built ones. From there they behave like any other tool: publish them, attach them to an [agent](/docs/cli/agents.md), and their runs land in `tools history list` like everything else. ## How to update and disconnect ```bash autessa mcp server-update --json '{...}' autessa mcp server-delete --json '{...}' ``` > `server-delete` disconnects the server's tools, resources and prompts from your company. Tools already imported and attached to agents are affected — check `autessa tools all` for anything tagged with that server's host before deleting. ## Full command reference ## Related - [Tools](/docs/cli/tools.md) — import and publish MCP tools - [Agents](/docs/cli/agents.md) — attach them - [MCP servers](/docs/guide/agentic/mcp-servers.md) in the builder - [MCP tool types](/docs/tools/mcp.md) - [MCP API](/docs/api/poweruser/mcp.md) --- # Phone and SMS Source: https://docs.autessa.com/docs/cli/phone > Find and purchase phone numbers, route inbound calls to an agent or a call flow, set recording and call limits, read call history, decide who answers texts, read and reply to the shared inbox, forward texts, send and read text messages, manage personal phone reminders, and look up SMS consent records. # Phone and SMS Three related namespaces: `phone-numbers` wires a real phone number to an agent so people can call or text it, and sends texts from it, `phone-reminders` calls *you* before calendar events, and `sms-consent` is the consent record-keeping behind outbound SMS. The first two are fully scriptable; consent records are web-app-only. ## Phone numbers Company-scoped. This is how a voice agent gets a number people can dial. ### How to find and purchase a number ```bash # see what's available autessa phone-numbers search-numbers --areaCode 206 --limit 10 # buy the one you picked autessa phone-numbers purchase-number --phoneNumber "+12065550142" ``` `search-numbers` takes an optional `--areaCode`, `--contains` (digits the number should include), and `--limit` (up to 20). Pass `--areaCode` alone to `purchase-number` to take the first available number in that area code, or no arguments to let a US number be selected automatically. Numbers are free to get and use for calls — no card is needed — and the response carries the `configId` you need for routing. Texting is off until you turn it on for the number, which starts a monthly texting fee; see [Turning on texting](/docs/guide/agentic/sms-setup.md#turning-on-texting-for-a-number). Until you set up routing, a new number still takes messages: callers go to voicemail and texts are saved to the inbox. See [Numbers with nothing set up](/docs/guide/agentic/phone-numbers.md#numbers-with-nothing-set-up). > Releasing a number is **not** available from the CLI — it is done from phone number settings in the web app. Purchase deliberately, since you cannot undo it from here. ### How to route calls to an agent ```bash autessa phone-numbers configs-list autessa phone-numbers config-get --configId ``` `configure-routing` decides what happens when the number rings: ```bash # straight through to one agent autessa phone-numbers configure-routing \ --configId \ --routingType DIRECT \ --directResourceType AGENT \ --directResourceId K2UYSwk3Yy4i ``` | Routing type | Behavior | | --- | --- | | `DIRECT` | Every call goes straight to one resource — set `directResourceType` (`AGENT` or `MANAGER`) and `directResourceId` | | `PRESS_DIGIT` | Callers hear a menu and press a digit to reach different resources — configured via `routingConfig` | `--locallyHostedStt` controls whether calls on the number use locally-hosted speech-to-text. > The target agent or [manager](/docs/cli/managers.md) must be **published**. Routing to a draft leaves callers hitting a resource that will not run. ### How to set up a call flow A call flow decides who answers: AI agents, people, a keypad menu, voicemail, or a mix, with business hours. See [Who answers](/docs/guide/agentic/phone-numbers.md#who-answers) for what each destination does. ```bash # ring the front desk on weekdays, voicemail when nobody answers or when closed autessa phone-numbers update-call-flow --configId --callFlow '{ "greeting": "Thanks for calling Acme Dental.", "businessHours": { "timezone": "America/Los_Angeles", "weekly": [ { "day": "MONDAY", "open": "09:00", "close": "17:00" }, { "day": "TUESDAY", "open": "09:00", "close": "17:00" }, { "day": "WEDNESDAY", "open": "09:00", "close": "17:00" }, { "day": "THURSDAY", "open": "09:00", "close": "17:00" }, { "day": "FRIDAY", "open": "09:00", "close": "17:00" } ], "closedDestination": { "type": "VOICEMAIL", "voicemail": { "greeting": "We are closed. Please leave a message." } } }, "destination": { "type": "RING", "ring": { "targets": [ { "to": "+14155550100", "label": "Front desk", "ringSeconds": 25 }, { "to": "client:", "label": "Dana (browser)" } ], "strategy": "SIMULTANEOUS", "ringSeconds": 20, "requireKeyPress": true, "whisper": "Call from the main line.", "noAnswer": { "type": "VOICEMAIL", "voicemail": { "finishOnKey": "#", "silenceSeconds": 5 } } } } }' # go back to sending every call straight to the agent set with configure-routing autessa phone-numbers update-call-flow --configId --callFlow null ``` A destination's `type` is `AI_AGENT`, `AI_MULTI_AGENT` (both take `resourceId`), `RING`, `MENU`, `VOICEMAIL`, or `HANG_UP`, and any destination can have a `message` (or a recorded `messageAudio`) played first. A `MENU` takes `prompt`, `promptAudio`, `options` (a map of digit to destination), `timeoutSeconds`, `invalidMessage`, `noInputMessage`, `retries`, and `noInput`. A `VOICEMAIL` takes `greeting`, `greetingAudio`, `maxSeconds`, `finishOnKey`, `silenceSeconds`, and `transcribe`. Each ring target is `{ "to": ..., "label": ..., "ringSeconds": ... }`, where `to` is a phone number in E.164 format or `client:` for a teammate's browser phone (the web app's call flow editor lets you pick teammates instead of typing identities). A ring also takes `requireKeyPress`, `whisper`, `callerId` (`BUSINESS_NUMBER` or `CALLER`), `waitAudio`, and `maxTotalSeconds`. Add `closedDates` (for example `[{ "date": "2026-12-25", "name": "Christmas" }]`) to `businessHours` for holidays. Every field is described in the [call flow object](/docs/api/poweruser/phone-numbers.md#the-call-flow-object) reference, and what each one does for callers is in [Who answers](/docs/guide/agentic/phone-numbers.md#who-answers). A flow that doesn't make sense, such as a bad time or an agent you can't use, is rejected with a plain-language message. ### How to use your own recordings Audio fields (`greetingAudio`, `messageAudio`, `promptAudio`, a voicemail's `greetingAudio`, and a ring's `waitAudio`) take an `https://` link to a file you host, or a file you upload in three steps: ```bash # 1. get an upload link for an MP3 (or "wav") autessa phone-numbers audio-upload-link --configId --fileType mp3 # 2. upload the file with the exact contentType from step 1 curl "" -X PUT -H "Content-Type: audio/mpeg" --data-binary @greeting.mp3 # 3. save the audioUri from step 1 into the call flow, for example as greetingAudio autessa phone-numbers update-call-flow --configId --callFlow '{ "greetingAudio": "", "destination": { "type": "AI_AGENT", "resourceId": "" } }' ``` The upload must use the `Content-Type` that step 1 returned, or it's refused. Upload before the link expires (`expiresInSeconds`). `update-call-flow` replaces the whole flow, so send your full flow with the new audio field in it. When an audio field is set, it plays instead of the matching text, and `config-get` returns a playable link for each uploaded file in `audioPreviewUrls`. ### How to set recording, transcripts, and limits ```bash autessa phone-numbers update-call-settings --configId --callSettings '{ "speech": { "language": "en-US" }, "transcript": { "save": true, "retentionDays": 365 }, "recording": { "save": true, "announcement": "This call may be recorded.", "retentionDays": 90 }, "redaction": { "enabled": true, "audioRedaction": "TONE" }, "limits": { "maxDurationMinutes": 30, "maxDurationWarningSeconds": 60, "silenceTimeoutSeconds": 20, "silencePromptRepeats": 2 }, "callerInfo": { "lookupCallerName": true }, "screening": { "blockedNumbers": ["+15555550123"], "rejectAnonymous": true }, "ai": { "interruptionSensitivity": "LOW", "endOfTurn": "PATIENT", "hangUpPhrases": ["Have a great day"] }, "answeringMachine": { "detection": "WAIT_FOR_BEEP", "timeoutSeconds": 30 } }' ``` The settings apply to every call on the number, incoming and outgoing. Sections you leave out use their defaults. - `speech` sets the voice (`voiceModel`, `voiceName`) and `language` for everything the number says. `autessa phone-numbers voice-options` lists the languages and voices you can use. - `retentionDays` under `transcript` or `recording` deletes those from each call that many days after it. - `redaction` turns on Call Redaction, which removes personal info from recordings and transcripts and costs $0.01 per call; leave out `redact` to remove every kind it supports, or list some of `CARD_NUMBER`, `CARD_EXPIRY`, `CARD_CVV`, `SSN`, `BANK_ACCOUNT`, `ACCOUNT_NUMBER`, `DATE_OF_BIRTH`, `PHONE`, `EMAIL`, `ADDRESS`. - `screening` blocks numbers and anonymous callers; add `blockedMessage` to say something before hanging up instead of giving a busy signal. - `ai` tunes AI calls: `interruptions`, `interruptionSensitivity` (`LOW`, `MEDIUM`, `HIGH`), `endOfTurn` (`FAST`, `NORMAL`, `PATIENT`), and `hangUpPhrases`. - `answeringMachine` sets detection on outgoing calls: `OFF`, `ON`, or `WAIT_FOR_BEEP`. See [Settings at a glance](/docs/guide/agentic/phone-numbers.md#settings-at-a-glance) for what each setting does, and the [call settings object](/docs/api/poweruser/phone-numbers.md#the-call-settings-object) for every field and range. ### How to read call history ```bash autessa phone-numbers calls-list --configId --limit 25 ``` Returns the number's calls newest first, from your company's [Phone calls table](/docs/guide/agentic/phone-numbers.md#the-phone-calls-table): direction, the other number, caller info, status, length, who handled it, the transcript, and short-lived links to the recording and voicemail. `--limit` is up to 50 (default 10); pass the previous page's `next` as `--after` to continue. `tableId` in the response is the Phone calls table itself, which you can query with the [AutessaDB commands](/docs/cli/autessadb.md). ### How to turn on quick automations ```bash # text back callers you missed (the number must be able to text) autessa phone-numbers set-automation --configId \ --automation TEXT_BACK_MISSED_CALLS --enabled true \ --smsMessage "Sorry we missed your call. We'll call you back shortly." # email new voicemails to up to 5 people autessa phone-numbers set-automation --configId \ --automation EMAIL_VOICEMAILS --enabled true \ --emails '["frontdesk@example.com"]' ``` Each automation is a regular trigger on the Phone calls table. `--enabled false` turns it off without deleting it. See [Quick automations](/docs/guide/agentic/phone-numbers.md#quick-automations). > `update-call-flow`, `update-call-settings`, `calls-list`, `set-automation`, `audio-upload-link`, and `voice-options` ship with the phone call controls release. If `autessa phone-numbers --help` doesn't list them yet, run `autessa login` again to refresh your command list. ### How to send a text ```bash autessa phone-numbers sms-send \ --configId \ --to "+14155550100" \ --body "Hi Sam, your order has shipped. Reply STOP to opt out." ``` The text reaches a real person, so it's checked first: your company needs a finished [Text Messaging registration](/docs/guide/agentic/sms-setup.md) (or the number comes from your own connected Twilio account), and the recipient needs an approved [consent record](/docs/guide/agentic/sms-consent.md) or a text to you within the last year. People who replied STOP are never texted. When a text is refused, `success` is `false`, `errorCode` says why, and `errorMessage` explains what to do — the codes are listed under the [Send SMS tool](/docs/tools/phone-sms.md#send-sms-). ### How to read text history ```bash autessa phone-numbers sms-messages-list --configId --pageSize 25 ``` Returns the number's texts newest first — incoming texts, replies, and sends that were refused, with the reason. Pass the previous page's `nextCursor` as `--cursor` to continue. Company owners can omit `--configId` to list every number. ### How to decide who answers texts ```bash # your team answers texts from the inbox, with an auto-reply and a keyword reply; # an AI agent answers outside the call flow's business hours autessa phone-numbers update-text-flow --configId --textFlow '{ "keywordReplies": [ { "keyword": "HOURS", "reply": "We are open Monday to Friday, 9 to 5." } ], "useCallFlowHours": true, "open": { "type": "NO_AI", "autoReply": { "message": "Thanks for your message. We will text you back shortly.", "onceEveryHours": 24 } }, "closed": { "type": "AI_AGENT", "resourceId": "" } }' # go back to the default autessa phone-numbers update-text-flow --configId --textFlow null ``` `open` and `closed` each take `type` (`AI_AGENT` or `NO_AI`), `resourceId` for `AI_AGENT` (leave it out to use the agent the number sends calls to), and `autoReply` for `NO_AI`. `onceEveryHours` sends the auto-reply to the same person at most once in that many hours (default 24, 0 for every text, up to 720). Give texts their own hours with `businessHours` (the same shape as a call flow's), or set `useCallFlowHours` to share the call flow's. Leave out `closed` to handle texts the same way at all hours. STOP, START, and HELP are always handled first and can't be changed. By default, with no text flow, texts are answered by the agent the number sends calls to directly; if calls don't go straight to one agent, texts get no reply and wait in the inbox. Every field is in the [text flow object](/docs/api/poweruser/phone-numbers.md#the-text-flow-object) reference, and what each setting does is in [Texts](/docs/guide/agentic/phone-numbers.md#texts). ### How to read and reply to the inbox ```bash # threads, most recent first autessa phone-numbers sms-threads-list --configId --limit 20 # one conversation, newest first autessa phone-numbers sms-thread-get --configId --otherNumber "+14155550100" --limit 50 # reply as the business number autessa phone-numbers sms-send --configId --to "+14155550100" --body "Yes, 10 to 2 on Saturdays." ``` `sms-threads-list` takes `--limit` up to 50 (default 20); pass the previous page's `next` as `--after`. `sms-thread-get` takes `--limit` up to 200 (default 50); pass `next` as `--before` for older messages. Each message's `handledBy` says who answered it: the agent's name, `Auto-reply`, `Keyword reply`, `Staff:` and a teammate's name, or `Automation`. Replies sent with `sms-send` show up as `Staff:` and your name, and are checked like any text (see [How to send a text](#how-to-send-a-text)). ### How to forward texts ```bash # send every incoming text on to up to 5 phones autessa phone-numbers set-automation --configId \ --automation FORWARD_TEXTS_SMS --enabled true \ --phones '["+14155550100"]' # email every incoming text to up to 5 people autessa phone-numbers set-automation --configId \ --automation FORWARD_TEXTS_EMAIL --enabled true \ --emails '["frontdesk@example.com"]' ``` Forwarded texts arrive as "From +14155550199: " followed by the message, sent from the business number, so the number must be able to text and each phone must be allowed to receive texts from it (texting the number once from each phone is enough). Texts from those phones are never forwarded. Both are regular triggers on your company's [Text messages table](/docs/guide/agentic/phone-numbers.md#the-text-messages-table), whose id is `textLogTableId` in `config-get`. > `update-text-flow`, `sms-threads-list`, `sms-thread-get`, and the `FORWARD_TEXTS_SMS` and `FORWARD_TEXTS_EMAIL` automations ship with the texting-without-AI release. If `autessa phone-numbers --help` doesn't list them yet, run `autessa login` again to refresh your command list. ### How to check your texting registration ```bash autessa phone-numbers sms-registration-get ``` Read-only: shows where your company's Text Messaging registration stands and what the carriers are waiting on. Submitting or changing the registration is done in the web app, on **Company → Text Messaging**. > The texting commands (`search-numbers`, `sms-send`, `sms-messages-list`, `sms-registration-get`) ship with the Text Messaging release. If `autessa phone-numbers --help` doesn't list them yet, run `autessa login` again to refresh your command list. ## Phone reminders **Personal, not company-scoped.** Autessa scans your connected calendars and calls you before your events. Only the owner can read or change their own setups — there is no sharing model here, and a teammate cannot see yours. ### How to create a setup ```bash autessa phone-reminders create \ --displayName "Work meetings" \ --reminderPhoneNumber "+15551234567" \ --leadTimeMinutes 10 \ --quietHoursEnabled true \ --quietHoursStart "22:00" \ --quietHoursEnd "07:00" \ --quietHoursTimezone "America/Los_Angeles" \ --calendarConnectorIds '[""]' ``` Times are `HH:mm` in the setup's own timezone. The calendar connectors come from [Connectors](/docs/cli/connectors.md) — `autessa connectors google-calendar all` or `connectors outlook-calendar all` gives you the ids. ### How to manage one ```bash autessa phone-reminders list autessa phone-reminders get --configId # change settings without touching the calendar set autessa phone-reminders update-settings --configId --leadTimeMinutes 15 --isEnabled true # replace which calendars it scans autessa phone-reminders update-calendars --configId --json '{...}' # don't wait for the next scheduled scan autessa phone-reminders scan-now --configId ``` `scan-now` is the web app's "Refresh" action — useful right after you add a calendar and want to confirm events are picked up rather than waiting for the next cycle. > Resetting a setup — which re-provisions it and **changes the Autessa phone number** that calls you — is web-app-only. `update-settings` and `update-calendars` cover everything else. ## SMS consent Consent records for your company's phone numbers. Both commands are web-app-only — recording and searching consent happen on the SMS consent page in the browser, because consent capture is a compliance flow that needs a human in it. ```bash autessa sms-consent create ``` ``` SMS consent records are created from your company's SMS consent page in the web app. ``` The commands remain in the help tree so discovery tells you the capability exists and where it lives. See [SMS consent](/docs/guide/agentic/sms-consent.md). ## Related - [Agents](/docs/cli/agents.md) — the agent a number routes to - [Connectors](/docs/cli/connectors.md) — the calendars reminders scan - [Phone numbers](/docs/guide/agentic/phone-numbers.md) in the builder - [Meeting reminders](/docs/guide/platform/meeting-reminders.md) - [Text Messaging setup](/docs/guide/agentic/sms-setup.md) - [Phone and SMS tool types](/docs/tools/phone-sms.md) --- # Documents and Meetings Source: https://docs.autessa.com/docs/cli/documents-and-meetings > Read e-signature envelopes and organize them into folders, inspect meeting-recorder bots and recordings, and list your voice-cloning library. # Documents and Meetings Three read-heavy namespaces. `e-signature` covers envelopes, folders, templates and contacts; `meetings` covers recorder bots and recordings; `voice` lists your voice-cloning library. All three follow the same pattern — reading and light organizing are scriptable, while anything that sends, dispatches or creates is a browser step. > The shape to expect across this page: **reads are yours, state changes are the web app's**. That is deliberate — sending a contract for signature, dispatching a bot into a live meeting, and minting a voice clone are all actions with real-world consequences that Autessa keeps behind a human confirmation. ## E-signature 46 commands, 15 of them runnable. Everything you can read, plus folder organization. ### Envelopes ```bash autessa e-signature envelope-list --json '{...}' autessa e-signature envelope-get --json '{...}' autessa e-signature inbox-list --json '{...}' ``` `envelope-list` filters by status, free-text search, folder, or only-unfiled, with sorting and pagination. `inbox-list` is narrower and often what you actually want: envelopes where **you** are a recipient — your signing queue. `envelope-get` returns full detail: recipients, fields, sharing, and the tamper-evident audit trail. ### Getting the document ```bash autessa e-signature envelope-view --json '{...}' # short-lived URL to view the current PDF autessa e-signature envelope-download --json '{...}' # short-lived URL to download it ``` Both return **short-lived** URLs — fetch and use them promptly rather than storing them. ```bash autessa e-signature envelope-direct-links --json '{...}' ``` Reveals an envelope's shareable signing links — a per-recipient link plus an envelope-level one — so you can deliver signing links outside of email (in a chat message, an app, an SMS). Treat these as sensitive: anyone holding a per-recipient link can sign as that recipient. ### Folders Folders are the one place here you can change state: ```bash autessa e-signature folder-create --json '{...}' autessa e-signature folder-list autessa e-signature folder-get --json '{...}' autessa e-signature folder-add-envelopes --json '{...}' autessa e-signature folder-remove-envelopes --json '{...}' ``` Filing is **many-to-many** — one envelope can sit in several folders — and `folder-remove-envelopes` removes only the folder link, never the envelope itself. ### Templates and contacts ```bash autessa e-signature template-list --json '{...}' autessa e-signature template-get --json '{...}' autessa e-signature contacts --json '{...}' autessa e-signature company-members --json '{...}' ``` `contacts` autocompletes past recipients and `company-members` lists people you can share an envelope or folder with — both exist to fill in a picker rather than to be browsed. > Sending, editing, sealing, voiding and sharing envelopes, renaming/deleting/sharing folders, and managing templates are all web-app-only, done from the e-signature page. ## Meetings Meeting-recorder reads: bots and their recordings. ```bash autessa meetings ... ``` The runnable commands cover listing and inspecting recorder bots — their config, health, permissions and upcoming calendar meetings — plus listing and fetching recordings, and previewing a bot share. Bot **health** is the one to reach for when a meeting was missed: it reports whether the bot's calendar connection and permissions are still good. Everything that changes state is done in meeting settings in the web app: creating and configuring bots, connecting a Google or Microsoft calendar as a bot, setting up a bot's custom subdomain with its DNS records and verification, dispatching, uploading and retrying recordings, sharing, inviting bots to events, and in-app alerts. ## Voice Read-only, and the smallest runnable surface in the CLI — two commands out of eighteen. ```bash autessa voice voices-list # instant clones — the usable voices autessa voice voice-models-list # deep, fine-tuned clones — the voice models ``` Both group results by owned, company-shared, and shared-with-you. The distinction matters when you are configuring a voice-enabled [agent](/docs/cli/agents.md) or [manager](/docs/cli/managers.md): `voices-list` gives you the names you can actually set as a `voiceName`. A manager's `get` response also returns `voiceOptionsList` scoped to its model, which is usually the quicker path. Creating, uploading, fine-tuning, sharing, transferring and deleting clones are all web-app-only — done from voice cloning in the web app. ## Related - [Document signing](/docs/guide/platform/document-signing.md) in the builder - [Meeting recorder](/docs/guide/platform/meeting-recorder.md) - [Custom voices](/docs/guide/custom-voices.md) - [Agents](/docs/cli/agents.md) and [Managers](/docs/cli/managers.md) — where a voice gets used - [Autessa CLI](/docs/cli.md) — what stays in the browser, and why --- # QR Codes Source: https://docs.autessa.com/docs/cli/qr-codes > Create dynamic QR codes whose destination you can change after printing, tag them for campaigns, share them with teammates, and read scan analytics. # QR Codes A QR code here is a **dynamic short link**: the printed code encodes an Autessa URL, not your destination, so you can repoint it later without reprinting anything. Every command in the namespace is runnable — this is one of the few surfaces with no web-app-only step at all. ## Key concepts ### Dynamic, so the destination is editable The code's public slug is assigned automatically — **you never set it** — and stays fixed for the life of the code. `update` changes where it points. A poster printed in January can send scanners somewhere new in June. ### Private by default A new code is visible only to its creator. Sharing is two independent things: a baseline visibility level, and per-member grants. ## How to create one ```bash autessa qr-codes create \ --name "Conference booth poster" \ --destinationUrl "https://example.com/signup" \ --active true ``` `name` is for your own reference in the list, not shown to scanners. The response carries the assigned slug and the public scan URL. Two optional objects shape the rest: ```bash autessa qr-codes create \ --name "Spring campaign" \ --destinationUrl "https://example.com/spring" \ --design '{...}' \ --utm '{...}' ``` - `design` — colors, logo and style for the rendered code. - `utm` — campaign parameters appended to the destination URL on redirect, so scans show up attributed in your analytics. Run `autessa qr-codes create help` for the exact `QrDesignDto` and `QrUtmDto` fields. ## How to manage one ```bash autessa qr-codes list autessa qr-codes get --json '{...}' autessa qr-codes update --json '{...}' autessa qr-codes delete --json '{...}' ``` `get` returns the destination, design, UTM tags, slug and public scan URL. `update` changes the name, destination, active state, design or UTM tags. > Setting `active` to `false` stops the code redirecting — scanners hit a dead end rather than your old destination. That is the safe way to retire a printed code, and it is reversible. `delete` is not. ## How to share Two independent controls: ```bash # baseline visibility autessa qr-codes set-share-level --json '{...}' # PRIVATE or COMPANY # per-member grants autessa qr-codes share --json '{...}' autessa qr-codes shares --json '{...}' autessa qr-codes unshare --json '{...}' ``` `PRIVATE` means the owner plus explicitly shared members; `COMPANY` means everyone in the company can see it. `shares` lists who currently has a grant and at what access level. ## How to read scan analytics ```bash autessa qr-codes stats --json '{...}' ``` Returns totals plus breakdowns of who scanned, when, and from where. Pair this with distinct codes per placement — one per poster location, say — and the comparison tells you which placement is actually working. ## Full command reference ## Related - [Command surface](/docs/cli/command-surface.md) - [Autessa CLI](/docs/cli.md) - [Custom apps](/docs/cli/apps.md) — a destination worth pointing a code at --- # Marketplace and Bundles Source: https://docs.autessa.com/docs/cli/marketplace > Browse the template marketplace, inspect a template's contents and requirements before installing, and see what your company has installed or authored. # Marketplace and Bundles A template is a shareable bundle of resources — agents, tools, tables, apps, webhooks — that can be installed into a company. The CLI is the **discovery** half: search the catalog, inspect a template in full, and see what you have installed or authored. Installing and publishing are browser steps. ## Key concepts ### Read-only by design Six of the marketplace's eighteen commands are runnable, and all six are reads. Installing a template writes resources across your whole company, so it stays a deliberate action in the web app with a pre-check step in front of it. That still makes the CLI useful before an install: you can inspect exactly what a template contains and what it will demand from you, without committing. ### Ids and slugs both work A template is addressable by its opaque id **or** its stable URL-friendly slug. Slugs are the better choice in a script you will read again later. ## How to find a template ```bash autessa marketplace list-templates \ --searchTerm "invoice" \ --categorySlug "finance" \ --tags '["quickbooks"]' \ --page 0 --pageSize 20 ``` | Filter | Effect | | --- | --- | | `searchTerm` | Free text against name and description | | `categorySlug` | Limit to one category | | `tags` | Keep only templates carrying **all** of these tags | | `visibility` | `PRIVATE`, `COMPANY`, or `APPROVED` | | `isBundleOnly` | Only bundle templates | ```bash autessa marketplace list-categories # the category tree autessa marketplace list-featured # what Autessa has featured ``` ## How to inspect one before installing ```bash autessa marketplace get-template --json '{...}' ``` This is the command worth running before any install. It returns the long description, **resource counts**, **required connectors**, and **variable placeholders** — that is, what the template will create, what you must already have connected, and what you will be asked to fill in. Checking required connectors first saves the common failure: installing a template that expects a Gmail or QuickBooks connector you have not set up. See [Connectors](/docs/cli/connectors.md). For your own templates, the response also carries review history. ## How to see what you have ```bash autessa marketplace list-installations # templates this company installed autessa marketplace list-my-templates # templates this company authored ``` `list-my-templates` covers drafts, company-shared and public submissions alike — useful for auditing what your company has published outward. > Installing, uninstalling, publishing, submitting for review, packaging from a manifest, and editing template metadata are all web-app-only. Running one prints where to go. See [Publishing](/docs/guide/marketplace/publishing.md). ## Resource bundles Import and export resource bundles between companies — browsing selectable resources and app versions, previewing and running exports and imports, and tracking recent installs. **Every command in this namespace is web-app-only.** Bundle import and export move resources across company boundaries with transitive dependency resolution and candidate matching, which needs the selector and preview UI to be safe: ```bash autessa resource-bundles preview-export ``` ``` Preview the transitive resources an export would include (done in the web app). ``` The commands stay listed so discovery tells you the capability exists. See [Bundles](/docs/guide/marketplace/bundles.md). ## Related - [Marketplace](/docs/guide/marketplace.md) in the builder - [Bundles](/docs/guide/marketplace/bundles.md) and [Publishing](/docs/guide/marketplace/publishing.md) - [Connectors](/docs/cli/connectors.md) — what a template may require - [Autessa CLI](/docs/cli.md) --- # Company and Account Source: https://docs.autessa.com/docs/cli/company > Read your identity and permissions, manage company members and roles, watch usage against limits, and read billing — balance, budgets, invoices and per-task costs. # Company and Account Four namespaces covering who you are and what your company is spending: `user` reads your own identity and scope, `company` administers members and roles, `billing` exposes the whole self-service billing picture, and `payments` is Autessa Payments. Reads are broadly open to members; the writes need the matching permission. ## Who am I? The first command to run when a script misbehaves or a permission error surprises you: ```bash autessa user role ``` ```json { "permissions": [ { "permission": "AGENTIC_WORKFLOW", "id": "lhQ63Oc2DbO" }, { "permission": "AUTESSADB", "id": "1NL5iAqgaBP" }, { "permission": "API_KEY", "id": "zfDFbdhOrxr" }, { "permission": "SCHEDULED_EVENTS", "id": "Ka62l7LpFzf" }, { "permission": "APP_BUILDER", "id": "V7ScwapLsCt" }, { "permission": "SCRIPTING", "id": "oj5g1rED8uP" } ] } ``` It returns your identity, your **effective permissions in the active company**, the active company (`currentScope`), and every company you can access (`availableCompanies`). When a command fails with a permission error, this tells you whether you lack the permission or are simply scoped to the wrong company. > To change which company you are working in, sign in again with that company selected — `autessa login`, or a separate [profile](/docs/cli/install.md) per tenant. Setting a default scope is web-app-only, and everything else in the `user` namespace is poweruser API-key management, also done in the browser. See [Poweruser keys](/docs/guide/api-management/poweruser-keys.md). ## Company Everything here acts on your **active company** except `create`, which makes a new one you own. ### Members and roles ```bash autessa company roles autessa company members ``` `roles` lists the six built-in roles plus any custom ones, with their ids. `members` lists each member and their effective permissions. You need role ids before you can invite or reassign: ```bash autessa company invite \ --email "teammate@example.com" \ --roleIds '["",""]' autessa company set-member-roles --userId --roleIds '[""]' autessa company remove-member --userId ``` > `set-member-roles` **replaces** the member's roles rather than adding to them. Read `company members` first and send the full intended set, or you will silently strip access. Administration needs the manage-team-members permission; reads are open to members. ### Usage against limits ```bash autessa company usage-status ``` Reports current usage against every limit dimension — entities, executions, LLM and vibecode credits, phone, storage, semantic search, browser, and AI-task metering. This is the command to poll before a bulk job, and the one to check first when something starts failing for no obvious reason. ### Creating and renaming ```bash autessa company create --json '{...}' autessa company update --json '{...}' ``` `create` makes a new company owned by you, optionally making it your default scope. `update` renames the current one. Ownership transfer, billing setup and SSO are web-app-only. ## Billing Money is **always in cents**. Reads are open to members; address and budget writes need the billing permission. ### Where the money stands ```bash autessa billing account # prepaid balance, payment state, recent ledger entries autessa billing plan # commitment, remaining credit, resolved per-task rate, next invoice date autessa billing budget # hard caps, soft warning target, accrued usage this period autessa billing invoices # newest first autessa billing invoice-preview # this period's invoice computed live, persisting nothing ``` `invoice-preview` is the useful one mid-month: it runs the same math as month-end without writing anything, so you can see where the period is heading. ### Per-task drill-down ```bash autessa billing tasks --period 202609 --page 0 --pageSize 50 autessa billing task --json '{...}' ``` `tasks` lists individual AI-task rows for a period (`YYYYMM`, defaulting to the current one). `task` returns the itemized receipt for one — metadata, token/voice/RAG usage, per-component cost at the resolved rate, and whether the amount is frozen because it has been invoiced or is still a live estimate. ### Setting budgets ```bash autessa billing set-budget \ --userHardBudgetCents 50000 \ --userSoftWarnPercents '[50,80,95]' \ --budgetNotifyEmails '["finance@example.com"]' autessa billing set-address --json '{...}' ``` A hard cap stops spend; a soft target only warns. `userSoftWarnPercents` are percentages of the cap at which warnings fire, and up to five extra recipients can be warned alongside the owner. > Paying an invoice, prepaying, changing your plan commitment, configuring auto-evaluation, and adding or removing payment methods are deliberately **not** in the CLI — they are done on the billing page in the web app. Reading all of it, including `sources` and `payment-config`, is available here. ## Payments Autessa Payments — onboarding to accept payments, payout bank accounts and schedule, tax registrations, wallet domains, transactions and tax summaries. Every command in this namespace is **web-app-only**. Payment onboarding uses embedded components that need a browser, and payout/bank changes are deliberately gated behind a human step: ```bash autessa payments add-bank-account ``` ``` Payout bank accounts are linked from your Autessa Payments settings in the web app. ``` ## Tutorials In-app tutorial and onboarding progress. Both commands are web-app-only. ## Related - [Authentication](/docs/guide/authentication.md) - [Team](/docs/guide/company/team.md) and [Usage limits](/docs/guide/company/usage-limits.md) - [Billing](/docs/guide/company/billing.md) - [API access](/docs/cli/api-access.md) - [Install and sign in](/docs/cli/install.md) — profiles, one per tenant --- # API Access Source: https://docs.autessa.com/docs/cli/api-access > API keys, OAuth clients and single sign-on — what the CLI lists, why all three are administered in the browser, and what to use for unattended automation instead. # API Access Three namespaces governing how systems and people authenticate into your company: `api-keys` for programmatic access, `oauth-clients` for registered applications, and `sso` for workforce identity. All 29 commands are listed by the CLI and **none are runnable** — every one is administered in the web app. ## Why none of this is scriptable These namespaces mint and rotate credentials, and grant the permissions those credentials carry. Autessa keeps that behind a browser session deliberately: a credential-minting endpoint reachable from a script is a credential-minting endpoint reachable from a stolen script. The commands still appear in the help tree so discovery tells you the capability exists and where it lives, rather than leaving you unsure whether you mistyped: ```bash autessa api-keys create ``` ``` Create a new API key (done in the web app). ``` > This is the same pattern as billing payments, SSO and payment methods — see [What stays in the browser](/docs/cli.md#what-stays-in-the-browser). Roughly 157 of the CLI's commands are listed-but-not-runnable for reasons of this kind. ## API keys Programmatic API keys and their **per-resource** permissions — created, listed, updated, enabled/disabled and rotated, with permissions granted or revoked per resource and per type. Managed from account settings in the web app. The namespace also carries the observability commands: `monthly-stats` for a key's usage and `transaction-history` for what it has actually done. Both are web-app views. What matters when you are working from the terminal: an API key is the credential a **client application** uses against the [Client API](/docs/api.md), scoped by the per-resource permissions you grant it. It is not what the CLI itself uses — `autessa login` mints a separate, short-lived, tenant-scoped session credential. See [Install and sign in](/docs/cli/install.md). ## OAuth clients Registering OAuth client applications for your company, rotating their secrets, and revoking them. Five commands, all web-app-only. Use these when an external application needs to act on behalf of your users, rather than with a fixed company-scoped key. ## Single sign-on Configuring the identity provider, managing verified email domains, setting the auto-join role, and toggling login enforcement. Domain verification involves DNS records and a verification step, and enforcement changes how everyone in the company signs in — both are firmly human-confirmation territory. ## What to use for unattended automation Since you cannot mint a credential from the CLI, the question this page usually raises is: how do I run automation with no human present? Three answers, in the order worth trying: 1. **Put the work on the platform.** [Scheduled jobs](/docs/cli/scheduled-jobs.md), [table triggers](/docs/cli/autessadb.md) and [inbound webhooks](/docs/cli/webhooks-and-queues.md) run server-side with no session to expire. Use the CLI to *create* them and let the platform run them. This is the durable answer for most cases. 2. **Use a poweruser key over HTTP.** The [Poweruser API](/docs/api/poweruser.md) covers the same operations the CLI exposes, and every CLI command has a poweruser equivalent. Keys are minted in the browser — see [Poweruser keys](/docs/guide/api-management/poweruser-keys.md). 3. **Use an API key from your application.** For a client app acting against the [Client API](/docs/api.md), with per-resource permissions scoped to exactly what it needs. What *not* to do is wrap the `autessa` CLI in a cron job: `autessa login` mints a credential that expires in roughly eight hours with no silent refresh, so the job will start failing and the failure will look like a bug in your script. ## Related - [Autessa CLI](/docs/cli.md) — what stays in the browser, and why - [Install and sign in](/docs/cli/install.md) — how the CLI's own credential works - [Poweruser API](/docs/api/poweruser.md) and [Poweruser keys](/docs/guide/api-management/poweruser-keys.md) - [API keys](/docs/guide/api-management/api-keys.md) and [OAuth clients](/docs/guide/api-management/oauth-clients.md) - [Company and account](/docs/cli/company.md) --- # Coding Agents Source: https://docs.autessa.com/docs/cli/coding-agents > Point a tool-using coding agent at Autessa, through the CLI in its shell or the hosted Autessa connector, so it can discover and build platform resources on its own. # Coding Agents A self-describing CLI is an unusually good tool surface for a coding agent. There is no API spec to embed in a prompt and no client library to keep in sync — the agent runs `help`, reads what came back, and calls the right command. Give it the CLI in its shell, or add the hosted Autessa connector to your AI app, and it can build working Autessa systems end to end. ## What it's for - **Building on Autessa from an agent session.** "Create a table for support tickets, a triage tool, and an agent that uses it" is a task a coding agent can carry out directly, from your terminal. - **Editing custom apps.** [`app pull` and `app push`](/docs/cli/apps.md) put a real React project on disk, which is exactly the shape a coding agent works best on. - **Migrations and bulk edits.** Renaming across a hundred rows, restructuring a schema, updating a set of tools — the CLI's JSON in and JSON out composes with whatever the agent already does. ## Why the CLI suits agents - **Discovery is built in.** `autessa help` → `autessa help` → `autessa help` gives an agent the full argument list, types and response shape without you priming it. - **Incomplete commands teach instead of failing.** Running a command that still needs arguments prints its help. An agent that guesses gets a correction, not a stack trace. - **Errors are actionable.** Validation failures come back as field tables; type errors name the JSON path. - **The surface is current by construction.** It is generated from the live SDK manifest, so an agent never works from a stale spec. - **Guardrails are server-side.** Scope, permissions, validation and call budget are enforced by the SDK. An agent cannot exceed what your account can do. ## How to set it up ### 1. Choose how the agent connects There are two ways in. Both act as you, under your permissions, so pick whichever fits your agent. | | CLI in the agent's shell | Autessa connector (MCP) | | --- | --- | --- | | How the agent calls it | Runs `autessa …` in a terminal | Calls the `autessa` tool with the command as a string | | Works with | Any agent that can run terminal commands | Any AI app that supports custom MCP connectors: web, desktop, mobile or command line | | Install | Node 18+ and the CLI | Nothing: you add a URL | | Sign-in | `autessa login` in a terminal, about every eight hours | Once in the browser; the connection stays signed in | | Company | Chosen at `autessa login`, or per command with `--profile` | Chosen when you connect, switchable from the chat | | Piping (`jq`, scripts, `$(…)`), local files | Yes | No: one command per call, results as JSON | | Best for | Scripted builds, bulk edits, uploads | Chat and IDE assistants, and agents with no shell access | ### 2. Set it up #### CLI in the agent's shell ```bash npm install -g @autessa/cli autessa login autessa status ``` The agent inherits your session. The credential is tenant-scoped and expires in roughly eight hours — when it lapses, the agent will see "Session expired" and you re-run `autessa login`. If the agent can run `autessa status` in its terminal, it is connected. #### Autessa connector (MCP) Autessa hosts an MCP server at: ``` https://api.autessa.com/autessa_mcp ``` It works with any AI app that supports custom MCP connectors. The steps are the same everywhere; only the names of the menus differ: 1. **Add the connector.** In your AI app's settings, find where you add connectors (sometimes called integrations, apps or MCP servers) and add a custom one. Name it `Autessa` and paste the URL. In a command-line agent, this is usually a single "add MCP server" command that takes a name and the URL. 2. **Choose the sign-in options if asked.** Pick OAuth as the sign-in method. If the app asks how it should identify itself, any option works; the default is fine. 3. **Connect.** Your browser opens Autessa. Sign in if you need to, choose the company the agent should start in, and click **Allow**. 4. **Try it.** Ask: *"List my Autessa companies."* The app never sees your password: it receives a token that only works on this connector, and you can disconnect it at any time from the app's connector settings. Some apps only offer custom connectors on certain plans, or need a workspace admin to add them first. The connector gives the agent two tools: | Tool | What it does | | --- | --- | | `autessa` | Runs one command. Its `args` input is everything you'd type after the word `autessa`, e.g. `skills`, `help`, `autessadb tables create help`. Every result ends with the company it ran in. | | `autessa_company` | Lists the companies you belong to, or switches which one `autessa` acts in. A switch applies to every later call on that connection. | The agent discovers commands the same way you do, so the tool list stays at two however many commands the platform adds. > Some CLI conveniences need your machine and aren't available through the connector: local files (`@file` arguments, uploads and downloads, `app pull`/`app push` to disk), the `drive` and `analytics` shorthand, and `--output` formatting. Pass JSON inline in single quotes instead. For anything file-based, use the CLI. #### Check the connection Ask the agent: *"Use Autessa to list my companies."* Over the connector it calls `autessa_company` and marks the company it's acting in. With the CLI, ask it to run `autessa status`. If something's off, see [Troubleshooting the connector](#troubleshooting-the-connector). ### 3. Have the agent read the built-in guide The CLI ships a guide written for models rather than humans: ```bash autessa skills ``` It prints the conventions that matter most when an agent is improvising — prefer Autessa connectors over external tools, prefer purpose-built tool types over hand-written scripts, keep apps in draft until asked to publish, and how to recover from each error class. Have the agent read it once at the start of a session. Over the connector this is the `autessa` tool with `args: "skills"`. The connector already tells the agent to begin there, but the standing instructions below make sure of it. ### 4. Give the agent standing instructions Most coding agents read a project instructions file from the repo root (`CLAUDE.md`, `AGENTS.md`, `.cursor/rules`, `.github/copilot-instructions.md`, `GEMINI.md`). Add something like this to yours: ```markdown ## Autessa This project builds on Autessa. Use the `autessa` CLI for all platform work. - Run `autessa skills` once before your first Autessa command. - Discover commands with `autessa help`, `autessa help`, `autessa help`. Never guess flags — a command with missing arguments prints its own help. - Use Autessa connectors for third-party services (Gmail, HubSpot, Notion, QuickBooks…). Do not use curl or vendor CLIs — the connector holds the credentials. - When giving an agent a capability, pick a purpose-built tool type from `autessa tool-types categories`. Use AUTESSA_SCRIPT only when nothing else fits. - Custom apps: push to the draft freely. NEVER run `autessa app publish` unless I explicitly ask to launch / ship / deploy / go live. - Don't report an app as live until `app publish-status` reports SUCCESS. - Prefer `--output json` (the default) and parse with jq. Never parse `--output table`. ``` If the agent uses the connector, change the first lines so it reaches for the tools rather than the shell: ```markdown ## Autessa This project builds on Autessa. Use the Autessa connector's `autessa` tool for all platform work. Its `args` is everything after the word `autessa`, e.g. `help` or `agents all`. - Call it with `skills` once before your first Autessa command. - Discover commands with `help`, ` help`, ` help`. Never guess flags — a command with missing arguments prints its own help. - Pass JSON inline in single quotes (`--schema '{...}'`). There are no local files. - Check which company you're in with `autessa_company` before writing anything, and never switch companies unless I ask. ``` Then keep the connector, tool-type and publishing rules from the first version. ### 5. Reduce permission prompts If your agent asks before each shell command, allowlisting the read-only ones keeps a session moving while leaving writes gated. The pattern to allow is the help tree plus the list commands — `autessa help`, `autessa * help`, `autessa * all` — while leaving `create`, `update`, `publish` and `delete` to prompt. Exactly how you express that depends on your agent's permission settings. Over the connector, apps grant permission per **tool**, and every command goes through the one `autessa` tool. So you can't allow reads and gate writes the way you can with shell patterns. You have two choices: - **Approve each call.** This is the default in most apps, and the safe choice against a production company. - **Always allow the tool.** Use this when the agent works in a sandbox company. Most apps offer an "always allow" option at the first prompt or in their connector settings. Keep `autessa_company` on approval even if you always allow `autessa`. Then the agent can work freely in the company you picked, but can't move itself into another one without asking you. ## Conventions worth encoding These are the mistakes agents make most often against this CLI. Each one is cheap to prevent with an instruction and expensive to debug after the fact. ### Publishing is a deploy The single most important rule. `autessa app publish` serves the draft to everyone with access, replacing what was live. An agent that publishes reflexively after each edit will ship half-finished work. Encode: **stay in the draft; publish only on an explicit request to go live.** The same caution applies more mildly to `agents publish` and `tools publish`, which create new live versions. ### Connectors, not curl If the task touches a third-party service, the Autessa connector already holds the authenticated credentials. An agent reaching for `curl` or a vendor CLI will either fail or route around the credential store. ```bash autessa connectors # what's available autessa gmail help # a connector's commands ``` Connectors that need OAuth are created in the browser — an agent should tell you to connect one rather than trying. ### Native tool types, not scripts A new draft tool starts as an `AUTESSA_SCRIPT` hello-world, which nudges an agent toward writing code. The platform ships hundreds of purpose-built types with the right config fields and auth already wired: ```bash autessa tool-types categories autessa tool-types AUTESSADB_QUERY_TABLE ``` Reach for `AUTESSA_SCRIPT` only when nothing fits. ### Read the response, don't assume the write Several update methods normalize what you send — argument definitions store as `isRequired` and `argumentSchema`, for instance. The response is the source of truth for what was saved. An agent that assumes its input round-tripped verbatim will build on a wrong picture. ### `SDK_UNKNOWN_ENDPOINT` means stale, not missing The CLI auto-refreshes its manifest on this error. The correct reaction is to re-run, and check `autessa help` if a name changed — not to conclude the command does not exist and improvise something else. ## A worked example What a well-instructed agent session looks like against this CLI: ```bash # 1. orient autessa skills autessa status # 2. discover, don't guess autessa tool-types categories autessa tool-types AUTESSADB_QUERY_TABLE autessa tools create help # 3. build TABLE_ID=$(autessa autessadb tables create --tableName tickets --schema '{...}' | jq -r '.tableId') TOOL_ID=$(autessa tools create --toolName find_tickets --toolDescription "..." | jq -r '.tool.toolId') autessa tools update --toolId "$TOOL_ID" --updatedTool '{...}' autessa tools publish --toolId "$TOOL_ID" # 4. verify against the real thing autessa tools execute --toolId "$TOOL_ID" --variables '{...}' autessa tools history list --toolId "$TOOL_ID" # 5. hand off autessa app visible-urls --appId "$APP_ID" ``` Step 4 matters more than it looks. The agent can run what it built and read the execution history back, so it verifies against real platform behavior rather than reporting success from a `201`. Step 5 is the handoff: every id printed is live in the builder, so you can open what the agent made and check it in a UI rather than reading a transcript. That is the practical value of [everything being immediately auditable](/docs/cli.md#immediately-auditable) — the agent works headless, and you review in the browser. ### The same session over the connector Over the connector the steps are identical. The only difference is that each command is a tool call, and the agent reads ids out of the JSON responses instead of capturing them with `$(… | jq)`. Rows are `autessa` tool calls unless noted: | # | Tool call `args` | What the agent does with the result | | --- | --- | --- | | 1 | `skills` | Reads the conventions | | 1 | `autessa_company` with `action: "list"` | Confirms the company before writing anything | | 2 | `tool-types AUTESSADB_QUERY_TABLE` | Learns the tool type's config fields | | 2 | `tools create help` | Learns the arguments | | 3 | `autessadb tables create --tableName tickets --schema '{...}'` | Notes `tableId` from the response | | 3 | `tools create --toolName find_tickets --toolDescription "..."` | Notes `tool.toolId` | | 3 | `tools update --toolId 123 --updatedTool '{...}'` | Checks the saved shape in the response | | 3 | `tools publish --toolId 123` | | | 4 | `tools execute --toolId 123 --variables '{...}'` | Verifies real output | | 5 | `app visible-urls --appId 45` | Hands you the links | ## Troubleshooting the connector **The tools don't show up, or the connector says it failed.** - Check the URL is exactly `https://api.autessa.com/autessa_mcp`. - Finish the sign-in: use the connect or authenticate option in your app's connector settings. Until you've signed in, the app has no tools to show. - `autessa mcp` in the CLI is unrelated: it's the command group for managing the MCP servers your Autessa agents connect to. **The browser says "Couldn't connect to Autessa".** The sign-in link expired (it lasts ten minutes) or came from an app the connector doesn't recognize. Start connecting again from the app. **The app asks you to sign in again.** A connection stays signed in for 30 days after it was last used, refreshing on its own. After a longer gap, or if you disconnected it, the app asks you to sign in again; that's expected. **The agent is working in the wrong company.** Ask it to list your companies with `autessa_company`; the current one is marked. Ask it to switch, or disconnect and reconnect to pick again. If you've been removed from a company, the connector moves to your default company on its own. **A command with JSON fails to parse.** Arguments are split on spaces, and only `'…'` and `"…"` group words together. There are no backslash escapes. Wrap JSON in single quotes. **A command needs a file, or `--output table` is ignored.** The connector has no access to your machine and always returns JSON. Use the CLI for uploads, downloads, `@file` arguments, and the `drive` and `analytics` shorthand. ## Autessa's own agents Separately from driving the CLI with an external coding agent, Autessa ships assistants inside the builder — the [AutoBots](/docs/guide/autobots.md): an agentic copilot for building agents and tools, a database copilot, a script copilot, and [Vibecode](/docs/guide/autobots/vibecode.md) for custom apps. The two approaches compose: build the scaffolding headless from your terminal, then refine in the builder with the copilots, or the reverse. ## Related - [Autessa CLI](/docs/cli.md) - [Install and sign in](/docs/cli/install.md) - [Discovering commands](/docs/cli/discovery.md) - [Custom apps](/docs/cli/apps.md) - [AutoBots](/docs/guide/autobots.md) --- # Command Surface Source: https://docs.autessa.com/docs/cli/command-surface > A map of every Autessa CLI namespace — what each covers, what stays in the web app, and where to read more. # Command Surface This page is a map, not a reference. The authoritative list of commands and arguments is the one your CLI prints, because it is generated from the SDK manifest your server publishes — so use this to find the right namespace, then run `help` inside it. ## Shape of the surface At the time of writing, a signed-in CLI exposes **around 550 commands across 30 namespaces**. Your install may differ: the surface tracks your server, and `autessa login` tells you the exact count. ``` Successfully signed in. you@example.com · tenant Acme Inc (544 commands) ``` Roughly 40% are reads and 60% are writes, and about 150 are listed-but-not-runnable — operations that live in the web app, which print where to go instead of executing. See [What stays in the browser](/docs/cli.md#what-stays-in-the-browser). ## Reading a namespace ```bash autessa help # every namespace with a paragraph on each autessa help # its commands ``` The namespace paragraphs are written to be read. They carry the intended lifecycle, the traps, and an explicit statement of what is *not* scriptable — which is usually the fastest way to find out whether the thing you want is possible from a terminal at all. ## Building blocks | Namespace | Covers | Read more | | --- | --- | --- | | `agents` | Create, configure, share, run and evaluate agents. Draft with `create`, configure with `update`, go live with `publish`. One-shot runs via `execute`; multi-turn via `execute-create` then `execute`. Conversation history and evaluation live here. | [Agents](/docs/cli/agents.md) | | `tools` | Create, configure, run and share the capabilities you attach to agents. `create` → `update` (set type and config) → `publish`. `execute` runs one directly; `history` reads past runs. | [Tools](/docs/cli/tools.md) | | `tool-types` | Read-only catalog of native tool types. `categories` browses; `autessa tool-types ` shows a type's config fields, output and example. | [Tool Types](/docs/tools.md) | | `managers` | Multi-agent orchestrators that delegate to sub-agents. Configure and evaluate them here — there is no `execute`; managers are run by hosted apps and end-user clients. | [Managers](/docs/cli/managers.md) | | `autessadb` | The system of record: `tables`, `query`, `trigger`, `roles` and `access-log`. The largest namespace in the CLI. | [AutessaDB](/docs/cli/autessadb.md) | | `app` | Custom web apps — create, pull and push source, publish, roll back, share, host and attach domains. | [Custom apps](/docs/cli/apps.md) | | `workqueues` | Durable background work queues. Create and configure queues, enqueue batches, monitor items, retry dead work, cancel a group. Items drain asynchronously, so `enqueue` returns immediately and you poll progress. | [Workqueues API](/docs/api/workqueues.md) | ## Automation and integration | Namespace | Covers | Read more | | --- | --- | --- | | `scheduled-events` | Run an action on a schedule — invoke an agent or tool, update a table, or run a system task, once or on a recurring cron. Set `actionType` and the one matching action config. | [Scheduled jobs](/docs/cli/scheduled-jobs.md) | | `custom-webhooks` | Inbound webhooks. A public URL receives a payload, transforms it into named variables via JSONPath or an AutessaScript transform, and substitutes them into a target resource that runs asynchronously. The API key is returned only at creation and rotation. | [Webhook handlers](/docs/guide/api-management/webhook-handlers.md) | | `connectors` | Third-party integrations, one family per provider, each with the same lifecycle surface plus provider-specific methods. Most are connected in the browser via OAuth; beehiiv, Ghost and Bluesky can be connected from the CLI. Stored credentials are never returned. | [Connectors](/docs/cli/connectors.md) | | `mcp` | External MCP servers — create, read, update, delete and test server configs, and list the tools, resources and prompts a server exposes. Auth credentials are write-only. | [MCP servers](/docs/guide/agentic/mcp-servers.md) | | `conversation-sync` | Rules that copy a **closed** agent conversation's message log into an AutessaDB table for reporting. Runs asynchronously off the conversation-logged queue, never retroactively — a config applies only to conversations closed after it is written. This is not a deployment mechanism. | [Conversation sync API](/docs/api/poweruser/conversation-sync.md) | | `trigger-history` | Company-wide table-trigger execution history, newest first, with `detail` returning full uncapped event and response payloads. | [Triggers](/docs/guide/autessadb/triggers.md) | ## Communication and media | Namespace | Covers | Read more | | --- | --- | --- | | `phone-numbers` | Purchase a number and configure how inbound calls route — this is how you wire a phone number to an agent. Releasing a number is done in the web app. | [Phone numbers](/docs/guide/agentic/phone-numbers.md) | | `phone-reminders` | Personal setups where Autessa scans your calendars and calls you before events. Owner-only. | [Meeting reminders](/docs/guide/platform/meeting-reminders.md) | | `sms-consent` | SMS consent records. Web-app-only. | [SMS consent](/docs/guide/agentic/sms-consent.md) | | `voice` | Read-only view of your voice-cloning library — instant clones and fine-tuned voice models, split by ownership and sharing. Creating, uploading, fine-tuning and sharing clones are web-app-only. | [Custom voices](/docs/guide/custom-voices.md) | | `meetings` | Meeting-recorder reads: bots with their config, health and upcoming meetings, plus recordings. Everything that changes state is done in meeting settings in the web app. | [Meeting recorder](/docs/guide/platform/meeting-recorder.md) | | `e-signature` | Read envelopes, your signing inbox, PDFs, signing links, folders, templates and contacts, and organize envelopes into folders. Sending, editing, sealing, voiding and sharing envelopes are web-app-only. | [Document signing](/docs/guide/platform/document-signing.md) | | `qr-codes` | Dynamic short links with scan analytics, custom design and UTM tagging. Slugs are assigned automatically, and a code's destination can change without reprinting. | — | ## Account and administration | Namespace | Covers | Read more | | --- | --- | --- | | `user` | The current user — identity, effective permissions, active company scope and every company you can access. Poweruser key management is web-app-only. | [Authentication](/docs/guide/authentication.md) | | `company` | Company profile, members and roles, invitations, and usage against every limit dimension. Plus `create` for a new company you own. Ownership transfer, billing and SSO are web-app-only. | [Team](/docs/guide/company/team.md), [Usage limits](/docs/guide/company/usage-limits.md) | | `api-keys` | Programmatic API keys and their per-resource permissions. Web-app-only. | [API keys](/docs/guide/api-management/api-keys.md) | | `oauth-clients` | OAuth client applications for your company. Web-app-only. | [OAuth clients](/docs/guide/api-management/oauth-clients.md) | | `sso` | Single sign-on — identity provider, verified domains, auto-join role, login enforcement. Web-app-only. | — | | `billing` | Balance and ledger, billing and tax address, budget limits and spend, plan and resolved per-task rate, invoices and mid-period preview, payment methods, per-task usage. Reads are open to members; address and budget writes need billing permission. Payments, plan changes and payment-method management are web-app-only. Money is always in cents. | [Billing](/docs/guide/company/billing.md) | | `payments` | Autessa Payments — onboarding, payout accounts, tax registrations, transactions. Web-app-only. | [Autessa Payments tools](/docs/tools/autessa-payments.md) | | `marketplace` | Read-only discovery over the template catalog — search, inspect, browse categories and featured picks, and see what your company installed or authored. Installing and publishing are web-app-only. | [Marketplace](/docs/guide/marketplace.md) | | `resource-bundles` | Import and export resource bundles between companies. Web-app-only. | [Bundles](/docs/guide/marketplace/bundles.md) | | `tutorials` | In-app tutorial and onboarding progress. Web-app-only. | — | ## Account commands Outside the namespace tree: | Command | Does | | --- | --- | | `autessa login` | Browser sign-in and tenant selection | | `autessa logout` | Revoke the credential and remove the profile | | `autessa status` / `whoami` | Identity, tenant, session validity, live credential check | | `autessa profiles` | List profiles; `profiles use ` sets the default | | `autessa update` | Reinstall the latest CLI; `--check` compares only | | `autessa version` | Print the CLI version | | `autessa skills` | Print the bundled guide for models driving the CLI | | `autessa connectors` | List the connector families | | `autessa tool-types ` | Show one tool type's reference | ## Poweruser API equivalence Every command in the manifest carries a poweruser equivalent, so anything you can do from the CLI you can also do over HTTP with a [poweruser key](/docs/guide/api-management/poweruser-keys.md). Reach for the [Poweruser API](/docs/api/poweruser.md) when you need unattended automation that outlives a CLI session, and the CLI when you want discovery, exploration and a human or an agent in the loop. ## Related - [Autessa CLI](/docs/cli.md) - [Discovering commands](/docs/cli/discovery.md) - [Poweruser API](/docs/api/poweruser.md) --- # All Commands Source: https://docs.autessa.com/docs/cli/all-commands > Every namespace and every command in the Autessa CLI, what each does, and which guide covers it. # All Commands Complete coverage of the CLI surface: all 30 namespaces and all 544 commands, each with what it does and whether it runs from the terminal or sends you to the web app. Every namespace links to the guide that covers it. > This page is generated from the SDK manifest, the same document your CLI builds its command surface from. It is a snapshot — your install tracks your server, so `autessa help` is always the authority. Regenerate with `npm run gen:cli`. ## How to read this page - **Runnable** commands dispatch to the SDK and do the thing. - **Web app** rows are listed by the CLI but performed in the browser — running one prints where to go rather than failing. This is deliberate: discovery should tell you a capability exists and where it lives. - Command names are shown as you type them. Run `autessa help` for arguments and response shape. ## Coverage | Namespace | Commands | Guide | | --- | --- | --- | | [`agents`](#agents) | 20 | [Agents](/docs/cli/agents.md) | | [`api-keys`](#api-keys) | 17 | [API access](/docs/cli/api-access.md) | | [`app`](#app) | 22 | [Custom apps](/docs/cli/apps.md) | | [`autessadb`](#autessadb) | 39 | [AutessaDB](/docs/cli/autessadb.md) | | [`billing`](#billing) | 19 | [Company and account](/docs/cli/company.md) | | [`company`](#company) | 8 | [Company and account](/docs/cli/company.md) | | [`connectors`](#connectors) | 156 | [Connectors](/docs/cli/connectors.md) | | [`conversation-sync`](#conversation-sync) | 5 | [Webhooks and queues](/docs/cli/webhooks-and-queues.md) | | [`custom-webhooks`](#custom-webhooks) | 8 | [Webhooks and queues](/docs/cli/webhooks-and-queues.md) | | [`e-signature`](#e-signature) | 46 | [Documents and meetings](/docs/cli/documents-and-meetings.md) | | [`managers`](#managers) | 17 | [Managers](/docs/cli/managers.md) | | [`marketplace`](#marketplace) | 18 | [Marketplace and bundles](/docs/cli/marketplace.md) | | [`mcp`](#mcp) | 9 | [MCP servers](/docs/cli/mcp.md) | | [`meetings`](#meetings) | 34 | [Documents and meetings](/docs/cli/documents-and-meetings.md) | | [`oauth-clients`](#oauth-clients) | 5 | [API access](/docs/cli/api-access.md) | | [`payments`](#payments) | 15 | [Company and account](/docs/cli/company.md) | | [`phone-numbers`](#phone-numbers) | 5 | [Phone and SMS](/docs/cli/phone.md) | | [`phone-reminders`](#phone-reminders) | 7 | [Phone and SMS](/docs/cli/phone.md) | | [`qr-codes`](#qr-codes) | 10 | [QR codes](/docs/cli/qr-codes.md) | | [`resource-bundles`](#resource-bundles) | 9 | [Marketplace and bundles](/docs/cli/marketplace.md) | | [`scheduled-events`](#scheduled-events) | 10 | [Scheduled jobs](/docs/cli/scheduled-jobs.md) | | [`sms-consent`](#sms-consent) | 2 | [Phone and SMS](/docs/cli/phone.md) | | [`sso`](#sso) | 7 | [API access](/docs/cli/api-access.md) | | [`tool-types`](#tool-types) | 1 | [Tools](/docs/cli/tools.md) | | [`tools`](#tools) | 14 | [Tools](/docs/cli/tools.md) | | [`trigger-history`](#trigger-history) | 2 | [AutessaDB](/docs/cli/autessadb.md) | | [`tutorials`](#tutorials) | 2 | [Company and account](/docs/cli/company.md) | | [`user`](#user) | 7 | [Company and account](/docs/cli/company.md) | | [`voice`](#voice) | 18 | [Documents and meetings](/docs/cli/documents-and-meetings.md) | | [`workqueues`](#workqueues) | 12 | [Webhooks and queues](/docs/cli/webhooks-and-queues.md) | Account commands that sit outside the namespace tree — `login`, `logout`, `status`, `profiles`, `update`, `version`, `skills` — are covered in [Install and sign in](/docs/cli/install.md) and [Command surface](/docs/cli/command-surface.md). --- ## agents Guide: **[Agents](/docs/cli/agents.md)** --- ## api-keys Guide: **[API access](/docs/cli/api-access.md)** Related: [API keys](/docs/guide/api-management/api-keys.md) in the builder. --- ## app Guide: **[Custom apps](/docs/cli/apps.md)** --- ## autessadb Guide: **[AutessaDB](/docs/cli/autessadb.md)** --- ## billing Guide: **[Company and account](/docs/cli/company.md)** Related: [Billing](/docs/guide/company/billing.md) in the builder. --- ## company Guide: **[Company and account](/docs/cli/company.md)** Related: [Team](/docs/guide/company/team.md), [Usage limits](/docs/guide/company/usage-limits.md). --- ## connectors Guide: **[Connectors](/docs/cli/connectors.md)** 24 provider families sharing a common six-command lifecycle. The full per-provider listing lives in the guide. --- ## conversation-sync Guide: **[Webhooks and queues](/docs/cli/webhooks-and-queues.md)** Related: [Conversation sync API](/docs/api/poweruser/conversation-sync.md). --- ## custom-webhooks Guide: **[Webhooks and queues](/docs/cli/webhooks-and-queues.md)** Related: [Webhook handlers](/docs/guide/api-management/webhook-handlers.md), [Custom webhooks API](/docs/api/custom-webhooks.md). --- ## e-signature Guide: **[Documents and meetings](/docs/cli/documents-and-meetings.md)** Related: [Document signing](/docs/guide/platform/document-signing.md). --- ## managers Guide: **[Managers](/docs/cli/managers.md)** Related: [Managers](/docs/guide/agentic/managers.md), [Manager history](/docs/guide/agentic/manager-history.md). --- ## marketplace Guide: **[Marketplace and bundles](/docs/cli/marketplace.md)** Related: [Marketplace](/docs/guide/marketplace.md). --- ## mcp Guide: **[MCP servers](/docs/cli/mcp.md)** Related: [MCP servers](/docs/guide/agentic/mcp-servers.md), [MCP tool types](/docs/tools/mcp.md). --- ## meetings Guide: **[Documents and meetings](/docs/cli/documents-and-meetings.md)** Related: [Meeting recorder](/docs/guide/platform/meeting-recorder.md). --- ## oauth-clients Guide: **[API access](/docs/cli/api-access.md)** Related: [OAuth clients](/docs/guide/api-management/oauth-clients.md). --- ## payments Guide: **[Company and account](/docs/cli/company.md)** Related: [Autessa Payments tool types](/docs/tools/autessa-payments.md). --- ## phone-numbers Guide: **[Phone and SMS](/docs/cli/phone.md)** Related: [Phone numbers](/docs/guide/agentic/phone-numbers.md). --- ## phone-reminders Guide: **[Phone and SMS](/docs/cli/phone.md)** Related: [Meeting reminders](/docs/guide/platform/meeting-reminders.md). --- ## qr-codes Guide: **[QR codes](/docs/cli/qr-codes.md)** --- ## resource-bundles Guide: **[Marketplace and bundles](/docs/cli/marketplace.md)** Related: [Bundles](/docs/guide/marketplace/bundles.md). --- ## scheduled-events Guide: **[Scheduled jobs](/docs/cli/scheduled-jobs.md)** --- ## sms-consent Guide: **[Phone and SMS](/docs/cli/phone.md)** Related: [SMS consent](/docs/guide/agentic/sms-consent.md). --- ## sso Guide: **[API access](/docs/cli/api-access.md)** --- ## tool-types Guide: **[Tools](/docs/cli/tools.md)** A read-only reference surface rather than a set of runnable operations. `autessa tool-types categories` browses the catalog and `autessa tool-types ` shows one type's config fields, output and example. Related: [Tool Types](/docs/tools.md). --- ## tools Guide: **[Tools](/docs/cli/tools.md)** --- ## trigger-history Guide: **[AutessaDB](/docs/cli/autessadb.md)** --- ## tutorials Guide: **[Company and account](/docs/cli/company.md)** --- ## user Guide: **[Company and account](/docs/cli/company.md)** Related: [Authentication](/docs/guide/authentication.md), [Poweruser keys](/docs/guide/api-management/poweruser-keys.md). --- ## voice Guide: **[Documents and meetings](/docs/cli/documents-and-meetings.md)** Related: [Custom voices](/docs/guide/custom-voices.md). --- ## workqueues Guide: **[Webhooks and queues](/docs/cli/webhooks-and-queues.md)** Related: [Workqueues API](/docs/api/workqueues.md). --- ## Related - [Command surface](/docs/cli/command-surface.md) — the same namespaces grouped by what they're for - [Discovering commands](/docs/cli/discovery.md) — how to read a command's arguments - [Autessa CLI](/docs/cli.md) - [Poweruser API](/docs/api/poweruser.md) — the same operations over HTTP