# run
Source: https://docs.narada.ai/api-reference/agent
Execute automation tasks using the Narada Python SDK
`Agent.run()` executes a natural-language task in a Narada environment and returns an `AgentResponse`.
Create an environment first, then bind an agent to it:
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env)
response = await agent.run(prompt="Search for Narada AI and summarize the first result.")
print(response.text)
```
`Agent.run()` is the current SDK interface. The older `Narada().open_and_initialize_browser_window()` and `window.agent(...)` pattern is no longer the recommended API.
## Constructor
```python theme={null}
class Agent:
def __init__(
self,
*,
environment: Environment,
kind: AgentKind | str = AgentKind.OPERATOR,
) -> None: ...
```
## Run Signature
```python theme={null}
async def run(
self,
prompt: str,
*,
reasoning: ReasoningEffort | None = None,
clear_chat: bool | None = None,
generate_gif: bool | None = None,
output_schema: type[BaseModel] | None = None,
previous_request_id: str | None = None,
chat_history: list[RemoteDispatchChatHistoryItem] | None = None,
additional_context: dict[str, str] | None = None,
attachment: File | IO[Any] | None = None,
time_zone: str = "America/Los_Angeles",
user_resource_credentials: UserResourceCredentials | None = None,
mcp_servers: list[McpServer] | None = None,
secret_variables: dict[str, str] | None = None,
input_variables: Mapping[str, Any] | None = None,
callback_url: str | None = None,
callback_secret: str | None = None,
callback_headers: Mapping[str, Any] | None = None,
on_input_required: InputRequiredCallback | None = None,
critic: CriticConfig | None = None,
timeout: int = 1000,
) -> AgentResponse
```
## Agent Kinds
Choose the agent kind when constructing `Agent`.
The agent to use for task execution.
Best for browser automation tasks like clicking, navigating, filling forms, and extracting data from pages.
```python theme={null}
from narada import Agent, AgentKind
agent = Agent(environment=env, kind=AgentKind.OPERATOR)
```
Best for read-only reasoning, answering questions about page content, and conversation-style tasks.
```python theme={null}
from narada import Agent, AgentKind
agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
```
Best for general productivity and non-browser-specific tasks.
```python theme={null}
from narada import Agent, AgentKind
agent = Agent(environment=env, kind=AgentKind.PRODUCTIVITY)
```
Invoke custom agents created in [Agent Studio](https://app.narada.ai/agent-studio) using the namespaced format.
```python theme={null}
# Use $USER shorthand for your own account.
agent = Agent(environment=env, kind="/$USER/my-data-analyst")
# Or reference a shared agent explicitly.
agent = Agent(environment=env, kind="/jane@company.com/sales-report-generator")
```
The same syntax works in the Narada chat side panel:
```text theme={null}
/$USER/my-agent Search for recent tech news
/jane@company.com/research-bot Find papers on transformers
```
Agents must be shared or published in [Agent Studio](/documentation/agent-studio) before other users can invoke them.
## Parameters
The natural-language instruction for the task. When using `AgentKind.OPERATOR`, Narada applies the correct Operator prefix automatically.
```python theme={null}
prompt="Search for machine learning jobs and extract the first 5 results"
```
Controls how much reasoning the Core Agent uses before responding. This option is only valid with `AgentKind.CORE_AGENT`.
```python theme={null}
from narada import AgentKind, ReasoningEffort
agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
response = await agent.run(
prompt="Analyze the page and explain the pricing model.",
reasoning=ReasoningEffort.MEDIUM,
)
```
Whether to clear chat context before executing the command.
```python theme={null}
clear_chat=True
```
Whether to capture an animated GIF of the automation trajectory.
```python theme={null}
generate_gif=True
```
A Pydantic model class defining the expected structured response.
```python theme={null}
from pydantic import BaseModel, Field
class JobListing(BaseModel):
title: str = Field(description="Job title")
company: str = Field(description="Company name")
location: str = Field(description="Job location")
response = await agent.run(
prompt="Extract the first job listing.",
output_schema=JobListing,
)
```
The prior request ID to continue a conversation.
```python theme={null}
first = await agent.run(prompt="Pick a lucky number.")
second = await agent.run(
prompt="What number did you pick?",
previous_request_id=first.request_id,
)
```
A file-like object to attach to the request. The SDK uploads the file automatically before dispatching the task.
```python theme={null}
with open("document.pdf", "rb") as f:
response = await agent.run(
prompt="Summarize the attached document.",
attachment=f,
)
```
The time zone for time-related operations.
```python theme={null}
time_zone="America/New_York"
```
MCP servers to connect during task execution.
```python theme={null}
from narada_core.models import AuthenticationNone, McpServer
mcp_server = McpServer(
url="https://your-mcp-server.example.com",
label="My MCP Server",
description="Custom tools",
authentication=AuthenticationNone(),
)
response = await agent.run(
prompt="Use the MCP tools to process this data.",
mcp_servers=[mcp_server],
)
```
Sensitive values substituted at action time. The LLM sees placeholders such as `${password}`, not the actual values. See [Secrets and Secret Variables](/documentation/secrets-and-secret-variables#sdk-and-rest-secret-variables).
```python theme={null}
import os
response = await agent.run(
prompt="Log in with username ${username} and password ${password}.",
secret_variables={
"username": os.environ["APP_USERNAME"],
"password": os.environ["APP_PASSWORD"],
},
)
```
Values passed into prompts using `{{$variable_name}}` syntax. File-like values are uploaded automatically.
```python theme={null}
response = await agent.run(
prompt="Summarize {{$doc}}.",
input_variables={"doc": file_obj},
)
```
URL to call when the task status changes or completes. Use this with `callback_secret` or `callback_headers` to authenticate callbacks.
Callback invoked when a task enters an input-required state.
Optional critic configuration that validates the run after the main agent finishes.
Maximum time in seconds to wait for task completion.
```python theme={null}
timeout=180
```
## Response
`Agent.run()` returns an `AgentResponse` object:
Unique identifier for this request. Use it for debugging, callbacks, or conversation continuation.
Execution status: `"success"`, `"error"`, or `"input-required"`.
The text response from the agent.
Parsed structured data when `output_schema` is provided. Otherwise `None`.
Discriminated union containing either text or structured data:
```typescript theme={null}
{ type: "text", content: string }
| { type: "structured", content: object }
```
Usage metrics:
* `actions`: Number of actions performed
* `credits`: Credits consumed for the task
Detailed log of agent actions. Top-level action trace entries include
`start_ts`, `end_ts`, and `duration_ms` fields. See [Action Trace](/documentation/action-trace)
for the trace format and timing semantics.
Workflow-level trace data when available.
Result from the optional critic step.
## Examples
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(
prompt="Search for Python tutorials on Google and get the title of the first result",
)
print(f"Status: {response.status}")
print(f"Result: {response.text}")
print(f"Actions used: {response.usage.actions}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
from pydantic import BaseModel, Field
class SearchResult(BaseModel):
query: str = Field(description="The search query used")
result_count: str = Field(description="Number of results found")
top_result_title: str = Field(description="Title of the first result")
top_result_url: str = Field(description="URL of the first result")
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(
prompt="Search for AI research papers and extract search details",
output_schema=SearchResult,
)
data = response.structured_output
assert data is not None
print(f"Query: {data.query}")
print(f"Results: {data.result_count}")
print(f"Top result: {data.top_result_title}")
print(f"URL: {data.top_result_url}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
```python theme={null}
import asyncio
from narada import Agent, AgentKind, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
try:
response = await agent.run(
prompt="Pick a lucky number between 1 and 100 for me.",
)
print("Agent:", response.text)
response = await agent.run(
prompt="What did you pick again?",
previous_request_id=response.request_id,
)
print("Agent:", response.text)
response = await agent.run(
prompt="What's double that number?",
previous_request_id=response.request_id,
)
print("Agent:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment, NaradaTimeoutError
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
max_attempts = 3
for attempt in range(max_attempts):
try:
response = await agent.run(
prompt="Search for complex data analysis tutorials and summarize the top 3 results",
timeout=60,
generate_gif=True,
)
print("Task completed successfully!")
print(response.text)
break
except NaradaTimeoutError:
if attempt == max_attempts - 1:
raise
print(f"Attempt {attempt + 1} timed out, retrying...")
await agent.reset_agent_state()
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
```python theme={null}
import asyncio
from narada import Agent, AgentKind, BrowserEnvironment
from pydantic import BaseModel, Field
class InvoiceData(BaseModel):
invoice_number: str = Field(description="Invoice number")
date: str = Field(description="Invoice date")
vendor_name: str = Field(description="Vendor or supplier name")
total_amount: str = Field(description="Total amount with currency")
line_items: list[str] = Field(description="List of items or services")
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
try:
with open("/path/to/invoice.pdf", "rb") as f:
response = await agent.run(
prompt="Extract the invoice details from the attached document.",
attachment=f,
output_schema=InvoiceData,
)
invoice = response.structured_output
assert invoice is not None
print(f"Invoice: {invoice.invoice_number}")
print(f"Date: {invoice.date}")
print(f"Vendor: {invoice.vendor_name}")
print(f"Total: {invoice.total_amount}")
print(f"Items: {', '.join(invoice.line_items)}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env, kind="/$USER/my-research-agent")
try:
response = await agent.run(
prompt="Search for recent machine learning papers.",
)
print(response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Best Practices
Create one environment and reuse it across multiple `Agent` instances when tasks should share a browser session.
Use `AgentKind.OPERATOR` for browser automation and `AgentKind.CORE_AGENT` for read-only reasoning or conversation.
Define Pydantic schemas for consistent, typed responses from data extraction tasks.
Use `secret_variables` or Agent Studio [Secrets and Secret Variables](/documentation/secrets-and-secure-variables) for passwords, API keys, or personal information that should not be exposed to the LLM.
Pass file-like objects with `attachment=` instead of calling a separate upload method.
Set appropriate timeouts and call `agent.reset_agent_state()` before retrying after a timeout.
## Migration from Older SDKs
Older SDK examples used a Narada client and window object:
```python theme={null}
async with Narada() as narada:
window = await narada.open_and_initialize_browser_window()
response = await window.agent(prompt='search for "jobs" on Google')
```
Use an environment and agent instead:
```python theme={null}
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(prompt='search for "jobs" on Google')
print(response.text)
finally:
await env.close()
```
For browser actions, call methods on `agent`:
```python theme={null}
await agent.go_to_url(url="https://example.com")
await agent.agentic_selector(
action={"type": "click"},
selectors={"aria_label": "Submit"},
fallback_operator_query="click the Submit button",
)
```
For lifecycle and IDs, use the environment:
```python theme={null}
await env.start()
print(env.browser_window_id)
await env.close()
```
# agentic_matching_selectors_finder
Source: https://docs.narada.ai/api-reference/agentic-matching-selectors-finder
Find all visible page elements matching a natural-language prompt and return reusable selector objects
The `agentic_matching_selectors_finder` method uses a narrow Operator agent to identify every visible element that matches a prompt, then returns `AgenticSelectors` objects you can pass directly to `agentic_selector`.
Use it when a workflow needs to loop over repeated controls, cards, rows, links, or accordion buttons and perform the same deterministic action on each match.
This method finds matching targets only. To click, fill, read, or hover over the returned elements, iterate through the selector objects and call [`agentic_selector()`](/api-reference/agentic-selector).
## Method Signature
```python theme={null}
async def agentic_matching_selectors_finder(
self,
*,
prompt: str,
timeout: int | None = 300,
) -> list[AgenticSelectors]
```
## Parameters
Natural-language description of the visible elements to find. Include the page section or container when possible so the finder returns the intended repeated targets.
```python theme={null}
prompt='Find all visible "More details" buttons in the Highlights section'
prompt="Find all visible down-arrow expand buttons in the accordion"
prompt="Find all visible row action menus in the Open invoices table"
```
Maximum time in seconds to wait. The default is 300 seconds because this method may run an Operator agent.
## Return Value
Returns a list of `AgenticSelectors` dictionaries. If no elements match, the method returns an empty list.
Each returned selector object may include one or more selector fields:
```python theme={null}
{
"id": "element-id",
"data_testid": "test-id",
"name": "input-name",
"aria_label": "Button label",
"role": "button",
"type": "submit",
"text_content": "More details",
"tag_name": "button",
"class_name": "btn-primary",
"dom_path": "html > body > div > button",
"xpath": "//button[normalize-space()='More details']"
}
```
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
await agent.go_to_url(url="https://example.com/highlights")
detail_button_selectors = await agent.agentic_matching_selectors_finder(
prompt='Find all visible "More details" buttons in the Highlights section',
)
print(f"Found {len(detail_button_selectors)} buttons")
for index, selectors in enumerate(detail_button_selectors, start=1):
await agent.agentic_selector(
action={"type": "click"},
selectors=selectors,
fallback_operator_query=(
f'Click "More details" button #{index} in the Highlights section'
),
)
await asyncio.sleep(1)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## When to Use This vs agentic\_selector
| | `agentic_matching_selectors_finder` | `agentic_selector` |
| --------------- | ----------------------------------- | --------------------------------------- |
| **Purpose** | Find many matching targets | Act on one target |
| **Returns** | `list[AgenticSelectors]` | `AgenticSelectorResponse` |
| **Best for** | Repeated controls or rows | Clicking, filling, reading, or hovering |
| **Typical use** | Collect once, then loop | Execute each deterministic action |
# agentic_mouse_action
Source: https://docs.narada.ai/api-reference/agentic-mouse-action
Perform mouse actions at recorded coordinates with Operator agent fallback
The `agentic_mouse_action` method performs mouse actions at specific screen coordinates captured during a recording. If the action fails at those coordinates (e.g., due to page layout changes), it falls back to the Operator agent using a natural language query.
This method is primarily used by agents generated through [Imitation Learning](/documentation/imitation-learning), where click coordinates are captured during recording. For most manual automations, prefer [`agentic_selector()`](/api-reference/agentic-selector) which targets elements by CSS selectors.
## Method Signature
```python theme={null}
async def agentic_mouse_action(
self,
*,
action: AgenticMouseAction,
recorded_click: RecordedClick,
fallback_operator_query: str,
resize_window: bool = True,
timeout: int | None = 60,
) -> None
```
## Parameters
The mouse action to perform. Supported actions:
```python theme={null}
{"type": "click"} # Left click
{"type": "right_click"} # Right click
{"type": "double_click"} # Double click
{"type": "fill", "text": "hello", "press_enter": False} # Type text at the location
{"type": "scroll", "horizontal": 0, "vertical": 300} # Scroll by pixel offset
```
The coordinates and viewport dimensions from when the action was originally recorded.
```python theme={null}
{
"x": 450, # X coordinate in pixels
"y": 320, # Y coordinate in pixels
"viewport": {
"width": 1920, # Viewport width when recorded
"height": 1080 # Viewport height when recorded
}
}
```
Natural language instruction for the Operator agent to use if the coordinate-based action fails.
```python theme={null}
fallback_operator_query="click the Submit button at the bottom of the form"
```
Whether to resize the browser window to match the recorded viewport dimensions before performing the action. This improves accuracy when the page layout is responsive.
Maximum time in seconds to wait for the operation to complete.
## Return Value
This method returns `None` and completes when the action has been performed successfully.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
await agent.go_to_url(url="https://www.google.com")
# Click at recorded coordinates, with Operator fallback
await agent.agentic_mouse_action(
action={"type": "click"},
recorded_click={
"x": 600,
"y": 450,
"viewport": {"width": 1440, "height": 900}
},
fallback_operator_query='click the "Google Search" button'
)
# Type into a field at recorded coordinates
await agent.agentic_mouse_action(
action={"type": "fill", "text": "Narada AI", "press_enter": True},
recorded_click={
"x": 600,
"y": 350,
"viewport": {"width": 1440, "height": 900}
},
fallback_operator_query='type "Narada AI" in the search box and press Enter'
)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## When to Use This vs agentic\_selector
| | `agentic_mouse_action` | `agentic_selector` |
| --------------- | -------------------------------- | -------------------------------------- |
| **Targets by** | Screen coordinates | CSS selectors / element attributes |
| **Best for** | Replaying recorded actions | Programmatic element targeting |
| **Resilience** | Depends on page layout stability | Depends on element attribute stability |
| **Typical use** | Generated by Imitation Learning | Written manually or in custom agents |
Both methods fall back to the Operator agent when their primary targeting strategy fails, ensuring robust automation even when pages change.
# agentic_selector
Source: https://docs.narada.ai/api-reference/agentic-selector
Perform actions on web elements using CSS selectors with Operator agent fallback
The `agentic_selector` method attempts to perform actions on web elements using traditional CSS selectors first. If the selectors don't produce a unique element, it falls back to using the Operator agent to perform the action intelligently.
This method combines the speed of traditional selectors with the reliability of AI agents. Use it when you want fast, precise element targeting with intelligent fallback for dynamic pages.
## Method Signature
```python theme={null}
async def agentic_selector(
self,
*,
action: AgenticSelectorAction,
selectors: AgenticSelectors,
fallback_operator_query: str,
timeout: int | None = 300,
) -> AgenticSelectorResponse
```
## Parameters
The action to perform on the element. Supported actions:
```python theme={null}
{"type": "click"} # Click the element
{"type": "right_click"} # Right-click the element
{"type": "double_click"} # Double-click the element
{"type": "hover"} # Hover over the element
{"type": "fill", "value": "text to enter"} # Fill input with text
{"type": "select_option_by_index", "value": 0} # Select dropdown option by index
{"type": "select_option_by_value", "value": "option_1"} # Select dropdown option by value
{"type": "get_text"} # Get the element's text content
{"type": "get_property", "property_name": "href"} # Get an element property
```
CSS selectors to identify the target element. Available selector types:
```python theme={null}
{
"id": "element-id",
"data_testid": "test-id",
"name": "input-name",
"aria_label": "Button label",
"role": "button",
"type": "submit",
"text_content": "Click me",
"tag_name": "button",
"class_name": "btn-primary",
"dom_path": "html > body > div > button",
"xpath": "//button[@id='submit']"
}
```
Natural language instruction for the Operator agent to use if selectors fail to find a unique element.
```python theme={null}
fallback_operator_query='click on "Images" near the top of the page'
fallback_operator_query='fill in the email field with test@example.com'
```
Maximum time in seconds to wait for the operation to complete. Default is 300 seconds since the Operator agent may need extra time when falling back.
```python theme={null}
timeout=60 # Wait up to 60 seconds
timeout=None # Use default timeout (300 seconds)
```
## Return Value
Returns an `AgenticSelectorResponse` object:
* For `get_text` and `get_property` actions: `response.value` contains the extracted string.
* For all other actions (click, fill, hover, etc.): `response.value` is `None`. The method completes when the action has been performed successfully.
```python theme={null}
# Extract text from an element
response = await agent.agentic_selector(
action={"type": "get_text"},
selectors={"data_testid": "price-display"},
fallback_operator_query="get the price text from the product card"
)
print(f"Price: {response.value}") # e.g., "$29.99"
```
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Navigate to Google
await agent.go_to_url(url="https://www.google.com")
# Try to click using aria-label, fall back to Operator if needed
await agent.agentic_selector(
action={"type": "click"},
selectors={"aria_label": "Search for Images "},
fallback_operator_query='click on "Images" near the top of the page'
)
# Fill a search box
await agent.agentic_selector(
action={"type": "fill", "value": "python programming"},
selectors={"name": "q"},
fallback_operator_query='type "python programming" in the search box'
)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# close
Source: https://docs.narada.ai/api-reference/close
Gracefully close the current browser environment
The environment `close` method gracefully closes the current browser environment. For local browser environments, you can also access the browser process ID and terminate the entire browser application.
## Method Signature
```python theme={null}
async def close(
self,
*,
timeout: int | None = None,
) -> None
```
## Parameters
Maximum time in seconds to wait for the environment to close. If `None`, uses the default system timeout.
```python theme={null}
timeout=5 # Wait up to 5 seconds
timeout=None # Use default timeout
```
## Return Value
This method returns `None` and completes when the environment has been closed.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Perform automation tasks
response = await agent.run(
prompt="Search for Python tutorials and open the first result"
)
print(f"Task completed: {response.text}")
finally:
# Close this specific browser environment
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
```python theme={null}
import asyncio
import os
import signal
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Perform automation tasks
response = await agent.run(
prompt="Search for Python tutorials and open the first result"
)
print(f"Task completed: {response.text}")
finally:
await env.close()
# Get the browser process ID (only available for locally launched browsers)
pid = env.browser_process_id
# Terminate the entire browser process
if pid is not None:
print(f"Terminating browser process with PID: {pid}")
os.kill(pid, signal.SIGTERM)
if __name__ == "__main__":
asyncio.run(main())
```
# get_full_html
Source: https://docs.narada.ai/api-reference/get-full-html
Retrieve the complete HTML content of the current page without internal annotations using the Python SDK
The `get_full_html` method retrieves the complete HTML content of the current browser page without internal annotations, making it suitable for HTML analysis, content extraction, and page archiving.
The HTML returned by this method has all internal annotations stripped, ensuring you get clean, production-ready HTML.
## Method Signature
```python theme={null}
async def get_full_html(
self,
*,
timeout: int | None = None,
) -> GetFullHtmlResponse
```
## Parameters
Maximum time in seconds to wait for the HTML retrieval to complete. If `None`, uses the default system timeout.
```python theme={null}
timeout=30 # Wait up to 30 seconds
timeout=None # Use default timeout
```
## Return Value
Returns a `GetFullHtmlResponse` object with the following structure:
The complete HTML content of the current page as a string, with all internal annotations removed.
```python theme={null}
# Example response
response.html = "......"
```
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Navigate to a page
await agent.go_to_url(url="https://example.com")
# Get the full HTML content
response = await agent.get_full_html()
# Save HTML to a file
with open("page.html", "w", encoding="utf-8") as f:
f.write(response.html)
print("HTML content retrieved and saved")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# Get Response
Source: https://docs.narada.ai/api-reference/get-response
GET /fast/v2/remote-dispatch/responses/{requestId}
Retrieve automation task status and results
```bash cURL theme={null}
curl -X GET 'https://api.narada.ai/fast/v2/remote-dispatch/responses/req_abc123def456' \
-H 'x-api-key: YOUR_API_KEY'
```
```javascript Node.js theme={null}
const response = await fetch(
`https://api.narada.ai/fast/v2/remote-dispatch/responses/${requestId}`,
{
headers: {
"x-api-key": process.env.NARADA_API_KEY,
},
}
);
const data = await response.json();
console.log(data); // { "status": "success", "response": { "output": { "type": "text", "content": "Successfully clicked on Insurance header..." } }, "createdAt": "...", "completedAt": "..." }
```
```python Python theme={null}
import requests
import os
response = requests.get(
f'https://api.narada.ai/fast/v2/remote-dispatch/responses/{request_id}',
headers={'x-api-key': os.getenv('NARADA_API_KEY')}
)
data = response.json()
print(data) # { "status": "success", "response": { "output": { "type": "text", "content": "Successfully clicked on Insurance header..." } }, "createdAt": "...", "completedAt": "..." }
```
```json 200 Pending theme={null}
{
"status": "pending",
"response": null,
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": null
}
```
```json 200 Success theme={null}
{
"status": "success",
"response": {
"output": {
"type": "text",
"content": "Successfully clicked on Insurance header and navigated to the insurance section"
},
"actionTrace": [
{
"url": "https://example.com",
"action": "Clicked the Insurance header",
"startTs": "2026-07-20T17:00:00.000Z",
"endTs": "2026-07-20T17:00:01.500Z",
"durationMs": 1500
},
{
"url": "https://example.com/insurance",
"action": "Opened the Claims section",
"startTs": "2026-07-20T17:00:02.000Z",
"endTs": "2026-07-20T17:00:02.800Z",
"durationMs": 800
},
{
"url": "https://example.com/insurance",
"action": "Done: Successfully clicked on Insurance header and navigated to the insurance section",
"credits": 0.0,
"startTs": "2026-07-20T17:00:03.000Z",
"endTs": "2026-07-20T17:00:03.600Z",
"durationMs": 600
}
]
},
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": "2024-12-19T00:00:30.000000+00:00"
}
```
```json 200 Error theme={null}
{
"status": "error",
"response": null,
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": "2024-12-19T00:00:30.000000+00:00"
}
```
```json 200 Input Required theme={null}
{
"status": "input-required",
"response": {
"output": {
"type": "text",
"content": "The operation could not be completed without additional user input."
}
},
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": null
}
```
Use this endpoint to poll for the completion status and results of a previously submitted automation task. This is an alternative to using webhooks for receiving task results.
## Authorization
Your Narada API key for authentication. Must be the same key used to create
the original task.
## Path Parameters
The unique identifier returned from the [Remote Dispatch
API](/api-reference/remote-dispatch) when the task was created.
## Response
Current status of the automation task. Possible values:
* `"pending"` - Task is still running
* `"success"` - Task completed successfully
* `"error"` - Task failed with an error
* `"input-required"` - Task requires additional user input to continue
The browser window ID where the task was executed. `null` if not applicable.
Task response data. `null` when status is `"pending"`.
The agent's output. Either a text response or structured data, depending on whether `responseFormat` was provided:
```typescript theme={null}
{ type: "text", content: string }
| { type: "structured", content: object }
```
A detailed log of every action the agent took during execution. See
[Action Trace](/documentation/action-trace) for the complete format.
Deprecated. Use `output.content` instead.
Deprecated. Use `output.content` instead (when `output.type` is `"structured"`). See [Structured Output](/documentation/structured-output) for details.
ISO 8601 timestamp when the task was initially created.
ISO 8601 timestamp when the task completed. `null` if still pending.
Usage statistics for the task. `null` if not yet available.
Number of actions performed during execution.
Credits consumed by the task.
## Response Handling Options
This endpoint provides synchronous access to task results. For production applications, consider these alternatives:
Receive real-time notifications when tasks complete. More efficient and scalable than polling.
Complete polling implementation examples with error handling and best practices.
**Rate Limiting**: Implement reasonable polling intervals (5-10 seconds) to
avoid overwhelming the API. Use exponential backoff for production
applications.
## Comparison: Polling vs Webhooks
| Feature | Polling | Webhooks |
| --------------------- | ------------------------ | ------------------- |
| **Setup Complexity** | Low | Medium |
| **Real-time Updates** | No (delayed by interval) | Yes |
| **Resource Usage** | Higher (active requests) | Lower |
| **Reliability** | Medium | High (with retries) |
| **Firewall Friendly** | Yes | Requires open ports |
# get_screenshot
Source: https://docs.narada.ai/api-reference/get-screenshot
Capture a screenshot of the current browser window and retrieve it as base64-encoded image data using the Python SDK
The `get_screenshot` method captures a screenshot of the current browser window and returns it as base64-encoded image data along with metadata. Unlike asking `Agent.run()` to take a screenshot and save it to disk, this method returns the screenshot data programmatically.
This method returns screenshot data directly in your code rather than saving it to disk. If you need screenshots saved automatically to your Downloads folder, use `Agent.run()` with a prompt like "take a screenshot of this page" instead.
## Method Signature
```python theme={null}
async def get_screenshot(
self,
*,
timeout: int | None = None,
) -> GetScreenshotResponse
```
## Parameters
Maximum time in seconds to wait for the screenshot capture to complete. If `None`, uses the default system timeout.
```python theme={null}
timeout=30 # Wait up to 30 seconds
timeout=None # Use default timeout
```
## Return Value
Returns a `GetScreenshotResponse` object with the following structure:
The screenshot image encoded as a base64 string. This can be decoded and saved to a file, displayed, or processed programmatically.
A suggested filename for the screenshot, typically in a format like "Screenshot 2026-01-14 at 10.54.54 AM".
The MIME type of the image format (e.g., "image/png", "image/jpeg").
ISO 8601 formatted timestamp indicating when the screenshot was captured.
## Example
```python theme={null}
import asyncio
import base64
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Navigate to a page
await agent.go_to_url(url="https://example.com")
# Capture a screenshot
response = await agent.get_screenshot()
# Decode and save the screenshot
image_data = base64.b64decode(response.base64_content)
extension = response.mime_type.split('/')[1] # "png" or "jpeg"
filename = f"{response.name}.{extension}"
with open(filename, "wb") as f:
f.write(image_data)
print(f"Screenshot saved as {filename}")
print(f"Captured at: {response.timestamp}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# get_simplified_html
Source: https://docs.narada.ai/api-reference/get-simplified-html
Retrieve simplified, processed HTML content optimized for content analysis using the Python SDK
The `get_simplified_html` method retrieves a simplified version of the current page's HTML that has been processed by Narada to optimize it for content extraction and analysis.
Unlike `get_full_html`, this method returns HTML that has been processed and simplified by Narada, removing unnecessary elements and optimizing the structure for content analysis.
## Method Signature
```python theme={null}
async def get_simplified_html(
self,
*,
timeout: int | None = None,
) -> GetSimplifiedHtmlResponse
```
## Parameters
Maximum time in seconds to wait for the simplified HTML retrieval to complete. If `None`, uses the default system timeout.
```python theme={null}
timeout=30 # Wait up to 30 seconds
timeout=None # Use default timeout
```
## Return Value
Returns a `GetSimplifiedHtmlResponse` object with the following structure:
The simplified HTML content of the current page as a string, processed and optimized by Narada's backend for content analysis.
```python theme={null}
# Example response
response.html = "Title
Content...
"
```
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Navigate to a page
await agent.go_to_url(url="https://example.com/article")
# Get simplified HTML for content analysis
response = await agent.get_simplified_html()
# Extract text content from simplified HTML
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.html, 'html.parser')
text_content = soup.get_text(separator=' ', strip=True)
print(f"Extracted text: {text_content[:200]}...")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# get_url
Source: https://docs.narada.ai/api-reference/get-url
Get the URL of the current active page in the browser window
The `get_url` method retrieves the URL of the currently active page in the browser window. This is useful for verifying navigation, capturing the current state, or building conditional logic based on the page location.
## Method Signature
```python theme={null}
async def get_url(
self,
*,
timeout: int | None = None,
) -> GetUrlResponse
```
## Parameters
Optional timeout in milliseconds. If not specified, uses the default timeout.
## Return Value
Returns a `GetUrlResponse` object:
The URL of the currently active page.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Navigate to a page
await agent.go_to_url(url="https://www.google.com", new_tab=True)
# Get the current URL
response = await agent.get_url()
print(f"Current URL: {response.url}")
# Output: Current URL: https://www.google.com/
# Use in conditional logic
await agent.run(prompt="Search for Narada AI")
response = await agent.get_url()
if "search" in response.url:
print("Search results page loaded successfully")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Use Cases
Confirm the browser navigated to the expected page after an action
Branch your automation based on the current page URL
Log the current URL at each step for debugging automation flows
Capture the final URL after redirects or form submissions
# go_to_url
Source: https://docs.narada.ai/api-reference/go-to-url
Navigate browser windows to specific URLs programmatically using the Narada Python SDK
The `go_to_url` method provides a simple, reliable way to navigate browser windows to specific URLs programmatically. Use this method to set up automation workflows that need to start from specific web pages or navigate between different sites during task execution.
The `go_to_url` method waits for the navigation to complete before returning. This makes it useful for setting up initial page states or navigating between pages in multi-step workflows.
## Method Signature
```python theme={null}
async def go_to_url(
self,
*,
url: str,
new_tab: bool = False,
timeout: int | None = None,
) -> None
```
## Parameters
The target URL to navigate to. Must be a valid HTTP or HTTPS URL.
```python theme={null}
url="https://www.google.com"
url="https://arxiv.org/list/cs.AI/recent"
```
Whether to open the URL in a new tab instead of navigating the current tab.
```python theme={null}
new_tab=False # Navigate current tab (default)
new_tab=True # Open in new tab
```
Maximum time in seconds to wait for navigation to complete. If `None`, uses the default system timeout.
```python theme={null}
timeout=30 # Wait up to 30 seconds
timeout=None # Use default timeout
```
## Return Value
This method returns `None` and completes when the navigation is successful.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Navigate to a specific page
await agent.go_to_url(url="https://www.google.com")
# Now perform actions on the current page
response = await agent.run(
prompt="search for 'python automation' and get the first result"
)
print(f"Result: {response.text}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Common Use Cases
**Navigate to starting pages**
* Set up initial page state
* Navigate to login pages
* Access specific application sections
* Load data entry forms
**Compare across multiple sources**
* Open comparison sites in new tabs
* Gather data from different pages
* Cross-reference information
* Aggregate research findings
**Process items one by one**
* Navigate through search results
* Visit each item in a list
* Process form submissions
* Follow workflow steps
**Gather resources systematically**
* Download files from multiple sources
* Collect images or documents
* Archive web content
* Build resource libraries
## Best Practices
Always use complete, valid URLs with protocol (https\://) for reliable navigation
Set appropriate timeouts for slow-loading pages or unreliable network conditions
Implement proper error handling for navigation failures and network issues
Use `new_tab=True` strategically to keep important pages open for reference
## Error Handling
The `go_to_url` method can raise the following exceptions:
Raised when navigation exceeds the specified timeout period.
```python theme={null}
from narada import NaradaTimeoutError
try:
await agent.go_to_url(url="https://slow-loading-site.com", timeout=10)
except NaradaTimeoutError:
print("Navigation timed out after 10 seconds")
```
Raised for general navigation errors (invalid URLs, network issues, etc.).
```python theme={null}
from narada import NaradaError
try:
await agent.go_to_url(url="https://invalid-url")
except NaradaError as e:
print(f"Navigation failed: {e}")
```
## Integration with Other Methods
The `go_to_url` method works seamlessly with other agent methods:
```python theme={null}
# Navigate then perform actions
await agent.go_to_url(url="https://example.com")
await agent.run(prompt="fill out the contact form")
# Navigate then use selectors
await agent.go_to_url(url="https://app.example.com")
await agent.agentic_selector(
action={"type": "click"},
selectors={"aria_label": "Login"},
fallback_operator_query="click the login button"
)
# Navigate then read data
await agent.go_to_url(url="https://sheets.google.com/spreadsheet/123")
data = await agent.read_google_sheet(
spreadsheet_id="123",
range="A1:C10"
)
```
## Performance Considerations
The `go_to_url` method waits for the page to fully load before returning. For pages with heavy JavaScript or many resources, consider using appropriate timeout values to avoid long wait times.
When working with multiple pages, consider opening them in new tabs rather than navigating back and forth, which can be more efficient for comparison tasks.
# Introduction
Source: https://docs.narada.ai/api-reference/introduction
Get started with the Narada API quickly and efficiently
# Narada API
Welcome to the **Narada API** documentation. Narada is an AI-powered browser automation platform that enables you to automate web interactions, extract data, run custom agents, and perform complex workflows through simple API calls.
## Quick Overview
The Narada API provides several endpoint groups:
Send commands to launch and control browser automation tasks
Poll for task completion and retrieve results
Use the Python SDK for programmatic browser control with `Agent.run()`, `agentic_selector()`, and more
Integrate via Agent-to-Agent protocol or Model Context Protocol
## What You Can Do
* **Web Automation**: Navigate websites, fill forms, click buttons using natural language
* **Data Extraction**: Extract structured data from any webpage using Pydantic models
* **Custom Agents**: Build and run multi-step workflows in the [Agent Studio](/documentation/agent-studio)
* **Cloud Browsers**: Run tasks in serverless [Cloud Browser Sessions](/documentation/cloud-browser-sessions) without a local Chrome
* **MCP Integration**: Connect external tools via [MCP servers](/documentation/mcp-builder)
* **File Operations**: Attach files, download documents, and capture screenshots
* **Webhooks**: Receive real-time notifications when tasks complete
# print_message
Source: https://docs.narada.ai/api-reference/print-message
Display messages in the Narada extension side panel chat using the Python SDK
The `print_message` method displays custom messages in the Narada extension's side panel chat. This is different from Python's regular `print()` function - use this method when you want to show messages to end users viewing the chat or inject messages into the chat history for agents to read later.
This method only displays messages in the Narada extension side panel chat. If you just want to print something to your Python console, use the regular `print()` function instead.
## Method Signature
```python theme={null}
async def print_message(
self,
*,
message: str,
timeout: int | None = None,
) -> None
```
## Parameters
The text message to display in the side panel chat.
```python theme={null}
message="Task completed successfully!"
message="Processing item 3 of 10..."
```
Maximum time in seconds to wait for the message to be displayed. If `None`, uses the default system timeout.
```python theme={null}
timeout=10 # Wait up to 10 seconds
timeout=None # Use default timeout
```
## Return Value
This method returns `None` and completes when the message has been displayed.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Show a message in the chat
await agent.print_message(message="Starting data extraction...")
# Perform automation
await agent.go_to_url(url="https://example.com")
response = await agent.run(prompt="extract the page title")
# Show results in the chat
await agent.print_message(message=f"Extracted title: {response.text}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# prompt_for_user_input
Source: https://docs.narada.ai/api-reference/prompt-for-user-input
Prompt the user for workflow variable values in the Narada browser UI
The `prompt_for_user_input` method shows a form in the Narada browser UI and waits for the user to provide values for one or more workflow variables.
Use this when a Python agent needs structured input before it can continue.
For a higher-level guide covering both human-in-the-loop methods, see [Human-in-the-Loop Steps](/documentation/human-in-the-loop).
## Method Signature
```python theme={null}
async def prompt_for_user_input(
self,
*,
step_id: str,
variables: list[PromptForUserInputVariable],
prompt_message: str | None = None,
timeout: int | None = 300,
) -> dict[str, Any]
```
## Parameters
A stable identifier for this prompt step.
```python theme={null}
step_id="collect-report-filters"
```
The variables to ask the user to fill. Each variable has:
* `name`: variable name
* `type`: one of `string`, `number`, `boolean`, `enum`, `dataTable`, `object`, or `array`
* `required`: whether the user must provide a value
* `enum_values`: allowed values for `enum` variables
```python theme={null}
variables=[
{"name": "region", "type": "enum", "required": True, "enum_values": ["west", "east"]},
{"name": "max_accounts", "type": "number", "required": True},
{"name": "notes", "type": "string", "required": False},
]
```
Text to show above the generated input fields. Use this to explain what the user should enter in plain language.
```python theme={null}
prompt_message="Enter the Jira project where the requirements are located."
```
Maximum time in seconds to wait for the user response. The default is 300 seconds, and the maximum is 3600 seconds.
## Return Value
Returns a dictionary keyed by variable name:
```python theme={null}
{
"region": "west",
"max_accounts": 25,
}
```
Optional variables may be omitted if the user leaves them blank. If the interaction is cancelled or aborted, the method raises `UserAbortedError`.
For `dataTable` variables, returned values use the canonical table shape:
```python theme={null}
{
"columnNames": ["Name", "Email"],
"rows": [
["Ada Lovelace", "ada@example.com"],
["Grace Hopper", "grace@example.com"],
],
}
```
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment, UserAbortedError
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
try:
values = await agent.prompt_for_user_input(
step_id="collect-search-options",
variables=[
{"name": "search_query", "type": "string", "required": True},
{"name": "max_results", "type": "number", "required": True},
{
"name": "source",
"type": "enum",
"required": False,
"enum_values": ["web", "crm", "docs"],
},
],
prompt_message="Choose what to search for and how many results to return.",
timeout=300,
)
except UserAbortedError:
print("The prompt was cancelled.")
return
response = await agent.run(
prompt=(
f"Search {values.get('source', 'web')} for "
f"{values['search_query']} and return the top {values['max_results']} results."
)
)
print(response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# read_google_sheet
Source: https://docs.narada.ai/api-reference/read-google-sheet
Read data from Google Sheets using the Narada Python SDK
The `read_google_sheet` method reads data from a specified range of cells in a Google Sheet. This method requires that the user has the appropriate Google Sheets permissions configured in their Narada extension.
This method requires Google Sheets access to be configured in the Narada extension. If this is your first time using Google Sheets methods, the Narada extension will prompt you to authorize access. The user must have read permissions for the specified spreadsheet.
## Method Signature
```python theme={null}
async def read_google_sheet(
self,
*,
spreadsheet_id: str,
range: str,
timeout: int | None = None,
) -> ReadGoogleSheetResponse
```
## Parameters
The ID of the Google Sheet. This is the long string in the Google Sheets URL.
```python theme={null}
# From URL: https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
```
The range of cells to read in A1 notation. Can include sheet name.
```python theme={null}
range="A1:C10" # Range on default sheet
range="Sheet1!A1:C10" # Range on specific sheet
range="Data!B2:E20" # Named sheet with range
```
Maximum time in seconds to wait for the operation to complete.
```python theme={null}
timeout=30 # Wait up to 30 seconds
timeout=None # Use default timeout
```
## Return Value
Returns a `ReadGoogleSheetResponse` object with the following structure:
A 2D array containing the cell values from the specified range. Each inner list represents a row.
```python theme={null}
# Example response
response.values = [
["Name", "Age", "City"],
["Alice", "25", "New York"],
["Bob", "30", "San Francisco"]
]
```
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Read data from a Google Sheet
response = await agent.read_google_sheet(
spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
range="Sheet1!A1:C10"
)
# Access the data
for row in response.values:
print(row)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# Remote Dispatch
Source: https://docs.narada.ai/api-reference/remote-dispatch
POST /fast/v2/remote-dispatch
Execute automation tasks remotely via the Narada API
```bash cURL theme={null}
curl -X POST 'https://api.narada.ai/fast/v2/remote-dispatch' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"prompt": "/Operator click on Insurance header",
"browserWindowId": "your-browser-window-id",
"clearChat": true
}'
```
```javascript Node.js theme={null}
const response = await fetch("https://api.narada.ai/fast/v2/remote-dispatch", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.NARADA_API_KEY,
},
body: JSON.stringify({
prompt: "/Operator click on Insurance header",
browserWindowId: "your-browser-window-id",
clearChat: true,
}),
});
const data = await response.json();
console.log(data); // { "requestId": "req_abc123def456" }
```
```python Python theme={null}
import requests
import os
response = requests.post(
'https://api.narada.ai/fast/v2/remote-dispatch',
headers={
'Content-Type': 'application/json',
'x-api-key': os.getenv('NARADA_API_KEY')
},
json={
'prompt': '/Operator click on Insurance header',
'browserWindowId': 'your-browser-window-id',
'clearChat': True
}
)
data = response.json()
print(data) # { "requestId": "req_abc123def456" }
```
```json 202 Accepted theme={null}
{
"requestId": "req_abc123def456"
}
```
```json 401 Unauthorized theme={null}
{
"error": "Unauthorized",
"message": "Invalid API key"
}
```
```json 404 Browser Window Not Found theme={null}
{
"error": "Browser Window Not Found",
"message": "The specified browser window ID is not available"
}
```
```json 400 Bad Request theme={null}
{
"error": "Invalid Request",
"message": "The prompt field is required and must start with /Operator"
}
```
The Remote Dispatch API allows you to execute automation tasks remotely on browser windows with the Narada Chrome Extension installed, or in serverless [Cloud Browser Sessions](/documentation/cloud-browser-sessions). This endpoint returns immediately with a request ID for tracking, while the actual automation runs asynchronously.
## Authorization
Your Narada API key for authentication. See [Authentication](/documentation/authentication) for how to obtain your key.
Must be set to `application/json`.
## Body
The natural language instruction for the task to execute.
When using the Python SDK's `Agent.run()` method with the default Operator agent, the `/Operator` prefix is added automatically. When calling the REST API directly, prefix your prompt with `/Operator` to use the web automation agent, or omit it for the Generalist agent.
Unique identifier for the specific browser window to target. Required for `"client"` execution mode. See [Authentication](/documentation/authentication) for more information.
How the task should be executed. Possible values:
* `"client"`: Runs in a browser window with the Narada extension installed (requires `browserWindowId`)
* `"cloud"`: Runs server-side without a browser
* `"cloud_browser"`: Creates a fresh [Cloud Browser Session](/documentation/cloud-browser-sessions) and runs the task in it (no extension needed)
Whether to clear the chat history before executing the command. Set to `false` to continue a previous conversation.
Link this request to a previous one for conversation continuity. When set, the agent retains context from the previous request.
Whether to capture an animated GIF of the automation process. When enabled, the GIF is automatically saved to the `Narada Downloads` directory. Learn more about [Action Trace](/documentation/action-trace).
Specify structured output format using JSON Schema. See [Structured Output documentation](/documentation/structured-output) for detailed examples.
Must be set to `"jsonSchema"`.
A valid JSON Schema object defining the expected response structure.
File attachments to provide to the agent. Each attachment is an object with a `key` field (obtained from the file upload endpoint).
```json theme={null}
"attachments": [{"key": "uploads/abc123/report.pdf"}]
```
The timezone for any time-related operations in the automation task.
MCP (Model Context Protocol) servers to connect during task execution. Each server provides additional tools the agent can use. See [A2A and MCP](/documentation/a2a-and-mcp).
Secret variables for prompt placeholders. Use `${variable_name}` syntax in your prompt. Values are substituted **after** the LLM plans its actions, so the LLM never sees the actual values. Ideal for passwords, API keys, and sensitive data. See [Secrets and Secret Variables](/documentation/secrets-and-secure-variables#sdk-and-rest-secret-variables).
```json theme={null}
{
"prompt": "Log in with username ${user} and password ${pass}",
"secretVariables": {"user": "", "pass": ""}
}
```
Input variables passed to custom agents. These are visible to the agent and accessible in custom agent workflows via the `variables` dictionary. See [Input Variables](/documentation/input-variables).
Webhook URL to receive the response asynchronously when the task completes. See [Webhooks documentation](/documentation/webhooks).
A secret key included in the webhook payload body for verification. Use this to validate that webhook requests are genuinely from Narada.
HTTP headers to include when sending the webhook POST request to your `callbackUrl`.
```json theme={null}
{
"callbackHeaders": {
"Authorization": "Bearer ",
"X-Custom-Header": "value"
}
}
```
Only `Authorization` and headers starting with `x-` (case-insensitive) are allowed. Other headers will be rejected with a 422 error for security reasons.
If `true`, the API blocks until the task completes and returns the full response inline (instead of returning just a `requestId`). Useful for simple scripts. Returns 408 if the task times out.
Optional label for the cloud browser session. Only used when `executionMode` is `"cloud_browser"`.
## Response
Unique identifier for tracking the request. Use this ID for: - Polling the
status via [GET /responses/](/api-reference/get-response) -
Correlating webhook responses - Debug and monitoring purposes
The API returns `202 ACCEPTED` immediately while the automation task runs in the background. Use the `requestId` to track progress through polling or webhooks.
## Example Commands
```bash theme={null}
# Simple navigation
/Operator navigate to the reports section
# Data extraction
/Operator extract customer information from the current table
# Multi-step workflow
/Operator 1) Navigate to customer database, 2) Search overdue accounts, 3) Export to CSV
# Conditional logic
/Operator if error messages exist, screenshot them, otherwise extract user count
# Cloud browser (no extension needed)
# Set executionMode to "cloud_browser", no browserWindowId required
```
## Best Practices
**Command Prefix**: Prefix automation commands with `/Operator` to invoke the web agent. When using the Python SDK's `Agent.run()` method with the default Operator agent, this prefix is added automatically.
**Descriptive Prompts**: Be specific about your intentions. Instead of
`/Operator click button`, use `/Operator click the Submit button in the
contact form`.
* **Use Browser Window IDs** to target specific browser contexts when working with multiple windows
* **Implement timeouts** for long-running automation tasks
* **Use webhooks** for better performance with asynchronous processing
* **Test with simple commands** before implementing complex workflows
* **Enable GIF generation** for debugging complex automation sequences
## Rate Limiting
API requests are subject to rate limiting based on your subscription plan. For high-volume usage or custom rate limits, contact [support@narada.ai](mailto:support@narada.ai).
# File attachments
Source: https://docs.narada.ai/api-reference/upload-file
Attach files to agent requests
The current Narada SDK uploads file attachments automatically when you pass a file-like object to `Agent.run(attachment=...)` or `input_variables={...}`. You do not need to call a separate public `upload_file()` method.
Attachments are useful for document analysis, data extraction, and workflows where the agent needs to process file content.
## Method Signature
```python theme={null}
async def run(
self,
prompt: str,
*,
attachment: File | IO[Any] | None = None,
...
) -> AgentResponse
```
## Parameters
A file-like object opened in binary read mode, or a `File` object returned by a lower-level upload flow.
```python theme={null}
with open("/path/to/document.pdf", "rb") as f:
response = await agent.run(
prompt="Summarize the attached document.",
attachment=f,
)
```
## Return Value
`Agent.run()` returns an `AgentResponse`. For text responses, read `response.text`. For structured output, read `response.structured_output`.
## Example
```python theme={null}
import asyncio
from narada import Agent, AgentKind, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
try:
with open("/path/to/report.pdf", "rb") as f:
response = await agent.run(
prompt="Summarize the key points from the attached report.",
attachment=f,
)
print(f"Summary: {response.text}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
You can also pass file-like objects inside `input_variables`; the SDK uploads them before dispatching the request.
# user_approval
Source: https://docs.narada.ai/api-reference/user-approval
Prompt the user to approve or reject an action in the Narada browser UI
The `user_approval` method shows an approval card in the Narada browser UI and waits for the user to choose one of two buttons.
Use this when a Python agent should not continue until a person explicitly approves or rejects the next action.
For a higher-level guide covering both human-in-the-loop methods, see [Human-in-the-Loop Steps](/documentation/human-in-the-loop).
## Method Signature
```python theme={null}
async def user_approval(
self,
*,
step_id: str,
prompt_message: str,
approve_label: str,
reject_label: str,
timeout: int | None = 300,
) -> bool
```
## Parameters
A stable identifier for this approval step.
```python theme={null}
step_id="approve-invoice-submission"
```
The message shown in the approval card.
```python theme={null}
prompt_message="Submit this invoice for approval?"
```
The label for the button that returns `True`.
```python theme={null}
approve_label="Submit"
```
The label for the button that returns `False`.
```python theme={null}
reject_label="Cancel"
```
Maximum time in seconds to wait for the user response. The default is 300 seconds, and the maximum is 3600 seconds.
## Return Value
`True` when the user clicks the approve button. `False` when the user clicks the reject button.
If the interaction is cancelled or aborted, the method raises `UserAbortedError`.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment, UserAbortedError
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
try:
approved = await agent.user_approval(
step_id="approve-send-message",
prompt_message="Send the drafted message to the customer?",
approve_label="Send",
reject_label="Do not send",
timeout=300,
)
except UserAbortedError:
print("The approval step was cancelled.")
return
if not approved:
print("The user rejected the action.")
return
await agent.run(prompt="Send the drafted message.")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# write_google_sheet
Source: https://docs.narada.ai/api-reference/write-google-sheet
Write data to Google Sheets using the Narada Python SDK
The `write_google_sheet` method writes data to a specified range of cells in a Google Sheet. This method requires that the user has the appropriate Google Sheets permissions configured in their Narada extension.
This method requires Google Sheets access to be configured in the Narada extension. If this is your first time using Google Sheets methods, the Narada extension will prompt you to authorize access. The user must have write permissions for the specified spreadsheet.
## Method Signature
```python theme={null}
async def write_google_sheet(
self,
*,
spreadsheet_id: str,
range: str,
values: list[list[str]],
timeout: int | None = None,
) -> None
```
## Parameters
The ID of the Google Sheet. This is the long string in the Google Sheets URL.
```python theme={null}
# From URL: https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
```
The range of cells to write to in A1 notation. Can include sheet name.
```python theme={null}
range="A1:C3" # Range on default sheet
range="Sheet1!A1:C3" # Range on specific sheet
range="Data!B2:E5" # Named sheet with range
```
A 2D array containing the data to write. Each inner list represents a row of data.
```python theme={null}
values=[
["Name", "Age", "City"],
["Alice", "25", "New York"],
["Bob", "30", "San Francisco"]
]
```
Maximum time in seconds to wait for the operation to complete.
```python theme={null}
timeout=30 # Wait up to 30 seconds
timeout=None # Use default timeout
```
## Return Value
This method returns `None` and completes when the data has been written to the sheet.
## Example
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Write data to a Google Sheet
await agent.write_google_sheet(
spreadsheet_id="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
range="Sheet1!A1:C3",
values=[
["Product", "Price", "Stock"],
["Laptop", "999", "15"],
["Mouse", "25", "50"]
]
)
print("Data written successfully!")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
# A2A and MCP Integration
Source: https://docs.narada.ai/documentation/a2a-and-mcp
Learn how to integrate with Narada using Agent-to-Agent (A2A) and Model Context Protocol (MCP) standards
Narada supports two industry-standard protocols for agent integration: **Agent-to-Agent (A2A)** and **Model Context Protocol (MCP)**. These protocols enable seamless communication between Narada and other AI agents or systems.
## Overview
Both A2A and MCP provide standardized ways to interact with Narada's capabilities:
* **A2A Protocol**: Enables direct agent-to-agent communication following the A2A specification
* **MCP Protocol**: Uses the Model Context Protocol with Streamable HTTP for structured interactions
Direct agent-to-agent communication using the A2A specification
Structured interactions using Model Context Protocol with Streamable HTTP
## A2A Protocol
The Agent-to-Agent (A2A) protocol enables direct communication between AI agents using a standardized specification.
### Agent Discovery
Narada's A2A agent information is available at the well-known endpoint. You can discover the agent card as :
```bash cURL theme={null}
curl -X GET 'https://api.narada.ai/fast/v2/a2a/.well-known/agent.json'
```
```json Agent Information theme={null}
{
"name": "Narada AI Agent",
"description": "AI agent for automating tasks through web browsing and integrations",
"version": "0.0.1",
"url": "https://api.narada.ai/fast/v2/a2a",
"authentication": {
"type": "bearer",
"description": "API key required"
},
// Additional fields...
}
```
### A2A Endpoint
The main A2A endpoint follows the exact A2A protocol specification:
**Endpoint**: `https://api.narada.ai/fast/v2/a2a`
The A2A endpoint implements the complete A2A protocol specification. Refer to
the [official A2A documentation](https://a2a-protocol.org/latest/) for detailed protocol requirements and message
formats.
### Authentication
A2A requests require authentication using your Narada API key:
Bearer token for API authentication. Format: `Bearer YOUR_API_KEY`
### Example A2A Request
This example demonstrates how to use the A2A Python SDK to discover Narada's agent card and send a streaming message request asking the Operator agent to navigate to google.
First, install the A2A Python SDK:
```bash theme={null}
pip install a2a-sdk
```
The A2A SDK is the official Python library for the Agent2Agent Protocol. You can find more details in the [A2A Python SDK repository](https://github.com/a2aproject/a2a-python).
```python wrap expandable theme={null}
import httpx
from a2a.client import A2ACardResolver, A2AClient
from a2a.types import (
MessageSendParams,
MessageSendConfiguration,
Message,
TextPart,
JSONRPCErrorResponse,
SendStreamingMessageRequest,
)
from uuid import uuid4
headers = {
"Authorization": f"Bearer "
}
agent = "https://api.narada.ai/fast/v2/a2a"
async with httpx.AsyncClient(timeout=30, headers=headers) as client:
card_resolver = A2ACardResolver(
client, base_url=agent, agent_card_path="/.well-known/agent.json"
)
card = await card_resolver.get_agent_card()
client = A2AClient(client, agent_card=card)
# Create message
message = Message(
role="user",
parts=[TextPart(text="/Operator navigate to google.com")],
message_id=str(uuid4()),
context_id=uuid4().hex,
)
# Send streaming request
response_stream = client.send_message_streaming(
SendStreamingMessageRequest(
id=str(uuid4()),
params=MessageSendParams(
id=str(uuid4()),
message=message,
configuration=MessageSendConfiguration(accepted_output_modes=["text"]),
),
)
)
# Print streaming responses
async for result in response_stream:
if isinstance(result.root, JSONRPCErrorResponse):
print(f"Error: {result.root.error}")
else:
event = result.root.result
print(f"stream event => {event.model_dump_json(exclude_none=True)}")
```
## MCP Protocol
The Model Context Protocol (MCP) provides structured communication using Streamable HTTP for efficient data exchange.
### MCP Endpoint
**Endpoint**: `https://api.narada.ai/fast/v2/mcp`
The MCP endpoint implements the complete Model Context Protocol specification. Refer to the [official MCP documentation](https://modelcontextprotocol.io/docs/) for the detailed protocol requirements and message formats
### Authentication
MCP requests use the same authentication method as A2A:
Bearer token for API authentication. Format: `Bearer YOUR_API_KEY`
### Example MCP Request
This example demonstrates how to use the FastMCP Python SDK to send a streaming message request asking the Operator agent to navigate to google.
First, install the FastMCP Python SDK:
```bash theme={null}
pip install fastmcp
```
FastMCP is a Python library for the Model Context Protocol. You can find more details in the [FastMCP documentation](https://gofastmcp.com/clients/client).
```python wrap expandable theme={null}
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
headers = {
"Authorization": f"Bearer "
}
url = "https://api.narada.ai/fast/v2/mcp"
client = Client(
transport=StreamableHttpTransport(
url,
headers=headers,
),
)
async with client:
await client.ping()
# Create a new task
result = await client.call_tool(
name="narada",
arguments={
"prompt": "/Operator navigate to google.com"
}
)
print(result.content)
```
## Error Handling
Both protocols return standardized error responses:
```json Error Response theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found",
"data": // Some data...
}
}
```
### Common Error Codes
* **401 Unauthorized**: Invalid or missing API key
* **400 Bad Request**: Invalid request format or parameters
* **422 Unprocessable Entity**: Request validation failed
* **500 Internal Server Error**: Unexpected server error
* **503 Service Unavailable**: Service temporarily unavailable
# Account Deletion
Source: https://docs.narada.ai/documentation/account-deletion
Learn how to permanently delete your Narada account
## Deleting Your Account
If you need to delete your Narada account, you can do so directly from the settings page.
Navigate to your account settings page in the Narada dashboard.
Locate and click the **Delete Account** button to permanently remove your account.
This action is permanent and cannot be undone. All your data, including saved prompts, custom instructions, and account history, will be permanently deleted.
# Action Trace
Source: https://docs.narada.ai/documentation/action-trace
Detailed logging and visual recording of automation workflows
Action tracing provides comprehensive visibility into your automation workflows through two complementary features: detailed step logging and visual GIF recording.
## Action Trace Response
Action traces provide detailed step-by-step logs of automation execution, returned in API responses for both custom agent shortcuts and `/Operator` commands.
### Operator Traces
Simple action logs for `/Operator` commands:
```json theme={null}
{
"actionTrace": [
{
"url": "https://arxiv.org/",
"action": "Typed 'artificial intelligence' and pressed Enter into search input",
"startTs": "2026-07-20T17:00:00.000Z",
"endTs": "2026-07-20T17:00:01.500Z",
"durationMs": 1500
},
{
"url": "https://arxiv.org/search/",
"action": "Clicked on search button",
"startTs": "2026-07-20T17:00:02.000Z",
"endTs": "2026-07-20T17:00:02.800Z",
"durationMs": 800
},
{
"url": "https://arxiv.org/search/",
"action": "Done: Found the requested results.",
"credits": 0.0,
"startTs": "2026-07-20T17:00:03.000Z",
"endTs": "2026-07-20T17:00:03.600Z",
"durationMs": 600
}
]
}
```
Each top-level action trace item includes:
| Field | Type | Description |
| ------------ | ------- | ----------------------------------------------- |
| `url` | string | Page URL associated with the action. |
| `action` | string | Human-readable action description. |
| `credits` | number | Credits charged for the action, when included. |
| `startTs` | string | UTC ISO 8601 timestamp when the action started. |
| `endTs` | string | UTC ISO 8601 timestamp when the action ended. |
| `durationMs` | integer | Time taken for the action, in milliseconds. |
These fields show how long each action took.
In the Python SDK, these fields are available as `start_ts`, `end_ts`, and `duration_ms`:
```python theme={null}
for item in response.action_trace or []:
if hasattr(item, "duration_ms"):
print(item.action, item.start_ts, item.end_ts, item.duration_ms)
```
### Custom Agent Traces
Detailed step logs for custom agent workflows with multiple action types:
```json theme={null}
{
"actionTrace": [
{
"url": "https://example.com",
"step_type": "goToUrl",
"description": "Navigated to https://example.com"
},
{
"url": "https://example.com",
"step_type": "agent",
"agent_type": "operator",
"action_trace": [...]
}
]
}
```
#### Supported Step Types
Custom agent traces use a `step_type` field to identify the action. The full list of supported step types:
| Step Type | Description |
| ------------------------------------------------- | ----------------------------------------------------- |
| `start` / `end` | Workflow boundaries |
| `goToUrl` | Navigate to a URL |
| `getUrl` | Get the current page URL |
| `agent` | Sub-agent invocation (contains nested `action_trace`) |
| `agenticSelector` | Element selection via CSS selectors with fallback |
| `agenticMouseAction` | Mouse action at recorded coordinates |
| `getFullHtml` / `getSimplifiedHtml` | HTML content capture |
| `getScreenshot` | Screenshot capture |
| `print` | Print a message to the chat |
| `python` | Inline Python code execution |
| `for` / `while` | Loop constructs |
| `if` | Conditional branching |
| `setVariable` | Variable assignment |
| `wait` | Delay step |
| `waitForElement` | Wait for an element to appear |
| `pressKeys` | Keyboard input |
| `readGoogleSheet` / `writeGoogleSheet` | Google Sheets operations |
| `dataTableExportAsCsv` | Export data as CSV |
| `objectExportAsJson` | Export data as JSON |
| `dataTableInsertRow` / `dataTableUpdateCellValue` | Data table operations |
| `objectSetProperties` | Object property updates |
| `runCustomAgent` | Invoke another custom agent workflow |
### Formatting Example
Convert action traces to readable markdown (handles multiple custom agent step types):
```python theme={null}
def format_action_trace(trace_data):
if not trace_data:
return "No action trace available"
markdown = "# Action Trace\n\n"
for i, step in enumerate(trace_data, 1):
if 'action' in step:
# Operator trace
duration = step.get('durationMs')
suffix = f" ({duration} ms)" if duration is not None else ""
markdown += f"{i}. **{step['url']}**\n - {step['action']}{suffix}\n\n"
else:
# Custom agent trace - handle different step types
step_type = step['step_type']
description = step.get('description', step_type)
if step_type == 'goToUrl':
markdown += f"{i}. 🌐 **Navigate** to {step.get('url', 'URL')}\n"
elif step_type == 'agent':
agent_type = step.get('agent_type', 'unknown')
markdown += f"{i}. 🤖 **Agent** ({agent_type}) - {description}\n"
if step.get('action_trace'):
markdown += f" - Contains {len(step['action_trace'])} sub-actions\n"
elif step_type in ['for', 'while']:
markdown += f"{i}. 🔄 **Loop** ({step_type}) - {description}\n"
elif step_type == 'python':
markdown += f"{i}. 🐍 **Code Execution** - {description}\n"
elif step_type == 'print':
markdown += f"{i}. 📝 **Message** - {description}\n"
elif step_type == 'agenticSelector':
markdown += f"{i}. 🎯 **Element Selection** - {description}\n"
elif step_type == 'agenticMouseAction':
markdown += f"{i}. 🖱️ **Mouse Action** - {description}\n"
else:
markdown += f"{i}. ⚡ **{step_type.title()}** - {description}\n"
markdown += "\n"
return markdown
```
## GIF Recording
```python highlight={11} theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Run automation with GIF recording enabled
response = await agent.run(
prompt='search for "machine learning" on Google and extract the number of results',
generate_gif=True # Enable GIF recording
)
print("Response:", response.text)
print("GIF saved to your Downloads/Narada Downloads folder!")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
The SDK automatically compiles frames into an animated GIF showing the complete
automation trajectory.
Narada automatically creates a directory structure for your GIFs:
1. **Main Directory**: `Narada Downloads` in your machine's Downloads folder
2. **Request Directory**: Directory named after your `request_id` from the [SDK response](/documentation/getting-started-with-sdk)
3. **File**: Automation saved as an animated `.gif` file
If the `Narada Downloads` directory doesn't exist, it will be created automatically.
Navigate to your Downloads folder and look for:
```
Downloads/
└── Narada Downloads/
└── req_abc123def456/
└── automation_recording.gif
```
Each GIF request gets its own directory named after the `request_id` for easy correlation with your SDK calls.
## Best Practices
Use action traces for programmatic debugging and GIFs for visual verification
Action traces provide detailed audit logs for compliance and monitoring
GIFs document visual workflows while action traces capture step-by-step logic
Parse action traces to analyze automation patterns and optimize workflows
# Agent Maker
Source: https://docs.narada.ai/documentation/agent-maker
Generate custom agents from natural language goal descriptions
## What is Agent Maker?
Agent Maker lets you create a custom agent by simply **describing what you want it to do** in natural language. Narada's AI generates a complete, runnable Python agent from your description, no coding or manual workflow building required.
Describe a goal like "Create an agent that checks website carbon footprint and generates a report" and Agent Maker builds the entire automation for you.
## Two Ways to Use Agent Maker
### Option 1: From the Chat
Type `/agentMaker` followed by your goal description directly in the Narada chat:
```
/agentMaker Create an agent that researches token pricing for OpenAI and Anthropic models
```
Narada will begin generating your agent immediately in the chat. The agent is created, tested, and opened for you automatically.
The `/agentMaker` command is case-insensitive. `/agentmaker`, `/AgentMaker`, and `/AGENTMAKER` all work.
### Option 2: From Agent Studio
Navigate to the **Agent Studio** from the Narada sidebar.
Click the **+ Create** button to open the creation dialog.
Choose **Agent Maker** from the options. The description reads: "Build an agent from your goal description."
Enter a clear description of what you want the agent to do. Be specific about:
* What websites or tools the agent should use
* What data it should extract or actions it should take
* What the expected output should look like
Click **Generate agent** to start the process. Narada will open the side panel and begin building your agent.
## Example Goals
Not sure what to write? Here are some example goals to get started:
```
Create an agent that creates a new Google contact. It gets first name, last name,
company, and email as input and then fills out this information in contacts.google.com/new
```
```
Create an agent that researches token pricing for OpenAI and Anthropic models.
It should go to their pricing pages, extract the per-token input and output costs
for each model, and compile them into a comparison table
```
```
Create an agent that checks a website's carbon footprint score on
websitecarbon.com and generates a summary report
```
```
Create an agent that searches for software engineering jobs on LinkedIn,
extracts the top 5 results with title, company, and location, and saves
them to a Google Sheet
```
## What Happens After Generation
When Agent Maker generates your agent, it goes through several steps:
Narada's AI writes a Python agent based on your goal description, using the Narada SDK for browser automation.
Agent Maker automatically runs your new agent to verify it works as described.
The generated agent opens in a new tab in Agent Studio. You can review the code, see test results, and make edits.
Agent Maker explains what it built and asks if you'd like any changes. You can iterate directly in the chat.
The generated agent is a [Python Agent](/documentation/python-agents), a code-based automation using the Narada Python SDK. You can edit the code directly in Agent Studio.
## Tips for Writing Good Goals
Include specific websites, data fields, and expected outputs. "Extract job titles from LinkedIn" is better than "find jobs."
Break down multi-step tasks: "Go to the pricing page, extract costs per model, compile into a table."
Specify what data the agent receives as input and what it should produce as output.
One clear goal per agent. Complex tasks are better split into multiple agents that call each other.
## Agent Maker vs Imitation Learning
Both features generate custom agents, but they work differently:
| | Agent Maker | Imitation Learning |
| ----------------------------- | ------------------------------- | ------------------------------------------- |
| **How you describe the task** | Type a goal in natural language | Record yourself doing it in the browser |
| **Best for** | Tasks you can describe clearly | Tasks that are easier to show than describe |
| **Input** | Text description | Browser recording + audio narration |
| **Output** | Python Agent | Workflow with visual steps |
| **Iteration** | Chat back and forth to refine | Edit steps in the workflow editor |
**When to use which?** If you can easily describe the task in a sentence or two, use Agent Maker. If the task involves complex UI interactions that are hard to put into words, use [Imitation Learning](/documentation/imitation-learning) instead.
## Requirements
The Narada Chrome Extension must be installed and active. Agent Maker uses the extension to generate and test agents.
You must be signed in to your Narada account.
## Troubleshooting
* Make sure the Chrome extension is installed and up to date
* Try simplifying your goal description
* Check that the target websites are accessible from your browser
* Provide more specific instructions in your goal
* Use the chat to give Agent Maker feedback: "The agent should also click the Download button after extracting the data"
* You can always edit the generated Python code directly in Agent Studio
* Ensure you're typing the command in the Narada chat side panel
* The command must start with `/agentMaker` followed by a space and your goal
* Check that you're signed in to your Narada account
# Agent Studio
Source: https://docs.narada.ai/documentation/agent-studio
Create powerful multi-step AI agent workflows with visual orchestration
## What is the Agent Studio?
The Agent Studio is your hub for creating, organizing, and managing AI agent automations. It supports multiple types of items and several ways to create agents, from visual workflow building to AI-powered generation.
Agent Studio is a full workspace: create agents, define custom tools (MCP servers), build knowledge bases (vector stores), organize everything into folders, and share with your team.
## What You Can Create
Agent Studio supports four types of items:
Visual workflow editor: chain together steps like navigation, clicks, data extraction, loops, and conditions
Code-based agent using the Narada Python SDK: full programming flexibility with loops, APIs, and data processing. See [Python Agents](/documentation/python-agents)
Custom tool definitions using the Model Context Protocol: create reusable tools your agents can call. See [MCP Builder](/documentation/mcp-builder)
Knowledge base with document upload and semantic search: give your agents access to custom knowledge. See [Vector Stores](/documentation/vector-stores)
## Ways to Create Agents
Describe your goal in natural language and Narada generates a Python Agent automatically. Also available via `/agentMaker` in chat.
Record your browser actions while narrating, and Narada generates a workflow from the recording.
Start from a pre-built template and customize it for your needs.
Build a GUI workflow or Python Agent manually from a blank canvas.
## Getting Started
Open the Agent Studio from the Narada sidebar.
Click the **+ Create** button to see all creation options: GUI Agent, Python Agent, Agent Maker, Imitation Learning, and templates.
Select the creation method that best fits your needs. For first-time users, we recommend starting with [Agent Maker](/documentation/agent-maker).
Configure your agent, test it, and run it directly from Agent Studio.
## Organizing Your Work
Agent Studio provides file-system-like organization:
* **Folders**: Group related agents, MCP servers, and vector stores
* **Drag and drop**: Reorder items and move them between folders
* **Rename**: Give descriptive names to your items
* **Copy and move**: Duplicate items or move them to different locations
## Sharing Agents
Share your agents with team members:
* **Share by email**: Invite specific users to access your agents
* **Public sharing**: Make an agent publicly accessible via a link
* **Access requests**: Team members can request access to agents you own
* **Shared folders**: Organize shared items in dedicated folders
## GUI Agent Workflows
The GUI Agent editor lets you build visual, step-by-step workflows:
## Step Types
GUI workflows are built by chaining together steps. The two most-configured step types are documented below; workflows also support control-flow steps (loops and conditionals) for branching and iteration.
### Agent Steps
Agent steps run an LLM-driven agent to accomplish a natural-language task — web automation, data extraction, reasoning, etc. Each agent step can be configured with:
#### Prompt
The natural language instruction that tells the agent what to accomplish in this step. Be specific and clear about the expected outcome.
Reference variables from previous steps using `{{$variableName}}` or `{{$arrObj[$i]}}` syntax to create dynamic, context-aware workflows.
#### Agent Type
Select the specialized agent for your task:
Web automation: clicks, navigation, form filling, and data extraction
Read-only observation: extract visible data, answer questions about page content, and web searches without taking browser actions
#### Attachments
Upload files to provide additional context to the agent. You can select multiple files that the agent will use to inform its actions.
Attachments are useful for providing reference documents, data files, or examples that the agent should consider during execution.
#### MCP Servers
Configure Model Context Protocol servers to provide additional tools and capabilities to the agent. Paste the URL of your MCP server to give the agent access to all tools within that server during this step's execution.
Create custom tools for your agents using the MCP Builder
#### Vector Stores
Add vector stores to provide context and knowledge to your agent. When the agent needs information, it will automatically retrieve relevant content from your vector stores using semantic search to answer questions and make informed decisions.
Create vector stores to store documents and knowledge for your agents
#### Other Tools
Create custom Python tools directly within the agent step. These tools are defined with Python code and will be available to the agent during execution.
* **Other Tools**: Quick, one-off functions specific to this agent workflow
* **MCP Servers**: Reusable tool collections you want to share across multiple workflows
#### Output Variables
Define structured outputs that the agent should extract during this step. Output variables allow you to capture specific data that can be referenced in subsequent steps.
Add at least one output variable if you want the agent to generate structured output. Without output variables, the agent generates free-form responses only.
**Example use case:**
* Step 1 outputs: `$jobListings` (array of job postings)
* Step 2 references: `"Analyze each job in {{$jobListings}} and extract requirements"`
#### Additional Options
Enable this option to start the step with a clean slate, removing context from previous steps. Useful when you want to prevent context bleed between unrelated tasks.
This is checked by default in the UI to ensure each step starts fresh.
### Python Code Steps
Python Code steps run inline Python as part of your workflow. Use them to transform data between steps, compute derived values, or implement deterministic logic that doesn't need an LLM.
Python Code steps are different from the **Other Tools** option inside an Agent step. Other Tools are Python functions the LLM can choose to call mid-step; a Python Code step is its own step in the workflow that always runs when reached.
#### Passing Variables In and Out
Python Code steps expose a special `variables` dictionary that bridges your Python code with the workflow's variables:
* **Read** workflow variables with `variables.get("varName")` or `variables["varName"]`
* **Write** workflow variables by assigning: `variables["varName"] = newValue`
Workflow input variables use this same `variables` dictionary. See [Input Variables](/documentation/input-variables) for how to declare and pass them when invoking a custom agent. Use [Secrets and Secret Variables](/documentation/secrets-and-secure-variables) for credentials and other sensitive values.
```python theme={null}
# Read existing workflow variables
jobs = variables.get("jobListings", [])
threshold = variables.get("salaryThreshold", 100000)
# Compute something with plain Python
filtered = [j for j in jobs if j["salary"] >= threshold]
# Write back to a workflow variable so later steps can use it
variables["filteredJobs"] = filtered
print(f"Kept {len(filtered)} of {len(jobs)} jobs")
```
Every workflow variable is automatically injected into `variables` when the step starts. Variables that haven't been assigned a value yet show up as `None`, so prefer `variables.get("name", default)` over `variables["name"]` to handle that case cleanly.
You can only **update variables that already exist** in the workflow. Assigning to a key that isn't a declared workflow variable has no effect after the step finishes — define the variable as an output on an earlier step (or as a workflow input) before writing to it from Python.
Values written back are coerced to match each variable's declared type. If a value can't be coerced (for example, assigning a string to a variable declared as a number), the step fails with an error naming the offending variable.
#### Output
Anything your code prints to stdout is captured and shown in the run's chat as the step's output, which makes `print()` a quick way to surface progress or debugging info.
## Building Complex Workflows
### Variable References
Pass data between steps using variable syntax:
Reference output from a previous step:
```
My data is {{$outputVariable}}
```
Access specific array elements:
```
Process item {{$items[$i]}}
```
Access object properties:
```
The user name is {{$user["name"]}}
```
## Example Workflow
Here's a complete example of a multi-step agent workflow:
**Prompt:** `"Search for software engineering jobs in Berkeley and extract the top 5 listings"`
**Output Variables:** `$jobListings`
The agent searches job boards and extracts structured data.
**Prompt:** `"For each job in {{$jobListings}}, analyze the requirements and identify required skills"`
**Output Variables:** `$skillsAnalysis`
The agent processes each job listing and extracts skills.
**Prompt:** `"Create a summary report of {{$skillsAnalysis}} showing the most common skills required"`
**Output Variables:** `$finalReport`
The agent synthesizes the data into a final report.
## Best Practices
Write specific, actionable prompts that clearly state the expected outcome for each step
Use output variables to extract structured data that subsequent steps can reliably reference
Break complex tasks into smaller, focused steps for better reliability and debugging
Use "Clear chat history" strategically to prevent context overload in long workflows
## Common Use Cases
Build workflows that search multiple sources, collect data, analyze findings, and generate comprehensive reports.
**Example steps:**
1. Search academic databases for papers
2. Extract key findings from each paper
3. Synthesize findings into a summary report
Create workflows that gather information from various sources and fill out complex forms across multiple pages.
**Example steps:**
1. Extract data from uploaded documents
2. Navigate to form website
3. Fill out multi-page form with extracted data
4. Verify submission and capture confirmation
Build pipelines that fetch data, transform it, validate it, and output it in a specific format.
**Example steps:**
1. Fetch data from API using MCP tool
2. Transform and clean data
3. Validate against business rules
4. Export to desired format
Create workflows that analyze information, make decisions based on criteria, and take appropriate actions.
**Example steps:**
1. Gather relevant data
2. Analyze against decision criteria
3. Branch based on analysis (using If conditions)
4. Execute appropriate action
## Tips for Success
**Test incrementally**: Build your workflow step by step, testing each stage before adding the next. This makes debugging much easier.
**Use descriptive variable names**: Name your output variables clearly (e.g., `$customerData`, `$searchResults`) so they're easy to reference later.
**Leverage MCP servers**: For common operations (API calls, data transformations), create reusable tools in MCP servers instead of rebuilding them in each workflow.
# Authentication
Source: https://docs.narada.ai/documentation/authentication
Learn how to obtain and configure your Narada API Key
## Authentication with the Narada SDK
The Narada Python SDK uses API key authentication to securely access the Narada platform.
The SDK automatically manages browser windows and extension communication,
eliminating the need for manual Browser Window ID management.
## API Key
An API Key is required to authenticate your requests to the Narada platform. This key identifies your account and authorizes access to all SDK functionality.
### How to Obtain Your API Key
Go to the Narada billing page to create an API key:
Click on **Create API Key** and follow the prompts.
The SDK automatically reads your API key from the `NARADA_API_KEY` environment variable. Set it up using one of these methods:
**Set the environment variable** (recommended):
```bash theme={null}
export NARADA_API_KEY=your-api-key-here
```
The SDK will automatically detect and use this environment variable.
**Pass directly to the SDK** (for testing):
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment(api_key="your-api-key-here")
agent = Agent(environment=env)
try:
response = await agent.run(prompt="Say hello from Narada.")
print(response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
This method is not recommended for production use. Use environment variables instead.
# Cloud Browser Sessions
Source: https://docs.narada.ai/documentation/cloud-browser-sessions
Run browser automation tasks in serverless cloud browsers without a local Chrome installation
## What are Cloud Browser Sessions?
Cloud Browser Sessions let you run Narada automation tasks in **serverless, cloud-hosted browsers** without a local Chrome installation or Narada extension. This is ideal for server-side automation, CI/CD pipelines, and running tasks at scale.
Run tasks from any server or script without a desktop browser
Spin up multiple cloud browsers in parallel for high-throughput automation
Sessions stay alive until you close them or they time out
## Getting Started with the SDK
Use `CloudBrowserEnvironment` to create a hosted browser session, then bind an `Agent` to that environment:
```python theme={null}
import asyncio
from narada import Agent, CloudBrowserEnvironment
async def main() -> None:
env = CloudBrowserEnvironment(
session_name="my-research-session",
session_timeout=3600,
)
agent = Agent(environment=env)
try:
response = await agent.run(
prompt=(
'Search for "LLM Compiler" on Google and open the first arXiv paper '
"on the results page, then tell me who the authors are."
)
)
print("Response:", response.text)
print("Session ID:", env.cloud_browser_session_id)
print("Window ID:", env.browser_window_id)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
`Agent.run()`, `go_to_url()`, `get_screenshot()`, structured output, GIF recording, MCP servers, and input variables work the same way on cloud browser environments as they do on local browser environments.
## Session Lifecycle
Cloud browser sessions have a different lifecycle than local browser windows:
Construct `CloudBrowserEnvironment(session_name=..., session_timeout=...)`.
Create `Agent(environment=env)` and call `agent.run()`, `agent.go_to_url()`, `agent.get_screenshot()`, or other agent methods.
After the environment initializes, read `env.cloud_browser_session_id` and `env.browser_window_id` if you need to reconnect later.
Use `RemoteBrowserEnvironment` with the saved IDs to reconnect to a running cloud browser session.
Call `await env.close()` when you're done. If you reconnect with `RemoteBrowserEnvironment`, calling `close()` on that environment stops the cloud session.
**Cost Warning**: Cloud browser sessions accrue costs until they are stopped or time out. Always close sessions when you're done to avoid unexpected charges.
## Reconnect to a Cloud Session
Save the session identifiers after creating a cloud browser:
```python theme={null}
env = CloudBrowserEnvironment(session_name="data-extraction")
agent = Agent(environment=env)
await env.start()
session_id = env.cloud_browser_session_id
window_id = env.browser_window_id
print(f"Session: {session_id}, Window: {window_id}")
await agent.run(prompt="Navigate to example.com and extract the main heading")
# Intentionally leave the cloud session running so you can reconnect later.
# Call env.close() after the final reconnecting script is done.
```
Reconnect later with `RemoteBrowserEnvironment`:
```python theme={null}
import asyncio
from narada import Agent, RemoteBrowserEnvironment
async def main() -> None:
env = RemoteBrowserEnvironment(
browser_window_id="saved-browser-window-id",
cloud_browser_session_id="saved-cloud-session-id",
)
agent = Agent(environment=env)
try:
response = await agent.run(prompt="What page am I currently on?")
print(response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
Closing a `RemoteBrowserEnvironment` with `cloud_browser_session_id` stops the backing cloud session.
## Download Files from a Session
Use `get_downloaded_files()` on a cloud-backed environment:
```python theme={null}
downloaded_files = await env.get_downloaded_files()
for item in downloaded_files:
print(item.file_name, item.size, item.download_url)
```
## Using Cloud Browsers via the REST API
You can also use cloud browsers through the [Remote Dispatch API](/api-reference/remote-dispatch) by setting `executionMode` to `"cloud_browser"`:
```bash theme={null}
curl -X POST 'https://api.narada.ai/fast/v2/remote-dispatch' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"prompt": "/Operator navigate to example.com and extract the heading",
"executionMode": "cloud_browser",
"cloudBrowserSessionName": "my-api-session"
}'
```
When using `executionMode: "cloud_browser"`, you do **not** need to provide a `browserWindowId`. A fresh cloud browser session is created automatically.
## Execution Modes Compared
The Remote Dispatch API supports three execution modes:
| Mode | Description | Requires Extension? | Requires browserWindowId? |
| ----------------- | ------------------------------------------------- | ------------------- | ------------------------- |
| `"client"` | Runs in a local browser with the Narada extension | Yes | Yes |
| `"cloud"` | Runs server-side without a browser | No | No |
| `"cloud_browser"` | Creates a cloud-hosted browser session | No | No |
## Managing Sessions in the Dashboard
You can view and manage your cloud browser sessions from the Narada web dashboard:
* **View active sessions** with their status, name, and creation time
* **Watch live sessions** via the session player
* **Replay completed sessions** to review what the agent did
* **Download action traces** as JSON for debugging
* **Download files** that were saved during the session
* **Stop sessions** that are no longer needed
## Best Practices
Call `await env.close()` when done to stop billing. Do not rely only on timeout.
Use `session_timeout` to auto-expire sessions you might forget to close manually.
Use `session_name` to label sessions so you can identify them in the dashboard.
Cloud browsers are suited for automated pipelines with no desktop browser or extension.
## Troubleshooting
* Verify your API key is valid and has sufficient credits
* Check your internet connection
* Contact support if the issue persists
* Increase `session_timeout` when creating the session
* Ensure your tasks complete within the timeout window
* For long-running tasks, break them into smaller steps
* Verify the session has not been stopped or timed out
* Check that you're using the correct `cloud_browser_session_id` and `browser_window_id`
* Sessions that have expired cannot be reconnected
# Cursor Integration
Source: https://docs.narada.ai/documentation/cursor-integration
Use Cursor IDE to write Narada Python SDK code and build custom agents with AI-powered assistance
# Cursor Integration
Cursor is an AI-powered code editor that can reference external documentation to provide context-aware assistance. By integrating Narada's documentation with Cursor, you can efficiently write Python code using the Narada SDK to build custom agents and web automation workflows.
## Adding Narada Documentation to Cursor
Navigate to **Cursor Settings** -> **Docs** in Cursor IDE.
Click **Add Doc** to add a new documentation source.
Enter the following details:
* **Name**: `Narada`
* **Prefix**: `https://docs.narada.ai`
* **Entrypoint**: `https://docs.narada.ai`
Cursor crawls and indexes the Narada documentation site.
Once indexed, reference Narada documentation with the `@` command, such as `@Narada`, in Cursor chat or inline edits.
## Adding Cursor Rules for Better Development
Cursor rules provide persistent project context so generated SDK code follows your team's current Narada patterns.
### Set Up Project Rules
1. Create a `.cursor/rules/` directory in your project.
2. Add a `.mdc` file, such as `narada-cursor-rules.mdc`.
3. Copy the rules from the next section.
4. Set `alwaysApply: true` so Cursor applies the rules automatically.
## Cursor Rules for Narada Development
Use this as a starting point for a project rule:
````mdx wrap expandable theme={null}
---
alwaysApply: true
---
# Rules for Narada Development
You are an AI coding assistant helping developers build web automation workflows and custom agents with the Narada Python SDK.
## Current SDK Model
Narada uses an environment + agent design:
- Create an execution target with an environment, such as `BrowserEnvironment`, `CloudBrowserEnvironment`, `RemoteBrowserEnvironment`, or `LambdaEnvironment`.
- Create an `Agent(environment=env, kind=...)`.
- Call `await agent.run(prompt=...)` for natural-language tasks.
- Call browser action helpers on `agent`, such as `go_to_url()`, `agentic_selector()`, `read_google_sheet()`, and `print_message()`.
- Use the environment for lifecycle and IDs, such as `await env.start()`, `env.browser_window_id`, `env.cloud_browser_session_id`, and `await env.close()`.
Do not use older APIs such as `Narada()`, `open_and_initialize_browser_window()`, `LocalBrowserWindow`, `RemoteBrowserWindow`, or `window.agent()`.
## Standalone SDK Script Pattern
```python
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(
prompt="Search for Narada AI and summarize the first result.",
)
print(response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Agent Studio Python Agent Pattern
In Agent Studio, `BrowserEnvironment()` targets the current browser window through the runtime environment.
```python
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env)
await agent.go_to_url(url="https://example.com", new_tab=True)
response = await agent.run(
prompt="Extract the main heading from this page.",
)
print(response.text)
```
Use `new_tab=True` for the first navigation in Agent Studio so the workflow runtime tab stays separate from the page being automated.
## Agent Kinds
Choose the agent kind when constructing the agent:
```python
from narada import Agent, AgentKind, BrowserEnvironment
env = BrowserEnvironment()
operator = Agent(environment=env) # Defaults to AgentKind.OPERATOR
core_agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
custom_agent = Agent(environment=env, kind="/$USER/my-custom-agent")
```
Use `AgentKind.OPERATOR` for browser automation. Use `AgentKind.CORE_AGENT` for read-only reasoning, page summaries, and conversation-style tasks. Use a namespaced string for custom agents from Agent Studio.
## Structured Output
Use Pydantic models with `output_schema` for reliable extraction:
```python
from pydantic import BaseModel, Field
class CompanyInfo(BaseModel):
name: str = Field(description="Company name")
valuation: str = Field(description="Latest known valuation")
source_url: str = Field(description="URL where the valuation was found")
response = await agent.run(
prompt="Extract company valuation data from this page.",
output_schema=CompanyInfo,
)
company = response.structured_output
assert company is not None
```
Prefer `response.text` for text responses and `response.structured_output` for typed responses.
## Navigation and Browser Actions
Use agent methods for browser actions:
```python
await agent.go_to_url(url="https://example.com/products", new_tab=True)
await agent.agentic_selector(
action={"type": "click"},
selectors={"aria_label": "Search"},
fallback_operator_query="click the search button",
)
current_url = await agent.get_url()
print(current_url.url)
```
Prefer resilient selectors such as ARIA labels, `data_testid`, roles, names, and stable text. Always include a clear fallback query.
## Attachments
Pass file-like objects directly to `Agent.run()`. The SDK uploads them automatically.
```python
from narada import AgentKind
document_agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
with open("financial_report.pdf", "rb") as f:
response = await document_agent.run(
prompt="Summarize the key financial metrics from this report.",
attachment=f,
)
```
Do not call a separate public `upload_file()` method.
## Google Sheets
Read from and write to Google Sheets with agent methods:
```python
sheet_data = await agent.read_google_sheet(
spreadsheet_id="your_sheet_id",
range="Companies!A1:C10",
)
results = []
for row in sheet_data.values:
company_name = row[0]
response = await agent.run(
prompt=f"Find the latest valuation for {company_name}.",
)
results.append([company_name, response.text])
await agent.write_google_sheet(
spreadsheet_id="your_sheet_id",
range="Results!A1:B5",
values=results,
)
```
## Progress Updates
Use `print_message()` for user-visible progress in the Narada side panel:
```python
await agent.print_message(message="Starting data extraction...")
await agent.print_message(message=f"Processed {len(results)} companies")
```
## Error Handling
Handle timeouts and reset agent state before retrying:
```python
from narada import NaradaError, NaradaTimeoutError
try:
response = await agent.run(
prompt="Complete a complex task on this slow page.",
timeout=60,
)
except NaradaTimeoutError:
await agent.reset_agent_state()
response = await agent.run(prompt="Try a simpler version of the task.")
except NaradaError as exc:
print(f"Narada failed: {exc}")
```
## Parallel Execution
Create one environment per independent browser task:
```python
import asyncio
from narada import Agent, BrowserEnvironment
async def analyze_company(company_url: str):
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
await agent.go_to_url(url=company_url, new_tab=True)
return await agent.run(prompt="Extract company valuation data.")
finally:
await env.close()
results = await asyncio.gather(*[
analyze_company(url) for url in company_urls
])
```
## Documentation Workflow
Before writing Narada code:
1. Read the relevant `@Narada` documentation page.
2. Check the current method signature and examples.
3. Plan the environment, agent kind, browser actions, response shape, and cleanup.
4. Implement with `try` / `finally` so environments close cleanly.
````
## Building Custom Agents with Cursor
Once integrated, Cursor can help you build Narada agents with the current SDK patterns:
* **Smart code completion**: Get suggestions for Narada SDK methods and parameters.
* **Context-aware assistance**: Ask questions about Narada APIs and get documentation-backed answers.
* **Code generation**: Generate boilerplate for common environment, agent, selector, sheet, and structured-output workflows.
* **Error prevention**: Avoid stale window-based SDK patterns and missing cleanup.
### Example Development Workflow
When building custom agents with the Narada Python SDK, you can ask Cursor:
* "How do I create an agent that extracts data from a website and saves it to Google Sheets?"
* "Show me how to use `agentic_selector()` with fallback handling."
* "Why is my `go_to_url()` call timing out?"
* "How can I make this agent more reliable with retry logic?"
Cursor will reference the indexed Narada documentation to provide accurate, current answers and code suggestions.
Add the Narada documentation to Cursor before relying on generated SDK code. Without indexed docs, Cursor may suggest outdated APIs.
# Custom Instructions
Source: https://docs.narada.ai/documentation/custom-instructions
Provide personal preferences and instructions for Narada to follow
# Custom Instructions
**Custom Instructions** allow users to provide personal preferences or general instructions that Narada takes into account in all of its actions. Tell Narada a little bit about how you do things and Narada will produce results that are quickly tailored to your needs!
## Creating Your Custom Instruction
Open your **Custom Instructions** by navigating to the chat icon at the bottom of your Narada interface.
Tell Narada things to keep in mind when helping you. Be specific about your preferences, work style, and common requirements.
Save your custom instructions to have Narada apply them to all future interactions.
Your custom instructions will now influence all of Narada's responses and actions.
## Example Custom Instructions
**Professional Settings**
```
I prefer morning meetings. I'm based in Berkeley, CA.
My Zoom link is https://zoom.us/j/123.
I work in Pacific Time and prefer concise communication.
```
**Personal Style**
```
I prefer bullet points over long paragraphs.
Always include action items at the end of meeting summaries.
Use professional tone for external communications.
```
**Tools and Platforms**
```
I use Google Workspace for all documents.
Prefer Slack for team communications.
Save all meeting recordings to my "Meetings" folder in Drive.
```
Remember the 200-character limit! Focus on the most important preferences that
will have the biggest impact on your daily interactions with Narada.
## Best Practices
Put all essential information in a few structured sentences
Iterate on your instructions to get optimal results
Update your instructions as your preferences change
Include specific examples of how you want things done
## What to Include in Custom Instructions
* Your time zone and location
* Preferred meeting times
* Working hours and availability
* Holiday schedules or time off
* Preferred communication style (formal, casual, concise) - How you like
information presented (bullet points, paragraphs) - Tone preferences for
different types of communications - Language or terminology preferences
* Preferred productivity tools (Google Workspace, Microsoft 365) -
Communication platforms (Slack, Teams, Email) - File storage preferences -
Meeting platforms and personal links
* How you like meetings summarized
* Action item formatting preferences
* Document organization preferences
* Follow-up preferences
## Examples of Effective Instructions
**Well-Rounded Instructions**
```
Pacific Time, prefer 9-11 AM meetings. Use Google Drive for files,
Slack for team chat. Format action items as numbered lists.
Based in San Francisco.
```
**Manager Focus**
```
Always include next steps in meeting summaries.
Prefer weekly 1:1s on Tuesdays. Copy team@company.com
on project updates. Keep emails concise.
```
**Developer Preferences**
```
Save code reviews to GitHub. Prefer async communication.
Working hours 10 AM - 6 PM PST. Use Jira for tracking.
Technical docs in Confluence.
```
***
Thank you for diving into the **Custom Instructions** feature documentation. We hope this guide empowers you to make the most out of this tool, ensuring outputs that are tailored to your needs. Should you have any further questions or concerns, our support team is always here to help.
# Document Analyzer
Source: https://docs.narada.ai/documentation/document-analyzer
Query and extract information from documents using natural language
# Document Analyzer
**Document Analyzer** is a powerful tool that enables you to query and extract information from documents. Whether you're looking to summarize content, extract specific data points, or understand complex material, Narada's analyzer can interpret your natural language instructions to provide the insights you need.
## Overview
Narada's Document Analyzer is a powerful tool that enables you to query and extract information from documents. Whether you're looking to summarize content, extract specific data points, or understand complex material, Narada's analyzer can interpret your natural language instructions to provide the insights you need.
## Using the Document Analyzer
Provide the document you want to be analyzed by clicking the attachment button on the bottom left corner of the chat window.
Multiple document formats are supported for comprehensive analysis.
Ask specific questions or make requests about the document using natural language.
Narada will process your request using its document analyzer capabilities and provide you with the information or summary you asked for.
## Supported Document Types
**Text-Based Files**
* **PDF files** - Documents, reports, manuals
* **Word documents (.docx)** - Proposals, contracts, letters
* **Text files (.txt)** - Plain text documents, notes
* **PowerPoint presentations (.pptx)** - Slides, presentations, proposals
**Data Files**
* **Excel files (.xlsx)** - Spreadsheets, financial data, lists
* **CSV files** - Data exports, contact lists, analytics
* **TIFF files** - Scanned documents, technical drawings
**Visual Documents**
* **Image files** (JPG, PNG, GIF) - Screenshots, charts, diagrams
* **Scanned documents** - Paper documents converted to images
* **Charts and graphs** - Visual data representations
## Analysis Capabilities
Generate comprehensive summaries of long documents
Extract specific information, dates, numbers, and key facts
Answer specific questions about document content
Understand tables, charts, and structured data within documents
## Query Examples
**Document Summaries**
```
"Summarize the main points of this report"
```
```
"What are the key findings in this research paper?"
```
```
"Give me an executive summary of this proposal"
```
**Specific Information**
```
"Extract all dates and events mentioned in this document"
```
```
"Find all contact information in this file"
```
```
"List all financial figures mentioned"
```
**Targeted Queries**
```
"What are the risks identified in this contract?"
```
```
"How does this year's performance compare to last year?"
```
```
"What recommendations are made in the conclusion?"
```
## Advanced Features
**Analyzing Multiple Documents**
Upload multiple related documents to:
* Compare information across documents
* Find connections and relationships
* Generate comprehensive insights from multiple sources
* Identify discrepancies or contradictions
**Understanding Document Structure**
Narada recognizes and interprets:
* Tables and spreadsheet data
* Charts and graphs
* Headers and sections
* Lists and bullet points
* Footnotes and references
**Intelligent Understanding**
The analyzer provides:
* Context-sensitive interpretations
* Understanding of domain-specific terminology
* Recognition of document types and purposes
* Appropriate response formatting based on query type
## Document Size and Limitations
Narada can handle documents up to 20MB in size. For optimal performance,
consider breaking very large documents into smaller sections if detailed
analysis is needed.
Maximum file size: **20MB per document**
Larger documents may take longer to analyze - typically a few seconds to
minutes
More specific queries yield better, more targeted results
## Best Practices
Ask specific questions for more accurate and relevant responses
Higher quality documents (clear text, good scans) produce better results
Provide context about what you're looking for to get targeted insights
Start with broad questions, then drill down into specific details
## FAQs and Troubleshooting
**Q: Can Narada analyze any document type?**
A: Narada can analyze .csv, .docx, .pdf, .pptx, .txt, .tiff, .xlsx, and any image files.
**Q: Is there a limit to the size of the document Narada can analyze?**
A: Narada can handle documents up to 20MB in size.
**Q: How specific do my queries need to be?**
A: The more specific your query, the better Narada can provide accurate and relevant information. Vague queries may lead to broad results, so clarity is key.
**Q: How is my document data handled?**
A: Documents are processed securely and are not stored after analysis. Narada maintains strict privacy and security standards for all uploaded content.
If your question isn't covered here, our support team at
[support@narada.ai](mailto:support@narada.ai) is always eager to help.
***
Narada's Document Analyzer is designed to enhance your productivity and understanding by providing deep insights into your documents. If you encounter any issues or have further questions, please don't hesitate to reach out. We're here to ensure your experience is as efficient and informative as possible.
# How to Download Files
Source: https://docs.narada.ai/documentation/downloading
Learn how to download files and documents using the Narada Python SDK
## Download Files with Narada SDK
The Narada Python SDK makes it easy to download files and documents during automation tasks. When you ask Narada to download content, it automatically saves files to your local Downloads folder with an organized directory structure.
## How to Download Files with the SDK
Use the SDK to download files with a simple command:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Download the current page
response = await agent.run(
prompt='download the current page'
)
print("Download completed!")
print("Response:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
The SDK automatically handles file organization and download management for you.
Optionally record the download process as a GIF for debugging:
```python highlight={11} theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Download with GIF recording
response = await agent.run(
prompt='navigate to the research paper and download the PDF',
generate_gif=True # Record the download process
)
print("Download completed with GIF recording!")
print("Response:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
The GIF recording helps you see exactly how the download process worked.
Narada automatically creates a directory structure for your downloads:
1. **Main Directory**: `Narada Downloads` in your machine's Downloads folder
2. **Request Directory**: Directory named after your `request_id` from the [SDK response](/documentation/getting-started-with-sdk)
3. **File**: Downloaded content saved as a `.pdf` file
If the `Narada Downloads` directory doesn't exist, it will be created automatically.
Navigate to your Downloads folder and look for:
```
Downloads/
└── Narada Downloads/
└── req_abc123def456/
└── downloaded_page.pdf
```
Each download request gets its own directory named after the `request_id` for easy correlation with your SDK calls.
## Download Organization
All downloaded pages and documents are saved as PDF files for universal
compatibility.
Each download directory is named after the `request_id` from the [SDK response](/documentation/getting-started-with-sdk), allowing you to easily track which SDK request generated which files.
Files are automatically named based on the page title or content type.
## Example Use Cases
* **Research**: Download academic papers or articles for offline reading
* **Documentation**: Save important web pages for reference
* **Archiving**: Create local copies of web content for backup
* **Processing**: Download files for further automated processing
You can download any page that's currently visible in your browser. Just make
sure the page has fully loaded before sending the download command.
# Error Handling
Source: https://docs.narada.ai/documentation/error-handling
Handle Narada SDK errors and exceptions
## Error Types
The Narada SDK raises specific exceptions for different error conditions. All errors inherit from `NaradaError`.
```python theme={null}
from narada import (
NaradaError,
NaradaTimeoutError,
NaradaUnsupportedBrowserError,
NaradaExtensionMissingError,
NaradaExtensionUnauthenticatedError,
NaradaInitializationError
)
```
## Error Handling Examples
Look at the following examples to see how to handle different errors.
Raised when operations exceed timeout limits.
```python theme={null}
from narada import NaradaTimeoutError
try:
response = await agent.run(
prompt='find a specific element',
timeout=30
)
except NaradaTimeoutError:
print("Operation timed out. Try increasing timeout or simplifying the task.")
```
Raised when using an unsupported browser.
```python theme={null}
from narada import BrowserEnvironment, NaradaUnsupportedBrowserError
env = BrowserEnvironment()
try:
await env.start()
except NaradaUnsupportedBrowserError:
print("Please use Chrome browser. Other browsers are not supported.")
finally:
await env.close()
```
Raised when the Chrome extension is not installed.
```python theme={null}
from narada import BrowserEnvironment, NaradaExtensionMissingError
env = BrowserEnvironment()
try:
await env.start()
except NaradaExtensionMissingError:
print("Narada extension not found. Please install it from the Chrome Web Store.")
finally:
await env.close()
```
Raised when the extension is not signed in.
```python theme={null}
from narada import BrowserEnvironment, NaradaExtensionUnauthenticatedError
env = BrowserEnvironment()
try:
await env.start()
except NaradaExtensionUnauthenticatedError:
print("Please sign in to your Narada account in the Chrome extension.")
finally:
await env.close()
```
Raised when SDK initialization fails.
```python theme={null}
from narada import BrowserEnvironment, NaradaInitializationError
env = BrowserEnvironment()
try:
await env.start()
except NaradaInitializationError:
print("Failed to initialize SDK. Check your API key and internet connection.")
finally:
await env.close()
```
## Retry Pattern
For transient errors, implement retry logic:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment, NaradaTimeoutError
async def retry_operation(agent, prompt, max_retries=3):
for attempt in range(max_retries):
try:
return await agent.run(prompt=prompt)
except NaradaTimeoutError:
if attempt == max_retries - 1:
raise
await agent.reset_agent_state()
await asyncio.sleep(2 ** attempt) # Exponential backoff
async def main():
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await retry_operation(
agent,
'search for "python tutorial"'
)
print(response.text)
finally:
await env.close()
asyncio.run(main())
```
## Best Practices
Handle specific error types rather than generic exceptions
Use retry logic for timeout and initialization errors
Log errors with context for debugging
Design fallback behavior when automation fails
Most errors are recoverable. Check the error message and implement appropriate retry or fallback logic.
# Getting Started with the SDK
Source: https://docs.narada.ai/documentation/getting-started-with-sdk
Step-by-step guide to using the Narada Python SDK with practical examples
You should have completed the [Installation](/documentation/installation) and
[Authentication](/documentation/authentication) sections before proceeding. If
you haven't, please complete them first.
## How the Narada SDK Works
The Narada Python SDK uses two core objects:
An environment is the execution target. Use `BrowserEnvironment` for a local browser, `CloudBrowserEnvironment` for a hosted browser, `RemoteBrowserEnvironment` for an existing browser window, or `LambdaEnvironment` for cloud execution without browser actions.
An agent runs tasks inside an environment. Create `Agent(environment=env)` and call `await agent.run(...)` for natural-language automation.
Browser actions such as `go_to_url()`, `agentic_selector()`, and Google Sheets helpers are methods on `Agent`. Lifecycle details such as `close()`, `browser_window_id`, and `cloud_browser_session_id` are methods or properties on the environment.
You do **not** need to prefix prompts with `/Operator` when using `Agent.run()`. The default agent kind is `AgentKind.OPERATOR`, which adds the correct command prefix for browser automation.
## Demo: Narada SDK in Action
Watch this demonstration of the complete SDK workflow:
This video demonstrates the complete workflow from initializing the SDK, through browser execution, to receiving results in Python.
## Your First SDK Automation
Start with a simple local browser automation:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(
prompt='Search for "software engineering jobs" on Google and tell me how many results you find',
)
print("Automation completed!")
print("Response:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
The first time you run a local browser environment, the SDK opens Chrome and guides you through any missing extension or login setup. The environment initializes lazily on the first `Agent.run()` call or browser action.
## Eagerly Initialize the Browser
You can call `await env.start()` when you need the browser window ID before running a task:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
await env.start()
print(f"Browser window ID: {env.browser_window_id}")
response = await agent.run(
prompt='Search for "Narada AI" and summarize the first result',
generate_gif=True,
clear_chat=True,
)
print("Response:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
When `generate_gif=True`, Narada records the automation trajectory. Read more in [Action Trace](/documentation/action-trace).
## Structured Output
Use a Pydantic model when you need typed data instead of free-form text:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
from pydantic import BaseModel, Field
class JobSearchResult(BaseModel):
total_jobs: int = Field(description="Total number of job listings found")
location: str = Field(description="The location searched")
top_company: str = Field(description="Name of the first company in results")
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(
prompt=(
'Search for "software engineering jobs in Berkeley" and extract '
"the job count, location, and first company name."
),
output_schema=JobSearchResult,
generate_gif=True,
)
job_data = response.structured_output
assert job_data is not None
print(f"Found {job_data.total_jobs} jobs in {job_data.location}")
print(f"Top company: {job_data.top_company}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Choose an Agent Kind
The default `Agent` uses `AgentKind.OPERATOR`, which is best for browser automation. Use `AgentKind.CORE_AGENT` for read-only reasoning and conversation-style tasks.
```python theme={null}
import asyncio
from narada import Agent, AgentKind, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
operator = Agent(environment=env)
core_agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
try:
await operator.go_to_url(url="https://example.com")
page_summary = await core_agent.run(
prompt="Summarize the visible content on this page.",
)
print(page_summary.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Error Handling
The SDK raises typed exceptions for common failures:
```python theme={null}
import asyncio
from narada import (
Agent,
BrowserEnvironment,
NaradaInitializationError,
NaradaTimeoutError,
)
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
response = await agent.run(
prompt='Search for "AI research papers" and summarize the first result',
timeout=60,
)
print("Response:", response.text)
except NaradaTimeoutError:
print("Task timed out. Try a simpler prompt or a longer timeout.")
await agent.reset_agent_state()
except NaradaInitializationError:
print("Browser initialization failed. Check your extension setup.")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Troubleshooting
1. Verify `NARADA_API_KEY` is set.
2. Make sure Chrome is installed and up to date.
3. Install and sign in to the Narada Chrome extension.
4. Check your internet connection and firewall settings.
1. Make prompts clear and specific.
2. Increase `timeout` for complex tasks.
3. Wait for slow pages to load before asking the agent to inspect them.
4. Call `await agent.reset_agent_state()` after a timeout to cancel inflight work while preserving browser pages.
## What's Next?
Run multiple browser environments simultaneously
Handle timeouts, retries, and initialization errors
Extract typed data using Pydantic models
Capture and analyze automation workflows
## Need Help?
Explore complete SDK examples
Contact support for assistance
# Human-in-the-Loop Steps
Source: https://docs.narada.ai/documentation/human-in-the-loop
Ask a user for approval or workflow variable values from a Narada Python agent
Human-in-the-loop steps let a Python agent pause at a controlled point, show a small card in the Narada browser UI, and continue after a person responds. Use them when an automation needs an explicit approval decision or values that should come from a human during the run.
Ask the user to approve or reject before the workflow continues. The step returns `True` for the approve button and `False` for the reject button.
Ask the user to fill one or more workflow variables. The step returns a dictionary keyed by variable name.
## When to Use These Steps
Use `user_approval` for checkpoints where the automation already has enough context, but should not continue without explicit consent. Examples include sending an email, submitting a form, approving a purchase, or making a destructive update.
Use `prompt_for_user_input` when the automation needs structured values before it can continue. Examples include asking for a date range, choosing an enum option, collecting a number, or asking for a small table of rows.
These steps require a Narada browser context that can show the human interaction UI.
## User Approval
Call `agent.user_approval()` when the workflow needs a yes/no decision before continuing. You provide the prompt text and the labels for the approve and reject buttons.
The reject button is a normal response and returns `False`. A cancellation or aborted interaction raises `UserAbortedError`, so handle that separately from an explicit rejection.
See the [`user_approval` API reference](/api-reference/user-approval) for the full signature and example.
## Prompt for User Input
Call `agent.prompt_for_user_input()` when the workflow needs structured values from the user. You provide a list of variable definitions, and the method returns a dictionary containing the values the user submitted.
Supported variable types are `string`, `number`, `boolean`, `enum`, `dataTable`, `object`, and `array`. Optional variables may be omitted from the returned dictionary if the user leaves them blank.
For `dataTable` values, Narada serializes the table as:
```json theme={null}
{
"columnNames": ["Name", "Email"],
"rows": [
["Ada Lovelace", "ada@example.com"],
["Grace Hopper", "grace@example.com"]
]
}
```
See the [`prompt_for_user_input` API reference](/api-reference/prompt-for-user-input) for the full signature, variable schema, and example.
## Best Practices
Set `step_id` to a stable, descriptive value. This makes traces and debugging easier when the same agent has multiple human interaction steps.
Approval text should describe the exact action that will happen next. Avoid vague prompts like "Continue?" when the next step sends data, submits a form, or changes an external system.
Use `required=True` only when the workflow cannot continue without the value. Optional values can be omitted from the returned dictionary.
Treat cancellation as control flow. Catch `UserAbortedError` and exit or branch cleanly instead of reporting it as an unexpected failure.
## API Reference
* [`user_approval`](/api-reference/user-approval)
* [`prompt_for_user_input`](/api-reference/prompt-for-user-input)
# Imitation Learning
Source: https://docs.narada.ai/documentation/imitation-learning
Record your browser actions and narration to automatically generate custom agents
## What is Imitation Learning?
Imitation Learning lets you **show** Narada how to do something by recording your browser actions while narrating what you're doing. Narada watches your actions, listens to your narration, and automatically generates a reusable agent workflow.
Think of it as screen recording meets AI. You demonstrate a task once, and Narada creates an agent that can repeat it.
## How It Works
Perform the task in your browser while narrating what you're doing. Narada captures your clicks, typing, navigation, screenshots, and audio.
Your narration is transcribed, actions are analyzed, and element selectors are validated for robustness.
Narada generates a complete agent workflow from your recording that can be reviewed, edited, and reused.
## Getting Started
### Starting a Recording
You can start an Imitation Learning recording from the **Agent Studio**:
Navigate to the **Agent Studio** from the Narada sidebar.
Click the **+ Create** button to open the creation dialog.
Choose **Imitation learning** from the options. The description reads: "Record your actions to teach the agent by example."
You'll see an intro screen with tips for best results:
* **Narrate your actions** as you perform them
* **Go at a natural pace**
* **Speak clearly** to describe what you're doing
Click **Start Recording** when you're ready.
The Narada Chrome Extension must be installed for Imitation Learning to work. If it's not installed, you'll be prompted to install it.
### During Recording
Once recording starts, you'll see a recording interface in the Narada side panel:
* A **red pulsing indicator** shows that recording is active
* A **timer** tracks elapsed time (MM:SS format)
* An **audio waveform** visualizes your microphone input in real-time
* A **live workflow editor** shows captured steps as they're detected
**Controls available during recording:**
| Button | Action |
| ------------------ | ---------------------------------------------------- |
| **Pause** | Temporarily pause recording (indicator turns yellow) |
| **Resume** | Continue recording after a pause |
| **Stop Recording** | End the recording and start processing |
Recordings have a maximum duration of **5 minutes**. If you reach the limit, the recording will automatically stop and begin processing.
### What Gets Captured
During your recording, Narada captures:
Clicks, text input, keyboard events (Tab, Enter, Escape), and page navigation
Your voice narration is transcribed with timestamps for each segment
Visual snapshots of the page at key moments during your workflow
HTML snapshots and element selectors for robust action replay
### After Recording
When you click **Stop Recording**, Narada processes your recording through several stages:
Element selectors are validated and filtered for robustness. You'll see progress: "Filtering X/Y selectors..."
Your narration is transcribed into text with segment-level timestamps.
Narada analyzes the recording context (actions, narration, screenshots, and page structure) to generate a complete agent workflow. This usually takes a few minutes.
Once generation is complete, you'll see a success screen with an **Open Workflow** button. Click it to review and edit the generated workflow in the Agent Studio.
## Editing the Generated Workflow
The generated workflow opens in the Agent Studio editor where you can:
* **Review each step** to verify it matches your intent
* **Edit step instructions** to refine the automation
* **Add or remove steps** to customize the workflow
* **Test the workflow** by running it directly
The generated workflow uses robust element selectors with Operator agent fallback, so it can adapt to minor UI changes on the target website.
## Tips for Best Results
Describe what you're doing and **why** as you perform each action. The narration helps Narada understand your intent, not just the mechanics.
Don't rush through actions. Give Narada time to capture each step properly.
Record one specific workflow per session. Complex multi-part tasks are better split into separate recordings.
Ensure pages are fully loaded before interacting with them. Avoid recording during loading spinners.
## Troubleshooting
* Ensure the Narada Chrome Extension is installed and you're signed in
* Check that your browser has microphone permissions enabled for Narada
* Try refreshing the page and starting again
* Make sure you narrated clearly during recording. Narration significantly improves generation quality
* Try recording at a slower pace
* You can always edit the generated workflow in Agent Studio to fix specific steps
* Check your browser's microphone permissions
* Ensure your microphone is not muted at the OS level
* Try a different microphone if available
# Input Variables
Source: https://docs.narada.ai/documentation/input-variables
Pass reusable data into custom Narada agents
## Overview
Input variables let you run the same custom agent with different data each time. Instead of hardcoding a customer name, date range, table of rows, or configuration object into the agent, pass that value at run time and let the workflow read it as a variable.
Use input variables for data the agent is allowed to see. For credentials, API keys, passwords, and other sensitive values, use [Secrets and Secret Variables](/documentation/secrets-and-secure-variables) instead.
Runtime values for custom agents. The agent can read them, reason about them, and use them in workflow steps.
Sensitive values for prompt substitution and Agent Studio secret handles. Use `${variable_name}` for one-off SDK and REST secrets, or `{{$secretVariable}}` for secret variables in Agent Studio.
## The Default `chat_input` Variable
When you call a custom agent with a plain prompt, Narada stores that prompt in the `chat_input` input variable. The SDK examples on this page use a `BrowserEnvironment`; see [Getting Started with the SDK](/documentation/getting-started-with-sdk) for setup.
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env, kind="/$USER/research-assistant")
response = await agent.run(
prompt="Search for the latest AI research papers",
)
```
Inside the custom agent, the run includes:
```json theme={null}
{
"chat_input": "Search for the latest AI research papers"
}
```
Use `chat_input` for the main user instruction in new custom agents.
## Before You Pass Variables
Declare the non-`chat_input` variables that the custom agent expects before you pass them at run time. In Agent Studio, add the variable to the custom agent and choose its type so Narada can validate incoming values and show the variable in the builder. Python custom agents should declare every non-`chat_input` value they read, because the agent's `variables` dictionary is built from declared workflow variables.
The declared type also tells Narada how to validate and coerce incoming values. For example, a declared `number` must receive a number, a declared `enum` must receive one of its allowed values, and a declared `dataTable` can receive one of the supported table shapes below.
## Passing Multiple Variables
For direct SDK, REST API, and prompt-text custom-agent runs, there are two supported formats for passing more than `chat_input`. Use structured input variables for new SDK and REST API calls. Narada also supports the prompt text `inputVariables={...}` format for prompt-based runs.
### SDK and REST API
When calling a custom agent from the SDK or REST API, pass an object whose keys match the variable names in the custom agent. In the Python SDK, use `input_variables`. In the REST API body, use `inputVariables`.
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env, kind="/$USER/vendor-reviewer")
response = await agent.run(
prompt="Find vendors for the Q3 renewal cycle",
input_variables={
"region": "west",
"maxResults": 10,
"includeInactive": False,
},
)
```
If you include `chat_input` in `input_variables`, Narada uses that value as the main instruction. If you omit it, the `prompt` text becomes `chat_input`.
When calling the REST API directly, use the `inputVariables` body field:
```json theme={null}
{
"prompt": "/$USER/vendor-reviewer Find vendors for the Q3 renewal cycle",
"browserWindowId": "your-browser-window-id",
"inputVariables": {
"region": "west",
"maxResults": 10,
"includeInactive": false
}
}
```
### Prompt Text Format
Narada also supports putting variables directly in the prompt by appending `inputVariables={...}` with valid JSON. Prefer the structured `input_variables` argument or `inputVariables` REST body field when you can control the caller.
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env, kind="/$USER/vendor-reviewer")
response = await agent.run(
prompt="""
Find vendors for the Q3 renewal cycle
inputVariables={
"region": "west",
"maxResults": 10,
"includeInactive": false,
"tags": ["renewal", "priority"]
}
""",
)
```
The text before `inputVariables={...}` becomes `chat_input` unless the JSON object already includes `chat_input`. If the JSON object includes `chat_input`, put `inputVariables={...}` at the start of the prompt with no leading instruction text.
The prompt text format must be valid JSON from the opening `{` through the end of the prompt. Do not add comments, trailing commas, or extra text after the closing `}`.
## Supported Types
These are the common JSON-shaped input types for custom agents, including object and data table inputs. Agent Studio also has specialized variable types such as `file`, `image`, `cssSelectors`, and `secretString` for attachment, selector, and secret-variable workflows; this page focuses on values you pass as JSON or Python data structures. See [Secrets and Secret Variables](/documentation/secrets-and-secure-variables) for `secretString`.
```json theme={null}
{
"searchTerm": "machine learning"
}
```
Use strings for search terms, labels, URLs, email addresses, and other text.
```json theme={null}
{
"maxResults": 10,
"threshold": 0.85
}
```
Use numbers for limits, scores, counters, and numeric settings.
```json theme={null}
{
"includeInactive": false,
"strictMode": true
}
```
Use booleans for on/off options.
```json theme={null}
{
"priority": "high"
}
```
If the workflow variable is declared as an enum, pass one of the allowed string values.
```json theme={null}
{
"keywords": ["ai", "ml", "research"],
"scores": [0.8, 0.9, 0.7]
}
```
Use arrays for lists of values. Arrays can contain JSON values, including objects, when the workflow expects that shape.
```json theme={null}
{
"customer": {
"name": "Acme Corp",
"renewalDate": "2026-09-30",
"contacts": [
{"name": "Ari", "role": "Legal"},
{"name": "Mina", "role": "Finance"}
]
}
}
```
Use objects for grouped configuration or structured records. Nested objects and arrays are valid JSON input.
```json theme={null}
{
"vendors": {
"columnNames": ["Name", "Category", "Spend"],
"rows": [
["Northwind", "Logistics", "12000"],
["Contoso", "Software", "8500"]
]
}
}
```
Use a data table variable when the workflow needs rows and columns for spreadsheet-like steps.
### Data Table Shapes
A data table input is passed under the variable name, such as `input_variables={"vendors": }`. The table value itself can use either shape below.
A declared `dataTable` variable accepts either the canonical table shape. In this shape, `columnNames` and every cell in `rows` must be strings:
```json theme={null}
{
"columnNames": ["Name", "Category", "Spend"],
"rows": [
["Northwind", "Logistics", "12000"],
["Contoso", "Software", "8500"]
]
}
```
or an array of objects with the same columns in every row:
```json theme={null}
[
{"Name": "Northwind", "Category": "Logistics", "Spend": 12000},
{"Name": "Contoso", "Category": "Software", "Spend": 8500}
]
```
For the array-of-objects shape, every row must have the same keys. Cell values may be strings, numbers, booleans, or `null`; Narada converts accepted cells to strings in the table, and `null` becomes an empty cell. Missing keys, extra keys, nested objects, and nested arrays are rejected.
An empty array (`[]`) is also accepted and creates an empty table.
## Referencing Variables in Agent Studio
In GUI workflow steps, reference input variables with the same variable syntax used for other workflow variables:
```text theme={null}
Search for {{$searchTerm}}.
Return at most {{$maxResults}} results.
```
For objects and arrays, use bracket access:
```text theme={null}
Customer: {{$customer["name"]}}
Renewal date: {{$customer["renewalDate"]}}
First keyword: {{$keywords[0]}}
```
For data tables, use a row index and column name:
```text theme={null}
First vendor: {{$vendors[0]["Name"]}}
First vendor spend: {{$vendors[0]["Spend"]}}
```
For Python agent code, see [Python Agents](/documentation/python-agents#using-input-variables). For Python Code steps inside GUI workflows, see [Agent Studio](/documentation/agent-studio#passing-variables-in-and-out).
## Best Practices
Define each expected non-`chat_input` input variable on the custom agent so Narada can validate its type before the run starts and make it available in Studio and Python agent code.
Prefer descriptive names like `searchTerm`, `maxResults`, `customer`, and `vendors` over short names like `q`, `max`, or `data`.
If the agent has one primary user instruction plus supporting data, put the instruction in `chat_input` and keep the other keys focused on structured inputs.
Use [Secrets and Secret Variables](/documentation/secrets-and-secure-variables) for passwords, API keys, bearer tokens, and other values the agent should not treat as ordinary data.
When using `inputVariables={...}` in the prompt text, keep the JSON valid and do not add extra text after the closing `}`.
Use a data table for spreadsheet-like rows. Use an object when you need nested configuration, grouped fields, or a single structured record.
# Installation
Source: https://docs.narada.ai/documentation/installation
Install and set up the Narada Python SDK
## Step-by-Step Installation
Install the Narada Python SDK using pip:
```bash theme={null}
pip install narada
```
Create a Python script to open a browser window:
```python theme={null}
import asyncio
from narada import BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
try:
await env.start()
print("Browser window opened successfully!")
print(f"Window ID: {env.browser_window_id}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
Running this script opens Chrome and initializes a Narada browser environment.
When the initialization page opens, you'll need to install the Narada Extension:
Download from Chrome Web Store
1. Click **Install Extension** on the initialization page or use the link above
2. Install the Narada Extension from the Chrome Web Store and sign in to your Narada account
3. Pin the extension to your Chrome toolbar
The video shows how to pin the extension to your toolbar after installation and sign in to your Narada account.
Type a message like "Hello" in the Narada sidepane.
If you get a response, the extension is working correctly!
# Introduction
Source: https://docs.narada.ai/documentation/introduction
Get started with Narada's Python SDK for Agentic Process Automation
## Get Started Quickly
Ready to start using Narada? Follow these guides in order:
Install the Narada Python SDK and Chrome Extension
Configure your API key
Build your first automation
The Narada platform enables you to:
* **Automate browser tasks** with natural language commands via the [Python SDK](/documentation/getting-started-with-sdk) or [REST API](/api-reference/remote-dispatch)
* **Build custom agents** visually in [Agent Studio](/documentation/agent-studio) or generate them with [Agent Maker](/documentation/agent-maker) and [Imitation Learning](/documentation/imitation-learning)
* **Run in the cloud** with serverless [Cloud Browser Sessions](/documentation/cloud-browser-sessions), no local Chrome needed
* **Extract structured data** using Pydantic models and [JSON schemas](/documentation/structured-output)
* **Connect external tools** via [MCP servers](/documentation/mcp-builder) and the [A2A protocol](/documentation/a2a-and-mcp)
* **Integrate with your tools**: Gmail, Calendar, Slack, Zoom, and more via [Plugins](/documentation/plugins)
* **Run parallel tasks** across multiple browser windows
***
## Welcome to Narada
Narada AI is a new **agentic process automation platform** that uses Large Action Models to automate complex tools and workflows in the enterprise. Based on new research from the [UC Berkeley AI Lab](https://bair.berkeley.edu/), Narada takes natural language commands and uses agentic reasoning to plan, orchestrate, and execute workflows across siloed tools and ecosystems.
## What Makes Narada Different
### Agentic Process Automation
Narada generates automations on the fly via prompts or voice commands, working via UI agents that operate through the browser if APIs are limited or non-existent. Unlike hard-coded automations, Narada uses agentic reasoning to make automations robust and scalable, adapting and self-healing to constantly changing tool interfaces.
### UC Berkeley Research Foundation
Our technology is built on breakthrough research from UC Berkeley AI Lab:
* **[Plan and Act (ICML '25)](https://arxiv.org/abs/2503.09572)**: SOTA model for WebAgents using text-only, open source models
* **[LLMCompiler (ICML '24)](https://arxiv.org/abs/2312.04511)**: Integrated into [LangChain](https://blog.langchain.dev/llmcompiler/) and [LlamaIndex](https://docs.llamaindex.ai/en/stable/examples/agent/llm_compiler/)
* **[TinyAgent (EMNLP '24)](https://arxiv.org/abs/2409.00608)**: Joint work with Apple achieving GPT-4 accuracy with 1B parameter on-device model
* **[Tool2Vec](https://arxiv.org/abs/2409.02141)**: ToolRAG method for accurate tool retrieval at scale
### Enterprise-Ready
* **On-premises deployment** options for data privacy
* **SOC 2 Type 2 certified** with CASA TIER 2, CCPA, GDPR, and HIPAA compliance
* **No AI training** on private customer data
## Narada in Action
Watch Narada automate real enterprise workflows across different tools and use cases:
### Front-office Tool Orchestration
Complete overview of Narada's capabilities
See the Narada UI Agent in action
Automating Concur expense reports from Outlook
### Back-office Workflow Automation
Automated accounts receivable processing
Narada integration with Microsoft Power Automate
Back office IT tests for Workspace ONE
### Market Research & QA Testing
BMW pricing market research automation
Testing Rakuten deals against specifications
Testing online services with complex reasoning
# MCP Builder
Source: https://docs.narada.ai/documentation/mcp-builder
Create custom tools for your agents using the Model Context Protocol (MCP) builder
## What is the MCP Builder?
The MCP Builder allows you to create custom code tools that extend your agent's capabilities. Using the Model Context Protocol (MCP), you can define Python functions with specific input schemas that your agents can call during execution.
MCP (Model Context Protocol) is a standardized way to provide tools and
context to AI agents. Narada's MCP Builder makes it easy to create, manage,
and share custom tools without writing boilerplate code.
## How It Works
With the MCP Builder, you can create custom Python functions with defined input schemas that perform specific tasks—from API calls to data processing to complex computations. Add multiple tools to a single MCP server, each with its own name, description, and input schema. Once configured, copy the MCP URL and paste it into any agent step in the Agent Studio to give your agent access to all tools in that server.
Build Python functions with custom logic and input schemas
Copy the server URL to use across multiple agent workflows
Agents automatically gain access to all tools in the MCP server
## Video Demo
Watch this video to see a complete walkthrough of creating an MCP server, building a custom tool, attaching it to an agent, and running the agent:
## Creating a Code Tool
Each code tool consists of four main components:
A clear, descriptive name for your tool (e.g., `getWeather`, `sendEmail`, `calculateROI`). This is how the agent will reference the tool.
Explain what the tool does and when to use it. The agent uses this description
to determine when to call your tool, so be specific and clear.
Write descriptions from the agent's perspective. Instead of "This tool gets
weather", write "Use this tool to retrieve current weather conditions for a
specific location."
The actual function implementation. Your code should:
* Accept parameters defined in the Input Schema
* Return a string result that the agent can understand
* Handle errors gracefully
```python theme={null}
async def main(args: InputSchema) -> str:
# Your Python code here
return f"Result: {args.field_name}"
```
Define the parameters your tool accepts using Pydantic models. This ensures type safety and provides clear documentation for the agent.
```python theme={null}
class InputSchema(BaseModel):
name: str = Field(description="The name of the person")
age: int = Field(description="The person's age")
```
## Example: Building a Weather Tool
Here's a complete example of creating a weather lookup tool:
```python Python Code theme={null}
async def main(args: InputSchema) -> str:
import requests
# Call weather API
response = requests.get(
f"https://api.weather.com/v1/current",
params={"location": args.location}
)
data = response.json()
return f"Weather in {args.location}: {data['temp']}°F, {data['conditions']}"
```
```python Input Schema theme={null}
class InputSchema(BaseModel):
location: str = Field(description="City name or ZIP code")
```
**Tool Name:** `getWeather`
**Description:** Use this tool to retrieve current weather conditions for any location. Provide a city name or ZIP code.
## Using MCP Tools in Agent Studio
Once you've created your MCP server with custom tools, you can add it to any agent step in Agent Studio:
If you're using an MCP server built within Narada, click the **Copy URL** button in the top-right corner of the MCP Builder to copy the server URL to your clipboard.
The URL will be copied to your clipboard automatically. You'll paste this URL in the next step.
In any Agent step, find the **MCP servers** section and click the **Add MCP server** button.
In the popup dialog, enter the following information:
* **URL**: Paste the MCP server URL you copied (or enter the URL for external MCP servers) (required)
* **Label**: An optional label to identify the server
* **Description**: An optional description for the server
* **Authentication**: Select the authentication method if required (or leave as "None")
Only use MCP servers you trust and verify. Ensure the URL and authentication credentials are correct.
Click **Connect** to establish a connection to the MCP server.
You'll be shown a screen displaying all available tools from the MCP server. Select the tools you want to expose to the agent for this step.
If the URL or authentication is malformed, you'll see an error message instead of the tool selection screen. Verify your configuration and try again.
Click **Connect** again to finalize the connection. The MCP server will appear in the list of MCP servers connected to the agent step.
You can add multiple MCP servers to a single agent step, giving your agent
access to a wide range of custom capabilities.
## Best Practices
Write tool descriptions that clearly explain when and how to use each tool. The agent relies on these descriptions to make decisions.
Always define comprehensive input schemas with field descriptions. This
prevents errors and improves agent reliability.
Include try-catch blocks in your code to handle failures gracefully and return
meaningful error messages.
Create focused tools that do one thing well, rather than large multi-purpose functions. This makes them easier to maintain and reuse.
## Common Use Cases
Create tools that call external APIs to fetch data, send notifications, or trigger actions in third-party services.
```python theme={null}
async def main(args: InputSchema) -> str:
response = requests.post(
"https://api.service.com/endpoint",
json={"data": args.data},
headers={"Authorization": f"Bearer {args.api_key}"}
)
return response.json()["result"]
```
Build tools that transform, analyze, or validate data before passing it to the next step.
```python theme={null}
async def main(args: InputSchema) -> str:
import pandas as pd
df = pd.DataFrame(args.data)
summary = df.describe()
return summary.to_string()
```
Implement company-specific calculations, validations, or workflows that your agents need to perform.
```python theme={null}
async def main(args: InputSchema) -> str:
# Custom pricing calculation
base_price = args.quantity * args.unit_price
discount = base_price * args.discount_rate
total = base_price - discount
return f"Total: ${total:.2f} (Discount: ${discount:.2f})"
```
# Morning Brief Emails
Source: https://docs.narada.ai/documentation/morning-brief-emails
Get daily briefings about your meetings with external attendees
# Morning Brief Emails with Narada
**Morning Brief Emails** feature is designed to enhance your meeting readiness by providing enriched information about the external attendees of your meetings. Get a head start on your day with detailed insights every morning!
## Overview
The Morning Brief Email feature is your daily briefing tool that arrives at 5 AM, tailored to optimize your readiness for meetings with external attendees. It sifts through your calendar to identify meetings that include participants from outside your organization and compiles vital information to help you navigate the day's discussions effectively.
## How It Works
**Every Morning at 5 AM**
Narada sends you an email containing:
* A list of all meetings scheduled for the day with external attendees
* RSVP status for each attendee
* Background information where available
You'll receive your briefing automatically without any setup required.
**Comprehensive Details**
The email includes:
* Meeting times and participant names
* Professional details of attendees
* Work history and educational background
* LinkedIn profiles where available
## Features and Benefits
Get to know who you will be meeting with background information to tailor your approach
Understand availability and confirmation status of your attendees
Direct links to view meeting details in Narada for additional information
Work history, education, and LinkedIn profiles help you prepare conversation topics
## Viewing Meetings in Narada
To view detailed information about any meeting:
Click on the "View meeting in Narada" link provided in your morning brief email.
This will direct you to the Narada app where you can see complete details
about your meetings.
After the meeting, return to Narada to see:
* Summary of the meeting
* Action items
* Full transcript
## What's Included in Your Brief
**Meeting Details**
* Meeting time and duration
* List of external attendees
* RSVP status for each participant
* Meeting location or video call details
**Background Information**
* Professional work history
* Educational background
* Current role and company
* LinkedIn profile links
* Previous interactions (if any)
**Meeting Context**
* Suggested conversation starters
* Relevant company or industry information
* Meeting agenda if available
* Previous meeting notes or context
## FAQs and Troubleshooting
**Q: What if I don't receive my morning brief email at 5 AM?**
A: Check your spam folder to ensure the emails aren't being redirected. If the problem persists, verify your settings in Narada to ensure email notifications are enabled.
**Q: How accurate is the background information provided about attendees?**
A: Narada tries to gather information from publicly available sources to prepare you for the meeting with information such as the work history or education history of each external participant. While we strive for accuracy, sometimes Narada may find inaccurate information.
**Q: Is the attendee information gathering compliant with privacy regulations?**
A: Yes, Narada only uses publicly available information and respects privacy settings. No private or confidential information is accessed.
**Q: Can I customize what information is included in my morning brief?**
A: Currently, the morning brief includes a standard set of helpful information. Customization options may be available in future updates.
If your question isn't covered here, our support team at
[support@narada.ai](mailto:support@narada.ai) is always eager to help.
***
This Morning Brief Email feature is designed to significantly enhance how you prepare for your interactions, making every meeting more productive and informed. Should you have any further questions or need assistance, our support team is readily available. Prepare to transform your meeting experience with Narada!
# Parallel Execution
Source: https://docs.narada.ai/documentation/parallel-execution
Run multiple automation tasks simultaneously with the Narada Python SDK
## Overview
The Narada Python SDK supports parallel execution, allowing you to run multiple automation tasks simultaneously across different browser windows. This feature significantly improves efficiency when you need to perform multiple independent operations.
Each browser window operates independently, so you can run completely
different automation tasks in parallel without interference.
## Parallel Execution Example
Watch this demonstration of running multiple automation tasks simultaneously:
You can also view the complete example code on GitHub:
See the full parallel execution example on GitHub
## How It Works
Each browser window operates independently, so you can run completely different automation tasks in parallel without interference. Use Python's `asyncio.gather` to run tasks concurrently:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
from pydantic import BaseModel, Field
class CompanyInfo(BaseModel):
name: str = Field(description="Company name")
valuation: str = Field(description="Company valuation")
async def research_company(company_name: str) -> CompanyInfo:
"""Research a single company in its own browser environment."""
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
await agent.go_to_url(
url=f"https://www.google.com/search?q={company_name}+valuation",
new_tab=True
)
response = await agent.run(
prompt=f"Extract the company name and latest valuation for {company_name}",
output_schema=CompanyInfo
)
result = response.structured_output
assert result is not None
return result
finally:
await env.close()
async def main():
companies = ["Apple", "Google", "Microsoft", "Amazon"]
# Run all research tasks in parallel, each gets its own browser environment.
results = await asyncio.gather(*[
research_company(name) for name in companies
])
for result in results:
print(f"{result.name}: {result.valuation}")
if __name__ == "__main__":
asyncio.run(main())
```
Each `BrowserEnvironment()` creates an independent browser target. You can also use [Cloud Browser Sessions](/documentation/cloud-browser-sessions) for parallel cloud-based execution without local Chrome.
# Plugins
Source: https://docs.narada.ai/documentation/plugins
Enable Narada to understand and interact with diverse applications and platforms
# Plugins
**Plugins** enable Narada to understand and interact with a diverse range of applications and platforms, expanding on Narada's core capabilities. By using plugins, Narada will leverage many of your personal tools, whenever applicable, to carry out the tasks you ask it to do.
## How to Use Narada Plugins
Navigate to the **Plugins** section from your Narada sidebar.
Browse through the list of available plugins to see what integrations are possible.
Enable the plugin you want to use by clicking on the toggle button next to it.
Follow any specific instructions to integrate the plugin with your account.
Each plugin will guide you through its specific setup process.
## Available Plugins
Schedule meetings, send invites, and join calls with one command
Gmail, Calendar, and Drive integration for seamless productivity
Search across workspaces and draft messages efficiently
Search contacts and deals with natural language commands
Access and manage your Salesforce data seamlessly
Real-time web search for current information and research
### Zoom Integration
Harness the power of Narada with Zoom for seamless meeting scheduling, organization, and more.
* Schedule Zoom meetings directly from Narada
* Automatically send invites and add to calendar
* Join meetings with one command
* Support for meeting titles and descriptions
1. Enable the Zoom plugin from the sidebar 2. Sign in to your Zoom account
through the integration 3. Use natural language commands like: - "Schedule a
Zoom meeting for Monday at 10 AM" - "Set up a Zoom call with the design team
for next Monday at 10 AM" - "Create a weekly Zoom meeting every Monday at 10
AM"
**Multiple Zoom accounts:** Narada uses the account you connect with. To switch accounts, disconnect the current one and connect the desired account.
**Recurring meetings:** Currently not supported for Zoom via Narada, but you can set up recurring Google Calendar meetings with your Zoom link.
### Google Workspace
Comprehensive integration with Gmail, Google Calendar, and Google Drive for seamless productivity.
* Compose, reply, and forward emails using natural language
* Search through your emails with specific criteria
* Attach files from Google Drive directly
* Schedule emails to be sent later
```
"Send an email to Sarah about the quarterly review"
"Search my emails for messages about the marketing campaign"
"Draft a follow-up email with the project proposal attached"
```
* Create and edit calendar invites with natural language
* Search for events by date range or keywords
* Check user availability across multiple time slots
* Set up recurring meetings and events
* Automatically schedule meetings with optimal times
```
"Schedule a meeting with Alex and Tina on July 15th at 3 PM for 60 minutes"
"Show me all my events for today"
"Am I available on September 21st between 2 PM and 3 PM?"
"Set up a weekly team meeting every Monday at 10 AM"
```
* Search for files by name, type, or content
* Share documents seamlessly with email attachments
* Access files across your entire Drive
* Organize and manage file permissions
```
"Find the quarterly report from last month"
"Share the presentation with team@company.com"
"Search for all PDFs related to the project proposal"
```
### Slack Integration
Search through multiple Slack workspaces and draft messages efficiently.
* Search across all your Slack workspaces simultaneously
* Find specific messages and conversations by keywords
* Workspace-specific searches for targeted results
* Access to message history and shared files
* Draft messages to users or channels - Review messages before sending -
Multi-workspace support for organizations - Integration with team
communication workflows
```
"Search Slack for 'budget discussions'"
"Draft a message to the #general channel"
"Search in [Workspace Name] for 'team meeting notes'"
"Draft a Slack message to John saying 'Let's catch up tomorrow at 3 PM'"
```
### HubSpot CRM Integration
Search contacts and deals in your HubSpot CRM with natural language commands.
* Search for contacts by name, email, or custom properties
* Access comprehensive contact information and history
* View contact engagement and interaction timeline
* Property-based search for targeted results
* Find deals by name or associated contact - Track deal status and pipeline
progression - Access deal details including value and close dates - Monitor
sales performance and engagement
```
"Search HubSpot contacts for John Doe"
"Find HubSpot deals with sarah@company.com"
"Search HubSpot contacts with the email john@example.com"
"Search HubSpot deals for Quentin's car"
```
Currently specialized in searching contacts and deals. Adding or modifying
entries will be supported in future updates.
### Salesforce Integration
Access and search your Salesforce data seamlessly with Narada's advanced web navigation.
* Search contacts, leads, and opportunities
* Access comprehensive account information
* Track sales pipeline and performance data
* Add call logs and activity records
* Create and modify contacts and accounts
* Powered by world-class web navigation algorithm - Real-time execution in
your Salesforce dashboard - Visual execution - watch Narada navigate for you -
Support for complex queries and modifications
```
"/Salesforce add call log with John Doe about product feedback"
"/Salesforce create new contact Jane Doe, email jane@example.com"
"/Salesforce search for annual sales report 2023"
"/Salesforce update contact John Smith with new phone number"
```
Use the `/Salesforce` shortcut followed by your command in natural language.
Narada will open a new Chrome tab and execute the command in your dashboard.
### Web Search Capabilities
Search the web for current information and data when you need information beyond your personal tools.
* Real-time web search results from multiple sources
* Automatic source selection and verification
* Synthesized answers from multiple web sources
* Current information and fact-checking capabilities
* Intelligent query routing (personal vs web data)
* Filters through web noise to find relevant information - Provides source
citations and links - Combines multiple sources for comprehensive answers -
Saves time by eliminating manual browsing
```
"What is the tallest mountain in the world?"
"Find the latest research on renewable energy sources"
"What are the current stock prices for tech companies?"
"Search for recent news about artificial intelligence developments"
```
Narada automatically determines when to use web search versus personal data
sources, so you don't need to specify the search method.
## Popular Use Cases
**Schedule and manage meetings across platforms:**
```text Zoom Meeting theme={null}
"Schedule a Zoom meeting with the design team for next Monday at 10 AM"
```
```text Calendar Check theme={null}
"What meetings do I have tomorrow?"
```
```text Time Blocking theme={null}
"Block my calendar for 2 hours this afternoon for focused work"
```
**Email and messaging workflows:**
```text Email Composition theme={null}
"Send an email to Sarah about the quarterly review with the latest report attached"
```
```text Slack Search theme={null}
"Search Slack for messages about the marketing campaign from last week"
```
```text Message Drafting theme={null}
"Draft a follow-up email to the client with the project proposal"
```
**CRM and business data:**
```text HubSpot Search theme={null}
"Find all deals in HubSpot associated with Acme Corp"
```
```text Salesforce Query theme={null}
"Search Salesforce for contacts in the technology industry"
```
```text Contact Information theme={null}
"Get the contact information and recent activity for our biggest client"
```
**Information gathering:**
```text Market Research theme={null}
"Search the web for current cryptocurrency trends and market analysis"
```
```text Industry News theme={null}
"Find the latest industry news about artificial intelligence and machine learning"
```
```text Financial Data theme={null}
"What are the current stock prices and performance for major tech companies?"
```
## Best Practices
Use specific, detailed commands with dates, names, and relevant context for better results
Provide context like timeframes, specific people, and relevant details to
improve accuracy
Test each plugin after enabling to ensure proper functionality and permissions
Keep your connected accounts and permissions up to date for optimal performance
## Support & Troubleshooting
**Authentication Problems:** Most issues are resolved by re-authenticating or checking account permissions in the plugin settings.
**Search Results:** If you can't find what you're looking for, try refining your query with more specific details or different keywords.
**Plugin Not Responding:** Ensure you're connected to the internet and your account permissions are up to date.
For more in-depth support or specific plugin issues:
* Check plugin-specific settings and permissions
* Review the integration setup steps
* Contact our support team at [support@narada.ai](mailto:support@narada.ai)
* Join our community for tips and best practices
Always verify important information, especially when dealing with sensitive
business data or making significant changes through plugin integrations.
# Polling
Source: https://docs.narada.ai/documentation/polling
Poll for automation task completion and retrieve results
## Overview
Polling provides a pull-based mechanism for retrieving automation task results. After submitting a task via the [Remote Dispatch API](/api-reference/remote-dispatch), you can periodically check for completion using the [Get Response](/api-reference/get-response) endpoint.
No webhook endpoints or server infrastructure required to get started
Works in restricted network environments without opening inbound ports
Perfect for scripts, CLI tools, and applications without a web server
You decide when and how often to check for results
## How Polling Works
When you submit an automation task, you receive a `requestId` that you can use to poll for the task status until it completes.
Send a request to the Remote Dispatch API and receive a `requestId`
Wait for an appropriate interval before checking status
Call the Get Response endpoint with your `requestId`
If status is `pending`, go back to step 2. Otherwise, process the result.
Polling is ideal for simple integrations, scripts, and environments where setting up webhook endpoints isn't feasible.
## Basic Implementation
Here's how to implement basic polling in different languages:
```bash cURL theme={null}
# Submit the task
REQUEST_ID=$(curl -s -X POST 'https://api.narada.ai/fast/v2/remote-dispatch' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"prompt": "/Operator search for software engineering jobs",
"browserWindowId": "your-browser-window-id"
}' | jq -r '.requestId')
echo "Task submitted: $REQUEST_ID"
# Poll for completion
while true; do
RESPONSE=$(curl -s -X GET "https://api.narada.ai/fast/v2/remote-dispatch/responses/$REQUEST_ID" \
-H 'x-api-key: YOUR_API_KEY')
STATUS=$(echo $RESPONSE | jq -r '.status')
if [ "$STATUS" != "pending" ]; then
echo "Task completed with status: $STATUS"
echo $RESPONSE | jq '.response'
break
fi
echo "Task still pending, waiting 5 seconds..."
sleep 5
done
```
```javascript Node.js theme={null}
async function submitAndPoll(prompt, browserWindowId) {
// Submit the task
const submitResponse = await fetch(
"https://api.narada.ai/fast/v2/remote-dispatch",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.NARADA_API_KEY,
},
body: JSON.stringify({
prompt,
browserWindowId,
}),
}
);
const { requestId } = await submitResponse.json();
console.log(`Task submitted: ${requestId}`);
// Poll for completion
while (true) {
const statusResponse = await fetch(
`https://api.narada.ai/fast/v2/remote-dispatch/responses/${requestId}`,
{
headers: {
"x-api-key": process.env.NARADA_API_KEY,
},
}
);
const result = await statusResponse.json();
if (result.status !== "pending") {
console.log(`Task completed with status: ${result.status}`);
return result;
}
console.log("Task still pending, waiting 5 seconds...");
await new Promise((resolve) => setTimeout(resolve, 5000));
}
}
// Usage
const result = await submitAndPoll(
"/Operator search for software engineering jobs",
"your-browser-window-id"
);
console.log(result.response?.text);
```
```python Python theme={null}
import requests
import time
import os
def submit_and_poll(prompt: str, browser_window_id: str) -> dict:
api_key = os.getenv('NARADA_API_KEY')
# Submit the task
submit_response = requests.post(
'https://api.narada.ai/fast/v2/remote-dispatch',
headers={
'Content-Type': 'application/json',
'x-api-key': api_key
},
json={
'prompt': prompt,
'browserWindowId': browser_window_id
}
)
request_id = submit_response.json()['requestId']
print(f"Task submitted: {request_id}")
# Poll for completion
while True:
status_response = requests.get(
f'https://api.narada.ai/fast/v2/remote-dispatch/responses/{request_id}',
headers={'x-api-key': api_key}
)
result = status_response.json()
if result['status'] != 'pending':
print(f"Task completed with status: {result['status']}")
return result
print("Task still pending, waiting 5 seconds...")
time.sleep(5)
# Usage
result = submit_and_poll(
'/Operator search for software engineering jobs',
'your-browser-window-id'
)
print(result.get('response', {}).get('text'))
```
## Polling with Timeout
Here's a complete example with timeout handling:
```python theme={null}
import requests
import time
import os
def poll_for_result(request_id: str, timeout: int = 300, poll_interval: int = 5) -> dict:
"""Poll for task completion with timeout."""
api_key = os.environ['NARADA_API_KEY']
deadline = time.time() + timeout
while True:
remaining = deadline - time.time()
if remaining <= 0:
raise TimeoutError(f"Task did not complete within {timeout}s")
# Check status
response = requests.get(
f'https://api.narada.ai/fast/v2/remote-dispatch/responses/{request_id}',
headers={'x-api-key': api_key}
)
result = response.json()
if result['status'] != 'pending':
return result
time.sleep(min(poll_interval, remaining))
# Usage
try:
result = poll_for_result('req_abc123def456', timeout=120)
if result['status'] == 'success':
print(f"Success: {result['response']['text']}")
elif result['status'] == 'error':
print(f"Error: {result['response']['text']}")
elif result['status'] == 'input-required':
print(f"Input required: {result['response']['text']}")
except TimeoutError as e:
print(f"Timeout: {e}")
```
## Response Handling
The Get Response endpoint returns different statuses that you should handle appropriately:
```json theme={null}
{
"status": "pending",
"response": null,
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": null
}
```
The task is still running. Continue polling after waiting.
```json theme={null}
{
"status": "success",
"response": {
"text": "Found 1,234 software engineering job listings.",
"structuredOutput": {
"count": 1234,
"query": "software engineering jobs"
}
},
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": "2024-12-19T00:00:45.000000+00:00"
}
```
The task completed successfully. Process the response data.
```json theme={null}
{
"status": "error",
"response": {
"text": "Failed to complete the task due to network timeout."
},
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": "2024-12-19T00:02:30.000000+00:00"
}
```
The task failed. Log the error and implement retry logic if appropriate.
```json theme={null}
{
"status": "input-required",
"response": {
"text": "The task requires authentication. Please log in to continue."
},
"createdAt": "2024-12-19T00:00:00.000000+00:00",
"completedAt": null
}
```
The task needs user input. Notify the user or handle programmatically.
## Best Practices
Poll every 5-10 seconds to balance responsiveness with API efficiency
Always implement maximum timeouts to prevent infinite polling loops
Implement handlers for success, error, input-required, and timeout scenarios
Log polling attempts for debugging and monitoring purposes
Wrap API calls in try-except to handle transient network failures gracefully
For production apps with many concurrent tasks, webhooks may be more efficient
## Comparison: Polling vs Webhooks
| Feature | Polling | Webhooks |
| --------------------- | ------------------------ | ------------------- |
| **Setup Complexity** | Low | Medium |
| **Infrastructure** | None required | Web server needed |
| **Real-time Updates** | No (delayed by interval) | Yes |
| **Resource Usage** | Higher (active requests) | Lower |
| **Firewall Friendly** | Yes | Requires open ports |
| **Scalability** | Limited | Excellent |
| **Best For** | Scripts, simple apps | Production systems |
Start with polling for development and simple use cases. Migrate to [webhooks](/documentation/webhooks) when you need real-time notifications or are handling high volumes of tasks.
## Common Issues
**Problem**: Making too many requests and hitting rate limits.
**Solution**: Use a reasonable polling interval of 5-10 seconds. This balances responsiveness with API efficiency.
```python theme={null}
POLL_INTERVAL = 5 # seconds - good balance for most use cases
time.sleep(POLL_INTERVAL)
```
**Problem**: Script runs forever if a task never completes.
**Solution**: Always set a maximum timeout for your polling loop.
```python theme={null}
start = time.time()
while time.time() - start < MAX_TIMEOUT:
# poll...
raise TimeoutError("Task did not complete in time")
```
**Problem**: Code breaks when receiving unexpected status values.
**Solution**: Handle all possible statuses: `pending`, `success`, `error`, and `input-required`.
```python theme={null}
if result['status'] == 'success':
handle_success(result)
elif result['status'] == 'error':
handle_error(result)
elif result['status'] == 'input-required':
handle_input_required(result)
```
**Problem**: Network errors crash the polling loop.
**Solution**: Wrap API calls in try-except and implement retry logic.
```python theme={null}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
except requests.RequestException as e:
logger.warning(f"API request failed: {e}, retrying...")
time.sleep(interval)
continue
```
# Prompt Library
Source: https://docs.narada.ai/documentation/prompt-library
Save and reuse your favorite prompts and commands
# Prompt Library
The **Prompt Library** is your own collection of prompts and commands that you might want to save and reuse. For repetitive tasks and actions, the prompt library will help you dispatch them with a button click or two. The list of saved prompts can be easily reordered, and the prompts at the top of the list are always visible and just a click away in the Narada sidebar.
## Saving Your Favorite Prompts
To keep a memorable or frequently used prompt within arm's reach:
Navigate to the prompt you want to save in your conversation history.
Look for the star icon located on the bottom right corner of the prompt's box and click it.
Give a title to your prompt and hit "Save".
You've now successfully saved the prompt to your **Prompt Library**!
## Using Your Favorite Prompts
Accessing and using prompts from your library is a breeze:
**Open Your Library**
1. Open your **Prompt Library** by navigating to the star icon
2. Browse through your saved prompts
**Use Quick Action Chips**
Use the quick action chip associated with each prompt for immediate application.
**Access Recent Prompts**
You can also view your most recent prompts by clicking on the **Recent** tab in the Prompt Library.
## Managing Your Prompt Library
Modify saved prompts directly within the library to keep them current
Remove prompts you no longer need to declutter your library
Easily reorder your prompts to keep the most important ones at the top
Access your top prompts directly from the Narada sidebar
Be cautious when deleting prompts. Once deleted, they cannot be recovered.
## Example Prompts to Save
```
"Schedule a team standup meeting for every Monday at 9 AM"
```
```
"Send a follow-up email to the client about the project status update"
```
```
"Search for the latest industry trends in renewable energy and summarize the key findings"
```
```
"Create a new project folder in Google Drive and share it with the team"
```
Your Prompt Library becomes more valuable as you use it. Start by saving
prompts you find yourself repeating, and gradually build a collection that
speeds up your daily tasks.
***
Thank you for exploring the **Prompt Library** feature. We hope this guide empowers you to make the most out of this tool, ensuring outputs that are tailored to your needs. Should you have any further questions or concerns, our support team is always here to help.
# Proxy Settings
Source: https://docs.narada.ai/documentation/proxy-settings
Route browser traffic through HTTP, HTTPS, or SOCKS5 proxies using the Narada Python SDK
## Overview
The Narada Python SDK allows you to route all browser traffic through a proxy server. This is useful for scenarios such as:
Mask your IP address and browse anonymously through proxy servers.
Access region-locked content by routing traffic through proxies in different locations.
Work within enterprise environments that require proxy authentication.
## Prerequisites
Make sure you have completed the [Getting Started with the SDK](/documentation/getting-started-with-sdk) guide before configuring proxy settings.
## Configuration Reference
The `ProxyConfig` class provides all the options you need to configure proxy settings:
The proxy server URL. Supports HTTP, HTTPS, and SOCKS5 protocols.
**Formats:**
* HTTP proxy: `http://proxy.example.com:8080`
* HTTPS proxy: `https://proxy.example.com:8080`
* SOCKS5 proxy: `socks5://proxy.example.com:1080`
```python theme={null}
proxy = ProxyConfig(server="http://proxy.example.com:8080")
```
Optional username for proxy authentication. Must be provided together with `password`.
```python theme={null}
proxy = ProxyConfig(
server="http://proxy.example.com:8080",
username="your_username",
password="your_password"
)
```
Optional password for proxy authentication. Must be provided together with `username`.
Never hardcode credentials in your source code. Use environment variables or a secrets manager instead.
Optional comma-separated list of domains that should bypass the proxy.
```python theme={null}
proxy = ProxyConfig(
server="http://proxy.example.com:8080",
bypass=".example.com, internal.corp, localhost"
)
```
Use a leading dot (`.example.com`) to match all subdomains of a domain.
Set to `True` to ignore SSL certificate errors. Required for proxies that perform HTTPS inspection (MITM).
Only enable this option if you trust your proxy server completely. Disabling certificate verification exposes your traffic to potential interception.
```python theme={null}
proxy = ProxyConfig(
server="http://corporate-proxy.example.com:8080",
ignore_cert_errors=True # Use with caution!
)
```
## Quick Start
Import `ProxyConfig` and `BrowserConfig` from the Narada SDK:
```python theme={null}
from narada import Agent, BrowserConfig, BrowserEnvironment, ProxyConfig
```
Define your proxy settings using `ProxyConfig`:
```python theme={null}
proxy = ProxyConfig(
server="http://proxy.example.com:8080",
username="your_username", # optional
password="your_password", # optional
)
```
Pass the proxy configuration to `BrowserConfig`:
```python theme={null}
config = BrowserConfig(proxy=proxy)
```
Use the configuration when creating a browser environment:
```python theme={null}
config = BrowserConfig(proxy=proxy)
env = BrowserEnvironment(config=config)
agent = Agent(environment=env)
try:
response = await agent.run(
prompt="Go to https://httpbin.org/ip and tell me what IP address is shown.",
)
print(response.text)
finally:
await env.close()
```
Your browser window is now configured to route all traffic through the proxy server.
## Troubleshooting
If you're seeing SSL/TLS certificate errors:
1. **Corporate proxy** - Your proxy may be performing HTTPS inspection (MITM)
2. **Enable ignore\_cert\_errors** - Set `ignore_cert_errors=True` if you trust the proxy
3. **Install CA certificate** - Alternatively, install your organization's CA certificate
Only use `ignore_cert_errors=True` with trusted proxies in controlled environments.
# Python Agents
Source: https://docs.narada.ai/documentation/python-agents
Build code-based agents in Agent Studio using the Narada Python SDK
## What are Python Agents?
Python Agents are code-based automations that run directly in Agent Studio using the Narada Python SDK. Unlike GUI workflow agents, Python Agents give you the full flexibility of Python code for loops, conditions, data processing, API calls, and browser automation.
Write Python code with access to the Narada SDK, Pydantic, and common libraries
Created automatically by [Agent Maker](/documentation/agent-maker) and [Imitation Learning](/documentation/imitation-learning)
Use `Agent.run()`, browser actions, Google Sheets helpers, and human-in-the-loop prompts
## How Python Agents Are Created
There are three ways to create a Python Agent in Agent Studio:
### 1. Agent Maker
Describe your goal in natural language, and [Agent Maker](/documentation/agent-maker) generates a complete Python Agent for you:
```text theme={null}
/agentMaker Create an agent that extracts product prices from amazon.com and saves them to a Google Sheet
```
### 2. Imitation Learning
Record yourself performing a task in the browser, and [Imitation Learning](/documentation/imitation-learning) generates an agent from your recording.
### 3. Manual Creation
Create a Python Agent from scratch in Agent Studio:
Navigate to Agent Studio and click **+ Create**.
Choose the Python Agent option from the creation dialog.
The editor opens with a starter template. Write your automation using the Narada SDK.
## Writing Python Agent Code
In Agent Studio, `BrowserEnvironment()` targets the current browser window. Create an `Agent` with that environment, then call `agent.run()` or browser action methods on the agent:
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env)
# Navigate to a starting page. Use new_tab=True for the first navigation.
await agent.go_to_url(url="https://example.com", new_tab=True)
response = await agent.run(
prompt="Extract the main heading from this page",
)
print(response.text)
```
Use `new_tab=True` for the first `go_to_url()` call in Agent Studio. This keeps the workflow runtime tab separate from the page your automation controls.
### Using Structured Output
Extract typed data using Pydantic models:
```python theme={null}
from narada import Agent, BrowserEnvironment
from pydantic import BaseModel, Field
class ProductInfo(BaseModel):
name: str = Field(description="Product name")
price: str = Field(description="Product price with currency")
rating: str = Field(description="Product rating out of 5")
env = BrowserEnvironment()
agent = Agent(environment=env)
await agent.go_to_url(url="https://example.com/product", new_tab=True)
response = await agent.run(
prompt="Extract the product information from this page",
output_schema=ProductInfo,
)
product = response.structured_output
assert product is not None
print(f"{product.name}: {product.price} ({product.rating})")
```
### Using Input Variables
Python Agents can accept input variables when invoked, making them reusable with different data:
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env)
# Access input variables with sensible defaults.
search_term = variables.get("searchTerm", "machine learning")
max_results = variables.get("maxResults", 5)
await agent.go_to_url(
url=f"https://www.google.com/search?q={search_term}",
new_tab=True,
)
response = await agent.run(
prompt=f"Extract the top {max_results} search result titles",
)
print(response.text)
```
Use `variables.get("key", default)` instead of `variables["key"]` so your agent can run standalone without input variables.
To pass values when invoking a custom Python agent, see [Input Variables](/documentation/input-variables). For API keys, passwords, and other sensitive values, see [Secrets and Secret Variables](/documentation/secrets-and-secret-variables).
### Google Sheets Integration
Read from and write to Google Sheets:
```python theme={null}
from narada import Agent, BrowserEnvironment
env = BrowserEnvironment()
agent = Agent(environment=env)
sheet_data = await agent.read_google_sheet(
spreadsheet_id="your-spreadsheet-id",
range="Sheet1!A2:A10",
)
companies = [row[0] for row in sheet_data.values]
results = []
for company in companies:
await agent.go_to_url(
url=f"https://www.google.com/search?q={company}+valuation",
)
response = await agent.run(
prompt=f"What is {company}'s latest valuation?",
)
results.append([company, response.text])
await agent.write_google_sheet(
spreadsheet_id="your-spreadsheet-id",
range="Sheet1!B2",
values=results,
)
await agent.print_message(message="Done! Results written to Google Sheet.")
```
## Available SDK Methods
Python Agents have access to the same agent-centered SDK methods as standalone scripts:
| Method | Description |
| ----------------------------------------------------------------- | ------------------------------------------------- |
| [`run()`](/api-reference/agent) | Execute an AI-powered automation task |
| [`go_to_url()`](/api-reference/go-to-url) | Navigate to a URL |
| [`agentic_selector()`](/api-reference/agentic-selector) | Interact with specific UI elements |
| [`agentic_mouse_action()`](/api-reference/agentic-mouse-action) | Replay recorded mouse actions with agent fallback |
| [`get_url()`](/api-reference/get-url) | Read the current page URL |
| [`get_screenshot()`](/api-reference/get-screenshot) | Capture a screenshot |
| [`get_full_html()`](/api-reference/get-full-html) | Get the page's full HTML |
| [`get_simplified_html()`](/api-reference/get-simplified-html) | Get cleaned HTML |
| [`print_message()`](/api-reference/print-message) | Show a message in the side panel chat |
| [`prompt_for_user_input()`](/api-reference/prompt-for-user-input) | Collect structured user input |
| [`user_approval()`](/api-reference/user-approval) | Ask the user to approve or reject a step |
| [`read_google_sheet()`](/api-reference/read-google-sheet) | Read data from Google Sheets |
| [`write_google_sheet()`](/api-reference/write-google-sheet) | Write data to Google Sheets |
## Agent Kinds
Choose the agent kind when you construct an `Agent`:
Browser automation: clicks, navigation, form filling, and data extraction from pages.
Read-only reasoning: extracting visible data, answering questions about page content, and conversation-style tasks.
```python theme={null}
from narada import Agent, AgentKind, BrowserEnvironment
env = BrowserEnvironment()
# Web automation (default)
operator = Agent(environment=env)
await operator.run(prompt="Click the Submit button")
# Read-only reasoning
core_agent = Agent(environment=env, kind=AgentKind.CORE_AGENT)
await core_agent.run(prompt="What products are listed?")
```
## Running Python Agents
### From Agent Studio
Click the **Run** button in the Agent Studio editor to execute your Python Agent in the current browser.
### From the SDK
Invoke a custom Python Agent from the SDK using the namespaced format:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
custom_agent = Agent(environment=env, kind="/$USER/my-research-agent")
try:
response = await custom_agent.run(
prompt="Search for machine learning papers",
)
print(response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
### From Chat
Type the agent shortcut in the Narada chat:
```text theme={null}
/$USER/my-research-agent Search for machine learning papers
```
## Best Practices
Open a new tab for the first navigation so the workflow runtime stays separate
Catch `NaradaTimeoutError` for slow pages and call `agent.reset_agent_state()` after timeouts
Define Pydantic models for reliable data extraction instead of parsing text responses
Use `print_message()` to show progress updates in the side panel during long workflows
# Remote Browser Control
Source: https://docs.narada.ai/documentation/remote-browser-control
Learn how to control an existing browser window using Narada
## Overview
Narada can control an existing browser window from another machine or process. Use `RemoteBrowserEnvironment` when you already have a browser window ID and want to run an `Agent` against that browser.
## Prerequisites
Install Narada on the remote machine
Install and sign in to the Narada extension in Chrome
Set up the Python SDK on the controlling machine
## Getting Started
On the remote machine:
1. Open Chrome with the Narada extension installed.
2. Visit [https://app.narada.ai/initialize](https://app.narada.ai/initialize).
3. Copy the browser window ID displayed by the initialization page.
Keep this browser window ID secure because it grants access to the remote browser.
Create a Python script on your controlling machine:
```python theme={null}
import asyncio
from narada import Agent, RemoteBrowserEnvironment
async def main() -> None:
browser_window_id = "REPLACE_WITH_BROWSER_WINDOW_ID"
env = RemoteBrowserEnvironment(browser_window_id=browser_window_id)
agent = Agent(environment=env)
response = await agent.run(
prompt=(
'Search for "LLM Compiler" on Google and open the first arXiv paper '
"on the results page, then tell me who the authors are."
)
)
print("Response:", response.text)
if __name__ == "__main__":
asyncio.run(main())
```
Replace `browser_window_id` with the ID from your remote browser.
Execute your control script:
```bash theme={null}
python your_script.py
```
If successful, the remote browser executes your command and returns the result.
## How It Works
1. **Browser Window ID**: Each initialized browser window has a unique ID that identifies the remote execution target.
2. **Remote Environment**: `RemoteBrowserEnvironment(browser_window_id=...)` points the SDK at that existing browser.
3. **Agent Execution**: `Agent(environment=env)` sends browser automation requests to the remote window.
4. **Response Handling**: `Agent.run()` returns an `AgentResponse` that you can inspect in Python.
## Reconnecting to Cloud Browser Sessions
If you're using [Cloud Browser Sessions](/documentation/cloud-browser-sessions), provide both the browser window ID and the cloud browser session ID:
```python theme={null}
import asyncio
from narada import Agent, RemoteBrowserEnvironment
async def main() -> None:
env = RemoteBrowserEnvironment(
browser_window_id="saved-browser-window-id",
cloud_browser_session_id="saved-cloud-session-id",
)
agent = Agent(environment=env)
try:
response = await agent.run(prompt="What page am I currently on?")
print("Response:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
Closing a `RemoteBrowserEnvironment` backed by a cloud session stops the entire cloud session. Make sure you're done before calling `close()`.
## Related Resources
* [Cloud Browser Sessions](/documentation/cloud-browser-sessions)
* [API Reference for Remote Dispatch](/api-reference/remote-dispatch)
* [Authentication Guide](/documentation/authentication)
* [Error Handling](/documentation/error-handling)
# Screenshots
Source: https://docs.narada.ai/documentation/screenshots
Learn how to take screenshots and save them locally using the Narada Python SDK
## Take Screenshots with Narada SDK
The Narada Python SDK makes it easy to capture screenshots of web pages during automation tasks. When you ask Narada to take a screenshot, it automatically saves the image to your local Downloads folder with a timestamped filename for easy organization.
## How to Take Screenshots with the SDK
Use the SDK to capture screenshots with a simple command:
```python theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Take a screenshot of the current page
response = await agent.run(
prompt='take a screenshot of this page'
)
print("Screenshot captured!")
print("Response:", response.text)
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
The SDK automatically handles screenshot capture and file organization for you.
Narada automatically creates a directory structure for your screenshots:
1. **Main Directory**: `Narada Downloads` in your machine's Downloads folder
2. **Request Directory**: Directory named after your `request_id` from the [SDK response](/documentation/getting-started-with-sdk)
3. **File**: Screenshot saved as a `.jpeg` file with timestamp
If the `Narada Downloads` directory doesn't exist, it will be created automatically.
Navigate to your Downloads folder and look for:
```
Downloads/
└── Narada Downloads/
└── req_abc123def456/
└── save-screenshot-20250828232853.jpeg
```
Each screenshot request gets its own directory named after the `request_id` for easy correlation with your SDK calls. The screenshot file is automatically named with a timestamp for unique identification.
## Screenshot Organization
All screenshots are saved as JPEG files for optimal file size and universal
compatibility.
Each screenshot directory is named after the `request_id` from the [SDK response](/documentation/getting-started-with-sdk), allowing you to easily track which SDK request generated which files.
Screenshots are automatically named with timestamps (e.g., `save-screenshot-20250828232853.jpeg`) to prevent filename conflicts and maintain chronological order.
## Example Use Cases
* **Documentation**: Capture screenshots of web interfaces for user guides
* **Quality Assurance**: Take screenshots during automated testing workflows
* **Monitoring**: Capture visual evidence of web page states
* **Reporting**: Include screenshots in automated reports and summaries
* **Debugging**: Visual documentation of automation steps and results
You can take a screenshot of any page that's currently visible in your browser. The screenshot captures the full viewport at the time of capture.
# Structured Output
Source: https://docs.narada.ai/documentation/structured-output
Structure automation responses with Pydantic models for reliable data extraction
## Overview
The Narada Python SDK uses Pydantic models to define structured output schemas, ensuring consistent and type-safe data extraction from automation tasks.
## Using Structured Output
Define your data structure using Pydantic models and pass it to the SDK:
```python highlight={7-12, 21} theme={null}
import asyncio
from narada import Agent, BrowserEnvironment
from pydantic import BaseModel, Field
# Define your data structure
class JobListing(BaseModel):
title: str = Field(description="Job title")
company: str = Field(description="Company name")
location: str = Field(description="Job location")
salary: str | None = Field(description="Salary range if available")
remote: bool = Field(description="Whether the job is remote")
async def main() -> None:
env = BrowserEnvironment()
agent = Agent(environment=env)
try:
# Use structured output to extract job data
response = await agent.run(
prompt='search for "Python developer" jobs on LinkedIn and extract details of the first job listing',
output_schema=JobListing
)
# Access structured data
job = response.structured_output
assert job is not None
print(f"Found job: {job.title} at {job.company}")
print(f"Location: {job.location}")
if job.salary:
print(f"Salary: {job.salary}")
print(f"Remote: {'Yes' if job.remote else 'No'}")
finally:
await env.close()
if __name__ == "__main__":
asyncio.run(main())
```
## Supported Data Types
```python theme={null}
from pydantic import BaseModel, Field
class UserProfile(BaseModel):
name: str = Field(description="User's full name")
age: int = Field(description="User's age")
email: str = Field(description="User's email address")
active: bool = Field(description="Whether user is active")
```
```python theme={null}
from typing import Optional
class Product(BaseModel):
name: str = Field(description="Product name")
price: float = Field(description="Product price")
description: str | None = Field(description="Product description")
stock: int | None = Field(description="Units in stock")
```
```python theme={null}
from typing import List
class SearchResults(BaseModel):
top_links: list[str] = Field(description="List of top result URLs")
```
```python theme={null}
class Address(BaseModel):
street: str = Field(description="Street address")
city: str = Field(description="City name")
country: str = Field(description="Country name")
class Company(BaseModel):
name: str = Field(description="Company name")
address: Address = Field(description="Company address")
employee_count: int = Field(description="Number of employees")
```
## Best Practices
Add descriptive Field annotations to guide data extraction
Use appropriate types and Optional fields for reliable data
Break complex schemas into smaller, reusable models
Let Pydantic handle data validation automatically
Use structured output whenever you need to extract specific data points from web pages. The SDK will ensure the data matches your schema exactly.
# Vector Stores
Source: https://docs.narada.ai/documentation/vector-stores
Create vector stores to provide context and knowledge to your agents using RAG (Retrieval-Augmented Generation)
## What is a Vector Store?
Vector Stores allow you to upload documents, PDFs, and text files that provide context and knowledge to your agents. When agents need information, they can retrieve relevant content from your vector store using RAG (Retrieval-Augmented Generation), enabling them to answer questions and make decisions based on your uploaded knowledge base.
Vector Stores use semantic search to find the most relevant information from
your uploaded files based on the agent's query. This allows agents to access
specific knowledge without needing to process entire documents.
## How It Works
Upload files to a vector store and provide a clear description of what information it contains. When you connect a vector store to an agent, the agent can automatically retrieve relevant content from your files to answer questions, make informed decisions, and provide accurate responses based on your knowledge base.
Add documents, PDFs, and text files to build your knowledge base
Agents automatically find relevant content using semantic search
Give agents access to specific knowledge domains and information
## Creating a Vector Store
Navigate to **Agent Studio** and click the **Create** button to open the "Create new item" modal.
Choose the **Vector Store** option from the creation modal.
In the vector store editor, provide a clear description of what information
this vector store contains. This description helps the agent understand when
to retrieve information from this vector store.
The description field is required. You must provide a description before you
can use your vector store with an agent.
Write descriptions that clearly explain the domain, topic, or type of
information stored. For example: "This vector store includes company
policies and employee handbooks."
Change the default name to something specific that describes what the vector
store contains. This makes it easier to identify and manage multiple vector
stores.
Use descriptive names like "Research Papers", "Company Policies", or
"Product Documentation".
Click the **Add files** button to add documents to your vector store.
Supported file types include documents, PDFs, and text files. The files will be processed and indexed for semantic search.
## Using Vector Stores in Agents
Add vector stores to agent steps to give agents access to your knowledge base. You can use managed vector stores that you create in Narada, or connect to external vector stores using Amazon Bedrock.
In any agent step, find the **Vector Stores** section and click to open the "Add vector store" modal.
Choose **Create new** to create a new vector store, or **Add existing** to select
from your existing vector stores.
Choose the type of vector store you want to add:
* **Managed**: Select from your existing vector stores created in Narada. These are vector stores you've created and uploaded files to.
* **Amazon Bedrock**: Connect by providing the required connection information and credentials.
When selecting an external vector store type, you'll need to provide the connection details specific to that service, such as API keys, region information, and other configuration required to connect to the external vector store.
Fill in the required information based on your vector store type and selection, then click **Add vector store** or **Create vector store** to complete the process.
Vector stores without descriptions cannot be used with agents until a description is added.
You can add multiple vector stores to a single agent step, giving your agent
access to different knowledge domains simultaneously.
## Best Practices
Write detailed descriptions that explain what information is stored. The agent uses this to determine when to retrieve from the vector store.
Upload related files to the same vector store. Group documents by topic or
domain for better organization.
Use specific, descriptive names for your vector stores to make them easy to
identify and manage.
Upload well-structured documents with clear, relevant information. Better source material leads to better agent responses.
## Common Use Cases
Store research papers, technical documentation, or reference materials that agents can consult when answering questions or performing research tasks.
**Example:** A vector store containing research papers about machine learning that agents can reference when discussing ML concepts.
Upload company policies, employee handbooks, or internal documentation to give agents access to organizational knowledge.
**Example:** A vector store with HR policies and employee guidelines that agents can reference when answering HR-related questions.
Store product documentation, specifications, or FAQs that agents can use to provide accurate product information.
**Example:** A vector store containing product manuals and specifications that support agents in customer service scenarios.
# Voice Messages
Source: https://docs.narada.ai/documentation/voice-messages
Interact with Narada using voice commands for hands-free operation
# Voice Messages
With Narada's **Voice Message** capabilities, you can interact with Narada using voice commands, enabling a hands-free, efficient, and conversational way to utilize Narada's features.
## Overview
Narada's Voice Message feature allows you to communicate and execute tasks using spoken language. This hands-free mode enhances accessibility and ease of use, providing a natural and intuitive way to interact with the various functionalities Narada offers.
## Using Voice Messages with Narada
Once everything is set up, you can start interacting with Narada using your voice.
Click on the microphone button on the Narada interface to start a voice message session.
If it is your first time using voice messages, you will be prompted to allow Narada to access your microphone. Click "Allow" to proceed.
Clearly state your request or message.
Examples of voice commands:
* "Schedule a meeting with the design team for next Monday at 10 AM"
* "What's on my calendar today?"
* "Send an email to John about the project update"
Narada will display the message it understood. Review the message, confirm it's correct, and send it!
Your voice command will be processed and executed by Narada.
## Voice Command Examples
**Meeting and Calendar Commands**
```
"Schedule a meeting with the design team for next Monday at 10 AM"
```
```
"What meetings do I have tomorrow?"
```
```
"Block my calendar for 2 hours this afternoon"
```
**Email and Messaging**
```
"Send an email to Sarah about the quarterly review"
```
```
"Reply to John's message about the budget proposal"
```
```
"Search my emails for messages from the marketing team"
```
**Task and File Management**
```
"Create a new document called Project Proposal"
```
```
"Find the Q4 budget spreadsheet in Google Drive"
```
```
"Add a reminder to follow up on the client presentation"
```
## Benefits of Voice Messages
Continue working while giving commands to Narada without typing
Speak naturally as you would to a colleague or assistant
Ideal for users with mobility limitations or when typing isn't convenient
Execute commands while focusing on other tasks or activities
## FAQs and Troubleshooting
**Q: Can I use voice messages in any language?**
A: Narada's voice message capability currently supports English only. We're working on adding support for other languages.
**Q: What should I do if Narada doesn't understand my message?**
A: Speak clearly and directly into the microphone. Ensure there's minimal background noise. If the issue persists, try rephrasing your message or speaking more slowly.
**Q: Are voice messages as accurate as typing?**
A: Voice messages are generally very accurate, but this can depend on factors like accent, speech clarity, and background noise. In some cases, typing might be more precise or faster, but voice messages offer convenience and accessibility, especially for complex tasks or when you're unable to type.
**Q: How is my voice data handled?**
A: Voice messages are processed securely and are not stored after transcription. Narada prioritizes your privacy and data security in all voice interactions.
If your question isn't covered here, our support team at
[support@narada.ai](mailto:support@narada.ai) is always eager to help.
***
We hope this guide helps you effectively utilize Narada's voice message capabilities. If you have any further questions or need assistance, please reach out. Enjoy the ease and efficiency of hands-free interaction with Narada!
# Webhooks
Source: https://docs.narada.ai/documentation/webhooks
Receive real-time notifications when automation tasks complete
## Overview
Webhooks provide a push-based mechanism for receiving automation task results in real-time. Instead of continuously polling for task status, Narada Cloud automatically sends the response to your specified endpoint when the task completes.
Receive results immediately when tasks complete, eliminating polling delays
Lower server load and API usage compared to continuous polling
Handle high-volume automation workflows efficiently
Process automation results without continuous API polling
## How Webhooks Work
When you submit an automation task via the [Remote Dispatch API](/api-reference/remote-dispatch), you can provide a `callbackUrl`. Once the task completes, Narada Cloud sends an HTTP POST request to your endpoint with the complete task results.
Include `callbackUrl` when creating a remote dispatch request
Narada Cloud processes your automation task asynchronously
When complete, Narada Cloud POSTs the results to your callback URL
Your endpoint receives and processes the webhook payload
Webhooks are recommended for production applications where real-time notifications and scalability are important.
## Configuration
### Setting Up a Webhook Endpoint
Configure webhooks by providing the `callbackUrl` parameter when creating a remote dispatch request:
```bash cURL theme={null}
curl -X POST 'https://api.narada.ai/fast/v2/remote-dispatch' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"prompt": "/Operator search for software engineering jobs and count the results",
"browserWindowId": "your-browser-window-id",
"callbackUrl": "https://your-domain.com/api/narada/webhook",
"callbackHeaders": {
"Authorization": "Bearer YOUR_WEBHOOK_TOKEN"
},
"callbackSecret": "your-secret-key-here"
}'
```
```javascript Node.js theme={null}
const response = await fetch("https://api.narada.ai/fast/v2/remote-dispatch", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.NARADA_API_KEY,
},
body: JSON.stringify({
prompt: "/Operator search for software engineering jobs and count the results",
browserWindowId: "your-browser-window-id",
callbackUrl: "https://your-domain.com/api/narada/webhook",
callbackHeaders: {
Authorization: `Bearer ${process.env.WEBHOOK_TOKEN}`,
},
callbackSecret: process.env.NARADA_CALLBACK_SECRET,
}),
});
const data = await response.json();
console.log(`Task submitted: ${data.requestId}`);
```
```python Python theme={null}
import requests
import os
response = requests.post(
'https://api.narada.ai/fast/v2/remote-dispatch',
headers={
'Content-Type': 'application/json',
'x-api-key': os.getenv('NARADA_API_KEY')
},
json={
'prompt': '/Operator search for software engineering jobs and count the results',
'browserWindowId': 'your-browser-window-id',
'callbackUrl': 'https://your-domain.com/api/narada/webhook',
'callbackHeaders': {
'Authorization': f'Bearer {os.getenv("WEBHOOK_TOKEN")}'
},
'callbackSecret': os.getenv('NARADA_CALLBACK_SECRET')
}
)
data = response.json()
print(f"Task submitted: {data['requestId']}")
```
### Configuration Parameters
The HTTPS URL where Narada Cloud will send the webhook POST request. Must be a publicly accessible endpoint that accepts POST requests.
**Optional.** HTTP headers to include when sending the webhook POST request. These headers are sent with the webhook notification, allowing you to authenticate or customize the request.
```json theme={null}
{
"callbackUrl": "https://example.com/webhook",
"callbackHeaders": {
"Authorization": "Bearer ",
"X-Custom-Header": "value"
}
}
```
`callbackHeaders` is optional and only needed if your webhook endpoint requires authentication or custom headers. If your endpoint doesn't require any special headers, you can omit this parameter.
Use `callbackHeaders` to send authentication tokens, API keys, or custom headers with your webhook requests. This is particularly useful when your webhook endpoint requires authentication.
Optional secret key included in the webhook payload body for verification. Use this to validate that webhook requests are genuinely from Narada Cloud.
**HTTPS Required**: Your callback URL must use HTTPS to ensure your `callbackSecret`, `callbackHeaders`, and webhook data are encrypted in transit.
## How Callback Headers Work
When you provide `callbackHeaders` (or `callback_headers` in Python), Narada Cloud stores these headers and includes them in the HTTP POST request when sending the webhook notification. This allows you to:
* **Authenticate webhook requests** using Bearer tokens, API keys, or custom authentication headers
* **Customize webhook delivery** with custom headers required by your endpoint
* **Integrate with existing systems** that require specific header formats
## Webhook Payload
When an automation task completes, Narada Cloud sends a POST request to your `callbackUrl` with the following JSON payload:
### Payload Structure
The unique identifier for the automation task. Use this to correlate webhook notifications with your original requests.
The `callbackSecret` you provided when creating the task. Returns `null` if no secret was specified.
Current status of the automation task. Possible values:
* `"input-required"` - Task requires additional user input
* `"success"` - Task completed successfully
* `"error"` - Task failed with an error
The browser window ID where the task was executed. Returns `null` if not applicable.
The automation task response data containing the results.
The main response text from the automation task.
Structured data if you specified a `responseFormat` in your original request. See [Structured Output documentation](/documentation/structured-output) for details.
ISO 8601 timestamp when the task was initially created.
ISO 8601 timestamp when the task completed.
Usage statistics for the automation task. Returns `null` if not available.
Number of actions performed during the automation task.
Credits consumed by the task.
### Example Payloads
```json theme={null}
{
"requestId": "req_abc123def456",
"secret": "your-secret-key-here",
"status": "success",
"browserWindowId": "window_xyz789",
"response": {
"text": "Found 1,234 software engineering job listings in the search results."
},
"createdAt": "2024-11-21T10:00:00.000000+00:00",
"completedAt": "2024-11-21T10:00:45.000000+00:00",
"usage": {
"actions": 12,
"credits": 5
}
}
```
```json theme={null}
{
"requestId": "req_abc123def456",
"secret": "your-secret-key-here",
"status": "error",
"browserWindowId": "window_xyz789",
"response": {
"text": "Failed to complete the task due to network timeout."
},
"createdAt": "2024-11-21T10:00:00.000000+00:00",
"completedAt": "2024-11-21T10:02:30.000000+00:00",
"usage": {
"actions": 8,
"credits": 3
}
}
```
```json theme={null}
{
"requestId": "req_abc123def456",
"secret": "your-secret-key-here",
"status": "input-required",
"browserWindowId": "window_xyz789",
"response": {
"text": "The task requires authentication. Please log in to continue."
},
"createdAt": "2024-11-21T10:00:00.000000+00:00",
"completedAt": "2024-11-21T10:00:15.000000+00:00",
"usage": {
"actions": 3,
"credits": 1
}
}
```
```json theme={null}
{
"requestId": "req_abc123def456",
"secret": "your-secret-key-here",
"status": "success",
"browserWindowId": "window_xyz789",
"response": {
"text": "Successfully extracted job information.",
"structuredOutput": {
"title": "Senior Software Engineer",
"company": "TechCorp Inc.",
"location": "San Francisco, CA",
"salary": "$150,000 - $200,000",
"remote": true
}
},
"createdAt": "2024-11-21T10:00:00.000000+00:00",
"completedAt": "2024-11-21T10:00:52.000000+00:00",
"usage": {
"actions": 15,
"credits": 6
}
}
```
## Implementing a Webhook Endpoint
### Basic Implementation
Create an endpoint that receives and processes webhook notifications:
```javascript theme={null}
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/narada/webhook', (req, res) => {
const payload = req.body;
// Verify authentication - check Authorization header from callbackHeaders
const authHeader = req.headers.authorization;
if (authHeader && authHeader !== `Bearer ${process.env.WEBHOOK_TOKEN}`) {
return res.status(401).json({ error: 'Invalid authorization header' });
}
// Also verify the secret if provided
if (payload.secret && payload.secret !== process.env.NARADA_CALLBACK_SECRET) {
return res.status(401).json({ error: 'Invalid secret' });
}
// Process the webhook payload
console.log(`Task ${payload.requestId} completed with status: ${payload.status}`);
if (payload.status === 'success') {
console.log('Response:', payload.response.text);
// Handle successful completion
handleSuccess(payload);
} else if (payload.status === 'error') {
console.error('Task failed:', payload.response.text);
// Handle error
handleError(payload);
} else if (payload.status === 'input-required') {
console.log('Input required:', payload.response.text);
// Handle input requirement
handleInputRequired(payload);
}
// Always respond with 200 to acknowledge receipt
res.status(200).json({ received: true });
});
function handleSuccess(payload) {
// Your success handling logic
// Store results, trigger next steps, etc.
}
function handleError(payload) {
// Your error handling logic
// Log errors, retry tasks, send alerts, etc.
}
function handleInputRequired(payload) {
// Your input handling logic
// Notify users, pause workflows, etc.
}
app.listen(3000, () => {
console.log('Webhook endpoint listening on port 3000');
});
```
```python theme={null}
from fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
from typing import Any, Literal, Mapping
import os
app = FastAPI()
class RemoteDispatchUsage(BaseModel):
actions: int
credits: int
class WebhookPayload(BaseModel):
requestId: str
secret: str | None
status: Literal["input-required", "success", "error"]
browserWindowId: str | None
response: Mapping[str, Any]
createdAt: str
completedAt: str
usage: RemoteDispatchUsage | None
@app.post("/api/narada/webhook")
async def handle_webhook(request: Request, payload: WebhookPayload):
# Verify authentication - check Authorization header from callback_headers
auth_header = request.headers.get("authorization")
expected_token = os.getenv('WEBHOOK_TOKEN')
if auth_header and expected_token:
if auth_header != f"Bearer {expected_token}":
raise HTTPException(status_code=401, detail="Invalid authorization header")
# Also verify the secret if provided
expected_secret = os.getenv('NARADA_CALLBACK_SECRET')
if expected_secret and payload.secret != expected_secret:
raise HTTPException(status_code=401, detail="Invalid secret")
# Process the webhook payload
print(f"Task {payload.requestId} completed with status: {payload.status}")
if payload.status == "success":
print(f"Response: {payload.response.get('text')}")
handle_success(payload)
elif payload.status == "error":
print(f"Task failed: {payload.response.get('text')}")
handle_error(payload)
elif payload.status == "input-required":
print(f"Input required: {payload.response.get('text')}")
handle_input_required(payload)
# Always respond with 200 to acknowledge receipt
return {"received": True}
def handle_success(payload: WebhookPayload):
# Your success handling logic
pass
def handle_error(payload: WebhookPayload):
# Your error handling logic
pass
def handle_input_required(payload: WebhookPayload):
# Your input handling logic
pass
```
### Response Requirements
Your webhook endpoint must:
Configure your endpoint to accept HTTP POST requests with JSON payloads.
Return a `200 OK` response promptly to acknowledge receipt. Perform long-running operations asynchronously.
## Security Best Practices
### 1. Use HTTPS
Always use HTTPS endpoints to ensure your `callbackSecret` and webhook data are encrypted in transit.
```bash theme={null}
# ✅ Good
https://api.yourdomain.com/webhook
# ❌ Bad
http://api.yourdomain.com/webhook
```
### 2. Verify Authentication
You can authenticate webhook requests using either `callbackHeaders` or `callbackSecret`:
**Using callbackHeaders (Recommended for API-based auth):**
```javascript theme={null}
// Verify Authorization header from callbackHeaders
const authHeader = req.headers.authorization;
if (authHeader !== `Bearer ${process.env.WEBHOOK_TOKEN}`) {
return res.status(401).json({ error: 'Unauthorized' });
}
```
**Using callbackSecret:**
```javascript theme={null}
// Verify secret from payload body
if (payload.secret !== process.env.NARADA_CALLBACK_SECRET) {
return res.status(401).json({ error: 'Unauthorized' });
}
```
**Using both (Maximum security):**
```javascript theme={null}
// Verify both header and secret
const authHeader = req.headers.authorization;
const isValidHeader = authHeader === `Bearer ${process.env.WEBHOOK_TOKEN}`;
const isValidSecret = payload.secret === process.env.NARADA_CALLBACK_SECRET;
if (!isValidHeader || !isValidSecret) {
return res.status(401).json({ error: 'Unauthorized' });
}
```
Store your authentication tokens and secrets in environment variables, never in your code or version control. Use `callbackHeaders` for API-based authentication and `callbackSecret` for payload-based verification.
## Common Issues
**Possible causes:**
* Callback URL is not publicly accessible
* Firewall blocking incoming requests
* HTTPS certificate issues
* Server is down or not responding
**Solutions:**
* Verify your endpoint is publicly accessible
* Check firewall and security group settings
* Ensure valid SSL certificate
* Monitor server uptime and health
## Comparison: Webhooks vs Polling
| Feature | Webhooks | Polling |
| --------------------- | ------------------- | ------------------------ |
| **Response Time** | Immediate | Delayed by poll interval |
| **Server Load** | Low | Higher (active requests) |
| **Implementation** | Moderate complexity | Simple |
| **Firewall Friendly** | Requires open ports | Yes |
| **Scalability** | Excellent | Limited |
| **Real-time Updates** | Yes | No |
| **API Efficiency** | High | Lower |
| **Production Ready** | Yes (recommended) | Yes |
Use webhooks for production applications where real-time notifications are important. Use [polling via Get Response](/api-reference/get-response) when webhook setup is not feasible.
## Best Practices Summary
Return `200 OK` promptly and process long-running tasks asynchronously
Verify webhooks using callbackHeaders or callbackSecret to ensure requests are from Narada Cloud
Maintain comprehensive logs for debugging and monitoring webhook delivery
Use HTTPS to protect your callbackHeaders, callbackSecret, and webhook data in transit