AutoGPT Documentation

AutoGPT is the vision of accessible AI for everyone, to use and to build on. Our mission is to provide the tools, so that you can focus on what matters.

RAW Doc

Technical Documentation: Significant-Gravitas/AutoGPT

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (README.md)

AutoGPT — AI agents that finish the work


The open-source platform for AI agents

AutoGPT lets you build, deploy, and run AI agents that carry out complete workflows. Describe an outcome in plain English or shape every step in the visual builder, then run the agent on demand, on a schedule, or from a trigger.

185,000+ GitHub stars. Cited by:

“Next frontier of prompt engineering imo: ‘AutoGPTs’.” Andrej Karpathy, founding member of OpenAI
“If you have a phone you can run AutoGPT. You don't even need to learn how to code.” Amjad Masad, co-founder & CEO of Replit
“AutoGPT might be the next big step in AI.” Lior Alexander, CEO of AlphaSignal

Four surfaces, one platform

| AutoPilot
Describe the job in plain English and turn the conversation into a working agent. | Agents
See every agent, run, cost, and action that needs your attention. |

| --- | --- |
| Marketplace
Start from proven agents, add one to your library, and customize it for your work. | Build
Drag, connect, branch, and inspect blocks for exact control over every step. |


Get started

AutoGPT Platform — public, hosted, and managed

The hosted Platform is publicly available. We manage the infrastructure, model access, credentials, reliability, and updates so you can focus on the work your agents perform.

Get started on AutoGPT Platform →

Take the interactive tour →

  • AutoPilot, Agents, Marketplace, and Build
  • 45+ connected platforms and hundreds of AI models
  • No model API keys or infrastructure setup
  • Agents that run on demand, on schedules, and from triggers

The hosted Platform is a paid service with usage-based agent runs. Compare plans and pricing →

Self-host AutoGPT

[!NOTE]
Self-hosting is the free path. You provide the infrastructure and model API keys, and you maintain the deployment. If you want zero setup, use the managed Platform.

The Linux and macOS single-container release installer is coming with the next
appliance release. Until the public installer endpoint and image tags pass the
documented release gates,
use the manual self-hosting guide.

The release installer will require an already-running local Docker daemon using
Linux containers on amd64 or arm64. It pulls the published appliance and
runs its immutable digest; it does not install Docker or build from source.
Windows users should continue with the manual self-hosting guide for now.

Read the self-hosting guide →


Managed Platform vs. self-hosting

AutoGPT Platform Self-hosted
Access Public signup Operate the published appliance or a development checkout
Cost Paid plan plus agent usage No license fee; pay your own infrastructure and model providers
Setup Managed Docker and configuration required
Model access Built in Bring your own API keys
Updates and operations Managed by AutoGPT Managed by you
Core builder and agent runtime Included Included
Data and infrastructure control Hosted by AutoGPT Runs on your infrastructure
Support Plan-dependent Community support

Both paths use the same repository. Choose the managed Platform when you want agents running immediately; self-host when infrastructure control matters more than operational convenience.


Why the hosted Platform is paid

Every agent run consumes real model usage, compute, storage, secrets management, and operational support. The managed Platform covers that infrastructure and funds continued development of the open-source project.

Self-hosting remains available without a license fee for people and teams that want to provide and operate those resources themselves.


What you can automate

Area Example
Executive operations Prepare a daily brief from internal and external signals
Sales Research every account before tomorrow's meetings
Marketing Turn a launch brief into campaign drafts across channels
Engineering Triage incidents and start with a likely cause
Customer support Draft replies, collect context, and flag escalations
Research Monitor sources and return structured reports when something changes

Integrations

Connect the apps that are yours. AutoGPT provides access to hundreds of AI models and connects agents to 45+ platforms, including:

Gmail · Google Calendar · Google Docs · Google Sheets · GitHub · Slack · Discord · Notion · HubSpot · Linear · Airtable · Jira · Salesforce · Stripe · Webflow

Explore the integrations →


Community and support

Resource Link
Discord Join the AutoGPT community
Documentation docs.agpt.co
Bug reports GitHub Issues
Feature requests GitHub Issues
Contributing CONTRIBUTING.md

License

Component License What it means
autogpt_platform/ Polyform Shield Free for personal and internal business use; cannot be sold as a competing hosted service
classic/ and everything else MIT Permissive open-source use

AutoGPT Classic

Looking for the original standalone AutoGPT agent? It remains available in classic/ under the MIT License.


Contributors


Deutsch |
Español |
français |
日本語 |
한국어 |
Português |
Русский |
中文


Content Index (`docs/content/index.md`)

AutoGPT Documentation

Welcome to the AutoGPT Documentation.

What is the AutoGPT Platform?

The AutoGPT Platform is a groundbreaking system that revolutionizes AI utilization for businesses and individuals. It enables the creation, deployment, and management of continuous agents that work tirelessly on your behalf, bringing unprecedented efficiency and innovation to your workflows.

Key Features

  • Seamless Integration and Low-Code Workflows: Rapidly create complex workflows without extensive coding knowledge.
  • Autonomous Operation and Continuous Agents: Deploy cloud-based assistants that run indefinitely, activating on relevant triggers.
  • Intelligent Automation and Maximum Efficiency: Streamline workflows by automating repetitive processes.
  • Reliable Performance and Predictable Execution: Enjoy consistent and dependable long-running processes.

Platform Architecture

The AutoGPT Platform consists of two main components:

1. AutoGPT Server

The powerhouse of our platform, containing:

  • Source Code: Core logic driving agents and automation processes.
  • Infrastructure: Robust systems ensuring reliable and scalable performance.
  • Marketplace: A comprehensive marketplace for pre-built agents.

2. AutoGPT Frontend

The user interface where you interact with the platform:

  • Agent Builder: Design and configure your own AI agents.
  • Workflow Management: Build, modify, and optimize automation workflows.
  • Deployment Controls: Manage the lifecycle of your agents.
  • Ready-to-Use Agents: Select from pre-configured agents.
  • Agent Interaction: Run and interact with agents through a user-friendly interface.
  • Monitoring and Analytics: Track agent performance and gain insights.

Platform Components

Agents and Workflows

In the platform, you can create highly customized workflows to build agents. An agent is essentially an automated workflow that you design to perform specific tasks or processes. Create customized workflows to build agents for various tasks, including:

  • Data processing and analysis
  • Task scheduling and management
  • Communication and notification systems
  • Integration between different software tools
  • AI-powered decision making and content generation

Blocks as Integrations

Blocks represent actions and are the building blocks of your workflows, including:

  • Connections to external services
  • Data processing tools
  • AI models for various tasks
  • Custom scripts or functions
  • Conditional logic and decision-making components

You can learn more under: Build your own Blocks

Available Language Models

The platform comes pre-integrated with cutting-edge LLM providers:

License Overview

We've adopted a dual-license approach to balance open collaboration with sustainable development:

  • MIT License: The majority of the AutoGPT repository remains under this license.
  • Polyform Shield License: Applies to the new autogpt_platform folder.

This strategy allows us to share previously closed-source components, fostering a vibrant ecosystem of developers and users.

Ready to Get Started?


Engineering README (`docs/engineering/README.md`)

Engineering notes

Working documents for the team and the agents that work on AutoGPT: analytics and tracking plans, rollout plans, runbooks, internal references, and design and architecture notes.

Nothing in this folder is on the docs site. The site at agpt.co/docs is built from docs/home/, docs/platform/ and docs/integrations/, and shows the pages listed in each folder's SUMMARY.md. A page written for people outside the team, whether they use, self-host or contribute to AutoGPT, goes there. Everything else goes here.

These notes are still public. The repository is public, and AutoPilot's documentation search indexes every Markdown file under docs/, this folder included.

  • Don't list a file from this folder in a SUMMARY.md.
  • To link to a note from a published page, use its GitHub URL on dev. The relative path does not exist on the docs site.

Home README (`docs/home/README.md`)


cover: .gitbook/assets/Banner_image.png
coverY: 56.53835084561286
layout:
width: wide
cover:
visible: true
size: full
title:
visible: false
visible: false
tableOfContents:
visible: false
outline:
visible: false
pagination:
visible: false
metadata:
visible: true


Developer Platform

AutoGPT Documentation

Cover image
:leaf: AutoGPT Platform Get started with the developer platform in 5 minutes. AutoGPT Platform AGPT_Platform.png
:server: Integrations Learn more about hosting the developer platform. Integrations Integrations.png
:terminal: Contribute Browse, test, and implement APIs. Contributing to the Docs Contribute.png

{% columns %}
{% column valign="middle" %}

{% endcolumn %}

{% column %}

{% endcolumn %}
{% endcolumns %}


Join a community of 65,000+ developers

:discord: Discord community Join our Discord community to post questions, get help, and share resources with our growing community of over 55,000 members. Join Discord https://discord.com/invite/autogpt
:github: GitHub Our product is 100% open source and built by developers just like you. Head to our GitHub repository to learn how to submit your first PR. Submit a PR https://github.com/Significant-Gravitas/AutoGPT

Integrations README (`docs/integrations/README.md`)


layout:
width: default
title:
visible: true
visible: true
tableOfContents:
visible: false
outline:
visible: true
pagination:
visible: true
metadata:
visible: true


AutoGPT Blocks Overview

AutoGPT uses a modular approach with various "blocks" to handle different tasks. These blocks are the building blocks of AutoGPT workflows, allowing users to create complex automations by combining simple, specialized components.

{% hint style="info" %}
Creating Your Own Blocks

Want to create your own custom blocks? Check out our guides:

Below is a comprehensive list of all available blocks, categorized by their primary function. Click on any block name to view its detailed documentation.

Basic Operations

Block Name Description
Add Memory Add new memories to Mem0 with user segmentation
Add To Dictionary Adds a new key-value pair to a dictionary
Add To Library From Store Add an agent from the store to your personal library
Add To List Adds a new entry to a list
Agent Date Input Block for date input
Agent Dropdown Input Block for dropdown text selection
Agent File Input Block for file upload input (string path for example)
Agent Google Drive File Input Agent-level input for a Google Drive file
Agent Input A block that accepts and processes user input values within a workflow, supporting various input types and validation
Agent Long Text Input Block for long text input (multi-line)
Agent Number Input Block for number input
Agent Output A block that records and formats workflow results for display to users, with optional Jinja2 template formatting support
Agent Short Text Input Block for short text input (single-line)
Agent Table Input Block for table data input with customizable headers
Agent Time Input Block for time input
Agent Toggle Input Block for boolean toggle input
Block Installation Given a code string, this block allows the verification and installation of a block code into the system
Concatenate Lists Concatenates multiple lists into a single list
Dictionary Is Empty Checks if a dictionary is empty
File Store Downloads and stores a file from a URL, data URI, or local path
Find In Dictionary A block that looks up a value in a dictionary, list, or object by key or index and returns the corresponding value
Find In List Finds the index of the value in the list
Flatten List Flattens a nested list structure into a single flat list
Get All Memories Retrieve all memories from Mem0 with optional conversation filtering
Get Latest Memory Retrieve the latest memory from Mem0 with optional key filtering
Get List Item Returns the element at the given index
Get Store Agent Details Get detailed information about an agent from the store
Get Weather Information Retrieves weather information for a specified location using OpenWeatherMap API
Human In The Loop Pause execution for human review
Interleave Lists Interleaves elements from multiple lists in round-robin fashion, alternating between sources
List Difference Computes the difference between two lists
List Intersection Computes the intersection of two lists, returning only elements present in both
List Is Empty Checks if a list is empty
List Library Agents List all agents in your personal library
Note A visual annotation block that displays a sticky note in the workflow editor for documentation and organization purposes
Print To Console A debugging block that outputs text to the console for monitoring and troubleshooting workflow execution
Remove From Dictionary Removes a key-value pair from a dictionary
Remove From List Removes an item from a list by value or index
Replace Dictionary Value Replaces the value for a specified key in a dictionary
Replace List Item Replaces an item at the specified index
Reverse List Order Reverses the order of elements in a list
Search Memory Search memories in Mem0 by user
Search Store Agents Search for agents in the store
Slant3D Cancel Order Cancel an order before production starts
Slant3D Create Order Order physical 3D-printed parts from Slant3D for manufacturing and delivery
Slant3D Estimate Order Quote a 3D-printed parts order including shipping by creating an uncharged Slant3D draft
Slant3D Estimate Shipping Estimate delivery costs for physical 3D-printed parts by creating an uncharged Slant3D order draft
Slant3D Filament Find available materials and colors for quoting or ordering physical 3D-printed parts from Slant3D
Slant3D Get Orders Get all orders for the account
Slant3D Process Order Submit an approved Slant3D draft to order physical 3D-printed parts for manufacturing and delivery
Slant3D Slicer Get a live 3D printing quote for physical parts from STL URLs or attached workspace files
Slant3D Tracking Track order status and shipping
Store Value Holds or receives a value and outputs it statically so that it can be used multiple times within the same agent run
Universal Type Converter This block is used to convert a value to a universal type
XML Parser Parses XML using gravitasml to tokenize and coverts it to dict
Zip Lists Zips multiple lists together into a list of grouped elements

Data Processing

Block Name Description
Airtable Create Base Create or find a base in Airtable
Airtable Create Field Add a new field to an Airtable table
Airtable Create Records Create records in an Airtable table
Airtable Create Table Create a new table in an Airtable base
Airtable Delete Records Delete records from an Airtable table
Airtable Get Record Get a single record from Airtable
Airtable List Bases List all bases in Airtable
Airtable List Records List records from an Airtable table
Airtable List Schema Get the complete schema of an Airtable base
Airtable Update Field Update field properties in an Airtable table
Airtable Update Records Update records in an Airtable table
Airtable Update Table Update table properties
Airtable Webhook Trigger Starts a flow whenever Airtable emits a webhook event
Baas Bot Delete Recording Permanently delete a meeting's recorded data
Baas Bot Fetch Meeting Data Retrieve recorded meeting data
Create Dictionary Creates a dictionary with the specified key-value pairs
Create List Creates a list with the specified values
Data For Seo Keyword Suggestions Get keyword suggestions from DataForSEO Labs Google API
Data For Seo Related Keywords Get related keywords from DataForSEO Labs Google API
Exa Create Import Import CSV data to use with websets for targeted searches
Exa Delete Import Delete an import
Exa Export Webset Export webset data in JSON, CSV, or JSON Lines format
Exa Get Import Get the status and details of an import
Exa Get New Items Get items added since a cursor - enables incremental processing without reprocessing
Exa List Imports List all imports with pagination support
File Read Reads a file and returns its content as a string, with optional chunking by delimiter and size limits
Google Calendar Read Events Retrieves upcoming events from a Google Calendar with filtering options
Google Docs Append Markdown Append Markdown content to the end of a Google Doc with full formatting - ideal for LLM/AI output
Google Docs Append Plain Text Append plain text to the end of a Google Doc (no formatting applied)
Google Docs Create Create a new Google Doc
Google Docs Delete Content Delete a range of content from a Google Doc
Google Docs Export Export a Google Doc to PDF, Word, text, or other formats
Google Docs Find Replace Plain Text Find and replace plain text in a Google Doc (no formatting applied to replacement)
Google Docs Format Text Apply formatting (bold, italic, color, etc
Google Docs Get Metadata Get metadata about a Google Doc
Google Docs Get Structure Get document structure with index positions for precise editing operations
Google Docs Insert Markdown At Insert formatted Markdown at a specific position in a Google Doc - ideal for LLM/AI output
Google Docs Insert Page Break Insert a page break into a Google Doc
Google Docs Insert Plain Text Insert plain text at a specific position in a Google Doc (no formatting applied)
Google Docs Insert Table Insert a table into a Google Doc, optionally with content and Markdown formatting
Google Docs Read Read text content from a Google Doc
Google Docs Replace All With Markdown Replace entire Google Doc content with formatted Markdown - ideal for LLM/AI output
Google Docs Replace Content With Markdown Find text and replace it with formatted Markdown - ideal for LLM/AI output and templates
Google Docs Replace Range With Markdown Replace a specific index range in a Google Doc with formatted Markdown - ideal for LLM/AI output
Google Docs Set Public Access Make a Google Doc public or private
Google Docs Share Share a Google Doc with specific users
Google Sheets Add Column Add a new column with a header
Google Sheets Add Dropdown Add a dropdown list (data validation) to cells
Google Sheets Add Note Add a note to a cell in a Google Sheet
Google Sheets Append Row Append or Add a single row to the end of a Google Sheet
Google Sheets Batch Operations This block performs multiple operations on a Google Sheets spreadsheet in a single batch request
Google Sheets Clear This block clears data from a specified range in a Google Sheets spreadsheet
Google Sheets Copy To Spreadsheet Copy a sheet from one spreadsheet to another
Google Sheets Create Named Range Create a named range to reference cells by name instead of A1 notation
Google Sheets Create Spreadsheet This block creates a new Google Sheets spreadsheet with specified sheets
Google Sheets Delete Column Delete a column by header name or column letter
Google Sheets Delete Rows Delete specific rows from a Google Sheet by their row indices
Google Sheets Export Csv Export a Google Sheet as CSV data
Google Sheets Filter Rows Filter rows in a Google Sheet based on a column condition
Google Sheets Find Find text in a Google Sheets spreadsheet
Google Sheets Find Replace This block finds and replaces text in a Google Sheets spreadsheet
Google Sheets Format Format a range in a Google Sheet (sheet optional)
Google Sheets Get Column Extract all values from a specific column
Google Sheets Get Notes Get notes from cells in a Google Sheet
Google Sheets Get Row Get a specific row by its index
Google Sheets Get Row Count Get row count and dimensions of a Google Sheet
Google Sheets Get Unique Values Get unique values from a column
Google Sheets Import Csv Import CSV data into a Google Sheet
Google Sheets Insert Row Insert a single row at a specific position
Google Sheets List Named Ranges List all named ranges in a spreadsheet
Google Sheets Lookup Row Look up a row by finding a value in a specific column
Google Sheets Manage Sheet Create, delete, or copy sheets (sheet optional)
Google Sheets Metadata This block retrieves metadata about a Google Sheets spreadsheet including sheet names and properties
Google Sheets Protect Range Protect a cell range or entire sheet from editing
Google Sheets Read A block that reads data from a Google Sheets spreadsheet using A1 notation range selection
Google Sheets Remove Duplicates Remove duplicate rows based on specified columns
Google Sheets Set Public Access Make a Google Spreadsheet public or private
Google Sheets Share Spreadsheet Share a Google Spreadsheet with users or get shareable link
Google Sheets Sort Sort a Google Sheet by one or two columns
Google Sheets Update Cell Update a single cell in a Google Sheets spreadsheet
Google Sheets Update Row Update a specific row by its index
Google Sheets Write A block that writes data to a Google Sheets spreadsheet at a specified A1 notation range
JSON Decoder Decodes a JSON string into the value or data structure, it represents, e
JSON Encoder Encodes any value or data structure into a JSON string
Keyword Suggestion Extractor Extract individual fields from a KeywordSuggestion object
Persist Information Persists a key-value pair for use across multiple runs of an agent
RMFG Analyze Design Uploads a STEP file to RMFG, a manufacturer that makes and ships real sheet-metal and tube-laser parts, and returns its analyzed parts: the first step before a live quote, cart or order
RMFG Create Cart Creates an RMFG cart with a website checkout link for one or more configured designs, priced live with shipping and tax once an address is set
RMFG Create DFM Report Runs an RMFG manufacturability (DFM) check on a configured design
RMFG Create Quote Gets a live price from RMFG, a manufacturer that laser-cuts, bends and ships real sheet-metal and tube parts, plus manufacturability findings
RMFG Create Review Link Creates an RMFG review link so a person can inspect and adjust a design
RMFG Get Cart Fetches an RMFG cart and its latest quote by ID
RMFG Get DFM Report Fetches an RMFG DFM report by ID
RMFG Get Design Fetches an RMFG design and its analyzed parts by ID
RMFG Get Order Fetches an RMFG order's status and shipment tracking
RMFG Get Quote Fetches an RMFG quote by ID
RMFG Get Review Link Fetches an RMFG review link and the configuration a person saved on it
RMFG List Finishes Lists the finishes RMFG can apply to sheet or tube parts
RMFG List Hardware Lists the taps, studs, nuts or standoffs RMFG can install
RMFG List Materials Lists the sheet-metal stock RMFG can cut and bend, with thickness in mm and inches
RMFG List Orders Lists the RMFG account's manufacturing orders
RMFG List Powder Coat Colors Lists the powder-coat colors RMFG offers
RMFG List Tube Profiles Lists the tube stock profiles RMFG can laser-cut
RMFG Pay Cart Pays an RMFG cart with the saved card and places a real production order
RMFG Update Cart Updates an open RMFG cart's address, shipping option or items
Read Spreadsheet Reads CSV and Excel files and outputs the data as a list of dictionaries and individual rows
Related Keyword Extractor Extract individual fields from a RelatedKeyword object
Retrieve Information Reads back a key-value pair previously saved by PersistInformationBlock
SQL Query Execute a SQL query
Screenshot Web Page Takes a screenshot of a specified website using ScreenshotOne API
Stripe Link Create Card Spend Request Create a Stripe Link spend request for a one-time virtual card
Stripe Link Create Token Spend Request MPP step 2 of 3: ask the user to authorize a payment to a merchant that answers HTTP 402, and provision a Shared Payment Token for it
Stripe Link Get Payment Challenge MPP step 1 of 3: read a merchant's HTTP 402 payment challenge to learn its network ID and amount
Stripe Link Get Shipping Address Get the delivery addresses saved on the user's Link wallet, with the default one resolved for you
Stripe Link Get Spend Request Status Check whether a Stripe Link spend request has been approved yet
Stripe Link Get User Info Get the Link account holder's name, email and phone
Stripe Link List Payment Methods List the cards and bank accounts in the user's Link wallet
Stripe Link MPP Pay MPP step 3 of 3: spend an approved Shared Payment Token at the merchant's endpoint
Stripe Link Retrieve Card Get the one-time virtual card number and CVC for an approved spend request, to type into a normal checkout form
Stripe Subscription Trigger Triggers on Stripe subscription events (new, upgrade, cancel)

Text Processing

Block Name Description
Code Extraction Extracts code blocks from text and identifies their programming languages
Combine Texts This block combines multiple input texts into a single output text
Countdown Timer This block triggers after a specified duration
Extract Text Information This block extracts the text from the given text using the pattern (regex)
Fill Text Template This block formats the given texts using the format template
Get Current Date This block outputs the current date with an optional offset
Get Current Date And Time This block outputs the current date and time
Get Current Time This block outputs the current time
Match Text Pattern Matches text against a regex pattern and forwards data to positive or negative output based on the match
Text Decoder Decodes a string containing escape sequences into actual text
Text Encoder Encodes a string by converting special characters into escape sequences
Text Replace This block is used to replace a text with a new text
Text Split This block is used to split a text into a list of strings
Word Character Count Counts the number of words and characters in a given text

AI and Language Models

Block Name Description
AI Ad Maker Video Creator Creates an AI‑generated 30‑second advert (text + images)
AI Condition Uses AI to evaluate natural language conditions and provide conditional outputs
AI Conversation A block that facilitates multi-turn conversations with a Large Language Model (LLM), maintaining context across message exchanges
AI Image Customizer Generate and edit custom images using Google's Nano-Banana models from Gemini
AI Image Editor Edit images using Flux Kontext or Google Nano Banana models
AI Image Generator Generate images using various AI models through a unified interface
AI List Generator A block that creates lists of items based on prompts using a Large Language Model (LLM), with optional source data for context
AI Music Generator This block generates music using Meta's MusicGen model on Replicate
AI Screenshot To Video Ad Turns a screenshot into an engaging, avatar‑narrated video advert
AI Shortform Video Creator Creates a shortform video using revid
AI Structured Response Generator A block that generates structured JSON responses using a Large Language Model (LLM), with schema validation and format enforcement
AI Text Generator A block that produces text responses using a Large Language Model (LLM) based on customizable prompts and system instructions
AI Text Summarizer A block that summarizes long texts using a Large Language Model (LLM), with configurable focus topics and summary styles
AI Video Generator Generate videos using FAL AI models
Bannerbear Text Overlay Add text overlay to images using Bannerbear templates
Claude Code Execute tasks using Claude Code in an E2B sandbox
Code Generation Generate or refactor code using an OpenAI API key or a connected ChatGPT plan through Codex App Server
Create Talking Avatar Video This block integrates with D-ID to create video clips and retrieve their URLs
Exa Answer Get an LLM answer to a question informed by Exa search results
Exa Create Enrichment Create enrichments to extract additional structured data from webset items
Exa Create Research Create research task with optional waiting - explores web and synthesizes findings with citations
Ideogram Model This block runs Ideogram models with both simple and advanced settings
Jev Ask Many Ask multiple Choice, Score, or Noul questions of one shared state with Jev in a single call
Jev Choice Make a typed choice with Jev
Jev Filter Filter items with sequential Jev scores, one call per item
Jev Pick Best Choose the best candidate using Jev and rank candidates directly by its probabilities
Jev Route Route data using Jev's typed choice
Jev Score Score evidence with Jev using an explicit ordered scale
Jev Yes No Ask Jev a plain-language yes/no question and forward data to the chosen pin, or unsure below your confidence threshold
Jina Chunking Chunks texts using Jina AI's segmentation service
Jina Embedding Generates embeddings using Jina AI
Orchestrator Uses AI to intelligently decide what tool to use
Perplexity Query Perplexity's sonar models with real-time web search capabilities and receive annotated responses with source citations
Replicate Flux Advanced Model This block runs Flux models on Replicate with advanced settings
Replicate Model Run Replicate models synchronously
Smart Search Natural-language search for people, leads or companies using DataForB2B's B2B database — describe your ideal lead or ICP in plain English and get matching profiles
Stagehand Act Interact with a web page by performing actions on a web page
Stagehand Extract Extract structured data from a webpage
Stagehand Observe Find suggested actions for your workflows
Unreal Text To Speech Converts text to speech using the Unreal Speech API
Video Narration Generate AI narration and add to video

Search and Information Retrieval

Block Name Description
AnySearch Searches the web using AnySearch - general queries plus vertical domains (finance, academic, health, legal, and more) via domain/sub_domain filters
AnySearch Extract Extracts readable content from a single URL using AnySearch, optimized for LLM consumption
AnySearch Parallel Search Runs several AnySearch queries in parallel (client-side concurrency via asyncio)
Ask Wolfram Ask Wolfram Alpha a question
Exa Bulk Webset Items Get all items from a webset in bulk (with configurable limits)
Exa Cancel Enrichment Cancel a running enrichment operation
Exa Cancel Webset Cancel all operations being performed on a Webset
Exa Cancel Webset Search Cancel a running webset search
Exa Contents Retrieves document contents using Exa's contents API
Exa Create Monitor Create automated monitors to keep websets updated with fresh data on a schedule
Exa Create Or Find Webset Create a new webset or return existing one by external_id (idempotent operation)
Exa Create Webset Create a new Exa Webset for persistent web search collections with optional waiting for initial results
Exa Create Webset Search Add a new search to an existing webset to find more items
Exa Delete Enrichment Delete an enrichment from a webset
Exa Delete Monitor Delete a monitor from a webset
Exa Delete Webset Delete a Webset and all its items
Exa Delete Webset Item Delete a specific item from a webset
Exa Find Or Create Search Find existing search by query or create new - prevents duplicate searches in workflows
Exa Find Similar Finds similar links using Exa's findSimilar API
Exa Get Enrichment Get the status and details of a webset enrichment
Exa Get Monitor Get the details and status of a webset monitor
Exa Get Research Get status and results of a research task
Exa Get Webset Retrieve a Webset by ID or external ID
Exa Get Webset Item Get a specific item from a webset by its ID
Exa Get Webset Search Get the status and details of a webset search
Exa List Monitors List all monitors with optional webset filtering
Exa List Research List all research tasks with pagination support
Exa List Webset Items List items in a webset with pagination support
Exa List Websets List all Websets with pagination support
Exa Preview Webset Preview how a search query will be interpreted before creating a webset
Exa Search Searches the web using Exa's advanced search API
Exa Update Enrichment Update an existing enrichment configuration
Exa Update Monitor Update a monitor's status, schedule, or metadata
Exa Update Webset Update metadata for an existing Webset
Exa Wait For Enrichment Wait for a webset enrichment to complete with progress tracking
Exa Wait For Research Wait for a research task to complete with configurable timeout
Exa Wait For Search Wait for a specific webset search to complete with progress tracking
Exa Wait For Webset Wait for a webset to reach a specific status with progress tracking
Exa Webset Items Summary Get a summary of webset items without retrieving all data
Exa Webset Status Get a quick status overview of a webset
Exa Webset Summary Get a comprehensive summary of a webset with samples and statistics
Extract Website Content This block scrapes the content from the given web URL
Fact Checker This block checks the factuality of a given statement using Jina AI's Grounding API
Firecrawl Crawl Firecrawl crawls websites to extract comprehensive data while bypassing blockers
Firecrawl Extract Firecrawl crawls websites to extract comprehensive data while bypassing blockers
Firecrawl Map Website Firecrawl maps a website to extract all the links
Firecrawl Scrape Firecrawl scrapes a website to extract comprehensive data while bypassing blockers
Firecrawl Search Firecrawl searches the web for the given query
Get Person Detail Get detailed person data with Apollo API, including email reveal
Get Wikipedia Summary This block fetches the summary of a given topic from Wikipedia
Google Maps Search This block searches for local businesses using Google Maps API
Hacker News Get Item Get a Hacker News story, comment, poll or job by its id or link, with its comments as a list in reading order
Hacker News Get Stories Get the top (front page), new, best, Ask HN, Show HN or job stories from Hacker News, in the order Hacker News ranks them
Hacker News Get User Get a Hacker News user's profile by username: karma, when the account was created, the about text and how many items they have posted
Hacker News Search Search Hacker News stories and comments by keyword, author, date, points or linked domain, sorted by relevance or newest first
Search Filter Typeahead Resolve the exact filter value (company, industry, job title, skill, school, investor, location, category) for people and company searches with DataForB2B
Search Organizations Search for organizations in Apollo
Search People Search for people in Apollo
Search The Web This block searches the internet for the given search query
Tavily Crawl Crawls a website with Tavily, following links from the root URL and extracting page content
Tavily Extract Extracts page content from one or more URLs using Tavily, optimized for LLM consumption
Tavily Map Maps a website's structure with Tavily, discovering its URLs without extracting content
Tavily Search Searches the web using Tavily's AI-native search API
Validate Emails Validate emails

Social Media and Content

Block Name Description
Approve Reddit Post Approves a Reddit post or comment from the mod queue
Ban Subreddit User Bans a user from a subreddit
Create Discord Thread Creates a new thread in a Discord channel
Create Reddit Post Create a new post on a subreddit
Delete Reddit Comment Delete a Reddit comment that you own
Delete Reddit Post Delete a Reddit post that you own
Delete Telegram Message Delete a message from a Telegram chat
Discord Channel Info Resolves Discord channel names to IDs and vice versa
Discord Get Current User Gets information about the currently authenticated Discord user using OAuth2 credentials
Discord User Info Gets information about a Discord user by their ID
Edit Reddit Post Edit the body text of an existing Reddit post that you own
Edit Telegram Message Edit the text of an existing message sent by the bot
Get Linkedin Profile Fetch LinkedIn profile data using Enrichlayer
Get Linkedin Profile Picture Get LinkedIn profile pictures using Enrichlayer
Get Reddit Comment Get details about a specific Reddit comment by its ID
Get Reddit Comment Replies Get replies to a specific Reddit comment
Get Reddit Inbox Get messages, mentions, and comment replies from your Reddit inbox
Get Reddit Post Get detailed information about a specific Reddit post by its ID
Get Reddit Post Comments Get top-level comments on a Reddit post
Get Reddit Posts This block fetches Reddit posts from a defined subreddit name
Get Reddit User Info Get information about a Reddit user including karma, account age, and verification status
Get Subreddit Flairs Get available link flair options for a subreddit
Get Subreddit Info Get information about a subreddit including subscriber count, description, and rules
Get Subreddit Rules Get the rules for a subreddit to ensure compliance before posting
Get Telegram File Download a file from Telegram using its file_id
Get User Posts Fetch posts by a specific Reddit user
Linkedin Person Lookup Look up LinkedIn profiles by person information using Enrichlayer
Linkedin Role Lookup Look up LinkedIn profiles by role in a company using Enrichlayer
Lock Reddit Post Locks or unlocks a Reddit post or comment to prevent or allow replies
Mod Queue Fetches the mod queue for a subreddit
Post Reddit Comment This block posts a Reddit comment on a specified Reddit post
Post To Bluesky Post to Bluesky using Ayrshare
Post To Facebook Post to Facebook using Ayrshare
Post To GMB Post to Google My Business using Ayrshare
Post To Instagram Post to Instagram using Ayrshare
Post To LinkedIn Post to LinkedIn using Ayrshare
Post To Pinterest Post to Pinterest using Ayrshare
Post To Reddit Post to Reddit using Ayrshare
Post To Snapchat Post to Snapchat using Ayrshare
Post To Telegram Post to Telegram using Ayrshare
Post To Threads Post to Threads using Ayrshare
Post To Tik Tok Post to TikTok using Ayrshare
Post To X Post to X / Twitter using Ayrshare
Post To YouTube Post to YouTube using Ayrshare
Publish To Medium Publishes a post to Medium
Read Discord Messages Reads new messages from a Discord channel using a bot token and triggers when a new message is posted
Reddit Get My Posts Fetch posts created by the authenticated Reddit user (you)
Remove Reddit Post Removes a Reddit post or comment as a moderator
Reply To Discord Message Replies to a specific Discord message
Reply To Reddit Comment Reply to a specific Reddit comment
Reply To Telegram Message Reply to a specific message in a Telegram chat
Search Reddit Search Reddit for posts matching a query
Send Discord DM Sends a direct message to a Discord user using their user ID
Send Discord Embed Sends a rich embed message to a Discord channel
Send Discord File Sends a file attachment to a Discord channel
Send Discord Message Sends a message to a Discord channel using a bot token
Send Mod Mail Sends a modmail message from a subreddit to a user
Send Reddit Message Send a private message (DM) to a Reddit user
Send Slack Message Send a text message to any Slack channel, DM, or thread
Send Telegram Audio Send an audio file to a Telegram chat
Send Telegram Document Send a document (any file type) to a Telegram chat
Send Telegram Message Send a text message to a Telegram chat
Send Telegram Photo Send a photo to a Telegram chat
Send Telegram Video Send a video to a Telegram chat
Send Telegram Voice Send a voice message to a Telegram chat
Telegram Message Reaction Trigger Triggers when a reaction to a message is changed
Telegram Message Trigger Triggers when a message is received or edited in your Telegram bot
Transcribe Youtube Video Transcribes a YouTube video using a proxy
Twitter Add List Member This block adds a specified user to a Twitter List owned by the authenticated user
Twitter Bookmark Tweet This block bookmarks a tweet on Twitter
Twitter Create List This block creates a new Twitter List for the authenticated user
Twitter Delete List This block deletes a specified Twitter List owned by the authenticated user
Twitter Delete Tweet This block deletes a tweet on Twitter
Twitter Follow List This block follows a specified Twitter list for the authenticated user
Twitter Follow User This block follows a specified Twitter user
Twitter Get Blocked Users This block retrieves a list of users blocked by the authenticating user
Twitter Get Bookmarked Tweets This block retrieves bookmarked tweets from Twitter
Twitter Get Followers This block retrieves followers of a specified Twitter user
Twitter Get Following This block retrieves the users that a specified Twitter user is following
Twitter Get Home Timeline This block retrieves the authenticated user's home timeline
Twitter Get Liked Tweets This block gets information about tweets liked by a user
Twitter Get Liking Users This block gets information about users who liked a tweet
Twitter Get List This block retrieves information about a specified Twitter List
Twitter Get List Members This block retrieves the members of a specified Twitter List
Twitter Get List Memberships This block retrieves all Lists that a specified user is a member of
Twitter Get List Tweets This block retrieves tweets from a specified Twitter list
Twitter Get Muted Users This block gets a list of users muted by the authenticating user
Twitter Get Owned Lists This block retrieves all Lists owned by a specified Twitter user
Twitter Get Pinned Lists This block returns the Lists pinned by the authenticated user
Twitter Get Quote Tweets This block gets quote tweets for a specific tweet
Twitter Get Retweeters This block gets information about who has retweeted a tweet
Twitter Get Space Buyers This block retrieves a list of users who purchased tickets to a Twitter Space
Twitter Get Space By Id This block retrieves information about a single Twitter Space
Twitter Get Space Tweets This block retrieves tweets shared in a Twitter Space
Twitter Get Spaces This block retrieves information about multiple Twitter Spaces
Twitter Get Tweet This block retrieves information about a specific Tweet
Twitter Get Tweets This block retrieves information about multiple Tweets
Twitter Get User This block retrieves information about a specified Twitter user
Twitter Get User Mentions This block retrieves Tweets mentioning a specific user
Twitter Get User Tweets This block retrieves Tweets composed by a single user
Twitter Get Users This block retrieves information about multiple Twitter users
Twitter Hide Reply This block hides a reply to a tweet
Twitter Like Tweet This block likes a tweet
Twitter Mute User This block mutes a specified Twitter user
Twitter Pin List This block allows the authenticated user to pin a specified List
Twitter Post Tweet This block posts a tweet on Twitter
Twitter Remove Bookmark Tweet This block removes a bookmark from a tweet on Twitter
Twitter Remove List Member This block removes a specified user from a Twitter List owned by the authenticated user
Twitter Remove Retweet This block removes a retweet on Twitter
Twitter Retweet This block retweets a tweet on Twitter
Twitter Search Recent Tweets This block searches all public Tweets in Twitter history
Twitter Search Spaces This block searches for Twitter Spaces based on specified terms
Twitter Unfollow List This block unfollows a specified Twitter list for the authenticated user
Twitter Unfollow User This block unfollows a specified Twitter user
Twitter Unhide Reply This block unhides a reply to a tweet
Twitter Unlike Tweet This block unlikes a tweet
Twitter Unmute User This block unmutes a specified Twitter user
Twitter Unpin List This block allows the authenticated user to unpin a specified List
Twitter Update List This block updates a specified Twitter List owned by the authenticated user
Unban Subreddit User Unbans a user from a subreddit

Communication

Block Name Description
Agent Mail Create Draft Create a draft email for review or scheduled sending
Agent Mail Create Inbox Create a new email inbox for an AI agent via AgentMail
Agent Mail Create List Entry Add an email address or domain to an allow/block list
Agent Mail Create Pod Create a new pod for multi-tenant customer isolation
Agent Mail Create Pod Inbox Create a new email inbox within a pod
Agent Mail Delete Draft Delete a draft or cancel a scheduled email
Agent Mail Delete Inbox Permanently delete an AgentMail inbox and all its messages, threads, and drafts
Agent Mail Delete Inbox Thread Permanently delete a conversation thread and all its messages
Agent Mail Delete List Entry Remove an email address or domain from an allow/block list to stop filtering it
Agent Mail Delete Pod Permanently delete a pod
Agent Mail Forward Message Forward an email message to one or more recipients
Agent Mail Get Draft Retrieve a draft email to review its contents, recipients, and scheduled send status
Agent Mail Get Inbox Retrieve details of an existing AgentMail inbox including its email address, display name, and configuration
Agent Mail Get Inbox Thread Retrieve a conversation thread with all its messages
Agent Mail Get List Entry Check if an email address or domain is in an allow/block list
Agent Mail Get Message Retrieve a specific email message by ID
Agent Mail Get Message Attachment Download a file attachment from an email message
Agent Mail Get Org Thread Retrieve a conversation thread by ID from anywhere in the organization, without needing the inbox ID
Agent Mail Get Pod Retrieve details of an existing pod including its client_id mapping and metadata
Agent Mail Get Thread Attachment Download a file attachment from a conversation thread
Agent Mail List Drafts List drafts in an AgentMail inbox
Agent Mail List Entries List all entries in an AgentMail allow/block list
Agent Mail List Inbox Threads List all conversation threads in an AgentMail inbox
Agent Mail List Inboxes List all email inboxes in your AgentMail organization with pagination support
Agent Mail List Messages List messages in an AgentMail inbox
Agent Mail List Org Drafts List all drafts across every inbox in your organization
Agent Mail List Org Threads List threads across ALL inboxes in your organization
Agent Mail List Pod Drafts List all drafts across all inboxes within a pod
Agent Mail List Pod Inboxes List all inboxes within a pod
Agent Mail List Pod Threads List all conversation threads across all inboxes within a pod
Agent Mail List Pods List all tenant pods in your organization
Agent Mail Reply To Message Reply to an existing email in the same conversation thread
Agent Mail Send Draft Send a draft immediately, converting it into a delivered message
Agent Mail Send Message Send a new email from an AgentMail inbox
Agent Mail Update Draft Update a draft's content, recipients, or scheduled send time
Agent Mail Update Inbox Update the display name of an AgentMail inbox
Agent Mail Update Message Add or remove labels on an email message
Baas Bot Join Meeting Deploy a bot to join and record a meeting
Baas Bot Leave Meeting Remove a bot from an ongoing meeting
Gmail Add Label A block that adds a label to a specific email message in Gmail, creating the label if it doesn't exist
Gmail Create Draft Create draft emails in Gmail with automatic HTML detection and proper text formatting
Gmail Draft Reply Create draft replies to Gmail threads with automatic HTML detection and proper text formatting
Gmail Forward Forward Gmail messages to other recipients with automatic HTML detection and proper formatting
Gmail Get Profile Get the authenticated user's Gmail profile details including email address and message statistics
Gmail Get Thread A block that retrieves an entire Gmail thread (email conversation) by ID, returning all messages with decoded bodies for reading complete conversations
Gmail List Labels A block that retrieves all labels (categories) from a Gmail account for organizing and categorizing emails
Gmail Read A block that retrieves and reads emails from a Gmail account based on search criteria, returning detailed message information including subject, sender, body, and attachments
Gmail Remove Label A block that removes a label from a specific email message in a Gmail account
Gmail Reply Reply to Gmail threads with automatic HTML detection and proper text formatting
Gmail Send Send emails via Gmail with automatic HTML detection and proper text formatting
Hub Spot Engagement Manages HubSpot engagements - sends emails and tracks engagement metrics

Developer Tools

Block Name Description
AllQuiet Create Incident Creates an incident in All Quiet and pages the on-call responder
AllQuiet Get Incident Fetches a single All Quiet incident by ID
AllQuiet Get On Call Looks up who is on call in All Quiet, now or at a given time
AllQuiet Incident Trigger Triggers a graph when All Quiet posts an incident to this webhook
AllQuiet List Incidents Searches All Quiet incidents by status, severity, team or text
AllQuiet List Teams Lists All Quiet teams and their IDs
AllQuiet Update Incident Investigates, resolves, escalates or comments on an All Quiet incident
Conductor Create Session Start a new agent session (chat) in an existing Conductor workspace, optionally with a first prompt, and optionally wait for the agent's reply
Conductor Create Workspace Create a Conductor cloud workspace for a project or repository, optionally start its agent with a prompt and wait for the reply
Conductor Get Account Get an overview of your Conductor account in one call: who you are, the projects (repositories) you can open workspaces in, your sections and your routines
Conductor Get Session Get a Conductor agent session: its details, whether the agent is idle, working or errored, and recent transcript messages
Conductor Get Workspace Get everything about one Conductor workspace: details, current status, shared preview URL and its agent sessions
Conductor List Workspaces List Conductor workspaces, optionally filtered by project, state, name, repository, creator or activity date
Conductor Manage Routine Create a Conductor routine (a saved prompt that runs a fresh agent in a project whenever its webhook URL is called) or rotate a routine's webhook secret
Conductor Manage Section Create or delete a Conductor cloud section
Conductor Manage Session Rename, cancel or archive a Conductor agent session
Conductor Manage Workspace Change a Conductor workspace: rename it, archive, unarchive or sleep it, share or stop sharing a port at its public preview URL, or move it into a section
Conductor Send Message Send a prompt to a Conductor agent session and, by default, wait for the agent to finish and return its reply
Exa Code Context Search billions of GitHub repos, docs, and Stack Overflow for relevant code examples
Execute Code Executes code in a sandbox environment with internet access
Execute Code Step Execute code in a previously instantiated sandbox
Github Add Label A block that adds a label to a GitHub issue or pull request for categorization and organization
Github Assign Issue A block that assigns a GitHub user to an issue for task ownership and tracking
Github Assign PR Reviewer This block assigns a reviewer to a specified GitHub pull request
Github Comment A block that posts comments on GitHub issues or pull requests using the GitHub API
Github Compare Branches This block compares two branches or commits in a GitHub repository
Github Create Check Run Creates a new check run for a specific commit in a GitHub repository
Github Create Comment Object Creates a comment object for use with GitHub blocks
Github Create File This block creates a new file in a GitHub repository
Github Create PR Review This block creates a review on a GitHub pull request with optional inline comments
Github Create Repository This block creates a new GitHub repository
Github Create Status Creates a new commit status in a GitHub repository
Github Delete Branch This block deletes a specified branch
Github Discussion Trigger This block triggers on GitHub Discussions events
Github Fork Repository This block forks a GitHub repository to your account or an organization
Github Get CI Results This block gets CI results for a commit or PR, with optional search for specific errors/warnings in logs
Github Get Notification Thread This block fetches a single GitHub notification thread
Github Get PR Review Comments This block gets all review comments from a GitHub pull request or from a specific review
Github Get Repository Info This block retrieves metadata about a GitHub repository
Github Get Repository Traffic Get a GitHub repository's views and clones over the last 14 days, with unique visitors and cloners, plus its top referring sites and most viewed pages
Github Get Repository Tree This block lists the entire file tree of a GitHub repository recursively
Github Get User Info This block fetches information about a GitHub user, or about the authenticated user (yourself) if no username is given
Github Issues Trigger This block triggers on GitHub issues events
Github List Branches This block lists all branches for a specified GitHub repository
Github List Comments A block that retrieves all comments from a GitHub issue or pull request, including comment metadata and content
Github List Commits This block lists commits on a branch in a GitHub repository
Github List Discussions This block lists recent discussions for a specified GitHub repository
Github List Issues A block that retrieves a list of issues from a GitHub repository with their titles and URLs
Github List Notifications This block lists GitHub notifications for the authenticated user, e
Github List PR Reviewers This block lists the requested reviewers for a specified GitHub pull request, optionally including users who have already submitted a review
Github List PR Reviews This block lists all reviews for a specified GitHub pull request
Github List Pull Requests This block lists all pull requests for a specified GitHub repository
Github List Releases This block lists releases for a specified GitHub repository, optionally filtered by type and date range
Github List Stargazers This block lists all users who have starred a specified GitHub repository
Github List Tags This block lists all tags for a specified GitHub repository
Github Make Branch This block creates a new branch from a specified source branch
Github Make Issue A block that creates new issues on GitHub repositories with a title and body content
Github Make Pull Request This block creates a new pull request on a specified GitHub repository
Github Mark Notification Thread As Done This block marks a GitHub notification thread as done, removing it from the notification inbox
Github Mark Notification Thread As Read This block marks a single GitHub notification thread as read
Github Mark Notifications As Read This block marks all GitHub notifications as read, optionally scoped to a single repository
Github Merge Pull Request This block merges a pull request using merge, squash, or rebase
Github Multi File Commit This block creates a single commit with multiple file upsert/delete operations using the Git Trees API
Github Pull Request Trigger This block triggers on pull request events and outputs the event type and payload
Github Read File This block reads the content of a specified file from a GitHub repository
Github Read Folder This block reads the content of a specified folder from a GitHub repository
Github Read Issue A block that retrieves information about a specific GitHub issue, including its title, body content, and creator
Github Read Pull Request This block reads the body, title, user, changes, and full raw object of a specified GitHub pull request
Github Release Trigger This block triggers on GitHub release events
Github Remove Label A block that removes a label from a GitHub issue or pull request
Github Resolve Review Discussion This block resolves or unresolves a review discussion thread on a GitHub pull request
Github Search Code This block searches for code in GitHub repositories
Github Star Repository This block stars a GitHub repository
Github Star Trigger This block triggers on GitHub star events
Github Submit Pending Review This block submits a pending (draft) review on a GitHub pull request
Github Unassign Issue A block that removes a user's assignment from a GitHub issue
Github Unassign PR Reviewer This block unassigns a reviewer from a specified GitHub pull request
Github Unsubscribe Notification Thread This block unsubscribes you from a GitHub notification thread, muting future notifications unless you are mentioned again
Github Update Check Run Updates an existing check run in a GitHub repository
Github Update Comment A block that updates an existing comment on a GitHub issue or pull request
Github Update File This block updates an existing file in a GitHub repository
Instantiate Code Sandbox Instantiate a sandbox environment with internet access in which you can execute code with the Execute Code Step block
MCP Tool Connect to any MCP server and execute its tools
Slant3D Order Webhook This block triggers on Slant3D order status updates and outputs the event details, including tracking information when orders are shipped

Media Generation

Block Name Description
Add Audio To Video Block to attach an audio file to a video file using moviepy
Loop Video Block to loop a video to a given duration or number of repeats
Media Duration Block to get the duration of a media file
Video Clip Extract a time segment from a video
Video Concat Merge multiple video clips into one continuous video
Video Download Download video from URL (YouTube, Vimeo, news sites, direct links)
Video Text Overlay Add text overlay/caption to video

Productivity

Block Name Description
Google Calendar Create Event This block creates a new event in Google Calendar with customizable parameters
Notion Create Page Create a new page in Notion
Notion Read Database Query a Notion database with optional filtering and sorting, returning structured entries
Notion Read Page Read a Notion page by its ID and return its raw JSON
Notion Read Page Markdown Read a Notion page and convert it to Markdown format with proper formatting for headings, lists, links, and rich text
Notion Search Search your Notion workspace for pages and databases by text query
Todoist Close Task Closes a task in Todoist
Todoist Create Comment Creates a new comment on a Todoist task or project
Todoist Create Label Creates a new label in Todoist, It will not work if same name already exists
Todoist Create Project Creates a new project in Todoist
Todoist Create Task Creates a new task in a Todoist project
Todoist Delete Comment Deletes a Todoist comment
Todoist Delete Label Deletes a personal label in Todoist
Todoist Delete Project Deletes a Todoist project and all its contents
Todoist Delete Section Deletes a section and all its tasks from Todoist
Todoist Delete Task Deletes a task in Todoist
Todoist Get Comment Get a single comment from Todoist
Todoist Get Comments Get all comments for a Todoist task or project
Todoist Get Label Gets a personal label from Todoist by ID
Todoist Get Project Gets details for a specific Todoist project
Todoist Get Section Gets a single section by ID from Todoist
Todoist Get Shared Labels Gets all shared labels from Todoist
Todoist Get Task Get an active task from Todoist
Todoist Get Tasks Get active tasks from Todoist
Todoist List Collaborators Gets all collaborators for a specific Todoist project
Todoist List Labels Gets all personal labels from Todoist
Todoist List Projects Gets all projects and their details from Todoist
Todoist List Sections Gets all sections and their details from Todoist
Todoist Remove Shared Labels Removes all instances of a shared label
Todoist Rename Shared Labels Renames all instances of a shared label
Todoist Reopen Task Reopens a task in Todoist
Todoist Update Comment Updates a Todoist comment
Todoist Update Label Updates a personal label in Todoist
Todoist Update Project Updates an existing project in Todoist
Todoist Update Task Updates an existing task in Todoist

Logic and Control Flow

Block Name Description
Calculator Performs a mathematical operation on two numbers
Condition Handles conditional logic based on comparison operators
Count Items Counts the number of items in a collection
Data Sampling This block samples data from a given dataset using various sampling methods
Exa Webset Ready Check Check if webset is ready for next operation - enables conditional workflow branching
If Input Matches Handles conditional logic based on comparison operators
Pinecone Init Initializes a Pinecone index
Pinecone Insert Upload data to a Pinecone index
Pinecone Query Queries a Pinecone index
Step Through Items Iterates over a list or dictionary and outputs each item

Input/Output

Block Name Description
Exa Webset Webhook Receive webhook notifications for Exa webset events
Generic Webhook Trigger This block will output the contents of the generic input for the webhook
Read RSS Feed Reads RSS feed entries from a given URL
Send Authenticated Web Request Make an authenticated HTTP request with host-scoped credentials (JSON / form / multipart)
Send Email This block sends an email using the provided SMTP credentials
Send Web Request Make an HTTP request (JSON / form / multipart)

Agent Integration

Block Name Description
Agent Executor Executes an existing agent inside your agent
AutoPilot Execute tasks using AutoGPT AutoPilot with full access to platform tools (agent management, workspace files, web fetch, block execution, and more)

CRM Services

Block Name Description
Add Lead To Campaign Add a lead to a campaign in SmartLead
Company Enrichment Look up and enrich a company using DataForB2B's B2B database — firmographics, headcount/size, industry, domain and social profiles from a company domain, name or LinkedIn URL
Company Search Search companies and accounts by structured filters — industry, headcount/size, location, funding, keywords — using DataForB2B's database
Create Campaign Create a campaign in SmartLead
Hub Spot Company Manages HubSpot companies - create, update, and retrieve company information
Hub Spot Contact Manages HubSpot contacts - create, update, and retrieve contact information
People Search Search people and B2B leads by structured filters — job title, company, location, industry, seniority, skills — using DataForB2B's database
Profile Enrichment Look up and enrich a professional profile from a LinkedIn URL using DataForB2B's B2B database — returns the full profile (current role, experience and skills) plus work email, personal email and GitHub
Save Campaign Sequences Save sequences within a campaign

AI Safety

Block Name Description
Nvidia Deepfake Detect Detects potential deepfakes in images using Nvidia's AI API

Issue Tracking

Block Name Description
Linear Create Comment Creates a new comment on a Linear issue
Linear Create Issue Creates a new issue on Linear
Linear Get Project Issues Gets issues from a Linear project filtered by status and assignee
Linear Search Issues Searches for issues on Linear
Linear Search Projects Searches for projects on Linear

Hardware

Block Name Description
Compass AI Trigger This block will output the contents of the compass transcription
RMFG Event Trigger Triggers when an RMFG design, quote, cart or order changes
RMFG Get Image Downloads RMFG's rendered picture of a design or part, with holes and bends labelled

Platform Getting Started (`docs/platform/getting-started.md`)

Getting Started with AutoGPT: Self-Hosting Guide

Introduction

This guide will help you setup the server and builder for the project.

!!! warning
DO NOT FOLLOW ANY OUTSIDE TUTORIALS AS THEY WILL LIKELY BE OUT OF DATE

Prerequisites

The single-container appliance's only product prerequisite is an installed,
running Docker CLI and daemon. Select a local Docker endpoint using Linux
containers on amd64 or arm64. The Unix bootstrap also uses Bash, curl, and
sha256sum or shasum. Docker Compose, Git, Node.js, and NPM are not required
for the appliance installer.

Install Docker from the official Docker documentation,
start it, then verify the selected daemon:

console
docker -v
docker info

Quick Setup with the Appliance Installer

The release installer pulls and starts the published single-container
appliance: one Docker container, one loopback port, no source checkout. It
needs a running Docker daemon with Linux containers on amd64 or arm64; it
does not install Docker or build AutoGPT from source. See
the installer reference for details.

The hosted installer is not live yet: setup.agpt.co/install.sh still serves
the Compose installer, and the appliance image tags are not public until the
release gates pass. This first release
supports Linux and macOS. Until then, and on Windows, use the
from-source setup below, which is also the path that supports
a fully offline install with a local LLM.

Manual Setup

Development prerequisites

The manual source checkout requires
Git,
Node.js and NPM, Docker, and
Docker Compose. Verify them before
continuing:

console
git --version
node -v
npm -v
docker -v
docker compose version

Cloning the Repository

The first step is cloning the AutoGPT repository to your computer.
To do this, open a terminal window in a folder on your computer and run:

text
git clone https://github.com/Significant-Gravitas/AutoGPT.git

If you get stuck, follow this guide.

Once that's complete you can continue the setup process.

Running the AutoGPT Platform

To run the platform, follow these steps:

  • Navigate to the autogpt_platform directory inside the AutoGPT folder:
    bash
    cd AutoGPT/autogpt_platform
  • Create the .env files and generate your local secrets:

    text
    make init-env

    This copies each .env.default to .env (for autogpt_platform, backend
    and frontend) and fills in the secrets that .env.default deliberately
    leaves blank — ENCRYPTION_KEY, UNSUBSCRIBE_SECRET_KEY and
    BETTER_AUTH_SECRET — with values generated for your machine. Those files
    are public, so shipping working values in them would mean every install in
    the world shared one publicly-readable key. It is safe to re-run: it never
    overwrites an existing .env or a value you set yourself. You can then edit
    the .env files to add your own environment variables.

    The backend refuses to start while ENCRYPTION_KEY is empty, so run this
    before bringing the stack up.

  • Run the platform services:

    text
    docker compose up -d --build

    This command will start all the necessary backend services defined in the docker-compose.yml file in detached mode.


🛠️ Using the Makefile for Common Tasks

The repository includes a Makefile with helpful commands to streamline setup and development. You may use make commands as an alternative to calling Docker or scripts directly.

Most-used Makefile commands

Inside the autogpt_platform directory, you can use:

Command What it Does
make init-env Create missing .env files from .env.default (autogpt_platform, backend, and frontend) and generate the secrets they leave blank
make start-core Start just the core services (Postgres, Redis, RabbitMQ) in background
make stop-core Stop the core services
make logs-core Tail the logs for core services
make format Format & lint backend (Python) and frontend (TypeScript) code
make migrate Run backend database migrations
make publish-skills Publish the skills catalog (marketplace skills and the expert roster) into your database; run make load-store-agents first if you want the experts' preload workflows too
make run-backend Run the backend FastAPI server
make run-frontend Run the frontend Next.js development server

Example usage:

sh
make init-env
make start-core
make migrate
make publish-skills
make run-backend
make run-frontend

docker compose up runs the migrations and the skills catalog publish for you: the publish_skills service downloads the public Significant-Gravitas/skills-catalog repository after migrate finishes, so the Skills Hub and the roster experts are there on first start. It needs network access to GitHub; offline, point SKILLS_CATALOG_PATH in backend/.env at a local checkout of that repository.

make init-env matters when running the frontend outside Docker: Next.js
only reads .env (not .env.default), and the frontend's embedded auth
service needs DATABASE_URL and BETTER_AUTH_SECRET from it.

You can always check available Makefile recipes by running:

sh
make help

(or just inspecting the Makefile in the repo root).


Checking if the application is running

You can check if the server is running by visiting http://localhost:3000 in your browser.

Notes:

By default the application for different services run on the following ports:

Frontend UI Server: 3000
Backend Websocket Server: 8001
Execution API Rest Server: 8006

Upgrading: secrets are generated per install

ENCRYPTION_KEY, UNSUBSCRIBE_SECRET_KEY and BETTER_AUTH_SECRET used to
come with a value in .env.default, so every install that did not set its own
ran on the same three values. They are now blank there and generated for each
install, and the backend does not start without an ENCRYPTION_KEY or
with the one .env.default used to contain.

A fresh install needs nothing beyond make init-env (or the installer script,
which does the same). An install that already set its own values needs
nothing either. Follow the steps below if you are upgrading an install that

  • has no autogpt_platform/backend/.env, or one without an ENCRYPTION_KEY
    line — it was running on the value from .env.default; or
  • stops on startup with ENCRYPTION_KEY is set to a value that was published…
    or ENCRYPTION_KEY is not set.

Your connected integrations are encrypted with ENCRYPTION_KEY, so the steps
move them to the new key instead of losing them. Run everything from
autogpt_platform/.

  1. Stop the stack:

    bash
    docker compose down
  2. Keep the key your data is currently encrypted with. If you never set one,
    it is the value .env.default contained up to release v0.7.4:

    bash
    export OLD_ENCRYPTION_KEY=$(git show autogpt-platform-beta-v0.7.4:autogpt_platform/backend/.env.default \
      | grep '^ENCRYPTION_KEY=' | cut -d= -f2-)

    If you did set one and are replacing it, export that value instead, and keep
    a copy of the file outside the checkout until step 4 reports nothing
    unreadable, because step 3 removes the only other place the key is written
    down:

    bash
    cp backend/.env ~/autogpt-backend.env.$(date +%Y%m%d-%H%M%S)
  3. Generate the new values. make init-env creates any missing .env file
    and fills in every secret whose line is present but empty; it never
    overwrites a value. So in backend/.env make sure these two lines exist
    with nothing after the =, and do the same for BETTER_AUTH_SECRET= in
    frontend/.env:

    text
    ENCRYPTION_KEY=
    UNSUBSCRIBE_SECRET_KEY=

    Then:

    bash
    make init-env

    Without make, these are the same steps by hand. Copy a .env.default
    only where no .env exists yet:

    bash
    cp -n .env.default .env
    cp -n backend/.env.default backend/.env
    cp -n frontend/.env.default frontend/.env
    python3 single-container/runtime_config.py fill-env --path .env
    python3 single-container/runtime_config.py fill-env --path backend/.env
    python3 single-container/runtime_config.py fill-env --path frontend/.env

    Do not re-run the installer script for this: it also starts the stack,
    which is step 5.

  4. Re-encrypt what is stored. Build the new images and bring the database up
    to date first; migrate starts the database on its own:

    bash
    docker compose build migrate rest_server
    docker compose run --rm migrate

    Then run the command, first as a dry run that only reports what it would
    change, then with --apply to write it. --no-deps keeps it from waiting
    on the rest of the stack, which it does not need:

    bash
    docker compose run --rm --no-deps -e OLD_ENCRYPTION_KEY rest_server cli rotate-encryption-key
    docker compose run --rm --no-deps -e OLD_ENCRYPTION_KEY rest_server cli rotate-encryption-key --apply

    It is safe to run more than once: values already under the new key are left
    alone, and a value that neither key can read is listed and not touched.
    Running the backend outside Docker, the same command is
    poetry run cli rotate-encryption-key in autogpt_platform/backend.

  5. Start the stack again. If BETTER_AUTH_SECRET changed in step 3, first
    clear the token signing key the frontend stored under the old value: it can
    no longer be decrypted, and until it is removed nobody can reach the
    backend, even after signing in again. A new one is created on the next
    sign-in. --build brings the remaining services to the release you built
    in step 4:

    bash
    docker compose up -d --wait db
    docker compose exec db psql -U postgres -c 'DELETE FROM platform."UserAuthJwks";'
    docker compose up -d --build

Two smaller effects of the new values: unsubscribe links in emails sent before
the upgrade stop working (UNSUBSCRIBE_SECRET_KEY), and everyone signs in
again once (BETTER_AUTH_SECRET).

Do step 4 before step 5. On a new key the stored values are still in the
database but read as empty, and a user who connects an integration in that
state replaces their stored set: their other credentials are marked revoked.
If the stack already ran on the new key, stop it and run step 4 now. Whatever
nobody touched is recovered; a user who connected something in between gets
their older credentials re-encrypted but still revoked, and reconnects those.

Upgrading an existing (Supabase-based) installation

Older versions of the platform ran authentication on a bundled Supabase
stack. If you self-hosted before the switch to the built-in auth service,
three things changed:

  1. Environment files: refresh your .env files against the new
    .env.defaults. make init-env copies .env.default → .env for
    autogpt_platform, backend and frontend, but only where no .env
    exists yet (it uses cp -n): it creates missing .env files and never
    overwrites an existing one. It does not merge newly-added variables
    into an .env you already have — for an existing install, diff each
    .env against its .env.default and copy the new keys across yourself.
    The SUPABASE_* URL/key variables are gone; the frontend now uses
    BETTER_AUTH_SECRET and DATABASE_URL.

    ENCRYPTION_KEY, UNSUBSCRIBE_SECRET_KEY and BETTER_AUTH_SECRET are no
    longer filled in by .env.default: follow
    Upgrading: secrets are generated per install
    as part of this step.

  2. Database location: the database now lives in a plain Postgres
    container (pgvector/pgvector:pg15) with its data in
    autogpt_platform/data/db/data. Your old data is untouched at
    autogpt_platform/db/docker/volumes/db/data but is no longer mounted.

    If you already booted the new stack while that folder was still called
    volumes/, move your data across before starting it again:

    sh
    mkdir -p autogpt_platform/data/db
    mv autogpt_platform/volumes/db/data autogpt_platform/data/db/data

    To carry the old Supabase data over, pick one of the two routes below.
    Neither has been validated against a real old volume yet, so back up
    autogpt_platform/db/docker/volumes/db/data before you start.

    The old bundled stack ran supabase/postgres:15.8.1.049 and the new db
    service runs pgvector/pgvector:pg15 — the same Postgres major, so
    reusing the data directory as-is is plausible rather than impossible. It
    is not guaranteed: a data directory is only portable between servers on
    the same major and with a compatible extension set /
    shared_preload_libraries. The Supabase image ships extensions and roles
    (supabase_admin, pgjwt, pgsodium, pg_graphql, …) that the plain
    pgvector image does not have, so a moved directory can fail to start, or
    start and then fail on objects that reference the missing extensions.

    Fast path — reuse the data directory:

    sh
    cd autogpt_platform
    docker compose down
    mkdir -p data/db
    rm -rf data/db/data                            # discards a freshly-initialised new DB
    cp -a db/docker/volumes/db/data data/db/data   # copy, so the old dir stays intact
    docker compose up -d db
    docker compose logs -f db

    On Linux the data directory is mode 0700 owned by the container's
    postgres user, so the copy needs sudo cp -a (the plain Postgres
    entrypoint fixes ownership on first boot). On Docker Desktop for
    macOS/Windows the plain cp -a is enough.
    It worked if the log settles on database system is ready to accept connections and your data is there:

    sh
    docker compose exec db psql -U postgres -c '\dn'
    docker compose exec db psql -U postgres -c 'select count(*) from platform."User"'

    It did not work if the container restart-loops with errors such as
    could not open configuration file, could not access file "$libdir/…",
    unrecognized configuration parameter, extension "…" is not available,
    data directory … has wrong ownership, or Permission denied — Postgres
    is either missing something the Supabase image provided, or can't read the
    copied files. In that case rm -rf data/db/data and use the fallback.

    Fallback — same-major dump and restore:

    Step 1 starts a real Postgres server against your original data
    directory, read-write. Make sure you took the backup above first.

    sh
    cd autogpt_platform
    # 1. Bring the OLD image up against the OLD data directory, on a spare port.
    docker run --rm -d --name old-db -p 5433:5432 \
      -e POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password \
      -v "$(pwd)/db/docker/volumes/db/data:/var/lib/postgresql/data" \
      supabase/postgres:15.8.1.049
    # 2. Dump without Supabase-owned ownership/ACL metadata.
    docker exec old-db pg_dump -U postgres -d postgres \
      --no-owner --no-privileges -Fc -f /tmp/old.dump
    docker cp old-db:/tmp/old.dump ./old.dump
    docker stop old-db
    # 3. Restore into the new db service (fresh volume).
    docker compose up -d db
    # A fresh volume runs db/init/00-init.sql, which creates an EMPTY auth.users
    # shim with only the columns the migrations need. Drop it first, or the
    # restore of your real auth.users collides with it and copies no users.
    docker compose exec db psql -U postgres -c 'DROP SCHEMA IF EXISTS auth CASCADE;'
    docker compose cp ./old.dump db:/tmp/old.dump
    docker compose exec db pg_restore -U postgres -d postgres \
      --no-owner --no-privileges /tmp/old.dump
    # 4. Confirm your accounts actually landed BEFORE migrating.
    docker compose exec db psql -U postgres -c 'select count(*) from auth.users'

    pg_restore reports errors for objects belonging to Supabase-only
    extensions and roles (storage, realtime, supabase_admin, pgsodium,
    …). Those are harmless. An error on auth.users is not: that table is
    where your accounts live, and the migration in step 3 below copies them out
    of it. If the count above is 0 — or pg_restore failed on auth.users —
    stop and fix the restore before continuing, or you will bring the stack up
    with no user accounts.

    Either way, finish with the migrations before bringing up the rest:

    sh
    docker compose run --rm migrate
    docker compose up -d
  3. User accounts and sessions: a normal upgrade (stack stopped, then
    restarted on the new version) needs no extra step here.

    • Existing users are copied from the Supabase auth.users table into the
      Better Auth tables by the backend Prisma migration
      20260716120000_copy_supabase_users_to_better_auth, which runs as part
      of the docker compose run --rm migrate step above.
    • Existing browser sessions keep working because the frontend recognises
      old Supabase JWT cookies and swaps them for a Better Auth session on
      the user's next visit. Keep SUPABASE_JWT_SECRET set in
      frontend/.env for as long as you want that bridge open.
    • frontend/scripts/migrate-supabase-auth.ts is optional and only
      applies to a live cutover, where Supabase kept accepting signups
      while the new stack was already running. It is a re-runnable sweep for
      those stragglers; if you stopped the stack to upgrade, skip it.
      sh
      cd frontend && DATABASE_URL=postgresql://postgres:<password>@localhost:5432/postgres npx tsx scripts/migrate-supabase-auth.ts

A fresh install (empty database) needs none of this.

Additional Notes

make init-env already generates a unique ENCRYPTION_KEY for your install, so
there is normally nothing to change here. To rotate it — for example if you
carried a key over from an older checkout, back when .env.default shipped a
working (and therefore public) one — generate a new key in python:

python
from cryptography.fernet import Fernet;Fernet.generate_key().decode()

Or run the following command in the autogpt_platform/backend directory:

bash
poetry run cli gen-encrypt-key

Then replace the value in autogpt_platform/backend/.env and re-encrypt the
stored integration credentials under it, with the previous value as
OLD_ENCRYPTION_KEY — steps 4 and 5 of
Upgrading: secrets are generated per install.
Without that step the credentials stored under the previous key are unreadable
and those integrations need reconnecting.

Auth transport security (JWKS over untrusted networks)

The backend verifies login tokens using signing keys it fetches from the frontend at JWT_JWKS_URL (.../api/auth/jwks). It trusts whatever keys that URL returns, so the fetch must run over a trusted path:

  • Plain http is fine for localhost and for container-to-container traffic on a single host (the default http://frontend:3000 over the Docker network) — there is no network segment for an attacker to sit on.
  • Use https on an untrusted network. If you split the backend and frontend across separate machines on a LAN, or expose them publicly, a cleartext JWKS fetch can be intercepted: an attacker who swaps the published keys can forge tokens for any user. Put the frontend behind TLS (a reverse proxy), or issue locally-trusted certificates (e.g. mkcert), and point JWT_JWKS_URL at the https:// URL.

The backend refuses to start if JWT_JWKS_URL is a cleartext http:// URL pointing at a non-local host. If your network path is trusted (e.g. an isolated private LAN), set JWKS_ALLOW_INSECURE_TRANSPORT=true to boot anyway — a startup warning stays on record so the tradeoff is visible in logs.

This is a property of stateless JWT/JWKS verification in general, not something specific to AutoGPT. On a standard single-host Docker install you don't need to change anything.

📌 Windows Installation Note

When installing Docker on Windows, it is highly recommended to select WSL 2 instead of Hyper-V. Using Hyper-V can cause compatibility issues with the platform's containers, leading to the db (Postgres) container being marked as unhealthy.

**Steps to enable WSL 2 for Docker:**

  1. Install WSL 2.
  2. Ensure that your Docker settings use WSL 2 as the default backend:
    • Open Docker Desktop.
    • Navigate to Settings > General.
    • Check Use the WSL 2 based engine.
  3. Restart Docker Desktop.

**Already Installed Docker with Hyper-V?**

If you initially installed Docker with Hyper-V, you don’t need to reinstall it. You can switch to WSL 2 by following these steps:

  1. Open Docker Desktop.
  2. Go to Settings > General.
  3. Enable Use the WSL 2 based engine.
  4. Restart Docker.

🚨 Warning: Enabling WSL 2 may erase your existing containers and build history. If you have important containers, consider backing them up before switching.

For more details, refer to Docker's official documentation.

⚠️ Podman Not Supported

AutoGPT requires Docker (Docker Desktop or Docker Engine). Podman and podman-compose are not supported and may cause path resolution issues, particularly on Windows.

If you see errors like:

text
Error: the specified Containerfile or Dockerfile does not exist, ..\..\autogpt_platform\backend\Dockerfile

This indicates you're using Podman instead of Docker. Please install Docker Desktop and use docker compose instead of podman-compose.

Development

Frontend Development

Running the frontend locally

To run the frontend locally, you need to have Node.js and PNPM installed on your machine.

Install Node.js to manage dependencies and run the frontend application.

Install PNPM to manage the frontend dependencies.

Run the service dependencies (backend, database, message queues, etc.):

sh
docker compose --profile local up deps_backend --build --detach

Go to the autogpt_platform/frontend directory:

sh
cd frontend

Install the dependencies:

sh
pnpm install

Generate the API client:

sh
pnpm generate:api-client

Run the frontend application:

sh
pnpm dev

Formatting & Linting

Auto formatter and linter are set up in the project. To run them:

Format the code:

sh
pnpm format

Lint the code:

sh
pnpm lint

Or for both frontend and backend, from the root:

sh
make format

Testing

To run the tests, you can use the following command:

sh
pnpm test

Backend Development

Running the backend locally

To run the backend locally, you need to have Python 3.10 or higher installed on your machine.

Install Poetry to manage dependencies and virtual environments.

Run the backend dependencies (database, message queues, etc.):

sh
docker compose --profile local up deps --build --detach

Or equivalently with Makefile:

sh
make start-core

Go to the autogpt_platform/backend directory:

sh
cd backend

Install the dependencies:

sh
poetry install --with dev

Run the backend server:

sh
poetry run app

Or from within autogpt_platform:

sh
make run-backend

Formatting & Linting

Auto formatter and linter are set up in the project. To run them:

Format the code:

sh
poetry run format

Lint the code:

sh
poetry run lint

Or format both frontend and backend at once:

sh
make format

Testing

To run the tests:

sh
poetry run pytest -s

Adding a New Agent Block

To add a new agent block, you need to create a new class that inherits from Block and provides the following information:

  • All the block code should live in the blocks (backend.blocks) module.
  • input_schema: the schema of the input data, represented by a Pydantic object.
  • output_schema: the schema of the output data, represented by a Pydantic object.
  • run method: the main logic of the block.
  • test_input & test_output: the sample input and output data for the block, which will be used to auto-test the block.
  • You can mock the functions declared in the block using the test_mock field for your unit tests.
  • Once you finish creating the block, you can test it by running poetry run pytest backend/blocks/test/test_block.py -s.
  • Create a Pull Request to the dev branch of the repository with your changes so you can share it with the community :)

Content Classic Index (`docs/content/classic/index.md`)

AutoGPT Agent

🔧 Setup
 | 
💻 User guide
 | 
🐙 GitHub

Location: classic/original_autogpt/ in the GitHub repo

Maintance Notice: AutoGPT Classic is not supported from a security perspective.
Dependencies will not be updated, nor will issues be fixed. If someone wishes to
contribute to novel development, we will give best effort merging to the changes that
pass the existing CI.

AutoGPT Classic was conceived when OpenAI published their GPT-4 model accompanied by a paper
outlining the advanced reasoning and task-solving abilities of the model. The concept
was (and still is) fairly simple: let an LLM decide what to do over and over, while
feeding the results of its actions back into the prompt. This allows the program to
iteratively and incrementally work towards its objective.

The fact that this program is able to execute actions on behalf of its user makes
it an agent. In the case of AutoGPT Classic, the user still has to authorize every action,
but as the project progresses we'll be able to give the agent more autonomy and only
require consent for select actions.

AutoGPT Classic is a generalist agent, meaning it is not designed with a specific task in
mind. Instead, it is designed to be able to execute a wide range of tasks across many
disciplines, as long as it can be done on a computer.

AutoGPT Classic Documentation

Welcome to the AutoGPT Classic Documentation.

The AutoGPT Classic project consists of four main components:

To tie these together, we also have a CLI at the root of the project.

🤖 Agent

📖 About AutoGPT Classic
 | 
🔧 Setup
 | 
💻 Usage

The former heart of AutoGPT, and the project that kicked it all off: a semi-autonomous agent powered by LLMs to execute any task for you*.

We continue to develop this project with the goal of providing access to AI assistance to the masses, and building the future transparently and together.

  • 💡 Explore - See what AI can do and be inspired by a glimpse of the future.

  • 🚀 Build with us - We welcome any input, whether it's code or ideas for new features or improvements! Join us on Discord and find out how you can join in on the action.

If you'd like to see what's next, check out the AutoGPT Platform.

* it isn't quite there yet, but that is the ultimate goal that we are still pursuing


🎯 Benchmark

🗒️ Readme

Measure your agent's performance! The agbenchmark can be used with any agent that supports the agent protocol, and the integration with the project's CLI makes it even easier to use with AutoGPT Classic and forge-based agents. The benchmark offers a stringent testing environment. Our framework allows for autonomous, objective performance evaluations, ensuring your agents are primed for real-world action.

  • 📦 agbenchmark on Pypi

  • 🔌 Agent Protocol Standardization - AutoGPT Classic uses the agent protocol from the AI Engineer Foundation to ensure compatibility with many agents, both from within and outside the project.


🏗️ Forge

📖 Introduction
 | 
🚀 Quickstart

Forge your own agent! The Forge is a ready-to-go template for your agent application. All the boilerplate code is already handled, letting you channel all your creativity into the things that set your agent apart.

  • 🛠️ Building with Ease - We've set the groundwork so you can focus on your agent's personality and capabilities. Comprehensive tutorials are available here.

💻 Frontend

🗒️ Readme

An easy-to-use and open source frontend for any Agent Protocol-compliant agent.

  • 🎮 User-Friendly Interface - Manage your agents effortlessly.

  • 🔄 Seamless Integration - Smooth connectivity between your agent and our benchmarking system.


🔧 CLI

The project CLI makes it easy to use all of the components of AutoGPT Classic in the repo, separately or
together. To install its dependencies, simply run ./run setup, and you're ready to go!

shell
$ ./run
Usage: cli.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  agent      Commands to create, start and stop agents
  benchmark  Commands to start the benchmark and list tests and categories
  setup      Installs dependencies needed for your system.

Common commands:

  • ./run agent start autogpt – runs the AutoGPT Classic agent
  • ./run agent create <name> – creates a new Forge-based agent project at agents/<name>
  • ./run benchmark start <agent> – benchmarks the specified agent

🤔 Join the AutoGPT Discord server for any queries:
discord.gg/autogpt

Glossary of Terms

  • Repository: Space where your project resides.
  • Forking: Copying a repository under your account.
  • Cloning: Making a local copy of a repository.
  • Agent: The AutoGPT you'll create and develop.
  • Benchmarking: Testing your agent's skills in the Forge.
  • Forge: The template for building your AutoGPT agent.
  • Frontend: The UI for tasks, logs, and task history.

Content Contribute Index (`docs/content/contribute/index.md`)

Contributing to the Docs

We welcome contributions to our documentation! Our docs are hosted on GitBook and published from GitHub.

How It Works

  • Documentation lives in the docs/ directory and changes like any other code: through a pull request to dev
  • The published docs match master. When a release reaches master, its docs/ directory is copied to the gitbook branch, and GitBook publishes that
  • The gitbook branch belongs to that copy. Don't open pull requests against it, and don't edit pages in GitBook: the next copy overwrites anything that isn't in master

Editing Docs Locally

  1. Clone the repository and create a branch from dev:

    shell
    git clone https://github.com/Significant-Gravitas/AutoGPT.git
    cd AutoGPT
    git checkout -b my-docs-change origin/dev
  2. Make your changes to markdown files in docs/

  3. Preview changes with any markdown preview tool

Adding a New Page

  1. Create a new markdown file in the appropriate docs/ subdirectory
  2. Add the new page to the relevant SUMMARY.md file to include it in the navigation
  3. Submit a pull request to the dev branch

Submitting a Pull Request

When you're ready to submit your changes, create a pull request targeting the dev branch. We will review your changes and merge them if appropriate. They appear on the published docs with the next release.


Platform Changelog README (`docs/platform/changelog/README.md`)

Changelog

What's new in AutoPilot and the AutoGPT Platform. We ship updates regularly and document everything here.

Release windows reflect how changes were grouped for publication and may overlap when work spans consecutive releases.

2026

Date Highlights
June 18 – July 9 File folders, shareable chats, Discord bot
May 7 – June 10 AutoPilot upgrades, redesigned login, live subscriptions, rebuilt settings
April 10 – May 1 Artifacts panel, agent briefings, AI chat in builder
March 23 – April 9 Themed prompt categories, live timer stats, redesigned onboarding, copy your prompts
March 20 – March 25 Import workflows from other tools, marketplace UI polish, dry-run mode, parallel AutoPilot actions
March 13 – March 20 Refreshed marketplace, usage monitor, feedback button, personalized prompts
March 5 – March 12 Folders, notifications, cleaner reasoning, inline outputs
February 26 – March 4 Connect any app, share files, browse the web, run code, text-to-speech
February 11 – 26 Telegram bots, agent folders, rebuilt flow editor, major AutoPilot upgrade
January 29 – February 11 Voice input, persistent file workspace, Claude Opus 4.6, marketplace agent customization, rebuilt chat streaming

Content Classic Setup Index (`docs/content/classic/setup/index.md`)

AutoGPT Agent setup

🐋 Set up & Run with Docker
 | 
👷🏼 For Developers

📋 Requirements

Linux / macOS

Windows (WSL)

Windows

!!! attention
We recommend setting up AutoGPT with WSL. Some things don't work exactly the same on
Windows and we currently can't provide specialized instructions for all those cases.

Setting up AutoGPT

Getting AutoGPT

Since we don't ship AutoGPT as a desktop application, you'll need to download the
project from GitHub and give it a place on your computer.

  • To get the latest bleeding edge version, use master.
  • If you're looking for more stability, check out the latest AutoGPT release.

!!! note
These instructions don't apply if you're looking to run AutoGPT as a docker image.
Instead, check out the Docker setup guide.

Completing the Setup

Once you have cloned or downloaded the project, you can find the AutoGPT Agent in the
original_autogpt/ folder.
Inside this folder you can configure the AutoGPT application with an .env file and (optionally) a JSON configuration file:

  • .env for environment variables, which are mostly used for sensitive data like API keys
  • a JSON configuration file to customize certain features of AutoGPT's Components

See the Configuration reference for a list of available environment variables.

  1. Find the file named .env.template. This file may
    be hidden by default in some operating systems due to the dot prefix. To reveal
    hidden files, follow the instructions for your specific operating system:
    Windows and macOS.

  2. Create a copy of .env.template and call it .env;
    if you're already in a command prompt/terminal window:

    shell
    cp .env.template .env
  3. Open the .env file in a text editor.

  4. Set API keys for the LLM providers that you want to use: see below.

  5. Enter any other API keys or tokens for services you would like to use.

    !!! note
    To activate and adjust a setting, remove the # prefix.

  6. Save and close the .env file.

  7. Optional: run poetry install to install all required dependencies. The
    application also checks for and installs any required dependencies when it starts.

  8. Optional: configure the JSON file (e.g. config.json) with your desired settings.
    The application will use default settings if you don't provide a JSON configuration file.
    Learn how to set up the JSON configuration file

You should now be able to explore the CLI (./autogpt.sh --help) and run the application.

See the user guide for further instructions.

Setting up LLM providers

You can use AutoGPT with any of the following LLM providers.
Each of them comes with its own setup instructions.

AutoGPT was originally built on top of OpenAI's GPT-4, but now you can get
similar and interesting results using other models/providers too.
If you don't know which to choose, you can safely go with OpenAI*.

* subject to change

OpenAI

!!! attention
To use AutoGPT with GPT-4 (recommended), you need to set up a paid OpenAI account
with some money in it. Please refer to OpenAI for further instructions (link).
Free accounts are limited to GPT-3.5 with only 3 requests per minute.

  1. Make sure you have a paid account with some credits set up: Settings > Organization > Billing

  2. Get your OpenAI API key from: API keys

  3. Open .env

  4. Find the line that says OPENAI_API_KEY=

  5. Insert your OpenAI API Key directly after = without quotes or spaces:

    ini
    OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    !!! info "Using a GPT Azure-instance"
    If you want to use GPT on an Azure instance, set USE_AZURE to True and
    make an Azure configuration file.

    Rename azure.yaml.template to azure.yaml and provide the relevant
    azure_api_base, azure_api_version and deployment IDs for the models that you
    want to use.

    E.g. if you want to use gpt-3.5-turbo and gpt-4-turbo:

    yaml # Please specify all of these values as double-quoted strings # Replace string in angled brackets (<>) to your own deployment Name azure_model_map: gpt-3.5-turbo: "<gpt-35-turbo-deployment-id>" gpt-4-turbo: "<gpt-4-turbo-deployment-id>" ...

    Details can be found in the openai/python-sdk/azure, and in the [Azure OpenAI docs] for the embedding model.
    If you're on Windows you may need to install an MSVC library.

!!! important
Keep an eye on your API costs on the Usage page.

Anthropic

  1. Make sure you have credits in your account: Settings > Plans & billing
  2. Get your Anthropic API key from Settings > API keys
  3. Open .env
  4. Find the line that says ANTHROPIC_API_KEY=
  5. Insert your Anthropic API Key directly after = without quotes or spaces:
    ini
    ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  6. Set SMART_LLM and/or FAST_LLM to the Claude 3 model you want to use.
    See Anthropic's models overview for info on the available models.
    Example:
    ini
    SMART_LLM=claude-3-opus-20240229

!!! important
Keep an eye on your API costs on the Usage page.

Groq

!!! note
Although Groq is supported, its built-in function calling API isn't mature.
Any features using this API may experience degraded performance.
Let us know your experience!

  1. Get your Groq API key from Settings > API keys
  2. Open .env
  3. Find the line that says GROQ_API_KEY=
  4. Insert your Groq API Key directly after = without quotes or spaces:
    ini
    GROQ_API_KEY=gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  5. Set SMART_LLM and/or FAST_LLM to the Groq model you want to use.
    See Groq's models overview for info on the available models.
    Example:
    ini
    SMART_LLM=llama3-70b-8192

Llamafile

With llamafile you can run models locally, which means no need to set up billing,
and guaranteed data privacy.

For more information and in-depth documentation, check out the llamafile documentation.

!!! warning
At the moment, llamafile only serves one model at a time. This means you can not
set SMART_LLM and FAST_LLM to two different llamafile models.

!!! warning
Due to the issues linked below, llamafiles don't work on WSL. To use a llamafile
with AutoGPT in WSL, you will have to run the llamafile in Windows (outside WSL).

text
Instructions

1. Get the `llamafile/serve.py` script through one of these two ways:
    1. Clone the AutoGPT repo somewhere in your Windows environment,
       with the script located at `classic/original_autogpt/scripts/llamafile/serve.py`
    2. Download just the [serve.py] script somewhere in your Windows environment
2. Make sure you have `click` installed: `pip install click`
3. Run `ip route | grep default | awk '{print $3}'` *inside WSL* to get the address
   of the WSL host machine
4. Run `python3 serve.py --host {WSL_HOST_ADDR}`, where `{WSL_HOST_ADDR}`
   is the address you found at step 3.
   If port 8080 is taken, also specify a different port using `--port {PORT}`.
5. In WSL, set `LLAMAFILE_API_BASE=http://{WSL_HOST_ADDR}:8080/v1` in your `.env`.
6. Follow the rest of the regular instructions below.

[serve.py]: https://github.com/Significant-Gravitas/AutoGPT/blob/master/classic/original_autogpt/scripts/llamafile/serve.py


* [Mozilla-Ocho/llamafile#356](https://github.com/Mozilla-Ocho/llamafile/issues/356)
* [Mozilla-Ocho/llamafile#100](https://github.com/Mozilla-Ocho/llamafile/issues/100)

!!! note
These instructions will download and use mistral-7b-instruct-v0.2.Q5_K_M.llamafile.
mistral-7b-instruct-v0.2 is currently the only tested and supported model.
If you want to try other models, you'll have to add them to LlamafileModelName in
llamafile.py.
For optimal results, you may also have to add some logic to adapt the message format,
like LlamafileProvider._adapt_chat_messages_for_mistral_instruct(..) does.

  1. Run the llamafile serve script:

    shell
    python3 ./scripts/llamafile/serve.py

    The first time this is run, it will download a file containing the model + runtime,
    which may take a while and a few gigabytes of disk space.

    To force GPU acceleration, add --use-gpu to the command.

  2. In .env, set SMART_LLM/FAST_LLM or both to mistral-7b-instruct-v0.2

  3. If the server is running on different address than http://localhost:8080/v1,
    set LLAMAFILE_API_BASE in .env to the right base URL


AGENTS (`docs/AGENTS.md`)

Documentation Guidelines

Where a Document Goes

  • Published pages live in docs/home/, docs/platform/ and docs/integrations/. GitBook publishes them at agpt.co/docs, and a page appears on the site once that folder's SUMMARY.md lists it. Put a page there only if it is written for people outside the team: people using, self-hosting or contributing to AutoGPT.
  • Engineering notes live in docs/engineering/: the team's working documents, such as analytics and tracking plans, rollout plans, runbooks, internal references, and design and architecture notes. They are not on the docs site. Don't list one in a SUMMARY.md.

docs/engineering/ is not private. The repository is public and AutoPilot's documentation search indexes the folder, so keep secrets and confidential material out of it, as with any file in the repository.

When unsure, write the document in docs/engineering/. Listing a page in a SUMMARY.md publishes it, so do that only for a page meant for the site.

The site follows master. .github/workflows/docs-gitbook-sync.yml copies master's docs/ to the gitbook branch, and GitBook publishes that branch. Change docs through a pull request to dev. Never commit to gitbook, open a pull request against it, or edit pages in GitBook: the next copy overwrites anything that isn't on master.

Every Markdown page under docs/platform/ must be listed in docs/platform/SUMMARY.md: every .md file other than SUMMARY.md itself and GitBook's own files under a .gitbook/ directory. A SUMMARY.md may list only pages inside its own folder. .github/workflows/scripts/test_docs_layout.py checks those two things on every pull request. It cannot tell whether a listed page is meant for the public; that is for the author and the reviewer to judge.

Block Documentation Manual Sections

When updating manual sections (``) in block documentation files (e.g., docs/integrations/basic.md), follow these formats:

How It Works Section

Provide a technical explanation of how the block functions:

  • Describe the processing logic in 1-2 paragraphs
  • Mention any validation, error handling, or edge cases
  • Use code examples with backticks when helpful (e.g., [[1, 2], [3, 4]] becomes [1, 2, 3, 4])

Example:

markdown
The block iterates through each list in the input and extends a result list with all elements from each one. It processes lists in order, so `[[1, 2], [3, 4]]` becomes `[1, 2, 3, 4]`.

The block includes validation to ensure each item is actually a list. If a non-list value is encountered, the block outputs an error message instead of proceeding.

Use Case Section

Provide 3 practical use cases in this format:

  • Bold Heading: Short one-sentence description

Example:

markdown
**Paginated API Merging**: Combine results from multiple API pages into a single list for batch processing or display.

**Parallel Task Aggregation**: Merge outputs from parallel workflow branches that each produce a list of results.

**Multi-Source Data Collection**: Combine data collected from different sources (like multiple RSS feeds or API endpoints) into one unified list.

Style Guidelines

  • Keep descriptions concise and action-oriented
  • Focus on practical, real-world scenarios
  • Use consistent terminology with other blocks
  • Avoid overly technical jargon unless necessary

Engineering Activation Metrics (`docs/engineering/activation-metrics.md`)

Activation Metrics & Experiments

How the platform measures activation, retention and unit economics across
every surface (classic agents, Autopilot, experts, schedules), and how to run
an A/B/C experiment whose results can be sliced in both PostHog and Looker.

This page is the contract between engineering and go-to-market: the event
names, the SQL views and the definitions below are the ones dashboards should
be built on. Change a definition here and in every view that repeats it: the
task predicate is spelled out in user_task_daily, user_lifecycle,
retention_task_weekly and unit_economics_monthly until a shared base view
exists, so a change to one must be made in all four.

Where each tool fits

Tool Owns Reads
Postgres analytics.* views The numbers. Every metric below is a view in autogpt_platform/analytics/queries/. Primary tables (platform.*, auth.*).
Looker Studio GTM dashboards. Only analytics.* views, via the analytics_readonly role.
PostHog Funnels, session replay, experiment bucketing and significance testing. Frontend autocapture + the server-side activation events below. Can also read analytics.* as a data-warehouse source.
LaunchDarkly Feature gating (on/off, rollouts, targeting). Not for A/B measurement. —
DataFast Marketing attribution: which channel brought a visitor who signed up, ran an agent, paid. Browser goals only (signup, paywall_view, run_agent, schedule_agent, ...).
Admin dashboard Ops views: platform costs, copilot usage export, execution accuracy. Backend admin routes.
Sentry Errors and stack traces behind agent_run_finished with status: failed. —

Definitions

  • Task: a unit of work a person asked for now. Either a human-started
    agent run (triggerSource in manual, api) or a human chat turn (a
    user message in an Autopilot or expert session whose origin is not
    automation). A run the copilot tool started (triggerSource = copilot)
    is counted once, through the chat turn that asked for it. Dry runs, nested
    sub-graph runs and dream/memory sessions are never tasks.
  • Automated task: a schedule fire, a webhook trigger, or a scheduled
    follow-up turn. Counted separately so "people doing things" and "agents
    running" can be compared.
  • Activated: at least 3 tasks on at least 2 distinct days within 14 days
    of signup. (analytics.user_lifecycle.activated)
  • Last active: latest of last task, last visit, last scheduled run,
    falling back to signup. Because a scheduled run counts, an account whose
    schedule fires daily is never stale_* or churned_30d while it keeps
    firing; retention_task_weekly deliberately ignores schedules and measures
    people coming back. Use the one that matches the question.
  • Login / visit: a session row, from auth.sessions (Supabase, history
    up to the Better Auth cutover on 2026-07-30) or platform.UserAuthSession
    (Better Auth, everything since). Every login column reads both.
  • Stale: no activity in 14 / 30 days (stale_14d, stale_30d).
  • Churned (30d): had at least one task ever, signed up more than 30 days
    ago, no activity in the last 30 days.
  • Never activated (30d): signed up more than 30 days ago and never did a task.
  • Cost to us: PlatformCostLog.costMicrodollars, our real provider spend,
    split into agent (block runs), copilot (turns) and background (dream passes).
  • Credits charged: CreditTransaction USAGE rows, in cents. A run
    billed to an organisation writes OrgCreditTransaction instead; the credit
    columns in user_task_daily, user_lifecycle and unit_economics_monthly
    include those rows under the user who initiated them (initiatedByUserId),
    while cost stays with the acting user, so per-user margin stays comparable
    for org members.

Activation events (PostHog)

Emitted server-side by backend/util/product_analytics.py with
distinct_id = user id, so they join the frontend's posthog.identify.
Every event carries environment and source: "platform".

GTM asked for Event Fires when Key properties
run_agent agent_run_started without expert_id A person starts an agent run (UI, API key, copilot tool). Not schedules, webhooks, sub-graphs or dry runs. via, via_ref, graph_id, graph_exec_id
run_autopilot chat_message_sent without expert_id A person sends a message in an Autopilot chat. chat_session_id, origin (web, slack, telegram, discord)
run_expert chat_message_sent / agent_run_started with expert_id A person sends a message in an expert chat or starts an expert workflow run (kind: workflow_run). expert_id, kind
schedule_agent_run / schedule_autopilot_run / schedule_expert_run schedule_created Any schedule is registered, from any surface. target: agent / autopilot / expert, cron, is_recurring, schedule_id
schedule_agent_ran / schedule_autopilot_ran / schedule_expert_ran schedule_fired A schedule produced work. target, schedule_id, graph_exec_id or chat_session_id
(trigger) trigger_fired A webhook produced a run. webhook_id, graph_exec_id, target
agent_fail agent_run_finished with status: failed A run reaches FAILED. via, failure_reason, expert_id
— agent_run_finished with status: completed A run reaches COMPLETED. via, cost_cents, duration_seconds
— expert_hired A user hires an expert from a template, from any surface. expert_id, template_id, surface
— integration_connected A user connects a credential, by OAuth or by pasting a key. provider, credential_type, method
— credential_oauth_started The backend issues an OAuth login URL (GET /api/integrations/{provider}/login). Not Codex, which signs in with a device login. provider
— credential_oauth_exchange_failed POST /api/integrations/{provider}/callback returns an error, on any path. Not Codex. provider, status_code (unset for an unexpected error), failure_class, detail (error message or class, secrets removed, at most 200 characters)
agent_idle, stale account (not events) Computed states, see agent_health and user_lifecycle. —

credential_oauth_started, integration_connected (method: oauth) and
credential_oauth_exchange_failed give started / connected / failed per
provider. They are sent by the backend so they are not held back by the
browser's analytics consent. failure_class says where the callback failed:

failure_class Status Cause
invalid_state 400 State token missing, expired, or issued for another provider.
provider_unavailable 400 / 404 / 500 / 501 No OAuth handler for the provider, or its client id and secret are not set.
token_exchange 400 The provider rejected the code, or the exchange raised.
credential_merge 400 The new token could not be stored on an existing credential (username or provider mismatch, managed or system credential).

An unexpected error (not an HTTP error we raise, e.g. a database error) keeps
the class of the step it happened in, with only the exception class as
detail and no status_code: the app's exception handlers pick the response
status later (ValueError is a 400, for example).

A granted scope set narrower than the one requested is not a callback
failure: the credential is stored and integration_connected fires.

The event names follow the product analytics plan; the chat events
(chat_tool_called, chat_outcome, ...) and billing events (topup_completed,
trial_*, subscription_*, ...) are listed there too. Every PostHog event, its sender, its properties and whether it is live,
planned or removed is listed in the PostHog Tracking Plan.

SQL views (Looker)

New or extended in this change. Existing views (retention_login_*,
retention_execution_*, users_activities, platform_cost_log, ...) are
untouched.

View Grain Answers
graph_execution (extended) one row per agent run Now also triggerSource, triggerRef, expertId, failureReason, isDryRun, isSubgraphRun.
chat_turn one row per human chat message Autopilot vs expert usage; the SQL twin of chat_message_sent.
user_task_daily user × day How many tasks people run, by surface and by how they started; failures; our cost; credits charged; logins.
user_lifecycle one row per user First/last activity per surface, first-two-weeks behaviour, cost and revenue, and the labels activated, stale_*, churned_30d, never_activated_30d. The feature table for churn analysis.
user_lifecycle_funnel_weekly signup-week cohort Signup → onboarded → first task in 7d → activated in 14d → schedule / expert / purchase → retained at week 4.
retention_task_weekly cohort × lifetime week Retention where "active" means any human task on any surface.
unit_economics_monthly user × month Cost to us per user per month, per task and per active day; credits charged; gross margin; by tier.
agent_health user × library agent Idle, never-run and failing agents.
experiment_assignment user × experiment The arm each user saw; join on user_id to split anything else by variant.
user_attribution one row per user Where the user came from: shared anonymous id, PostHog device id, DataFast visitor id, first landing page, UTM tags, signup method. Join to user_lifecycle for activation by channel.

Apply them to a database with:

bash
cd autogpt_platform/backend
poetry run analytics-views

Then add each new view as a data source in Looker Studio (the header of every
.sql file documents its columns and example queries). triggerSource is
NULL on runs created before September 2026; the views expose those as
agent_runs_untagged and count them as human tasks.

One identity across LaunchDarkly, PostHog and the database

The two tools used to disagree about who a visitor is. LaunchDarkly only
knew a user after login (every logged-out visitor shared the literal key
anonymous, so it could not bucket pre-signup at all), while PostHog minted
its own anonymous id and merged it into the user id at identify. Nothing
tied a pre-signup arm to a post-signup person, and nothing tied either to
the DataFast visitor that marketing sees.

Now the frontend owns one first-party anonymous id
(src/services/analytics/anonymous-id.ts, stored in localStorage, adopting
an existing PostHog device id when there is one) and hands it to everything:

  • PostHog is bootstrapped with it as the anonymous distinct id, so
    identify(user.id) merges it into the person as usual.
  • LaunchDarkly gets it as the anonymous user key before login, and as
    a device context alongside the user context after login. Rules on the
    user kind are unchanged; a rule that buckets by device keeps the same
    arm across signup.
  • The database stores it on the user at first login (UserAttribution,
    reported by the browser to POST /api/analytics/attribution) together
    with the PostHog device id, the DataFast visitor and session ids (from the
    request headers the frontend already sends), the first landing page, UTM
    tags and the signup method. analytics.user_attribution exposes it.

Rule of thumb: LaunchDarkly gates, PostHog measures, the database joins.
Server-side flag evaluation stays keyed by user id (the backend has no
device context yet), so bucket pre-login experiments on the client.

Running an experiment

  1. Create a multivariate feature flag in PostHog with the arm keys you want
    (control, variant-a, variant-b, ...) and make it an experiment there.
  2. In the frontend, read it through useExperiment(key) from
    src/services/experiments/useExperiment.ts. It returns { variant, isResolved }:
    render the control experience until isResolved, and do not fire one-shot
    side effects before then, or a late variant is mis-recorded as control.
    The hook reports the arm to POST /api/experiments/assignments once per
    user and experiment; the backend keeps the first arm it sees.
  3. Backend-decided experiments can call
    backend.data.experiments.record_assignment(user_id, key, variant, source="backend").
  4. Read results in PostHog (exposure + the activation events above as goals),
    and in Looker by joining analytics.experiment_assignment to
    user_lifecycle, user_task_daily or unit_economics_monthly.

The onboarding paywall experiment (subscription-pricing-page-initial-state)
is already wired this way.

Deploying this change

  1. Run the migrations 20260902120000_add_execution_trigger_source (adds
    AgentGraphExecution.triggerSource / triggerRef),
    20260902120100_add_experiment_assignment (the ExperimentAssignment
    table) and 20260902120200_add_user_attribution (the UserAttribution
    table). Existing analytics.* views are unaffected: no column they select
    from changes type.
  2. Deploy the backend. From that moment every run row carries its trigger and
    every activation event flows to PostHog.
  3. Nick Tindle runs poetry run analytics-views against production right
    after the deploy that carries this change, before any Looker source is
    added. Before that, on staging: EXPLAIN (ANALYZE, BUFFERS) on
    user_lifecycle, user_lifecycle_funnel_weekly, experiment_assignment
    and user_attribution. user_lifecycle has no date bound (it is a
    per-user lifetime table), so every refresh aggregates the full history of
    AgentGraphExecution, ChatMessage, PlatformCostLog and
    CreditTransaction; if staging shows that hurting, the follow-up is a
    nightly-refreshed materialized view (a generate_views.py change), not a
    Looker source on the live view. Then wire the new views into Looker Studio.
  4. Confirm in PostHog that agent_run_started, chat_message_sent,
    schedule_created and agent_run_finished are arriving with
    source = platform.

The questions GTM will ask next

The list GTM gave us is the first layer. These are the questions that follow
once they see the data, with where the answer already lives or what is still
missing.

Activation

  • Which first action predicts retention: an agent run, an Autopilot chat, an
    expert, or a schedule?
    — user_lifecycle has the first-time timestamps
    and early counts per surface; group by the earliest one and compare
    churned_30d.
  • How long from signup to first task, and where do people stall? —
    hours_to_first_task plus the onboarding funnel view; combine with PostHog
    session replay on users with never_activated_30d.
  • Does connecting an integration change activation? —
    integrations_connected_total and first_integration_connected_at in
    user_lifecycle (from IntegrationCredential), connected_integration in
    the funnel, and the integration_connected event in PostHog.
  • Which marketing channel brings activated users? — join
    user_attribution (UTM tags, referrer, landing page, DataFast visitor id)
    to user_lifecycle. For channels only DataFast knows, export DataFast by
    visitor id and join on datafast_visitor_id.

Retention and churn

  • Are we retaining people, or just their schedules? — retention_task_weekly
    (people) next to retention_execution_weekly (all runs incl. automated).
  • Which early behaviours separate churned from retained users? — the
    example query on user_lifecycle; the next step is a proper model, which
    needs the same table exported to a notebook.
  • Who is about to churn? — stale_14d users with tasks_28d dropping vs
    their prior 28 days; agent_health.idle_7d and failing are the earliest
    per-agent signals. Gap: a weekly "at-risk" list is a view away, but
    acting on it (email, in-product nudge) needs the alerts pipeline.
  • Do failures cause churn? — agent_runs_failed_total and
    agent_runs_no_credits_total in user_lifecycle; failure_reason in
    graph_execution. Insufficient balance is already separated because it is
    a pricing problem, not a product problem.

Cost and pricing

  • What will a free trial with card on file cost us? —
    unit_economics_monthly by tier and by month-of-life; the
    PERCENTILE_CONT example shows the tail, which is what a trial cap must
    cover.
  • Cost per task by surface and model? — split exists by surface in
    user_task_daily; per-model cost is in platform_cost_log. Gap: chat
    turns are not linked to the exact cost rows (copilot cost rows carry the
    session id in graphExecId), so cost per turn is an average, not exact.
  • Gross margin per user and per tier? — gross_margin_usd in
    unit_economics_monthly, with the caveat that credits are prepaid.
  • How much do we spend on users who never come back? — join
    unit_economics_monthly to user_lifecycle.churned_30d.
  • Background cost (dream/memory passes) as a share of total? —
    background_cost_usd in user_task_daily and unit_economics_monthly.

Experiments

  • Did the pricing-page arm change activation or retention, not just
    conversion?
    — experiment_assignment joined to user_lifecycle.
  • Can we A/B backend behaviour (model routing, default expert, prompt)? —
    yes via record_assignment(source="backend"); LaunchDarkly stays the
    gate, PostHog and the assignment table hold the arm.
  • Can the team keep using LaunchDarkly for arms? — not in the browser
    today: the unused useLaunchDarklyExperiment bridge and its
    experiment_exposed event were removed (SECRT-2722), so a LaunchDarkly arm
    reaches the assignment table only through record_assignment. Gap: the
    backend's LaunchDarkly context has no device kind yet, so server-evaluated
    flags cannot bucket by the anonymous id.

Experts and schedules

  • Do hired experts get used after day one? — expert_turns_total,
    expert_workflow_runs_total, experts_active in user_lifecycle;
    expert_hired event in PostHog for the funnel.
  • How many schedules are created and then silently stop? — creations are in
    ActivityEvent, fires are in graph_execution.triggerSource = 'schedule'.
    Gap: schedules live only in the APScheduler job store, so there is no
    durable schedule row to join pauses and deletions to; a Schedule table
    would make schedule health first-class.

Operational gaps to close next

  • No lastActiveAt on User; user_lifecycle derives it, which is fine for
    dashboards but not for real-time gating.
  • Frontend PostHog is not bootstrapped with flags, so experiment variants
    resolve after first paint. useExperiment.isResolved papers over this;
    server-side bootstrap would remove the delay.
  • DataFast goals fire only after analytics consent, so they undercount
    relative to the server-side events. Use the server-side numbers as truth
    and DataFast for channel attribution only.

Engineering Codex Subscription Preview (`docs/engineering/codex-subscription-preview.md`)

ChatGPT/Codex subscription transport preview

This private preview lets a user connect a ChatGPT account and use that
account's Codex allowance for three explicit execution paths:

  • The existing Code Generation block through the codex_app_server
    transport.
  • A newly created AutoPilot chat session when ChatGPT/Codex is the only
    connected subscription transport, or is selected from multiple connected
    subscription transports.
  • An AutoPilot graph block with an explicitly selected ChatGPT/Codex
    connection.

codex is the credential provider and Codex App Server is the execution
runtime. A ChatGPT plan is not unlimited inference: these calls consume the
connected account's Codex allowance or credits and remain subject to its rate
limits. Subscription-backed usage records tokens and
billing_mode=user_subscription; it is not reported as AutoGPT provider USD
spend.

The preview does not route Orchestrator, shared LLM blocks, image generation,
or background system work through the connected account. Existing graphs and
AutoPilot sessions keep their stored route. New AutoPilot sessions use a
connected subscription transport automatically when exactly one is available.

Run locally

From autogpt_platform:

sh
docker compose up -d --build

Codex pairing and its supported execution routes are available without feature
flag environment variables. The stack mounts /run/autogpt-codex as a
memory-backed temporary filesystem for REST, executor, and Copilot executor
processes.

Open http://localhost:3000, then open Settings > Integrations, choose
OpenAI, select ChatGPT, and click Sign in with ChatGPT. In the sign-in
window, open the ChatGPT verification page and enter the one-time code.

Test the Code Generation block

  1. Add a Code Generation block to a graph.
  2. Set Transport to Codex App Server.
  3. Select the connected ChatGPT for Codex credential.
  4. Run the graph.

Existing graphs continue to default to the OpenAI API transport. The block's
model dropdown applies to that API transport. The subscription transport uses
App Server's current account-compatible default; its live catalog can change
independently of public API model names.

Test AutoPilot

  1. Open AutoPilot and start a new task.
  2. If ChatGPT/Codex is the only connected subscription transport, it is used
    automatically and no connection selector is shown. If multiple subscription
    transports are connected, choose the desired connection before sending the
    first message.
  3. Choose Balanced or Advanced as available, then send the message. Text and
    permitted AutoGPT tool calls stream through the existing AutoPilot event
    surface.

The route and credential are stored on the new session and are immutable for
that session. Start another task to use a different connected subscription
transport. The server reloads the authoritative session route before each
queued turn; queued messages carry only the credential ID, never the OAuth
tokens. A missing, revoked, or busy Codex credential fails visibly. There is no
silent fallback to an AutoGPT-funded model or another user's credential.

The visible model tier and the server-selected execution path select a Codex
route from the shared model catalog, then validate it against the models
advertised by the connected account.

Execution path Balanced Advanced
Fast GPT-5.6 Luna GPT-5.6 Terra
Claude Agent SDK GPT-5.6 Terra GPT-5.6 Sol

If the account does not expose the mapped model, the transport uses the visible
account default. File attachments, agent-building tools, and SDK sub-sessions
use the same Claude Agent SDK path as platform-funded AutoPilot.
Builder-panel-bound sessions remain platform-funded in this preview because
their persistent session is created without an AI connection selector.

The Copilot executor keeps one exclusive credential lease and one Codex runtime
per connected account, then multiplexes overlapping chats onto separate Codex
threads. Canceling or finishing one chat does not stop its siblings. This pool
is process-local: a request routed to another executor process or replica cannot
join the owner yet and still fails with the bounded, retryable
codex_credential_busy error. A hosted multi-replica rollout therefore needs
credential-owner routing or a dedicated bridge. The normal platform AutoPilot
route is unchanged.

Test the AutoPilot graph block

  1. Add an AutoPilot block to a graph.
  2. Select the saved connection under ChatGPT / Codex connection.
  3. Run the graph.

The credential field is a reference-only credential input: graph validation
checks ownership and export strips the credential ID, while the graph executor
does not hold the outer credential lease. The Copilot executor acquires the
single runtime lease when it consumes the queued turn. This preserves normal
credential rebinding without deadlocking the nested execution path. Leaving the
field empty preserves the existing platform-funded behavior.

Claude Agent SDK transport

AutoPilot keeps the existing Claude Agent SDK and Claude Code CLI as its agent
harness. A request-scoped loopback Anthropic Messages endpoint translates model
rounds to the pinned Codex App Server's experimental dynamicTools protocol.
Claude Code still owns the MCP tool loop, permission hooks, builder behavior,
transcript, resume, compaction, and sub-session machinery. Codex supplies the
model response using the selected user's ChatGPT/Codex credential.

The loopback endpoint is bound to 127.0.0.1 on an ephemeral port and accepts
only a random capability generated for that turn. The capability is placed in
the Claude CLI child environment; ChatGPT access, refresh, and ID tokens are
never placed there. Anthropic text and tool-use events are mapped to the same
Claude SDK response adapter used by normal AutoPilot.

The App Server process itself is fail-closed: native shell, filesystem, web
search, apps, plugins, environments, workspace roots, and approval requests are
disabled, and its sandbox is read-only. Any side effect comes only through a
tool that Claude Code was already permitted to invoke through AutoGPT's MCP and
security hooks.

ChatGPT is reached over HTTPS: sign-in, refresh, the model catalog and
inference all run against auth.openai.com and chatgpt.com, so no Codex
binary ships in the image and there is no version to pin. The Claude Agent SDK
and its CLI are still pinned by the backend lock. Because the compatibility
surface is version-sensitive, updates require the focused protocol and Messages
conformance tests to pass before rollout.

Verify an existing local login

To test an existing local Codex login before starting the stack, run this from
autogpt_platform/backend:

sh
poetry run python -m scripts.codex_preview_smoke

The smoke test reads ~/.codex/auth.json without writing to it, checks the
account and model catalog, and performs one subscription-backed turn with a tool
call over HTTPS -- failing if the model never calls the tool or returns no text.
It reports the quota the turn's response headers carried. It does consume a
small amount of the connected account's usage.

Deploy a private cloud preview

Codex pairing and supported execution routes do not require availability flags
or frontend build-time overrides. Scope a private preview with the deployment's
ordinary access controls instead of hiding device login with environment
variables. The transport selector is driven by the connected, compatible
subscription providers: it is hidden for zero or one and shown when more than
one is available.

REST, executor, and Copilot executor must share the normal AutoGPT credential
database, encryption configuration, and Redis cluster. Give every process that
may launch App Server an isolated, memory-backed CODEX_TEMP_ROOT; never mount
it on a persistent volume.

Set FRONTEND_BASE_URL, NEXT_PUBLIC_FRONTEND_BASE_URL, and BETTER_AUTH_URL
to the exact public origin of the preview, including its https:// scheme and
without a path. REST deliberately builds the device-login URL from this
configured origin instead of trusting forwarded host headers. Leaving a
production or localhost origin in a cloud preview sends the popup to a host
that does not have the preview's Better Auth cookie.

NEXT_PUBLIC_FRONTEND_BASE_URL is compiled into the frontend bundle. Pass it as
a frontend image build argument, then set BETTER_AUTH_URL on the frontend at
runtime and FRONTEND_BASE_URL on REST at runtime. The development Compose file
forwards all three exported values and keeps http://localhost:3000 as their
local default.

Device-login status and ownership are Redis-backed, so polling and cancellation
can reach different REST replicas. The active App Server login actor is still
process-local, however: an owner-pod restart interrupts the login and another
replica cannot take it over or resume it. Keep rolling deploys away from active
sign-ins and ask the user to restart an interrupted login. Broad cloud rollout
requires moving the login actor into the dedicated bridge described below.

The containers need outbound HTTPS access to OpenAI's ChatGPT/Codex endpoints.
No callback ingress from OpenAI is required because Codex App Server polls for
device-code completion.

Preview safety boundary

  • The encrypted, USER-scoped IntegrationCredential row remains the source of
    truth.
  • Raw ChatGPT tokens do not enter Redis, RabbitMQ, frontend responses,
    container-wide environment variables, or AutoPilot session records.
  • Tokens are held in memory for the life of a turn and never written to disk:
    there is no on-disk auth home, because there is no process to hand one to.
    A client is built per turn and never cached, so one user's subscription can
    never serve another's request. Refresh is owned by the credentials manager
    and serialized behind its Redis mutex, since OpenAI rotates the refresh token
    on every exchange.
  • Code Generation turns expose no dynamic tools or host workspace. AutoPilot
    exposes only the tools registered by its existing Claude SDK/MCP harness.
  • Runtime reuse is scoped to the exact user and credential. Credentials are
    never pooled across users or substituted for another account.
  • There is no silent platform-key fallback.

The current implementation launches Codex App Server in-process from the
ordinary REST, executor, and Copilot executor containers. Those containers
currently run as root and retain the backend services' broader database, Redis,
filesystem, and network privileges. The temporary-home and fail-closed thread
configuration reduce exposure, but they do not make this a safe boundary for a
broad external cloud rollout.

Treat the current shape as local development or a tightly access-controlled
staff/private preview only. Before enabling external users, move runtime and
device-login supervision into a dedicated unprivileged bridge with a non-root
user, read-only root filesystem, dropped capabilities, memory-only credential
homes, process and concurrency limits, narrowly restricted egress, and a
least-privilege internal credential lease interface. The bridge must also own
login actor takeover or explicit restart semantics. Complete the commercial
and policy review for hosted subscription passthrough before broader rollout.


Engineering Feature Flags (`docs/engineering/feature-flags.md`)

Feature flags

Which vendor answers a flag read is set per deployment by FEATURE_FLAG_BACKEND:
launchdarkly (the default), posthog, or dual — evaluate both, serve
LaunchDarkly's answer, and log every disagreement. Call sites are unaffected by
the choice; they read through evaluate_feature_flag() on the backend and
useGetFlag / useFlagStatus on the frontend.

One PostHog project per environment

Dev and Prod each have their own PostHog project, and each project holds its
own flags. A deployment reads its flags from the same project its analytics go
to, the one POSTHOG_API_KEY names, and evaluates them locally with that
project's Feature Flags Secure API Key in POSTHOG_SECRET_KEY. Where local
evaluation can't decide a read, the SDK asks PostHog's /flags endpoint
instead, unless the read is local-only, as a signed-out visitor's own read is.

Dev Prod
PostHog project 281733 117338
POSTHOG_API_KEY, POSTHOG_SECRET_KEY (backend) Dev project's Prod project's
NEXT_PUBLIC_POSTHOG_KEY (Vercel) Dev project's Prod project's

PostHog has no environments within a project, so a flag both deployments read
is defined in each project. The Projects tab on the
flag list
and on each flag's page shows a flag across both projects side by side.

Until Prod switches to PostHog, Prod serves LaunchDarkly's answers
(FEATURE_FLAG_BACKEND=dual only compares PostHog's against them), so a change
to a Prod flag is made in LaunchDarkly and mirrored in the Prod PostHog project.

The browser. With NEXT_PUBLIC_FEATURE_FLAG_BACKEND=server the browser
gets its flags from the backend's GET /api/feature-flags, which evaluates
them through the same path as the backend's own reads, so the browser and the
backend give one answer from one project. A Vercel preview in server mode
gets the answer of the backend it calls. The other values read flags in the
browser itself: launchdarkly (the default) from LaunchDarkly, and posthog /
dual from the project NEXT_PUBLIC_POSTHOG_KEY names. useExperiment reads
posthog-js in every mode.

The environment property. Every PostHog flag read the backend makes, its
own and the browser's in server mode, carries the person property
environment: the backend's APP_ENV (local, dev or prod), the same
value its analytics events carry. A release condition can use it in either
project like any other person property. It is only an evaluation input, never
stored on a person. Browser reads through posthog-js (posthog / dual mode)
don't carry it: they send buildFlagPersonProperties (email, email domain,
role, created at) only, so a condition on environment matches only the
backend's reads there.

Splitting signed-out visitors. A visitor who allowed analytics is bucketed
under visitor:, but only by conditions their read can
decide: a rollout percentage, environment and is_anonymous, with no email.
A condition group on another person property or a cohort (the migrated
"email in employees", for one) can't be decided for a visitor, so a visitor
whom no decidable group matches gets the shared signed-out answer instead.
"Employees OR 20%" is then all or nothing for visitors, whichever side the
shared anonymous key lands on. For a real 20% of visitors, add
is_anonymous is not true to the rollout group: the shared answer is then
off, and only each visitor's own bucket turns the flag on.

Sharing PostHog's flag definitions

PostHog evaluates flags locally, in the process, from a set of flag definitions
it polls for every 30 seconds. PostHog
bills one definitions fetch as ten flag requests,
so a poller in every process makes that bill scale with replica count: at two
polls a minute, each process costs 28,800 flag requests a day whether or not it
reads a single flag.

So one process is elected to refresh the definitions and write them to Redis,
and every other process evaluates from that shared copy. The fleet then costs
one process's worth of polling — 28,800 flag requests a day in total, instead of
28,800 per replica.

This is PostHog's own FlagDefinitionCacheProvider interface; the SDK calls it
from its polling thread, never from a flag read, so no request can block on
Redis.

Settings

Setting Default What it does
POSTHOG_FLAG_DEFINITION_CACHE redis redis shares definitions through Redis; memory keeps them in the process; none restores one poller per process.
POSTHOG_FLAG_DEFINITION_REFRESH_SECONDS 30 How often the refresher fetches, and how often everyone else re-reads the shared copy.
POSTHOG_FLAG_DEFINITION_CACHE_TTL_SECONDS 600 How long the shared copy stays readable after the last refresh.

None of them is read unless FEATURE_FLAG_BACKEND is posthog or dual and
POSTHOG_SECRET_KEY is set — without that key the SDK evaluates over the
wire and has no definitions to share.

Per environment. Leave the defaults on dev and prod: both run several
replicas against a shared Redis, which is what the cache is for. Local
development and CI run FEATURE_FLAG_BACKEND=launchdarkly, so none of this is
reached; set POSTHOG_FLAG_DEFINITION_CACHE=memory if you want to exercise
PostHog locally without Redis.

What the refresher is

Every process tries SET <key> NX PX on one Redis key each time it polls.
The one that gets it fetches the definitions from PostHog and writes them to
Redis; the others read that copy. The winner renews its own lock on every poll,
so it keeps the job. The lock expires two poll intervals after its last renewal,
and the next process to poll takes it, so a refresher that dies is replaced
within 90 seconds at the defaults. One shutting down cleanly releases the lock
immediately, and is replaced on the next poll.

Telling it is working

One process logs, once, when it takes the job:

text
This process now refreshes the shared PostHog flag definitions (<host>:<pid>:<id>)

and the others log Another process now refreshes the shared PostHog flag definitions when they lose it.

The counter autogpt_posthog_flag_definition_cache_events_total on /metrics
carries the same story, by outcome:

outcome meaning
refresher / stored this process holds the lock and wrote its definitions to the shared copy, which it does every poll whether or not PostHog reported a change
follower / cached this process read the shared copy
stale the shared copy has not been rewritten for five poll intervals, so no refresher is writing it; it is served anyway
empty nothing in the cache; a process with no definitions yet fetches from PostHog directly, and one that has some keeps them
error Redis was unreachable

Healthy, with N replicas: stored increments once per poll on exactly one
process, cached once per poll on each of the other N−1, and empty and
error stay flat.

Failure modes

What happens What the platform does
Cache empty at boot The process fetches from PostHog directly, once, and the refresher fills the cache.
Redis unreachable Every process polls PostHog for itself, as it would without the cache, and logs once per five minutes. Flag reads are unaffected.
Shared copy goes stale It is served anyway, with a warning, while the refresher catches up. Stale definitions beat none.
The refresher dies Its lock expires and another process takes the job on its next poll.
A definitions fetch fails The definitions already in memory stay in use, and the refresher keeps writing that copy to the cache.

Nothing here can make a flag read wait on Redis: evaluation reads the
definitions already in the process, and only a read they can't decide waits on
PostHog's /flags.


Engineering Hosted Mcp Catalog (`docs/engineering/hosted-mcp-catalog.md`)

Maintaining MCP Integrations

mcp_catalog.json defines the official services shown in Integrations and the AutoPilot MCP guide. mcp_catalog.py validates the records and exposes them through get_mcp_catalog().

Add or update a service

  • Keep a stable mcp_... ID and link directly to the vendor's setup documentation.
  • Describe the server's purpose and required plan, admin approval, region, endpoint, and credentials in description and setup_instructions.
  • Choose the connection setup that matches the vendor's server; see Choose how users connect.
  • Order the supported auth_methods by preference; the first method is selected initially. Choices are oauth, bearer, basic, and none.
  • Set oauth_server_url when the vendor supplies a separate signed-in endpoint.
  • Set documented oauth_scopes for the initial grant and separate oauth_write_scopes for the optional Allow changes choice. An empty default list omits the scope parameter; omission/null uses discovery defaults.

Choose how users connect

Match the entry to where the vendor runs its MCP server. Every URL must be a public HTTPS URL.

  • One public URL for every customer: use connection_mode: hosted with server_url. The connect dialog shows the URL locked, and AutoPilot uses it. Examples: PostHog, Notion.
  • One public URL, and the service can also be self-hosted: use hosted with server_url and allow_custom_url: true. The dialog opens on server_url, and users can replace it with their own instance's URL. AutoPilot still uses server_url. Say in setup_instructions how to connect a self-hosted instance, including any sign-in setup it needs. Example: OpenSEO.
  • A separate URL per region: use connection_mode: custom with one server_url_options entry per region and no server_url. Users pick their region, and the picker's Custom URL option covers self-hosted instances. Prefer this to one entry per region. Don't make it a hosted entry that defaults to one region, because users in the other regions would connect to the wrong one. Examples: Amplitude, Langfuse.
  • A URL unique to each customer account: use connection_mode: custom with no server_url and no options. Say in setup_instructions where users find their URL. The dialog opens with an empty URL field. Examples: Chargebee, Sourcegraph.

Both custom setups show Setup required in the integrations list. AutoPilot tells users to add their URL under Settings → Integrations before it can use the server.

Validate the change

Check the vendor's current endpoint and authentication contract. Verify initialization and tool listing with the MCP client, then a benign read using the intended permissions. For OAuth, check registration, consent, and refresh with the deployment's callback and an authorized test account.

Update affected catalog, provider, guide, and page integration tests. Cover changes to region selection, custom URLs, token handling, or optional grants. Regenerate API types when the metadata schema changes, and run the relevant backend and frontend checks.


Engineering Oauth Integration Flow (`docs/engineering/oauth-integration-flow.md`)

OAuth Integration Flow Documentation

Overview

The AutoGPT platform implements OAuth 2.0 in two distinct contexts:

  1. User Authentication (SSO): Handled by Better Auth, embedded in the Next.js frontend at /api/auth/*
  2. API Integration Credentials: Custom OAuth implementation for third-party service access

This document focuses on the API Integration OAuth flow used for connecting to external services. For the list of supported providers, see autogpt_platform/backend/backend/integrations/providers.py. For user authentication documentation, see autogpt_platform/frontend/src/lib/auth/SESSION_VALIDATION.md.

Trust Boundaries

1. Frontend Trust Boundary

  • Location: Browser/Client-side application
  • Components:
    • CredentialsInput component (autogpt_platform/frontend/src/components/contextual/CredentialsInput/CredentialsInput.tsx)
    • OAuth callback route (autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/route.ts)
  • Trust Level: Untrusted - user-controlled environment
  • Security Measures:
    • CSRF protection via state tokens
    • Popup-based flow to prevent URL exposure
    • Message validation for cross-window communication

2. Backend API Trust Boundary

  • Location: Server-side FastAPI application
  • Components:
    • Integration router (autogpt_platform/backend/backend/api/features/integrations/router.py)
    • OAuth handlers (autogpt_platform/backend/backend/integrations/oauth/)
    • Credentials store (autogpt_platform/backend/backend/integrations/credentials_store.py)
  • Trust Level: Trusted - server-controlled environment
  • Security Measures:
    • JWT-based authentication
    • Encrypted credential storage
    • Token refresh handling
    • Scope validation

3. External Provider Trust Boundary

  • Location: Third-party OAuth providers
  • Components: Provider authorization endpoints
  • Trust Level: Semi-trusted - external services
  • Security Measures:
    • HTTPS-only communication
    • Provider-specific security features
    • Token revocation support

Component Architecture

Frontend Components

1. CredentialsInput Component

  • Purpose: UI component for credential selection and OAuth initiation
  • Key Functions:
    • Displays available credentials
    • Initiates OAuth flow via popup window
    • Handles OAuth callback messages
    • Manages credential selection state

2. OAuth Callback Route

  • Path: /auth/integrations/oauth_callback
  • Purpose: Receives OAuth authorization codes from providers
  • Flow:
    1. Receives code and state parameters from provider
    2. Posts message to parent window with results
    3. Auto-closes popup window

Backend Components

1. Integration Router

  • Base Path: /api/integrations
  • Key Endpoints:
    • GET /{provider}/login - Initiates OAuth flow
    • POST /{provider}/callback - Exchanges auth code for tokens
    • GET /credentials - Lists user credentials
    • DELETE /{provider}/credentials/{id} - Revokes credentials

2. OAuth Base Handler

  • Purpose: Abstract base class for provider-specific OAuth implementations
  • Key Methods:
    • get_login_url() - Constructs provider authorization URL
    • exchange_code_for_tokens() - Exchanges auth code for access tokens
    • refresh_tokens() - Refreshes expired access tokens
    • revoke_tokens() - Revokes tokens at provider

3. Credentials Store

  • Purpose: Manages credential persistence and state
  • Key Features:
    • Redis-backed mutex for concurrent access control
    • OAuth state token generation and validation
    • PKCE support with code challenge generation
    • Default system credentials injection

OAuth Flow Sequence

1. Flow Initiation

mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant Backend
    participant Redis
    participant Provider

    User->>Frontend: Click "Sign in with Provider"
    Frontend->>Backend: GET /api/integrations/{provider}/login
    Backend->>Redis: Store state token + code verifier
    Backend->>Frontend: Return login URL + state token
    Frontend->>Frontend: Open popup window
    Frontend->>Provider: Redirect to authorization URL

2. Authorization

mermaid
sequenceDiagram
    participant User
    participant Provider
    participant Callback
    participant Frontend
    participant Backend

    User->>Provider: Authorize application
    Provider->>Callback: Redirect with code + state
    Callback->>Frontend: PostMessage with code + state
    Frontend->>Backend: POST /api/integrations/{provider}/callback
    Backend->>Provider: Exchange code for tokens
    Provider->>Backend: Return access + refresh tokens
    Backend->>Backend: Store credentials
    Backend->>Frontend: Return credential metadata

3. Token Refresh

mermaid
sequenceDiagram
    participant Application
    participant Backend
    participant Provider

    Application->>Backend: Request with credential ID
    Backend->>Backend: Check token expiry
    Backend->>Provider: POST refresh token
    Provider->>Backend: Return new tokens
    Backend->>Backend: Update stored credentials
    Backend->>Application: Return valid access token

System Architecture Diagram

text
/* Detailed source-code truncated for AI context efficiency. */

Data Flow Diagram

mermaid
graph LR
    subgraph "Data Types"
        ST[State Token]
        CV[Code Verifier]
        CC[Code Challenge]
        AC[Auth Code]
        AT[Access Token]
        RT[Refresh Token]
    end
    
    subgraph "Frontend Flow"
        U1[User Initiates]
        U2[Receives State]
        U3[Opens Popup]
        U4[Receives Code]
        U5[Sends to Backend]
    end
    
    subgraph "Backend Flow"
        B1[Generate State]
        B2[Store in Redis]
        B3[Validate State]
        B4[Exchange Code]
        B5[Store Credentials]
    end
    
    U1 --> B1
    B1 --> ST
    B1 --> CV
    CV --> CC
    B2 --> U2
    U3 --> AC
    AC --> U4
    U5 --> B3
    B3 --> B4
    B4 --> AT
    B4 --> RT
    AT --> B5
    RT --> B5

Security Architecture

mermaid
graph TB
    subgraph "Security Layers"
        subgraph "Transport Security"
            HTTPS[HTTPS Only]
            CSP[Content Security Policy]
        end
        
        subgraph "Authentication"
            JWT[JWT Tokens]
            STATE[CSRF State Tokens]
            PKCE[PKCE Challenge]
        end
        
        subgraph "Storage Security"
            ENC[Encrypted Credentials]
            SEC[SecretStr Type]
            MUTEX[Redis Mutex Locks]
        end
        
        subgraph "Access Control"
            USER[User Scoped]
            SCOPE[OAuth Scopes]
            EXPIRE[Token Expiration]
        end
    end
    
    HTTPS --> JWT
    JWT --> USER
    STATE --> PKCE
    PKCE --> ENC
    ENC --> SEC
    SEC --> MUTEX
    USER --> SCOPE
    SCOPE --> EXPIRE

Credential Lifecycle

mermaid
stateDiagram-v2
    [*] --> Initiated: User clicks sign-in
    Initiated --> Authorizing: Popup opened
    Authorizing --> Authorized: User approves
    Authorizing --> Failed: User denies
    Authorized --> Active: Tokens stored
    Active --> Refreshing: Token expires
    Refreshing --> Active: Token refreshed
    Refreshing --> Expired: Refresh fails
    Active --> Revoked: User deletes
    Failed --> [*]
    Expired --> [*]
    Revoked --> [*]
    
    note right of Active: Credentials can be used
    note right of Refreshing: Automatic process
    note right of Revoked: Tokens revoked at provider

OAuth Types Comparison

User Authentication (SSO) via Better Auth

  • Purpose: Authenticate users to access the AutoGPT platform
  • Provider: Better Auth, embedded in the Next.js frontend (supports Google, GitHub, and Discord SSO)
  • Flow Path: /login → /api/auth/* (Better Auth) → provider OAuth → back to the app
  • Session Storage: Better Auth-managed cookies
  • Token Management: Automatic by Better Auth; the Python backend validates JWTs via the JWKS endpoint (/api/auth/jwks)
  • User Experience: Single sign-on to the platform

API Integration Credentials

  • Purpose: Grant AutoGPT access to user's third-party services
  • Providers: Examples include GitHub, Google APIs, Notion, and others
    • Full list in autogpt_platform/backend/backend/integrations/providers.py
    • OAuth handlers in autogpt_platform/backend/backend/integrations/oauth/
  • Flow Path: Integration settings → /api/integrations/{provider}/login → /auth/integrations/oauth_callback
  • Credential Storage: Encrypted in PostgreSQL
  • Token Management: Custom refresh logic with mutex locking
  • User Experience: Connect external services to use in workflows

Data Flow and Security

1. State Token Flow

  • Generation: Random 32-byte token using secrets.token_urlsafe()
  • Storage: Redis with 10-minute expiration
  • Validation: Constant-time comparison using secrets.compare_digest()
  • Purpose: CSRF protection and request correlation

2. PKCE Implementation

  • Code Verifier: Random string generated using secrets.token_urlsafe(128) (approximately 171 characters when base64url encoded, though RFC 7636 recommends 43-128 characters)
  • Code Challenge: SHA256 hash of verifier, base64url encoded
  • Storage: Stored with state token in database (encrypted) with 10-minute expiration
  • Usage: Enhanced security for public clients (currently used by Twitter provider)

3. Credential Storage

  • Structure:

    python
    OAuth2Credentials:
      - id: UUID
      - provider: ProviderName
      - access_token: SecretStr (encrypted)
      - refresh_token: Optional[SecretStr]
      - scopes: List[str]
      - expires_at: Optional[int]
      - username: Optional[str]
  • Persistence: PostgreSQL via Prisma ORM

  • Access Control: User-scoped with mutex locking

4. Token Security

  • Storage: Tokens stored as SecretStr type
  • Transport: HTTPS-only, never logged
  • Refresh: Automatic refresh 5 minutes before expiry
  • Revocation: Supported for providers that implement it

Provider Implementations

Supported Providers

The platform supports various OAuth providers including GitHub, Google, Notion, Twitter, and others. For the complete list, see:

  • autogpt_platform/backend/backend/integrations/providers.py - All supported providers
  • autogpt_platform/backend/backend/integrations/oauth/ - OAuth implementations

Provider-Specific Security Considerations

  • GitHub: Supports optional token expiration - tokens may be non-expiring by default
  • Linear: Returns scopes as space-separated string, requiring special parsing
  • Google: Requires explicit offline access scope for refresh tokens
  • Twitter: Uses PKCE for enhanced security on public clients

Each provider handler implements the security measures defined in BaseOAuthHandler, ensuring consistent token management and refresh logic across all integrations.

Security Best Practices

1. Frontend Security

  • Use popup windows to prevent URL tampering
  • Validate state tokens before processing callbacks
  • Clear sensitive data from window messages
  • Implement timeout for OAuth flows (5 minutes)

2. Backend Security

  • Store client secrets in environment variables
  • Use HTTPS for all OAuth endpoints
  • Implement proper scope validation
  • Log security events without exposing tokens
  • Use database transactions for credential updates

3. Token Management

  • Refresh tokens proactively (5 minutes before expiry)
  • Revoke tokens when credentials are deleted
  • Never expose tokens in logs or error messages
  • Use constant-time comparison for token validation

Error Handling

Common Error Scenarios

  1. Invalid State Token: 400 Bad Request
  2. Provider Configuration Missing: 501 Not Implemented
  3. Token Exchange Failure: 400 Bad Request with hint
  4. Webhook Conflicts: 409 Conflict, requires confirmation
  5. Credential Not Found: 404 Not Found

Error Response Format

json
{
  "detail": {
    "message": "Human-readable error description",
    "hint": "Actionable suggestion for resolution"
  }
}

Testing Considerations

Unit Testing

  • Mock OAuth providers for flow testing
  • Test state token generation and validation
  • Verify PKCE implementation
  • Test concurrent access scenarios

Integration Testing

  • Use provider sandboxes when available
  • Test full OAuth flow with real providers
  • Verify token refresh mechanisms
  • Test error scenarios and recovery

Logging Guidelines

  • Log flow initiation and completion
  • Log errors with context (no tokens)
  • Track provider-specific issues
  • Monitor for suspicious patterns

Engineering Org Feature Map (`docs/engineering/org-feature-map.md`)

Organization Feature Map

A capability map for org/team tenancy on the AutoGPT Platform: what the
foundation shipped in the org-workspace PR series provides, and the feature
surface we expect to build on top of it. Each section lists the existing
hooks in the codebase that a feature would attach to, so scoping starts from
real anchor points instead of a blank page.

Foundation (shipped)

  • Schema: Organization, Team, OrgMember, TeamMember,
    OrgInvitation, TeamInvite, OrganizationProfile, OrganizationAlias,
    OrgBalance, OrgCreditTransaction, OrganizationSubscription,
    OrganizationSeatAssignment, TransferRequest, AuditLog
  • Request context: RequestContext (org_id, team_id, role flags,
    seat_status) resolved per-request via get_request_context, with
    X-Org-Id / X-Team-Id header overrides and personal-org fallback
  • Execution context: ExecutionContext.organization_id / team_id
    threaded through graph executions, webhooks, schedules, copilot runs
  • Billing: get_credit_model(user_id, org_id) routes to
    OrgCreditModel when an org is active; executor charging follows
  • Resources: graphs, executions, API keys, chat sessions, store
    listings carry organizationId / teamId; marketplace listings support
    org ownership and cross-org transfers
  • Frontend: org/team switcher in the navbar, OrgTeamProvider
    bootstraps context on login, zustand store persists the active org/team
  • Migration: idempotent personal-org bootstrap
    (create_orgs_for_existing_users) behind a Redis lock at startup

Deferred from the foundation series: seat enforcement (paywall/subscription
gating work owns this) and the NOT-NULL cutover migration for
organizationId columns.

1. Governance & policy controls

The headline ask: org/team admins control what members can do and which
capabilities agents may use.

Feature Sketch
Block enable/disable per org/team New BlockPolicy table (orgId, teamId?, blockId, allowed, optional reason). Enforcement at three choke points: get_graph_blocks (filter the palette), validate_graph (reject save/activation of graphs using denied blocks), and executor node resolution (defense-in-depth refusal at run time, since policies can change after a graph is saved). Admin UI under org settings; team policy overrides org policy only toward more-restrictive.
Provider/credential policy Same pattern keyed on provider name instead of block ID: restrict which integrations (HTTP request, code execution, specific SaaS providers) teams may attach credentials for. IntegrationCredential already carries an org relation to hang this on.
Model allowlist + per-team spending caps Policy keyed on LLM model family; enforced where block costs resolve (block_usage_cost) and in copilot model selection. Caps check OrgCreditTransaction aggregates per team before dispatch.
Sensitive-action approval routing ExecutionContext.sensitive_action_safe_mode and human-in-the-loop review already exist per-user; org version routes pending reviews to org/team admins instead of only the owner, using the existing review queue.
Safe-mode defaults per org Org settings JSON already exists on Organization; add admin-managed defaults that seed human_in_the_loop_safe_mode / sensitive_action_safe_mode for all member executions, member-overridable only if policy allows.

2. Identity & access

Feature Sketch
SSO (SAML/OIDC) Supabase auth supports external IdPs; org-level config maps IdP groups → teams. OrganizationAlias (domain aliases) is the natural anchor for domain-capture ("anyone @acme.com joins Acme org").
SCIM provisioning Sync engine writing to OrgMember/TeamMember; deprovisioning must also revoke API keys and seats (OrganizationSeatAssignment).
Custom roles / RBAC Today roles are three booleans (isOwner, isAdmin, isBillingManager) on both org and team membership. A Role table with permission grants replaces the booleans when customers need finer slicing (e.g. "can publish to marketplace but not manage billing"). RequestContext already centralizes the flag derivation, so the cutover is contained.
Audit log surfacing AuditLog model exists with org relation; needs consistent write coverage across mutating routes (member changes, policy changes, transfers, key creation/revocation) and an admin-facing viewer with filtering/export.
Session & network policy Org-enforced re-auth interval, IP allowlists for API-key traffic (check at validate_api_key where org context is already resolved).

3. Resource management & collaboration

Feature Sketch
Team-shared agent library AgentGraph.teamId exists; the library views currently scope by user. Team-visible library = library queries keyed by team membership plus visibility (enum already on graphs/library agents/chat sessions).
Org-shared credentials IntegrationCredential already relates to Organization; needs scoping rules (org-owned credentials usable by member teams), admin management UI, and executor credential resolution preferring team → org → user.
Environment promotion (dev → prod teams) TransferRequest covers cross-org moves; intra-org promotion is a lighter copy with provenance (forkedFromId already tracked on graphs).
Quotas per team Run-concurrency and storage ceilings checked in add_graph_execution (org/team already threaded there) and workspace upload paths.
Shared chat / handoff Chat sessions carry organizationId/teamId and a sharing layer (share tokens, linked execution shares) exists; team-visible sessions are a visibility policy on top.

4. Billing & finance

Feature Sketch
Seat management & enforcement Schema shipped (OrganizationSubscription, OrganizationSeatAssignment, seat_status on RequestContext); enforcement intentionally deferred to the paywall/subscription workstream.
Budgets & alerts per team Aggregate OrgCreditTransaction by team; thresholds trigger the existing notification pipeline (low-balance handling already has a user-level analog in the executor).
Cost attribution dashboards Transactions already record user, team, graph, and execution metadata — the work is aggregation endpoints + admin UI, not new write paths.
Org auto-top-up Organization.topUpConfig field exists; needs the org analog of set_auto_top_up plus Stripe customer on org (stripeCustomerId present).
Invoicing (PO / net terms / tax IDs) Stripe invoice plumbing per org customer; OrgCreditModel.list_invoices is stubbed for this.

5. Marketplace

Feature Sketch
Org-private marketplace Store listings already support owningOrgId; an org-internal visibility tier reuses the listing/version review flow with org-scoped queries instead of the public StoreAgent view.
Publishing approval chain Submission review exists for platform admins; org version inserts an org-admin approval step before a listing leaves the org boundary.
Listing transfers Shipped (TransferRequest source/target org with admin approval on both ends).

6. Observability & compliance

Feature Sketch
Org-wide run history & analytics Executions carry org/team; admin endpoints mirror the existing per-user execution listings with org-role authorization.
Data retention policy Org settings-driven TTL sweeping executions/chat sessions; deletion paths (cascade rules on shares, workspaces) already exist to build on.
Export / portability Org-scoped bulk export of graphs (graph export logic exists per-graph), execution history, and transactions.
Compliance evidence Once AuditLog coverage is complete, scheduled export bundles cover the SOC2-style asks.

Suggested build order

  1. Block/provider policy (per-team enable/disable) — highest admin demand,
    clear enforcement choke points, no schema risk beyond one table
  2. Audit log write coverage + viewer — prerequisite trust layer for
    everything else admins touch
  3. Team-shared library + org credentials — turns tenancy from isolation
    into collaboration; biggest day-to-day user value
  4. Budgets/caps + cost attribution — natural follow-on to org billing
    already in place
  5. RBAC generalization — only when boolean roles demonstrably block a
    customer; cutover is contained in get_request_context
  6. SSO/SCIM — enterprise gate; heavy lift, schedule against real demand

Engineering Tests (`docs/engineering/tests.md`)

Testing

The AutoGPT Platform uses several test frameworks at different layers:

  • Backend tests use pytest.
  • Frontend integration tests use Vitest, React Testing Library, and MSW. These
    are the primary frontend tests.
  • End-to-end browser tests use Playwright.
  • Design system components use Storybook stories for visual coverage.

Run these Bash commands from the repository root after installing dependencies:

Backend tests require Docker running and the platform environment files. Run
make -C autogpt_platform init-env to create them with fresh local secrets.
Playwright requires the backend running; pnpm test builds
and starts the frontend, reusing an existing server when available.

bash
(cd autogpt_platform/backend && poetry run test)
(cd autogpt_platform/autogpt_libs && poetry run pytest)
(cd autogpt_platform/frontend && pnpm test:unit) # no servers required
(cd autogpt_platform/frontend && pnpm test) # E2E: start the backend first

For the frontend's fast edit/test loop, run pnpm test:unit:watch from its workspace.

See the backend testing guide
and frontend testing guide
for patterns, commands, and guidance on choosing integration or end-to-end
coverage.

Before you start

Playwright tests require the backend server. pnpm test builds and starts the
frontend for you, or reuses an existing server. Wait for the backend to be ready
before running browser tests.

Running the Playwright tests

To run the tests, you can use the following commands:

Running the tests without the UI, and headless:

bash
pnpm test

If you want to run the tests in a UI where you can identify each locator used you can use the following command:

bash
pnpm test-ui

You can also pass --debug to the test command to open the browsers in view mode rather than headless. This works with both the pnpm test and pnpm test-ui commands.

bash
pnpm test --debug

In CI, the full-stack workflow
runs the Chromium project headlessly with --retries=0 --trace=retain-on-failure,
overriding the shared config's CI defaults. Its JSON validator requires every test to have exactly one successful
attempt and rejects skipped, flaky, unexpected, or missing results and top-level
errors. See the shared settings in
playwright.config.ts.

Failure traces and test artifacts may contain requests, cookies, and session
state. Use isolated test accounts, never production accounts, and avoid exposing
credentials in traced requests or application output.

Continuous integration

Sharding

The backend workflow
and frontend workflow
define the shard matrix. CI divides the backend and frontend integration suites into disjoint parallel
shards. Backend shards select data, copilot, and util/executor paths; the
remainder uses normal workspace discovery excluding those paths, including new
top-level test files and directories. All shards explicitly load the backend's
pytest configuration. Backend shards use real PostgreSQL, RabbitMQ, and Redis,
with an isolated database, virtual host, and Redis cluster for each shard.
Each shard pays its own service-startup cost, so adding shards also multiplies setup work.

Caching

Dependency and browser caches are keyed to their lockfiles. Browser caches can
fall back to a previous cache for the same runner OS, then install missing versions.
Docker builds reuse unchanged layers. Full-stack cache export is limited to trusted dev pushes or an
explicit cache-publishing dispatch. Single-container builds use GHCR caches per
architecture instead of the repository Actions cache. Pull requests read the shared
dev validation cache but never write one; on a pull request a failed GHCR login
or an unreadable cache gives a cold build, not a failed job. Manual dispatches read and update a
branch-specific cache, with the shared dev validation cache as a read-only fallback.
The helper writes that shared tag only on a push to dev, but repository workflows
with package-write permission can overwrite it. Release publication therefore
builds each platform digest cold, without importing this cache, and smoke-tests
and scans the exact pushed digest before publishing the multi-platform manifest.
Validation cache exports are best-effort; failed image builds, smoke tests, and
security scans still fail validation.
The generated E2E seed-data cache is keyed to the files that determine the
seeded data, so it restores across commits that do not change them. Warm
caches may avoid repeated dependency downloads, seed generation, or cache
export, but they never bypass tests, linting, type checks, coverage collection, image builds, image
smoke tests, or security scans.

Test reports

Every test job in the validation workflows uploads a JUnit XML or JSON report,
plus coverage artifacts where applicable; this does not describe release-only
publication jobs. The JUnit validator
and Playwright validator
fail validation on missing or malformed reports,
zero discovered tests, count mismatches, failures, and errors. Frontend
integration and Playwright reports also reject skipped tests; the
single-container appliance workflow
allows one exact test ID to skip when Bash 3 is unavailable. The
unittest reporter
enforces this policy, including class, module, and subtest skips. Backend skips must match exact test IDs in
.github/scripts/backend-allowed-skips.json, seeded from the 136 existing skips
per Python version in run 33918906861; a new skip fails validation. Do not add an
ID merely to make CI green: investigate and review the changed skip policy.
An entirely skipped backend shard also fails.
Wrapped commands and malformed backend/frontend reports produce a synthetic
machine-readable error. Single-container validation instead uploads its original
reports and leaves status.json marked validated: false if verification fails.

Manual dispatch

The backend, frontend, full-stack, single-container, Classic, CodeQL, and block-docs
workflows have no required manual-dispatch inputs. The separate advisory PR-overlap
workflow still requires pr_number; it is not a CI-status checker. Backend
dispatches can refresh a stacked PR's backend coverage with
gh workflow run platform-backend-ci.yml --ref -f pr_number=;
the optional pr_number only selects the Codecov upload target. Full-stack
dispatches import caches by default; set publish_build_cache only when a test
commit should deliberately refresh them. A manually dispatched single-container
run builds, smoke-tests, and scans the image but cannot publish it; publication
jobs are reachable only from a release event. Single-container validation also
runs on every dev push and release, while pull requests trigger it only when
appliance packaging inputs change.

When CI fails

Open the failed Actions run and download its test-report and coverage artifacts
from the run summary. JUnit failures identify the test and traceback; validator
errors identify missing, malformed, or unexpected skipped results. Do not treat
a successful upload as proof the tests passed.

To reproduce a backend shard after configuring the same local services, run
poetry run pytest -c pyproject.toml backend/copilot from the backend workspace
(substitute the failing shard's paths). For a frontend shard, run
pnpm test:unit --shard=1/4 from the frontend workspace, using its CI shard number.

Debugging tests

There's a lot of different ways to debug tests.

My preferred is a mix of playwright's test editor and vscode.

No matter what you do, you should always double check that your locators are correct. Playwright will often "time out" and not give you the error message that the locator is incorrect because it can't find the element. You can do this via devtools on your browser and they should be visible on the elements tab when you use the inspect and select elements tools.

Using the playwright test editor

If you need to debug a test, you can use the below command to open the test in the playwright test editor. This is helpful if you want to see the test in the browser and see the state of the page as the test sees it and the locators it uses.

bash
pnpm test --debug --test-name-pattern="test-name"

Using vscode

You can install the Playwright Test for VSCode extension to get autocomplete for the playwright api (id: ms-playwright.playwright).

Installing this will enable the Test Explorer view in vscode which allows you to run, debug, and view all tests in the current project. Adding breakpoints to your tests and running them will automatically open the test editor with the correct context.

Setting up for generating tests

With playwright, you can generate tests from existing recordings of user sessions. This is useful for creating tests that are more representative of how a user would interact with the application. We generally use this for checking what ids stuff will have and what needs ids to be added.

It is super annoying to continuously login so I highly recommend using a saved session for your tests.
This will save a file called .auth/gentest-user.json that can be loaded for all future gentests so that you don't have to login every time.

Saving a session for gen tests to always use

bash
pnpm gentests --save-storage .auth/gentest-user.json

Stop your session with CTRL + C after you are logged in and swap the --save-storage flag with --load-storage to load the session for all future tests.

Loading a session for gen tests to always use

bash
pnpm gentests --load-storage .auth/gentest-user.json

How to make a new test

Tests are composed of page objects and test files.

Do not commit skipped or todo Vitest/Playwright tests. Fix the prerequisite or
ask maintainers to review an explicit policy exception; do not hide a failing
test behind an environment check.

The current Playwright config discovers *-happy-path.spec.ts files under
autogpt_platform/frontend/src/playwright/. Other filenames are not collected;
follow that naming pattern for tests intended to run in CI.

A page object is a class that contains methods for interacting with a page.

A test file is a file that contains tests for a page or a set of pages.

Making a new Page Object

For tests, we use the page object model. This is a pattern where each page is a class that contains all the methods and locators for that page.
This is useful for keeping your tests organized and easy to read as well as ensuring that your tests only need to be updated in one place when the UI changes.

You should make a new page object (only when needing to add a new page, or UI element that is across multiple tests) using the following example.

We extend the BasePage class which contains shared methods for pages that have the common functionality like a navbar. If you add something like that (for example a sidebar) you should add it to the BasePage class. Otherwise, you should make a new page object.

Each page object should be in its own file and be named like page-name.page.ts.
A page object should contain methods that are actions that a user can do on that page. For example, clicking a button, filling out a form, etc. It should also contain the various helpful abstractions that are unique to that page. For example, the BuildPage has a method to connect blocks together.

This is a shortened example of a page object for the profile page:

typescript title="frontend/src/playwright/pages/profile.page.ts"
--8<-- "autogpt_platform/frontend/src/playwright/pages/profile.page.ts:ProfilePageExample"
}

Making a new Test File

For tests, we use our page objects to create tests. Each test file should be in autogpt_platform/frontend/src/playwright/ and be named like test-name-happy-path.spec.ts. A test file can contain multiple tests. Each of which should be related to the same conceptual function. For example, a test file for the build page could have tests for building agents, creating inputs and outputs, and connecting blocks. If you wanted to specifically test building agents, you could make a new test called building-agents-happy-path.spec.ts.

Tests can inherit from one or more page objects, have pre-actions, and have post-actions, as well as many other features. You can learn more about the different features and how to use them here.

A good focused (unit or single concept) test will:

  • Have a short name that describes what it is testing
  • Have a single concept (building a agent, adding all blocks, connecting two blocks, etc.)
  • Check pre-conditions, actions, and post-conditions, as well as have multiple validations along the way

A good non-focused (integration or multiple concepts) test will:

  • Have a short name that describes what it is testing
  • Have multiple concepts (building agents, creating-?exporting->importing->running an agent, connecting blocks in multiple ways with multiple inputs and outputs, etc.)
  • Have a clear user experience that they are making sure works (for example, clicking the build button and making sure the agent is built, or clicking the export button and making sure the agent is exported and shows up in the monitoring system)
  • Not focus on a single concept, but instead test the flow of the application as a whole. Remember you're not testing the pixel perfect UI, but the user experience.

A good test suite will have a healthy mix of focused and non-focused tests.

Example focused test

This uses the current coverage fixture and builder page object. Save new cases
under autogpt_platform/frontend/src/playwright/ using the
*-happy-path.spec.ts filename pattern.

typescript title="frontend/src/playwright/building-agents-happy-path.spec.ts"
import { expect, test } from "./coverage-fixture";
import { E2E_AUTH_STATES } from "./credentials/accounts";
import { BuildPage } from "./pages/build.page";

test.use({ storageState: E2E_AUTH_STATES.builder });

test("builder saves an agent", async ({ page }) => {
  const buildPage = new BuildPage(page);
  await buildPage.createAndSaveSimpleAgent("Example Agent");

  await expect(page).toHaveURL(/flowID=/);
  expect(await buildPage.isRunButtonEnabled()).toBeTruthy();
});

The coverage fixture preserves coverage collection. The storage state supplies
an isolated test account, the page object encapsulates UI interactions, and the
assertions verify both navigation and the saved agent's runnable state.
See the current
builder tests
for full scenarios and timeout choices.

Passing information within a test

Keep tests independent: do not make one test consume an ID created by another.
Use local variables or fixtures to share setup within a test, and use
testInfo.attach for safe diagnostics. Avoid attaching credentials or session
state. See Playwright's
fixtures guide
and TestInfo API.

See Also


Engineering Tracking Plan (`docs/engineering/tracking-plan.md`)

PostHog Tracking Plan

The single list of PostHog events: for every user action, one event name, one
sender and one set of properties. Activation Metrics & Experiments
explains how PostHog fits next to the SQL views, Looker, LaunchDarkly and
DataFast; this page is the event contract.

Event names follow the product analytics plan, Every Second Counts (owner:
Toran), section "What we will record". Where that plan names an event for an
action, this list uses its name; Differences from the analytics plan
lists what is not aligned yet.

In code the names live in two modules. Browser call sites may still pass a
literal (trackBrainDump("brain_dump_started")), which is type-checked
against these modules, and backend funnel data_index keys embed the name
too (briefing_generated:, briefing_delivered:). A
rename therefore has to search for the old string, not just edit the modules:

Sender Module Pin test
Backend autogpt_platform/backend/backend/util/posthog_events.py (PostHogEvent, PlannedPostHogEvent) posthog_events_test.py
Browser autogpt_platform/frontend/src/services/analytics/posthog-events.ts (PostHogEvent and its groups, PlannedPostHogEvent) __tests__/posthog-events.test.ts

Rules

Naming

  • Use the analytics plan's name. When the plan names an event for an
    action, that is the name, even if we used to send something else.
  • Otherwise the name is object_action, snake_case, action in the past
    tense: signup_completed, checkout_started, paywall_viewed.
  • Chat (Autopilot) events start with chat_, never copilot_. The trial
    lifecycle is trial_*.
  • Rename once, then never again. PostHog stores the raw string, so a
    rename empties every insight, funnel and cohort built on the old name, and
    past events cannot be backfilled. The live names that differed from the
    plan were renamed together in SECRT-2722 (see Renamed); the
    pin tests reserve the old names and fail on any change to a live one.
  • Put a new name in PlannedPostHogEvent when it is agreed, and move it into
    the live list in the change that starts sending it.

Who sends it

  • Outcomes are sent by the backend: something was created, charged, run,
    hired or delivered. The backend sees every path (web, API, copilot, Slack,
    Telegram, webhooks), cannot be blocked by an ad blocker, and is what the
    analytics.* SQL views count.
  • Interactions are sent by the browser: views, clicks, dismissals and
    client-side timings that the backend never sees.
  • One action has one event. Where two emitters describe the same action,
    the backend event survives and the other stops being sent (see
    Removed).

Base properties

Every event must carry:

Property Value
environment settings.config.app_env on the backend (local, dev, prod), the matching app environment in the browser.
source Which emitter sent it: chat_copilot (the events copilot/tracking.py captures itself; chat_message_sent goes through product_analytics and is platform), platform (every other backend emitter), web (the browser).

Every backend event goes through capture() in
backend/util/posthog_client.py, which applies both last, so a caller cannot
overwrite them. The browser registers them once as PostHog super properties
(src/services/analytics/posthog-base-properties.ts), again after reset()
at logout; a Vercel preview reports environment: preview, while the dev
deployment, which is Vercel's staging custom environment rather than a
preview, reports environment: dev.

source is reserved for the emitter. The three events that used it for
something else now use hire_started.surface,
expert_recommended.team_source and workflow_installed_on_expert.workflow_source.

Identity

  • distinct_id is the platform user id for every backend event, the same
    id the browser passes to posthog.identify(user.id), so both halves land
    on one person.
  • Before login the browser uses the first-party anonymous id
    (src/services/analytics/anonymous-id.ts), passed to posthog.init as the
    bootstrap distinctID. identify merges it into the user at login, and
    resetAnalyticsIdentity mints a fresh one at logout.
  • The browser waits for analytics consent (Cookiebot,
    src/providers/posthog/posthog-consent.ts). PostHog starts opted out:
    without consent, browser events are dropped, identify is not called and
    the anonymous id lasts only for the page load. Backend events do not
    depend on it.
  • No synthetic ids. A backend event with no user is dropped
    (posthog_client.capture does it for every emitter), since a made-up id
    creates a person nobody can merge.
  • No email or name in event properties. Identify ids only. The email and
    display name are person properties set by identify and nowhere else.
    (expert_hired.name is the expert's display name, not a person's.)

What counts as a task

The analytics.* views (user_task_daily, user_lifecycle,
retention_task_weekly, unit_economics_monthly) define a task as a unit of
work a person asked for now:

  • a top-level, non-dry-run agent run whose triggerSource is manual or
    api (or NULL, for rows older than the column), expert workflow runs
    included; or
  • a user message in an Autopilot or expert chat whose session origin is not
    automation and whose kind is not dream.

A run the copilot started (triggerSource = copilot) is not a second
task: the chat turn that asked for it already counts. agent_run_started does fire
for copilot-started runs (with trigger: copilot), because it measures
human-initiated runs, not tasks. user_lifecycle.agent_runs_human_total is
its SQL twin and includes copilot runs too.

The same definition as a PostHog filter:

sql
event IN ('agent_run_started', 'chat_message_sent')
AND coalesce(properties.via, '') != 'copilot'

In the insight UI: events agent_run_started and chat_message_sent,
filtered by via "is not" copilot. Chat turns carry no via, so
the filter must keep events where it is unset. The emitters already leave
out dry runs, sub-graph runs, schedule and webhook runs, and automation-origin
turns.

Automated work is measured separately: schedule_fired and trigger_fired.

Status legend

Status Meaning
live Sent today under this name. Keep it.
add Not sent yet; the name is in PlannedPostHogEvent until the change that starts sending it.

Events that are no longer sent are listed under Removed, with
what replaced them.

Acquisition

Event Sender Status Required properties Fires when
$pageview browser live $current_url A route or query string changes (PostHogPageViewTracker).
tour_started browser live — The public /tour page is opened (once per tab).
tour_scenario_started browser live scenario A tour scenario starts playing.
tour_scenario_completed browser live scenario A tour scenario reaches its end.
tour_cta_clicked browser live label (free-trial, signup, pricing, another-scenario, self-host, share), placement where the CTA has one, surface (tour, marketplace) on sidebar-card clicks A tour or logged-out marketplace call to action is clicked.
marketing_opted_out browser live surface (signup) Someone refuses marketing email in the app. signup: "opt out" is clicked in the legal line under the signup buttons (not "Undo"); nothing else is sent, since there is no user yet, so it only gives the opt-out rate. The refusal itself is stored on the user (marketingOptOutAt).
signup_completed backend live signup_method (the auth provider, e.g. email, google: from the user's first Better Auth account row, where credential is reported as email, else from a Supabase token's app_metadata.provider; omitted when neither has it) The user row is created (data/user.py), whichever request creates it.

Sidebar CTA clicks retain the tour event names for compatibility. Filter
surface to distinguish the marketplace from the tour; older sidebar events
do not carry this property. For tour-only sidebar reports after deployment,
require surface = tour. The signup CTA uses free-trial in cloud mode and
signup on self-hosted installations, where creating an account starts no trial.

The tour funnel also goes to DataFast (tour_start, tour_scenario_start,
tour_scenario_complete, tour_cta_click); the PostHog events mirror it with
the same properties and nothing identifying in them: /tour is public and
pre-signup, so they land on the visitor's anonymous id. /tour sends the
DataFast goals without consent (SECRT-2713), but the PostHog events wait for
analytics consent like every browser event, so the two funnels count
differently.

Onboarding and activation

Event Sender Status Required properties Fires when
onboarding_step_viewed browser live step (team, autopilot, role, pain_points, connect, hire, preparing) A wizard step is shown (once per tab, same keys as the DataFast onboarding_<step> goals). The paywall step reports paywall_viewed instead.
onboarding_completed backend live — ONBOARDING_COMPLETE is first recorded for the user (complete_onboarding_step).
brain_dump_started browser live — Recording starts.
brain_dump_completed browser live input_mode, duration_secs and finalize_latency_ms (voice) or chars (typed) A dump was accepted and the wizard advances. finalize_latency_ms is the finalizeBrainDump() round trip, timed after the upload flush.
brain_dump_canceled browser live — Recording is cancelled.
brain_dump_skipped browser live — The step is skipped.
brain_dump_recovery_shown browser live parts A saved partial recording is offered back.
brain_dump_recovery_used browser live — The saved recording is used.
brain_dump_retry browser live attempt A failed finalize is retried.
brain_dump_restarted browser live — Recording starts over.
brain_dump_download browser live — The recording is downloaded after a failure.
brain_dump_permission_denied browser live — Microphone permission is refused.
brain_dump_typed_fallback browser live reason The user types instead of speaking.
transcription_failed browser live error_code, finalize_latency_ms Finalize returns a failure.
welcome_dialog_closed browser live — The first-landing welcome dialog closes.
capability_card_viewed browser live card_index, deck A capability card is shown.
capability_cards_completed browser live card_index, deck The deck is finished.
capability_cards_skipped browser live card_index, deck The deck is skipped.
intro_path browser live path The intro path (A/B) is chosen.
intro_followup_sent browser live chars First message after the intro card.
later_dump_completed browser live — A brain dump sent later from the composer.
expert_recommended browser live template_id, position, team_source A recommended expert card is shown.
expert_recommendation_clicked browser live template_id, position A recommended expert card is clicked.
hire_step_continued browser live hired, recommended The hire step is left.
tab_intro_shown browser live tab A tab intro card is shown.
tab_intro_cta_clicked browser live tab, cta Its primary CTA is used.
tab_intro_dismissed browser live tab It is dismissed any other way.
integration_connected backend live provider, credential_type, method A credential is stored (OAuth, key, device code).
credential_oauth_started backend live provider The backend issues an OAuth login URL (GET /api/integrations/{provider}/login). Not Codex.
credential_oauth_exchange_failed backend live provider, status_code (unset for an unexpected error), failure_class (invalid_state, provider_unavailable, token_exchange, credential_merge), detail (redacted, at most 200 characters) POST /api/integrations/{provider}/callback returns an error, on any path. Not Codex.
credential_card_never_rendered browser live provider, failure_class A provider is missing from the provider map.
credential_oauth_popup_blocked browser live provider, failure_class Both the popup and the new tab were blocked.
credential_oauth_flow_timed_out browser live provider, failure_class The OAuth flow timed out.
credential_scope_shortfall_blocked_selection browser live provider, failure_class The granted scopes are narrower than required.
credential_proceed_stuck_after_connect browser live provider, failure_class A connected credential never reached the card.

Engagement

Event Sender Status Required properties Fires when
agent_run_started backend live graph_id, graph_exec_id, via (manual, api, copilot), via_ref, preset_id; an expert's workflow run adds expert_id and kind: workflow_run A person starts an agent run.
chat_message_sent backend live chat_session_id, origin (web, or the bot platform the chat was opened from: slack, discord, ...), kind: chat_turn, message_length; expert_id in an expert chat A person sends a message in an Autopilot or expert chat.
agent_run_finished backend live status (completed, failed), graph_id, graph_exec_id, via, expert_id, cost_cents, duration_seconds, is_subgraph_run; failure_reason when failed A run reaches COMPLETED or FAILED (sub-graph and automated runs included). A top-level expert run is expert_id set and is_subgraph_run false. Deduplicated on graph_exec_id, so a requeue or resume that finishes the same run again sends no second event.
schedule_created backend live schedule_id, target (agent, autopilot, expert), expert_id, cron, is_recurring, run_at, graph_id, chat_session_id Any schedule is registered, from any surface.
chat_tool_called backend live chat_session_id, tool_name, tool_call_id The copilot calls a tool.
chat_outcome backend live outcome_type (agent_run_success, schedule_created), chat_session_id; agent_run_success: graph_id, execution_id, library_agent_id; schedule_created: target (agent: graph_id, schedule_id, cron, library_agent_id; followup: schedule_id, target_chat_session_id, is_recurring) A moment of value in a chat: the copilot started a (non-dry) run or created a schedule for the user. agent_run_started / schedule_created still count the run or schedule itself.
chat_library_check_outcome backend live chat_session_id, outcome, matches_count, top_score The create-agent library check ends.
voice_mode_started browser live entry Voice mode is switched on.
voice_mode_stopped browser live turns, state Switched off by the user.
voice_mode_timed_out browser live turns, state Closed by the silence timeout.
voice_turn_sent browser live turn_index, transcript_chars, transcribe_latency_ms A spoken turn is sent.
voice_turn_dropped browser live reason (vad_misfire, transcribe_failed, filler_or_empty, interrupted); transcribe_latency_ms with filler_or_empty; turn_index and first_sound_latency_ms (null when nothing played yet) with interrupted A spoken turn is discarded, or with interrupted a sent turn's reply is cut off by switching voice mode off or leaving the page. A sent turn ends in exactly one of voice_turn_completed or this.
voice_turn_completed browser live turn_index, first_sound_latency_ms (null when nothing played) The mic reopens after the reply.
voice_transcribe_retried browser live turn_index A failed transcription is retried.
voice_recording_downloaded browser live turn_index The audio is downloaded instead.
voice_mode_permission_denied browser live stage Microphone permission is refused.
voice_mode_error browser live stage The VAD, synthesis or send failed.
experts_section_viewed browser live — The marketplace experts shelf renders with results.
expert_profile_opened browser live template_id An expert profile page opens.
hire_started browser live template_id, surface (onboarding, expert_page) A hire button is clicked.
hire_flow_abandoned browser live template_id, stage The hire dialog is closed before hiring.
expert_hired backend live expert_id, template_id, name, failed_preloads_count, surface (onboarding, expert_page, copilot; unset for other API callers) An expert is hired or an archived one revived, from any surface (experts_db.hire_expert). Sent once the hire's background setup settles (_run_hire_setup), so failed_preloads_count is known; a revival with nothing to set up sends it at once. An idempotent re-hire of an active expert does not fire.
hire_failed backend live template_id, failed_preloads_count Hiring raised.
expert_thread_created browser live expert_id A new expert chat is created.
writing_style_added backend live expert_id A writing style is saved on an expert.
workflow_installed_on_expert backend live expert_id, workflow_source (library, marketplace), library_agent_id or store_listing_version_id A workflow is attached to an expert.
home_viewed browser live — The home dashboard renders with data.
home_attention_actioned browser live kind, action A "needs you" item is approved or declined.
home_team_member_clicked browser live expert_id A team member row is clicked.
listing_added_to_library backend live store_listing_version_id, graph_id, library_agent_id A marketplace agent is added to the library for the first time. Only a user adding a listing counts (the library route and the add-to-library block). Restoring an entry the user already had does not, and neither does a system install: an add inside another write's transaction, an expert's hire preloads, or attaching a marketplace workflow to an expert (that is workflow_installed_on_expert).
listing_downloaded backend live store_listing_version_id, graph_id A signed-in user downloads a marketplace agent file. Signed-out downloads have no user and are not sent.

Monetization

Event Sender Status Required properties Fires when
paywall_viewed browser live surface (onboarding, paywall_gate, billing) A paywall or plan picker is shown (once per tab per surface): the onboarding subscription step, the in-app paywall modal, the billing page's plan card. The pricing arm comes from PostHog's own $feature/... properties.
plan_selected browser live subscription_tier (PRO, MAX, BUSINESS for Team), billing_cycle, surface; pricing_variant on onboarding, only when PostHog has assigned an arm (omitted while flags load and for users outside the rollout, who are not in control) A plan is picked: a plan card on either paywall, or Upgrade / Downgrade on the billing page.
billing_portal_opened browser live surface (billing for the plan card, billing_payment_method for the payment method card) The Stripe billing portal is opened.
checkout_started backend live checkout_kind (subscription, top_up, trial), subscription_tier, billing_cycle, surface (sent by the client; unset for the low-credit top-up dialog and the legacy /profile/credits page), $feature/<experiment> for every recorded experiment arm A Stripe Checkout session is created and its URL returned (subscription, credit top-up or trial card setup). A paid plan changed in place opens no Checkout and sends nothing.
checkout_abandoned browser live checkout_kind, surface The user returns from Stripe Checkout without paying (the cancel URL: subscription=cancelled, topup=cancel, trial=cancelled, or the paywall modal's paywall_checkout=cancelled marker). Browser-sent: Stripe only reports the expiry a day later. A trial abandonment is sent once per tab per surface until the next trial checkout starts, so a refresh on the cancel URL does not count twice but a second real abandonment does.
trial_offer_viewed browser live trial_offer_version, subscription_tier, trial_duration_days, surface A trial offer card is shown.
trial_cancel_popup_viewed browser live days_left (whole days of trial access left, rounded up; 1 under a day) The "Cancellation confirmed" popup opens, once, right after a cancel that sets the trial to cancel at its end, so trial access continues until then. A reload never reopens it.
trial_resume_clicked browser live days_left Resume trial is clicked on a trial set to cancel at its end, in the popup or on the trial card. Sent on the click; trial_resumed (backend) confirms it took effect.
trial_subscribe_now_clicked browser live days_left Subscribe now is clicked in the cancellation popup. It opens Billing. There, the trial's own plan converts the trial at once on the saved card (trial_converted, no checkout_started); any other plan, or a card that asks for authentication, goes through Stripe Checkout (checkout_started), which ends the trial without a trial_ended.
trial_started backend live trial_id, trial_offer_version, subscription_tier, billing_cycle, trial_duration_days The trial starts. Like every trial lifecycle event, it is sent only when its notification email is queued.
trial_ending backend live as above The reminder window opens.
trial_canceled backend live as above The trial is set to cancel at its end: the cancellation is scheduled, trial access continues until the end date, and the card is not charged.
trial_resumed backend live as above A trial set to cancel is resumed before its end date, so it converts to paid at its end as first agreed.
payment_failed backend live as above The conversion charge fails.
trial_converted backend live as above The trial converts to paid.
trial_ended backend live as above The trial ends without converting. Not sent when the trial subscription ends because another plan was bought while the trial was set to cancel; that plan's checkout_started and payment_succeeded cover it.
subscription_changed backend live change_type: upgrade, previous_subscription_tier, subscription_tier, billing_cycle A paid tier upgrade takes effect. Downgrades and cancellations are not sent here yet.
payment_succeeded backend live subscription_tier, billing_cycle, amount_cents (the invoice's amount_paid), currency A subscription invoice is paid. Deduplicated on the invoice id: Stripe delivers both invoice.payment_succeeded and invoice_payment.paid for one invoice, and redelivers either, so revenue is counted once.
topup_completed backend live amount_credits, top_up_type, amount_cents (the Checkout total or the auto top-up charge), currency Credits are bought.
subscription_cancellation_scheduled backend live subscription_tier A paid plan is set to cancel at period end.
subscription_ended backend live subscription_tier, billing_cycle, reason (Stripe cancellation_details.reason: cancellation_requested, payment_failed, payment_disputed; payment_failed too when our own payment-failure handler cancelled the subscription because the balance could not cover a failed renewal, which Stripe would stamp cancellation_requested) A paid subscription ends (Stripe customer.subscription.deleted), once per subscription, alongside the ended email. A trial that ends unconverted is trial_ended instead; a trial ended by buying another plan sends neither.
subscription_tier_reconciled backend live direction, previous_subscription_tier, subscription_tier, via Ops signal: Stripe and the stored tier disagreed. Not a user action; keep out of funnels.

The paywall funnel in PostHog is paywall_viewed → plan_selected →
checkout_started → payment_succeeded, with checkout_abandoned
as the way out. The onboarding paywall's DataFast goals (paywall_view,
paywall_checkout_cancelled) keep firing alongside.

Browser events captured before PostHog captures (a page's mount effect on a
full page load, such as the return from Stripe, runs before posthog.init
and before the consent opt-in) are held by
src/services/analytics/posthog-capture.ts while the consent answer is
unknown. With analytics granted (an earlier answer, or a region where
Cookiebot consents on its own once uc.js loads) they are sent, with the
time they happened, once PostHog is opted in. When analytics is declined,
there is no consent manager, or the banner is asking, they are dropped: an
answer given on the banner covers what happens after it, not what was held
before it. The once-per-tab guards on paywall_viewed, checkout_abandoned,
onboarding_step_viewed and tour_started are set only when the event is
sent or dropped this way, not while it is held.

Retention

Event Sender Status Required properties Fires when
schedule_fired backend live schedule_id, target, expert_id, graph_id, graph_exec_id or chat_session_id A schedule produces work.
trigger_fired backend live webhook_id, graph_id, graph_exec_id, expert_id, preset_id, target A webhook produces a run.
briefing_generated backend live run_count, decision_count, has_content A morning briefing is composed (or found empty).
briefing_delivered backend live briefing_id It is posted to the user's thread.
briefing_opened browser live — The briefing renders on home. Not the plan's briefing_opened_in_chat, which is opening it in the chat thread.
briefing_outcome_clicked browser live status An outcome row in the briefing is clicked.
expert_fired backend live expert_id An expert is fired.

Return visits are $pageview; account-level retention is computed in
retention_task_weekly and user_lifecycle, not sent as events.

LLM analytics

AutoPilot's LLM calls and the ratings of its replies, in PostHog's LLM
analytics (SECRT-2730). Both are sent server-side for every user, whatever
their cookie consent, like every backend event. The event names are
PostHog's own, not the analytics plan's.

Event Sender Status Required properties Fires when
$ai_generation OpenRouter Broadcast live set by OpenRouter from the request (backend/copilot/llm_attribution.py): distinct_id = the request's user (the platform user id), $ai_session_id = its session_id (the chat session id), $ai_span_name = trace.generation_name (copilot-title, copilot-sdk, copilot-baseline), metadata_environment (local, dev, prod), metadata_impersonated; model, tokens, cost and latency from OpenRouter An AutoPilot LLM call goes through OpenRouter: a session title, an SDK turn (main agent, sub-agents and the CLI's compaction, via CLAUDE_CODE_EXTRA_BODY) or a baseline turn.
$ai_metric backend live $ai_metric_name (user-feedback, copy), $ai_metric_value (1 thumbs up or copy, 0 thumbs down), $ai_session_id and chat_session_id (the chat session id), message_id (the reply's row id), message_sequence, feedback_id (the stored rating, shared with its Langfuse score), impersonated; $ai_feedback_text when a thumbs-down has a comment A reply is rated (POST /api/chat/sessions/{id}/feedback, copilot/feedback.py). Rating a reply again sends another event; the latest per feedback_id stands.

What joins them:

  • Person. A generation's distinct_id and a rating's are both the
    platform user id, the id every other event and posthog.identify use, so
    both land on the person with their product events and replays.
  • Chat. $ai_session_id is the chat session id on both, so a chat's
    generations and its ratings group in the Sessions tab and join on it.
    message_id says which reply was rated; a rating carries no
    $ai_trace_id, since Broadcast assigns the generations' trace ids.
  • Environment. OpenRouter forwards our trace keys as metadata_<key>,
    so a generation's environment is metadata_environment, not the
    environment base property; $ai_metric has both base properties as usual.
  • Impersonation. A turn or rating an admin made while impersonating the
    user (X-Act-As-User-Id) is still sent, as the user, with
    metadata_impersonated / impersonated true, so it can be filtered out.
    The mark is kept for a turn queued behind the running-turn cap and for
    sub-sessions such a turn starts. A follow-up sent while a turn runs takes
    that turn's mark, and an engine-switch continuation or an approval wake
    starts unmarked.

Calls that skip OpenRouter never reach Broadcast and have no
$ai_generation: direct Anthropic and Claude Code subscription deployments,
a user's connected Codex account and the local transport. Auxiliary calls
that are not attributed yet (the baseline path's context compaction, the
action gate's classifiers, web search, Graphiti memory) arrive without the
user or chat when their client goes through OpenRouter.

Experiments

Event Sender Status Required properties Fires when
$feature_flag_called browser (posthog-js) live set by PostHog A PostHog flag is read (useExperiment).
feature_flag_mismatched browser live flag, launchdarkly (value, resolved), posthog (value, resolved) Ops signal: with both flag vendors configured, LaunchDarkly and PostHog resolved a flag to different values (useDualFlag). Not a user action; keep out of funnels.

Arms are also stored in the database (analytics.experiment_assignment),
so an experiment can be read in PostHog and Looker alike.

Person properties

The backend keeps these on the person (distinct id = platform user id) with
a $set event (PostHogEvent.SET_PERSON_PROPERTIES), sent from
backend/data/posthog_lifecycle_sync.py after signup, tier changes and every
Stripe subscription sync, and by a daily sweep at 04:15 UTC. The lifecycle
events above mark the moment something happens; these properties hold the
person's current state, so a cohort or breakdown can filter on them without
replaying events.

Property Value
subscription_status signed, in_trial, trial_canceled (a trial set to cancel, which keeps access until its end and goes back to in_trial on resume, or a trial that ended unconverted), subscribed, subscription_canceled (set to cancel, active until the period ends), payment_failed (a renewal or the first charge after a trial failed; Stripe is retrying), subscription_ended. Worked out from the current user, trial and Stripe state, never from the last event received.
signup_at When the user row was created.
trial_started_at When the trial started.
subscription_started_at Start of the current or last paid subscription; for a converted trial, the conversion.
subscription_canceled_at When the subscription was canceled, while subscription_canceled or subscription_ended.
subscription_ended_at When the subscription ended, while subscription_ended.

Dates are ISO-8601 UTC strings of the lifecycle moment, not of the sync. A
date that doesn't apply to the current status is $unset, never sent as
null.

The role picked in the onboarding wizard is set with a $set of its own when
the profile is submitted at the Preparing step (POST /api/onboarding/profile,
product_analytics.set_onboarding_role), so funnels and retention can be split
by role. Someone who left the wizard before Preparing has neither property.

Property Value
onboarding_role The pick, as labelled in the wizard: Founder / CEO, Operations, Sales / BD, Marketing, Product / PM, Engineering, HR / People or Other.
onboarding_role_other What was typed after picking Other (trimmed, at most 100 characters); null for any other pick.

The pick is kept on UserOnboarding, not read back from the AutoPilot
business understanding, whose copy AutoPilot rewrites. poetry run cli onboarding-role-backfill sets both for earlier accounts with $set_once, from
exact picks only: any other stored value may be Other's text or a rewrite,
so it is left unset. MailerLite gets the same values as role and
role_other.

The fields GTM segments checkout openers on in MailerLite are on the person
too (SECRT-2895, backend/notifications/posthog_audience.py). They are worked
out by the same code (audience_enrichment.checkout_fields) and named as in
MailerLite, except the checkout open, which is a moment here and a day there.
Every account gets the first two at signup. Every checkout opener gets all of
them from the checkout routes and the completed-checkout webhook. Unlike
MailerLite, PostHog also gets accounts that opted out of marketing or that a
signal places in Iran or Russia: it is analytics, not marketing.

Property Value
email_type business, personal (a webmail provider) or education.
signin_method google or email; google wins for an account that has both.
checkout_opened_at When the account first opened Stripe checkout.
country The country's English name, from the strongest source seen (see country_source).
country_code The same country as an ISO 3166-1 alpha-2 code.
country_source stripe (the billing address), ip (the visitor's IP country at checkout) or timezone (the browser's).
exclude_de_at yes when any signal points at Germany or Austria, else no.

MailerLite reads the subscriber before each write so it never makes a value
worse; PostHog can't be read on the way, so $set_once, which only fills in
what the person doesn't hold yet, keeps the same rules. The first open stays.
exclude_de_at yes is set and no only filled in, so it never goes back to
no. A billing country is set, so it always wins, and an IP or timezone
country is only filled in, so it never replaces one. The one difference:
PostHog keeps the first IP or timezone country it gets, where MailerLite moves
on to a later IP country. poetry run cli posthog-audience-backfill sends them for earlier accounts, by the same rules,
from Stripe's first Checkout Session (set outright, since Stripe holds every
open) and its billing country or else the browser's timezone. An IP country is
never backfilled, since it was never stored.

Removed

No longer sent (SECRT-2722, SECRT-2723). The names stay reserved: the pin tests fail if
one comes back, because reusing it would splice a different action onto the
history PostHog already holds.

Event Was sent by Read instead
subscription_trial_checkout_started browser checkout_started with checkout_kind: trial, sent by the backend once the trial's Checkout session exists; surface is the same onboarding / billing. trial_offer_version is dropped; trial_started carries it.
hire_completed backend expert_hired (same hire; it now also skips idempotent re-hires). The DataFast hire_completed goal is unchanged.
hire_flow_completed browser expert_hired with surface: expert_page. elapsed_ms is PostHog's time to convert from hire_started; voice_picked is dropped.
onboarding_expert_hired browser expert_hired with surface: onboarding. The card position is on expert_recommendation_clicked.
copilot_message_sent backend chat_message_sent, which carries message_length.
expert_run_completed backend agent_run_finished with expert_id set and is_subgraph_run: false.
finalize_latency_ms browser The finalize_latency_ms property on brain_dump_completed and transcription_failed.
voice_transcribe_latency_ms browser The transcribe_latency_ms property on voice_turn_sent (and voice_turn_dropped with reason: filler_or_empty).
voice_first_sound_latency_ms browser The first_sound_latency_ms property on voice_turn_completed, or on voice_turn_dropped with reason: interrupted for a reply that was cut off.
experiment_exposed browser Nothing: useLaunchDarklyExperiment had no caller and was deleted. PostHog-bucketed experiments use $feature_flag_called.
copilot_trigger_setup backend Nothing: never sent (no caller).
intro_card_dismissed, raise_door_clicked, intro_start_with_autopilot browser Nothing: declared, never sent.

Renamed

Renamed once to the analytics plan's names (SECRT-2722). The old names are
reserved like removed ones; an insight on the old name must be pointed at the
new one.

Old name New name Filter to keep the old meaning
run_agent agent_run_started expert_id is not set
run_expert (kind: workflow_run) agent_run_started expert_id is set
run_autopilot chat_message_sent expert_id is not set
run_expert (kind: chat_turn) chat_message_sent expert_id is set
agent_run_completed agent_run_finished status = completed
agent_run_failed agent_run_finished status = failed
copilot_tool_called chat_tool_called —
copilot_library_check_outcome chat_library_check_outcome —
copilot_agent_run_success chat_outcome outcome_type = agent_run_success. Dry runs no longer count; graph_name is dropped.
copilot_agent_scheduled chat_outcome outcome_type = schedule_created and target = agent. graph_name and schedule_name are dropped.
copilot_followup_scheduled chat_outcome outcome_type = schedule_created and target = followup. chat_session_id is now the chat that scheduled it; the destination is target_chat_session_id.
subscription_trial_offer_viewed trial_offer_viewed —
subscription_trial_started trial_started —
subscription_trial_ending trial_ending —
subscription_trial_canceled trial_canceled —
subscription_trial_resumed trial_resumed —
subscription_trial_converted trial_converted —
subscription_trial_ended trial_ended —
subscription_trial_payment_failed payment_failed — (only trial conversion charges send it today)
subscription_upgraded subscription_changed change_type = upgrade
subscription_payment_success payment_succeeded —
credit_topup_success topup_completed —
subscription_tier_reconciliation_discrepancy subscription_tier_reconciled —
feature_flag_mismatch feature_flag_mismatched — (not in the analytics plan; renamed to the object_action past-tense form before it reached production)

Renamed properties

Renamed in the same release as the events, to the analytics plan's envelope,
so filters and breakdowns break once. Values are unchanged unless noted.

Old property New property On
session_id chat_session_id chat_message_sent, chat_tool_called, chat_outcome, chat_library_check_outcome, schedule_created, schedule_fired
target_session_id target_chat_session_id chat_outcome (target = followup)
trigger via agent_run_started, agent_run_finished
trigger_ref via_ref agent_run_started
surface (chat, or the bot platform) origin (web, or the bot platform) chat_message_sent. The plan's origin is the channel.
origin (interactive) removed chat_message_sent. Automation turns are not sent, so it was always interactive.

Differences from the analytics plan

Left for follow-up changes, so this list and the plan can be compared line by
line:

  • Events the plan folds into others keep their names until that change.
    The brain-dump events (brain_dump_*, transcription_failed,
    later_dump_completed) and the wizard's intro_path and
    hire_step_continued become properties of onboarding_step_viewed /
    _completed / _skipped / _back. Spoken turns (voice_turn_sent)
    become chat_message_sent with input_mode: voice.
  • subscription_changed covers upgrades only. The plan also counts
    cancellations and downgrades there; subscription_cancellation_scheduled
    and subscription_ended stay separate events.
  • chat_outcome has two outcome types. Only agent_run_success and
    schedule_created have an emitter. agent_created, trigger_setup,
    artifact_created, file_produced and answer_only do not.
  • Plan events we do not send yet (signup_started, chat_session_started,
    chat_response_completed, chat_blocked, screen_viewed,
    screen_engaged, milestone_reached, the builder, library and email
    families, ...) come with the plan's phases, not with this list.
  • Events the plan has no name for keep their own, e.g.
    integration_connected, schedule_created, hire_started,
    billing_portal_opened, marketing_opted_out, tour_*,
    tab_intro_*, voice_* and credential_*. briefing_opened is the
    briefing shown on home, a different action from the plan's
    briefing_opened_in_chat.

Events not in the constants modules

These are sent by PostHog or a third party, not by our code, and are listed
so nobody adds a duplicate:

  • $pageleave, $autocapture, $identify and $feature_flag_called:
    posthog-js (capture_pageleave and autocapture are on).
  • $ai_generation: OpenRouter Broadcast, for every AutoPilot LLM call that
    goes through OpenRouter (see LLM analytics).

Engineering Transactional Email Design (`docs/engineering/transactional-email-design.md`)

Transactional email design

All platform-generated Postmark emails use the Edition 1 email design system
(2 October 2026). The renderer builds HTML and plain text from the same event
payload; Postmark receives those bodies rather than a hosted template ID.

Inventory

Family Variants Design and delivery
Lifecycle Welcome, payment failed, final notice, cancellation confirmed, cancellation reversed, ended (payment/cancellation) Billing sender; contact reply-to; service footer; no unsubscribe
Trial Started, ending, canceled, resumed, converted, payment failed, ended Same billing rules; neutral Otto; one plan/payment action
Briefing Daily, weekly, monthly; quiet/standard; attention/no attention Product sender; hello reply-to; signed cadence links; one-click unsubscribe
Alert Each cause, including coalesced alerts Amber needs-you strip and primary attention card; secondary actions are text links
Verdict Approved; changes requested with/without notes State eyebrow; reviewer feedback preserved and escaped; one marketplace action
Ops Refund request, refund processed Internal amount band and compact Otto; admin action; no unsubscribe
Auth Verify email, set/reset password, confirm new email Compact layout without hero; action button and complete fallback URL; HTML and text; original subjects and origin validation

Organization invitations have no email sender yet. SMTP/Gmail workflow blocks
send user-authored content and are not platform transactional notices. The
onboarding/changelog files remain marketing references; updating them does not
publish content or settings to MailerLite.

Design contract

backend/notifications/templates/_email_ui.j2 is the shared kit. It defines the
560px sheet, 48px desktop/22px phone gutters, Geist with system fallbacks, purple
#6144DF actions, ink #171925, white sheet and lavender paper. Facts stack on
phones. At 600px and below, the paper has no side padding and the sheet has
square corners; content keeps its 22px gutters. The sheet is fluid even when a
client strips the stylesheet. Button links own their full padded surface,
including when styles are removed. MSO-only rules transfer that padding to
table cells for Outlook without doubling it. Inline font stacks preserve
the fallback appearance in Outlook; action glyphs request text
presentation instead of emoji. Each family owns its HTML, subject/preheader,
and text templates. The preheader appears in the lede; supplied user names and
feedback stay escaped, and customer-authored punctuation is preserved.

Plan labels use a middle dot (Pro · monthly). Trial prices use the same
formatted amount as lifecycle emails, with a currency code retained when the
formatter has no currency symbol (20.00 CAD / month, 2,000 JPY / year).

One primary button is the default. Cancellation confirmation uses a billing
text link. A Briefing without an attention item uses highlight/run-history text
links; its cadence controls are signed for that recipient. Only the welcome is
signed by a person. Its Expert CTA is selected using the recipient's
HIRE_EXPERTS flag; older queued messages and failed flag lookups default to
the workflow action. Secondary Marketplace copy follows the same selection.

Billing has no unsubscribe promise it cannot honor. Product emails match the
footer unsubscribe URL to both one-click headers. Auth and internal ops have no
unsubscribe. All include the registered address; auth has only the Discord
footer link and ops has no footer links.

Images and configuration

EMAIL_ASSET_BASE_URL defaults to https://platform.agpt.co/email, containing
logo-light.png (790x356 served at 100x45). Otto uses the same URL origin plus
/autogpt-characters/v1.1/otto/neutral-transparent/256.png, served at 160x160 or
80x80 for ops. Its transparent background lets the surrounding hero band show
through when a mail client recolors the message in dark mode. The original
opaque export remains available for already-delivered messages. The 256px,
512px and 1024px transparent exports come unchanged from the approved October 6
design handoff.

The logo and transparent Otto ship in the frontend public assets. A custom
asset host must serve both paths publicly over HTTPS without cookies or
authentication. Deploy the frontend/static assets before the backend renderer
so the new image URL resolves as soon as templates start using it.

Images use hosted PNG URLs, explicit dimensions, and descriptive alt text.
There are no data URIs or inline SVGs. The same text, facts and action links
remain when an email client blocks images. Historical image files are retained
because already-delivered messages can still reference their URLs; new
transactional templates do not use the retired art.

Deployment overrides still control POSTMARK_SENDER_EMAIL,
BILLING_SENDER_EMAIL, PRODUCT_SENDER_EMAIL, OPS_SENDER_EMAIL, and
POSTMARK_TRANSACTIONAL_STREAM. This migration preserves those From values.
BILLING_REPLY_TO_EMAIL defaults to [email protected] and
PRODUCT_REPLY_TO_EMAIL to [email protected]. Configure a verified named auth
sender in deployment; the repository must not invent or activate a sender.

The auth RPC remains compatible across old and new API and notification-service
versions during deployment and rollback. The client sends named JSON fields;
an older service ignores the new text_body field and still sends the HTML,
while the new service accepts requests without that field. Plain text is
included when both components use the new code. The auth RPC itself does not
require a particular service deployment order.

The trial payload formatting change does require ordered rollout: deploy the
frontend/static assets first, the notification service second, and the API
producer last. The new notification service accepts both the exact old and new
plan labels/prices while still rejecting changed trial terms. An older service
would consider the new producer's formatting obsolete and drop the queued
notice. For rollback, revert the producer first and drain new-format messages
before reverting the notification service. Keep the published assets available.

Production configuration and reminder checks

The repository's deployment workflows dispatch to the infrastructure repository;
the defaults here do not prove the effective production values. Before rollout:

  • Set POSTMARK_SENDER_EMAIL to the existing Postmark-verified auth address with
    its approved display name. Check the effective notification-service value;
    [email protected] is only a repository placeholder. Do not invent a new
    sending identity as part of a template deploy.
  • Set [email protected] and
    [email protected] explicitly in deployment, and inspect
    delivered message headers. Preserve the configured billing, product and ops
    senders and the transactional Postmark stream.
  • Check that the new public Otto URL returns a PNG without authentication from
    the configured asset host before deploying the notification service.

To verify the trial-ending reminder, follow the delivery path in order:

  1. In the live Stripe account, confirm the enabled platform webhook endpoint
    subscribes to customer.subscription.trial_will_end.
  2. Check the Stripe Dashboard's Trial ending events setting is three days.
    Compare a recent event's creation time with its subscription's trial_end.
    The code only sends an ending notice inside the final three days, while the
    trial is active, the card is verified and cancellation is not scheduled.
  3. Check the endpoint's delivery result and backend processing. A successful
    Stripe webhook response is not proof that an email was delivered.
  4. Check PostHog for trial_ending, the current event name, and
    subscription_trial_ending for older deployments. This event records a
    successful notification enqueue, not Postmark delivery.
  5. Check Postmark activity in the configured transactional stream for the
    billing sender and subject beginning Your AutoGPT trial ends on, then check
    the delivery result for the affected recipient.

Stripe's trial-ending webhook setting
is separate from its hosted reminder emails,
which are sent seven days before trial end. Keep the hosted reminder disabled
when AutoGPT owns this message, so a seven-day trial does not receive a second
reminder at signup.

Validation

The original migration passed 258 focused tests and a browser audit of 31
scenarios at two widths (62 renders), including images blocked, with 1,548 text
contrast checks and no failures. Those results and the screenshots below
predate the transparent Otto and email-client compatibility follow-up.

The notification design fixtures cover each lifecycle/trial branch, welcome
feature cohorts, Briefing cadences/attention, coalesced alerts, verdicts, and
refunds. Tests check frame/tokens, safe images, CTA exceptions, size, footers,
link parity and escaped feedback. Auth tests check all four actions, URL
escaping, origin restrictions, and plain-text delivery. Transport tests inspect
the actual Postmark request fields with the network client mocked.

For changes to the shared kit, render at 640px, 390px and 360px with images on
and off, with styles stripped, and with simulated dark backgrounds. Check
horizontal overflow, text and button visibility, and contrast against the
effective background.
Browser evidence is not an actual Gmail, Apple Mail or Outlook delivery test.
Before release, send staging samples to authorized test inboxes and inspect
light/dark mode, images off, plain text, button and fallback links, and signed
unsubscribe/cadence controls. Also exercise destinations signed out, with the
feature disabled, and after the relevant task is complete. No email was sent,
production configuration changed, or deployment performed by this PR.

Review previews

The client compatibility follow-up was checked across all 32 fixture/auth
variants at 640px, 390px and 360px in five modes: normal, stylesheet removed,
images blocked, dark preference, and simulated dark backgrounds (480 layout
and asset checks). No case overflowed; the largest HTML was 16,204 bytes. The
dark simulation checks the transparent artwork against recolored backgrounds;
it does not reproduce a mail client's color algorithm or establish dark-mode
text contrast. Real inbox validation remains part of the release checklist.

Both button sizes also passed 60 browser clicks across the three widths with
normal and stripped styles, including each padded edge. Previously, all 48
edge clicks missed the link; only the 12 label-center clicks succeeded.

The comparisons below use local checked-in assets and synthetic fixture data.
Desktop remains 560px wide. Phones now use the viewport width, and the inline
fluid layout still fits when the stylesheet is removed.

Scenario Before follow-up After follow-up
Desktop, 640px
Phone, 390px
Phone, stylesheet removed

Original migration

These screenshots use synthetic data and the supplied production logo/Otto
assets. The before column is the original dev template; after is this shared
kit. Each comparison includes desktop (640px) and phone (390px) renderings.


Engineering Workspace Media Architecture (`docs/engineering/workspace-media-architecture.md`)

Workspace & Media File Architecture

This document describes the architecture for handling user files in AutoGPT Platform, covering persistent user storage (Workspace) and ephemeral media processing pipelines.

Overview

The platform has two distinct file-handling layers:

Layer Purpose Persistence Scope
Workspace Long-term user file storage Persistent (DB + GCS/local) Per-user, session-scoped access
Media Pipeline Ephemeral file processing for blocks Temporary (local disk) Per-execution

Database Models

UserWorkspace

Represents a user's file storage space. Created on-demand (one per user).

prisma
model UserWorkspace {
  id        String   @id @default(uuid())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  userId    String   @unique
  Files     UserWorkspaceFile[]
}

Key points:

  • One workspace per user (enforced by @unique on userId)
  • Created lazily via get_or_create_workspace()
  • Uses upsert to handle race conditions

UserWorkspaceFile

Represents a file stored in a user's workspace.

prisma
model UserWorkspaceFile {
  id          String    @id @default(uuid())
  workspaceId String
  name        String    // User-visible filename
  path        String    // Virtual path (e.g., "/sessions/abc123/image.png")
  storagePath String    // Actual storage path (gcs://... or local://...)
  mimeType    String
  sizeBytes   BigInt
  checksum    String?   // SHA256 for integrity
  isDeleted   Boolean   @default(false)
  deletedAt   DateTime?
  metadata    Json      @default("{}")

  @@unique([workspaceId, path])  // Enforce unique paths within workspace
}

Key points:

  • path is a virtual path for organizing files (not actual filesystem path)
  • storagePath contains the actual GCS or local storage location
  • Soft-delete pattern: isDeleted flag with deletedAt timestamp
  • Path is modified on delete to free up the virtual path for reuse

WorkspaceManager

Location: backend/util/workspace.py

High-level API for workspace file operations. Combines storage backend operations with database record management.

Initialization

python
from backend.util.workspace import WorkspaceManager

# Basic usage
manager = WorkspaceManager(user_id="user-123", workspace_id="ws-456")

# With session scoping (CoPilot sessions)
manager = WorkspaceManager(
    user_id="user-123",
    workspace_id="ws-456", 
    session_id="session-789"
)

Session Scoping

When session_id is provided, files are isolated to /sessions/{session_id}/:

python
# With session_id="abc123":
manager.write_file(content, "image.png")  
# → stored at /sessions/abc123/image.png

# Cross-session access is explicit:
manager.read_file("/sessions/other-session/file.txt")  # Works

Why session scoping?

  • CoPilot conversations need file isolation
  • Prevents file collisions between concurrent sessions
  • Allows session cleanup without affecting other sessions

Core Methods

Method Description
write_file(content, filename, path?, mime_type?, overwrite?) Write file to workspace
read_file(path) Read file by virtual path
read_file_by_id(file_id) Read file by ID
list_files(path?, limit?, offset?, include_all_sessions?) List files
delete_file(file_id) Soft-delete a file
get_download_url(file_id, expires_in?) Get signed download URL
get_file_info(file_id) Get file metadata
get_file_info_by_path(path) Get file metadata by path
get_file_count(path?, include_all_sessions?) Count files

Storage Backends

WorkspaceManager delegates to WorkspaceStorageBackend:

Backend When Used Storage Path Format
GCSWorkspaceStorage private_user_data_bucket (or the legacy bucket fallback) is configured gcs://bucket/workspaces/{ws_id}/{file_id}/{filename}
LocalWorkspaceStorage No GCS bucket configured local://{ws_id}/{file_id}/{filename}

Public and private media

Hosted storage has two trust boundaries:

  • PRIVATE_USER_DATA_BUCKET is the default destination for user uploads,
    generated library images, custom Expert avatars, workspaces, transcripts,
    and temporary agent inputs. Private media is read through an authenticated,
    non-cacheable API that serves a file to its owner, admins and members of
    an organization the owner belongs to.
  • PUBLIC_SITE_MEDIA_BUCKET contains only objects that a trusted publication
    flow explicitly copied after approval, plus media whose purpose is inherently
    public such as OAuth consent-screen logos. Anonymous exact-object reads are
    allowed, but anonymous bucket listing is not.

An upload caller cannot select the public destination. Publication is a
separate privileged operation; sharing a private resource grants access through
its opaque application URL and does not make its storage prefix public.

Moving a single-bucket deployment to split buckets

Stored rows hold gcs:///... paths and full GCS URLs, and the storage
code only reads from the configured private bucket, so the existing bucket must
become the private one:

  1. Create the new public bucket. Deploy with PRIVATE_USER_DATA_BUCKET set to
    the existing MEDIA_GCS_BUCKET_NAME bucket and PUBLIC_SITE_MEDIA_BUCKET
    set to the new one. Pointing PRIVATE_USER_DATA_BUCKET at a new bucket
    instead makes every existing workspace file and transcript unreadable.
    Cloud startup rejects that unsafe partial migration. After every stored
    bucket-qualified path has been migrated to a new private bucket, clear
    MEDIA_GCS_BUCKET_NAME before selecting the new private bucket.
    Deploy the frontend first: once the backend has both names set, uploads
    return /api/store/submissions/media/..., which a frontend without the new
    rewrite answers with a 404.
    If anonymous users hold roles/storage.objectViewer on the old bucket,
    they can list every object in it until step 4. Swap that binding for
    roles/storage.legacyObjectReader first: existing links keep working and
    listing stops.
  2. Copy everything that is already public to the public bucket and repoint its
    rows: poetry run python scripts/publish_live_media.py (dry run), then
    again with --apply until it exits 0. A non-zero exit means a live
    reference was not published (copy failure, conflict, an object outside the
    listing's owners or an unrecognised URL) and would break in step 4. This
    covers approved listing media, the avatars of creators with a public
    listing, library copies of listing images and OAuth app logos. It is also
    the repair tool when a copy at approval time failed.
  3. Rewrite the remaining private media URLs to the authenticated API path:
    poetry run python scripts/backfill_private_media_urls.py (dry run), then
    --apply, re-running until it reports no conflicts. It exits 2 while any
    reference stays on the old bucket (held as public, cross-user, ambiguous,
    malformed or unrecognised): those stop loading in step 4, so check the
    counts before going on. Both scripts commit in small batches and can be
    re-run.
  4. Run step 2's dry run once more and check it exits 0. Then remove every
    public binding from the old bucket and turn on public access prevention, so
    no object-level grant can expose a file again.

The private media endpoint serves a file to its owner, to admins and to
members of an organization the owner belongs to, so a leaked URL is useless to
anyone else. Published copies are never deleted automatically: when a listing
is taken down or a creator changes their avatar, the old public copy stays in
the public bucket until someone removes it by hand.

Hosted private image uploads are limited to 4 MiB so the frontend proxy can
buffer and deliver the complete authenticated response below Vercel's body
limit. Private videos retain the general 50 MiB upload limit and are delivered
in bounded range responses. An oversized object written before this limit is
served only after the same authorization check, using a 60-second signed URL
that bypasses the frontend proxy without granting bucket listing access.


store_media_file()

Location: backend/util/file.py

The media normalization pipeline. Handles various input types and normalizes them for processing or output.

Purpose

Blocks receive files in many formats (URLs, data URIs, workspace references, local paths). store_media_file() normalizes these to a consistent format based on what the block needs.

Input Types Handled

Input Format Example How It's Processed
Data URI data:image/png;base64,iVBOR... Decoded, virus scanned, written locally
HTTP(S) URL https://example.com/image.png Downloaded, virus scanned, written locally
Workspace URI workspace://abc123 or workspace:///path/to/file Read from workspace, virus scanned, written locally
Cloud path gcs://bucket/path Downloaded, virus scanned, written locally
Local path image.png Verified to exist in exec_file directory

Return Formats

The return_format parameter determines what you get back:

python
from backend.util.file import store_media_file

# For local processing (ffmpeg, MoviePy, PIL)
local_path = await store_media_file(
    file=input_file,
    execution_context=ctx,
    return_format="for_local_processing"
)
# Returns: "image.png" (relative path in exec_file dir)

# For external APIs (Replicate, OpenAI, etc.)
data_uri = await store_media_file(
    file=input_file,
    execution_context=ctx,
    return_format="for_external_api"
)
# Returns: "data:image/png;base64,iVBOR..."

# For block output (adapts to execution context)
output = await store_media_file(
    file=input_file,
    execution_context=ctx,
    return_format="for_block_output"
)
# In CoPilot: Returns "workspace://file-id#image/png"
# In graphs:  Returns "data:image/png;base64,..."

Execution Context

store_media_file() requires an ExecutionContext with:

  • graph_exec_id - Required for temp file location
  • user_id - Required for workspace access
  • workspace_id - Optional; enables workspace features
  • session_id - Optional; for session scoping in CoPilot

Responsibility Boundaries

Virus Scanning

Component Scans? Notes
store_media_file() ✅ Yes Scans all content before writing to local disk
WorkspaceManager.write_file() ✅ Yes Scans content before persisting

Scanning happens at:

  1. store_media_file() — scans everything it downloads/decodes
  2. WorkspaceManager.write_file() — scans before persistence

Tools like WriteWorkspaceFileTool don't need to scan because WorkspaceManager.write_file() handles it.

Persistence

Component Persists To Lifecycle
store_media_file() Temp dir (/tmp/exec_file/{exec_id}/) Cleaned after execution
WorkspaceManager GCS or local storage + DB Persistent until deleted

Automatic cleanup: clean_exec_files(graph_exec_id) removes temp files after execution completes.


Decision Tree: WorkspaceManager vs store_media_file

text
┌─────────────────────────────────────────────────────┐
│ What do you need to do with the file?               │
└─────────────────────────────────────────────────────┘
                         │
           ┌─────────────┴─────────────┐
           ▼                           ▼
    Process in a block          Store for user access
    (ffmpeg, PIL, etc.)         (CoPilot files, uploads)
           │                           │
           ▼                           ▼
    store_media_file()           WorkspaceManager
    with appropriate             
    return_format                
           │                           
           │                           
    ┌──────┴──────┐                    
    ▼             ▼                    
 "for_local_   "for_block_
 processing"   output"
    │             │
    ▼             ▼
 Get local    Auto-saves to
 path for     workspace in
 tools        CoPilot context

Store for user access
    │
    ├── write_file() ─── Upload + persist (scans internally)
    ├── read_file() / get_download_url() ─── Retrieve
    └── list_files() / delete_file() ─── Manage

Quick Reference

Scenario Use
Block needs to process a file with ffmpeg store_media_file(..., return_format="for_local_processing")
Block needs to send file to external API store_media_file(..., return_format="for_external_api")
Block returning a generated file store_media_file(..., return_format="for_block_output")
API endpoint handling file upload WorkspaceManager.write_file() (handles virus scanning internally)
API endpoint serving file download WorkspaceManager.get_download_url()
Listing user's files WorkspaceManager.list_files()

Key Files Reference

File Purpose
backend/data/workspace.py Database CRUD operations for UserWorkspace and UserWorkspaceFile
backend/util/workspace.py WorkspaceManager class - high-level workspace API
backend/util/workspace_storage.py Storage backends (GCS, local) and WorkspaceStorageBackend interface
backend/util/file.py store_media_file() and media processing utilities
backend/util/virus_scanner.py VirusScannerService and scan_content_safe()
schema.prisma Database model definitions

Common Patterns

Block Processing a User's File

python
async def run(self, input_data, *, execution_context, **kwargs):
    # Normalize input to local path
    local_path = await store_media_file(
        file=input_data.video,
        execution_context=execution_context,
        return_format="for_local_processing",
    )
    
    # Process with local tools
    output_path = process_video(local_path)
    
    # Return (auto-saves to workspace in CoPilot)
    result = await store_media_file(
        file=output_path,
        execution_context=execution_context,
        return_format="for_block_output",
    )
    yield "output", result

API Upload Endpoint

python
from backend.util.virus_scanner import VirusDetectedError, VirusScanError

async def upload_file(file: UploadFile, user_id: str, workspace_id: str):
    content = await file.read()

    # write_file handles virus scanning internally
    manager = WorkspaceManager(user_id, workspace_id)
    try:
        workspace_file = await manager.write_file(
            content=content,
            filename=file.filename,
        )
    except VirusDetectedError:
        raise HTTPException(status_code=400, detail="File rejected: virus detected")
    except VirusScanError:
        raise HTTPException(status_code=503, detail="Virus scanning unavailable")
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))

    return {"file_id": workspace_file.id}

Configuration

Setting Purpose Default
private_user_data_bucket Private GCS bucket for user media, workspace storage, transcripts, and temporary uploads Falls back to media_gcs_bucket_name, then local storage
public_site_media_bucket Public GCS bucket for explicitly published marketplace media and OAuth app logos Falls back to media_gcs_bucket_name for compatibility
media_gcs_bucket_name Legacy single-bucket setting for self-hosted compatibility None
workspace_storage_dir Local storage directory {app_data}/workspaces
max_file_size_mb Maximum file size in MB 100
clamav_service_enabled Enable virus scanning true
clamav_service_host ClamAV daemon host localhost
clamav_service_port ClamAV daemon port 3310
clamav_max_concurrency Max concurrent scans to ClamAV daemon 5
clamav_mark_failed_scans_as_clean If true, scan failures pass content through instead of rejecting (⚠️ security risk if ClamAV is unreachable) false

Integrations SUMMARY (`docs/integrations/SUMMARY.md`)

Table of contents

Guides

Block Integrations


Platform SUMMARY (`docs/platform/SUMMARY.md`)

Table of contents

Using the Platform

Self-Hosting

Tutorials

Building Blocks

Using AI Services

API & Integrations

Contributing

Changelog


Platform Advanced Setup (`docs/platform/advanced_setup.md`)

Advanced Setup

The advanced steps below are intended for people with sysadmin experience. If you are not comfortable with these steps, please refer to the basic setup guide.

Introduction

For the advanced setup, first follow the basic setup guide to get the server up and running. Once you have the server running, you can follow the steps below to configure the server for your specific needs.

Configuration

Setting config via environment variables

The server uses environment variables to store configs. You can set these environment variables in a .env file in the root of the project. The .env file should look like this:

bash
# .env
KEY1=value1
KEY2=value2

The server will automatically load the .env file when it starts. You can also set the environment variables directly in your shell. Refer to your operating system's documentation on how to set environment variables in the current session.

The valid options are listed in .env.default in the root of the builder and server directories. You can copy the .env.default file to .env and modify the values as needed.

bash
# Copy the .env.default file to .env
cp .env.default .env

Secrets directory

The secret directory is located at ./secrets. You can store any secrets you need in this directory. The server will automatically load the secrets when it starts.

An example for a secret called my_secret would look like this:

bash
# ./secrets/my_secret
my_secret_value

This is useful when running on docker so you can copy the secrets into the container without exposing them in the Dockerfile.

Database selection

PostgreSQL

We use PostgreSQL (with the pgvector extension) as the database. You will swap the commands you use to generate and run prisma to the following

bash
poetry run prisma generate --schema postgres/schema.prisma

This will generate the Prisma client for PostgreSQL. You will also need to run the PostgreSQL database in a separate container. You can use the docker-compose.yml file in the rnd directory to run the PostgreSQL database.

bash
cd autogpt_platform/
docker compose up -d --build

You can then run the migrations from the backend directory.

bash
cd ../backend
prisma migrate dev --schema postgres/schema.prisma

Redis

The platform uses Redis for caching, distributed locks, rate limits, and the streams that carry agent output to the browser.

The Docker Compose stack runs its own Redis Cluster: three redis:7 shards on ports 17000 to 17002, joined by a one-shot redis-init service. make start-core starts it along with the other dependencies.

Using an external Redis cluster

The backend connects with a cluster client only, so the deployment must have cluster mode enabled. A standalone server, with cluster mode disabled, does not work.

The cluster also needs:

  • Redis 7.0 or later, or a compatible engine such as Valkey. The backend uses sharded pub/sub (SPUBLISH, SSUBSCRIBE), which Redis 7.0 introduced, along with Streams and Lua scripts. It uses no Redis modules.
  • Shard addresses the backend can reach. With REDIS_USE_ANNOUNCED_ADDRESS=true, the backend connects to each shard at the address the cluster announces, so those addresses must resolve from where the backend runs. Without it, the backend connects to every shard at REDIS_HOST and keeps only the announced port, which works only when every shard is reachable on that one host.
  • A private network between the backend and every shard. The backend connects without TLS and does not send a username. It sends REDIS_PASSWORD when one is set, and a cluster without a password works too. Nothing on the connection is encrypted, so don't route it over a network you don't trust.

Point REDIS_HOST and REDIS_PORT at any node of the cluster, and set REDIS_PASSWORD if the cluster has one.

In the Compose stack, REDIS_HOST, REDIS_PORT and REDIS_USE_ANNOUNCED_ADDRESS are set in the x-backend-env block of autogpt_platform/docker-compose.platform.yml, which takes precedence over backend/.env, so change them there. REDIS_PASSWORD is read from backend/.env. The bundled shards still start, because the backend services wait for redis-0 to be healthy.

AutoGPT Agent Server Advanced set up

This guide walks you through a dockerized set up, with an external DB (postgres)

Setup

We use the Poetry to manage the dependencies. To set up the project, follow these steps inside this directory:

  1. Install Poetry

    sh
    pip install poetry
  2. Configure Poetry to use .venv in your project directory

    sh
    poetry config virtualenvs.in-project true
  3. Enter the poetry shell

    sh
    poetry shell
  4. Install dependencies

    sh
    poetry install
  5. Copy .env.default to .env

    sh
    cp .env.default .env
  6. Generate the Prisma client

    sh
    poetry run prisma generate

    In case Prisma generates the client for the global Python installation instead of the virtual environment, the current mitigation is to just uninstall the global Prisma package:

    sh
    pip uninstall prisma

    Then run the generation again. The path should look something like this:
    <some path>/pypoetry/virtualenvs/backend-TQIRSwR6-py3.12/bin/prisma

  7. Run the postgres database from the /rnd folder

    sh
    cd autogpt_platform/
    docker compose up -d
  8. Run the migrations (from the backend folder)

    sh
    cd ../backend
    prisma migrate deploy

Running The Server

Starting the server directly

Run the following command:

sh
poetry run app

Platform Agent Blocks (`docs/platform/agent-blocks.md`)

**How to Create an AI Agent as a Block in AutoGPT**

**Overview**

This guide explains how to create a reusable agent block that can be used as a component in other agents.

**What Are Agent Blocks?**

Agent blocks are pre-configured, reusable AI workflows that can be used as components within larger automation systems. Think of them as "smart building blocks" - each agent block is itself a complete workflow that can:

  • Accept specific inputs
  • Process data using AI and traditional automation
  • Produce defined outputs
  • Be easily reused in different contexts

The power of agent blocks lies in their modularity. Once you create an agent with a specific capability (like translating text or analyzing sentiment), you can reuse it as a single block in other workflows. This means you can:

  • Combine multiple agent blocks to create more complex automations
  • Reuse proven workflows without rebuilding them
  • Share agent blocks with other users
  • Create hierarchical systems where specialized agents work together

For example, a content creation workflow might combine several agent blocks:

  • A research agent block that gathers information
  • A writing agent block that creates the initial draft
  • An editing agent block that polishes the content
  • A formatting agent block that prepares the final output

**Creating the Base Agent**

**Required Components**

  1. Input Block
  2. AI Text Generator Block
  3. Output Block

**Step-by-Step Setup**

  1. Add and Configure Blocks
    • Add an Input Block
    • Add an AI Text Generator Block
    • Add an Output Block
  2. Connect Components
    • Connect Input's result to AI Text Generator's Prompt
    • Connect AI Text Generator's response to Output's value
  3. Name the Components
    • Name the Input Block: "question"
    • Name the Output Block: "answer"
  4. Save the Agent
    • Choose a descriptive name (e.g., "Weather Agent")
    • Click Save

**Converting to a Block**

  1. Access the Block Menu
    • Go to the Builder interface
    • Click the Blocks menu
    • Click the agent tag or search the name of your agent
  2. Using the Agent Block
    • Click on the agent block to add to your workflow
    • Save the new agent with a descriptive name (e.g., "Weather Agent")

**Testing the Agent Block**

  1. Run the Agent
    • Enter a test question (e.g., "How far is the Earth from the Moon?")
    • Click Run
  2. View Results
    • Option 1: Check "Agent Outputs" section*
    • Option 2: Click "View More" for detailed results

*Note: if there is no output block then the "Agent Outputs" button will show up blank. You can see the output under view more or at bottom of the block.

**Advanced Usage**

  • You can make more complex agents by combining multiple agent blocks
  • Chain different agents together for more sophisticated workflows

**Note**

This is a basic example that can be expanded upon to create more complex agent blocks with additional functionality.


Platform Agent Builder Guide (`docs/platform/agent-builder-guide.md`)

Agent Builder Guide

Overview

The Agent Builder is the visual editor where you design and build agents by connecting blocks together on a canvas. Each block is a single action — like generating text, calling an API, or processing data — and you wire them together to create automated workflows.

URL: platform.agpt.co/build

The Builder Interface

When you open the builder, you'll see:

  • Canvas: The main workspace where you place and connect blocks
  • Blocks Menu: A panel on the left-hand side where you browse and search for blocks
  • Save Button: Save your agent with a title and description

Working with Blocks

Types of Blocks

Blocks fall into three categories:

Type Description
Input Blocks Define what information the agent needs when it runs. These become the input fields users fill in when starting a task. Types include text inputs, file inputs, and more.
Action Blocks Perform operations — AI text generation, image creation, API calls, data processing, and hundreds of integrations with external platforms.
Output Blocks Define what the agent returns as its result. These become the visible output when a task completes.

Input and output blocks define the schema of your agent — they determine what users see when running the agent. All other blocks inside the agent are internal and not exposed to the user at runtime.

{% hint style="info" %}
There is also a special type of input block: Trigger Blocks. These allow your agent to be activated by external events via webhooks rather than manual input. See Scheduling & Triggers for details.
{% endhint %}

Adding Blocks

  1. Open the Blocks menu on the left-hand side of the builder
  2. Browse categories or use the search bar to find a specific block
  3. Click on a block to add it to the canvas

There are hundreds of blocks available, integrating with many platforms and services.

Connecting Blocks

Blocks have input pins and output pins. Pins are typed — they handle specific data types like text, numbers, files, and more.

To connect blocks:

  1. Click on an output pin of one block
  2. Drag to an input pin of another block (or simply click the output pin, then click the input pin)
  3. A connection line will appear between the two pins

When the agent runs, data flows along these connections. This is visually represented by a coloured bead that slides along the connection line from the output pin to the input pin.

Configuring Blocks

Many blocks have settings you can configure directly on the block. For example:

  • The AI Text Generator block lets you choose which language model to use
  • Integration blocks may require credentials (see Integrations & Credentials)
  • Some blocks allow you to hardcode values on their input pins instead of connecting them to other blocks

If a block requires a credential you haven't connected yet, a credential bar will appear prompting you to add it (via OAuth, API key, or username/password depending on the service).

Saving Your Agent

To save your agent:

  • Press Ctrl+S (or Cmd+S on Mac), or
  • Click the Save button in the builder

Saving creates or updates the agent graph version in your library. The builder also keeps a local recovery draft while you edit; a successful save clears that draft. Publishing to the marketplace is a separate action—see Publishing to the Marketplace.

After saving, click the Agents button in the navigation bar to return to your library and find your agent.

Editing an Existing Agent

To open an existing agent in the builder:

  1. Go to your Agent Library (click Agents in the nav bar)
  2. Click on the agent you want to edit
  3. Click the three dots menu (⋯) on the far right-hand side of the screen
  4. Select Edit Agent

This will open the agent in the builder with all its existing blocks and connections.

Error Handling

When a block fails during execution, it produces data on its error pin. As the agent creator, you decide how to handle errors:

  • Surface the error: Connect the error pin to an output block so the error is returned as the agent's result
  • Handle gracefully: Connect the error pin to other blocks that provide fallback behaviour, ensuring the agent continues working even when individual blocks encounter problems

Tips

  • Name your input and output blocks clearly — these names become the labels users see when running your agent
  • Test incrementally — save and run your agent frequently as you build to catch issues early
  • Use the search in the blocks menu to quickly find what you need among hundreds of available blocks
  • Check the pin types — connections only work between compatible pin types

Platform Agent Library (`docs/platform/agent-library.md`)

Agent Library

Overview

The Agent Library is your personal collection of agents. From here you can run agents, view task history, set up schedules and triggers, edit agents, and manage your collection.

URL: platform.agpt.co/library

Access: Click the Agents button in the navigation bar.

Library View

Your library displays all of your agents, including agents you've built yourself and agents you've added from the marketplace. Each agent shows its name and key information at a glance.

Favorites and folders

Click the heart icon on an agent to add or remove it from the Favorites tab. Use folders to organize agents; create a folder from the library and choose Move to folder from an agent's menu.

Agent Detail View

Click on any agent to open its detail view. This screen provides:

  • Tasks: A full history of every time this agent has been executed
  • Scheduled: A list of active schedules for this agent
  • Triggers: Configured webhook triggers for trigger-based agents
  • Templates: Saved task inputs and credential references for reuse

These are accessible via tabs on the left-hand side of the agent screen (e.g., Tasks 286 | Scheduled 1 | Templates 0).

Agent Actions Menu

Click the three dots (⋯) on the far right-hand side of the agent screen to access:

Action Description
Edit Agent Opens the agent in the builder; marketplace agents are read-only until duplicated
Delete Agent Permanently removes the agent from your library
Export Agent to File Downloads the agent as a file you can share with others

Running an Agent

To run an agent manually:

  1. Open the agent from your library
  2. Click New agent task
  3. Fill in the required inputs and credentials
  4. Click Start Task

The task will begin executing immediately. You can watch the progress and view the results once it completes.

{% hint style="warning" %}
For a trigger-based agent, use the same New agent task dialog and click Set up Trigger. Trigger-based agents cannot be started manually. See Scheduling & Triggers for details.
{% endhint %}

Viewing Task Results

Every time an agent runs, it creates a task. To view task results:

  1. Open the agent from your library
  2. In the left-hand pane, browse the list of completed tasks
  3. Click on a task to view its details

A task detail view shows:

  • Inputs: The values that were provided when the task started
  • Outputs: The results the agent produced
  • Cost: The run's reported cost, displayed at the top of the task detail view

To share an output, click Share results and explicitly enable sharing; copying the authenticated task URL does not create a public link. See Sharing & Exporting Agents for visibility and revocation details.

Importing an Agent

To import an agent from a file:

  1. Go to your Agent Library
  2. Click Import
  3. Keep AutoGPT agent selected
  4. Choose the exported JSON file, complete the requested metadata, and click Upload

The agent will be added to your library and can be run or edited like any other agent. This is useful for importing agents shared by other users outside of the marketplace.

Deleting an Agent

  1. Open the agent from your library
  2. Click the three dots (⋯) on the far right
  3. Select Delete Agent
  4. Confirm the deletion when prompted

{% hint style="danger" %}
Deleting an agent is permanent and cannot be undone. Make sure you want to remove it before confirming.
{% endhint %}


Platform Aimlapi (`docs/platform/aimlapi.md`)

🧠 Running AI/ML API with AutoGPT

Follow these steps to connect AI/ML API with the AutoGPT platform for high-performance AI text generation.


✅ Prerequisites

  1. Make sure you have gone through and completed the AutoGPT Setup Guide, and AutoGPT is running locally at http://localhost:3000.
  2. You have an API key from AI/ML API.

⚙️ Setup Steps

1. Start AutoGPT Locally

Follow the official guide:
📖 AutoGPT Getting Started Guide

Make sure AutoGPT is running and accessible at:
http://localhost:3000

💡 Keep AutoGPT running in a terminal or Docker throughout the session.


2. Open the Visual Builder

Open your browser and go to:
http://localhost:3000/build

Or click “Build” in the navigation bar.


3. Add an AI Text Generator Block

  1. Click the "Blocks" button on the left sidebar.

  2. In the search bar, type AI Text Generator.

  3. Drag the block into the canvas.


4. Select an AI/ML API Model

Click the AI Text Generator block to configure it.

In the LLM Model dropdown, select the model currently available from AI/ML API:

Model Model ID
Llama 3.3 70B Instruct Turbo meta-llama/Llama-3.3-70B-Instruct-Turbo

✅ This model is available via the OpenAI-compatible API from AI/ML API


5. Configure the Prompt and API Key

Inside the AI Text Generator block:

  1. Enter your prompt text in the Prompt field.
  2. Enter your AI/ML API Key in the designated field.

🔐 You can get your key from:
https://aimlapi.com/app/keys/


6. Save Your Agent

Click the “Save” button at the top-right of the builder interface:

  1. Give your agent a name (e.g., aimlapi_test_agent).
  2. Click “Save Agent” to confirm.

💡 Saving allows reuse, scheduling, and chaining in larger workflows.


7. Run Your Agent

From the workspace:

  1. Press “Run” next to your saved agent.
  2. The request will be sent to the selected AI/ML API model.

8. View the Output

  1. Scroll to the AI Text Generator block.
  2. Check the Output panel below it.
  3. You can copy, export, or pass the result to further blocks.

🔄 Expand Your Agent

Now that AI/ML API is connected, expand your workflow by chaining additional blocks:

  • 🔧 Tools – fetch URLs, call APIs, scrape data
  • 🧠 Memory – retain context across interactions
  • ⚙️ Actions / Chains – create full pipelines

🎉 You’re now generating AI responses using enterprise-grade models from AI/ML API in AutoGPT!


Platform Autopilot (`docs/platform/autopilot.md`)

AutoPilot

Overview

AutoPilot is your AI assistant built directly into the AutoGPT Platform. It can perform supported platform actions through natural conversation — from running agents to generating images, conducting research, and building agents for you.

Accessing AutoPilot

Open AutoPilot from Home or New Task in the main navigation, or go directly to platform.agpt.co/copilot.

What AutoPilot Can Do

AutoPilot can use supported platform tools. Here's what it can help you with:

Run Agents

Ask AutoPilot to run a supported agent in your Agent Library. It will request any missing inputs or credentials before executing it.

Build Agents

Describe the workflow you want and AutoPilot will create an agent for you in the builder. You can also ask it to edit your existing agents; duplicate a read-only marketplace agent before editing it. See the Agent Builder Guide for hands-on editing.

Browse the Marketplace

Ask AutoPilot to find agents for a specific use case and it will search the marketplace for you. You can also browse the Marketplace directly.

Execute Blocks Directly

AutoPilot can run supported blocks directly without building a full agent. This means you can:

  • Conduct research with Perplexity
  • Generate images with the latest image models
  • Edit pictures using AI image editing blocks
  • Generate videos using video generation blocks
  • Run supported models on inference services such as Replicate
  • Make custom HTTP requests to APIs you configure
  • Write and execute code with supported coding tools

Manage Your Library

AutoPilot can help you manage your agent library, view task results, set up schedules, and more.

Tips for Using AutoPilot

  • Be specific: The more detail you provide, the better AutoPilot can assist you. Instead of "make me an agent", try "build me an agent that takes a blog topic as input, generates an outline with Claude, then writes the full article".
  • Iterate: You can refine results by asking follow-up questions or requesting changes.
  • Explore capabilities: If you're unsure whether AutoPilot supports something, ask it or browse the available blocks.

AutoPilot vs. the Agent Builder

AutoPilot and the Agent Builder offer complementary ways to work on the platform. AutoPilot can create and edit reusable agents through natural language and run supported blocks directly, while the builder provides hands-on control over the graph.

AutoPilot Agent Builder
Who is it for Everyone — no technical knowledge required Technical users with a grasp of visual programming
Best for Natural language interaction, doing things fast, and accessing blocks directly Hands-on visual control over exactly how an agent is wired together
How it works Conversational — describe what you want Visual — drag, drop, and connect blocks on a canvas
Can build agents Yes — describe what you want and it builds the agent for you Yes — you build the agent manually
Can edit agents Yes — for user-created or duplicated editable agents Yes — full visual editing
Block access Can run supported blocks directly without building an agent Blocks must be connected into an agent workflow

Choose whichever approach suits you. Many users use both — AutoPilot for speed, and the builder when they want fine-grained visual control over their workflow.


Platform Block Sdk Guide (`docs/platform/block-sdk-guide.md`)

Block Creation with SDK

This guide explains how to create new blocks for the AutoGPT Platform using the SDK pattern with advanced features.

Overview

Blocks are reusable components that perform specific tasks in AutoGPT workflows. They can integrate with external services, process data, or perform any programmatic operation.

Basic Structure

1. Create Provider Configuration

First, create a _config.py file to configure your provider using the ProviderBuilder:

!!! note "Simple API key provider"
```python
from backend.sdk import BlockCostType, ProviderBuilder

text
my_provider = (
    ProviderBuilder("my_provider")
    .with_api_key("MY_PROVIDER_API_KEY", "My Provider API Key")
    .with_base_cost(1, BlockCostType.RUN)
    .build()
)
```

For OAuth providers:

!!! note "OAuth provider configuration"
```python
from backend.sdk import BlockCostType, ProviderBuilder
from ._oauth import MyProviderOAuthHandler

text
my_provider = (
    ProviderBuilder("my_provider")
    .with_oauth(
        MyProviderOAuthHandler,
        scopes=["read", "write"],
        client_id_env_var="MY_PROVIDER_CLIENT_ID",
        client_secret_env_var="MY_PROVIDER_CLIENT_SECRET",
    )
    .with_base_cost(1, BlockCostType.RUN)
    .build()
)
```

2. Create the Block Class

Create your block file (e.g., my_block.py):

text
/* Detailed source-code truncated for AI context efficiency. */

!!! note "Input Schema Fields"
- credentials: Use my_provider.credentials_field() to add provider authentication
- query: Simple string field with description
- limit: Integer field with validation constraints (ge=1, le=100)
- advanced_option: Marked with advanced=True to hide from basic UI

!!! note "Output Schema Fields"
- results: List of results from the block
- count: Total count of results
- The error output pin is already defined on BlockSchemaOutput

!!! note "Block Initialization"
- id: Generate a unique ID using uuid.uuid4()
- description: Brief description of what the block does
- categories: Choose from BlockCategory enum (e.g., SEARCH, AI, PRODUCTIVITY)
- input_schema / output_schema: Assign the Input and Output classes

!!! note "Run Method"
- Implement your block logic in process_data() helper method
- Use credentials.api_key.get_secret_value() to access the API key
- Use yield to output results
- Do NOT wrap your logic in try/except — the executor framework handles errors at a higher level. Raise exceptions (e.g., BlockExecutionError, BlockInputError) for user-fixable errors; let unexpected errors propagate naturally. See Error Handling in new_blocks.md for details.

Key Components Explained

Provider Configuration

!!! tip "Simple API Key Integrations"
For integrations that only use API key authentication, define credentials in _auth.py and use a small _config.py to register the provider description and supported auth types for the settings UI. See API-Key-Only Provider Auth in the main block guide for the complete pattern.

The ProviderBuilder is available for advanced use cases (OAuth, webhooks, cost tracking). It allows you to:

  • .with_api_key(): Add API key authentication
  • .with_oauth(): Add OAuth authentication
  • .with_base_cost(): Set resource costs for the block
  • .with_webhook_manager(): Add webhook support
  • .with_user_password(): Add username/password auth

Block Schema

  • Input/Output classes: Define the data structure using BlockSchema
  • SchemaField: Define individual fields with validation
  • CredentialsMetaInput: Special field for handling credentials

Block Implementation

  1. Unique ID: Generate using uuid.uuid4()
  2. Categories: Choose from BlockCategory enum (e.g., SEARCH, AI, PRODUCTIVITY)
  3. async run(): Main execution method that yields outputs
  4. Error handling: Error output pin is already defined on BlockSchemaOutput

Advanced Features

Testing

Add test configuration to your block:

!!! note "Test Configuration"
python def __init__(self): super().__init__( # ... other config ... test_input={ "query": "test query", "limit": 5, "credentials": { "provider": "my_provider", "id": str(uuid.uuid4()), "type": "api_key" } }, test_output=[ ("results", ["result1", "result2"]), ("count", 2) ], test_mock={ "process_data": lambda *args, **kwargs: ["result1", "result2"] } )

OAuth Support

Create an OAuth handler in _oauth.py:

!!! note "OAuth Handler Implementation"
```python
from backend.integrations.oauth.base import BaseOAuthHandler

text
class MyProviderOAuthHandler(BaseOAuthHandler):
    PROVIDER_NAME = "my_provider"

    def _get_authorization_url(self, scopes: list[str], state: str) -> str:
        # Implement URL generation for OAuth flow
        pass

    def _exchange_code_for_token(self, code: str, scopes: list[str]) -> dict:
        # Implement token exchange logic
        pass
```

Webhook Support

Create a webhook manager in _webhook.py:

!!! note "Webhook Manager Implementation"
```python
from backend.integrations.webhooks._base import BaseWebhooksManager

text
class MyProviderWebhookManager(BaseWebhooksManager):
    PROVIDER_NAME = "my_provider"

    async def validate_event(self, event: dict) -> bool:
        # Implement event validation logic
        pass
```

File Organization

text
backend/blocks/my_provider/
├── __init__.py          # Export your blocks
├── _config.py           # Provider configuration
├── _oauth.py           # OAuth handler (optional)
├── _webhook.py         # Webhook manager (optional)
├── _api.py             # API client wrapper (optional)
├── models.py           # Data models (optional)
└── my_block.py         # Block implementations

Best Practices

  1. Error Handling: Use BlockInputError for validation failures and BlockExecutionError for runtime errors (import from backend.util.exceptions). These inherit from ValueError so the executor treats them as user-fixable. See Error Handling in new_blocks.md for details.
  2. Credentials: Use the provider's credentials_field() method
  3. Validation: Use SchemaField constraints (ge, le, min_length, etc.)
  4. Categories: Choose appropriate categories for discoverability
  5. Advanced Fields: Mark complex options as advanced=True
  6. Async Operations: Use async/await for I/O operations
  7. API Clients: Use Requests() from SDK or external libraries
  8. Testing: Include test inputs/outputs for validation

Common Patterns

Making API Requests

python
from backend.sdk import Requests

async def run(self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs):
    headers = {
        "Authorization": f"Bearer {credentials.api_key.get_secret_value()}",
        "Content-Type": "application/json"
    }

    response = await Requests().post(
        "https://api.example.com/endpoint",
        headers=headers,
        json={"query": input_data.query}
    )

    data = response.json()
    yield "results", data.get("results", [])

Multiple Auth Types

python
async def run(
    self,
    input_data: Input,
    *,
    credentials: OAuth2Credentials | APIKeyCredentials,
    **kwargs
):
    if isinstance(credentials, OAuth2Credentials):
        # Handle OAuth credentials
        token = credentials.access_token.get_secret_value()
    else:
        # Handle API key credentials
        token = credentials.api_key.get_secret_value()

!!! note "Authentication Types"
- OAuth2Credentials: Access token via credentials.access_token.get_secret_value()
- APIKeyCredentials: API key via credentials.api_key.get_secret_value()

Handling Files

When your block works with files (images, videos, documents), use store_media_file():

python
from backend.data.execution import ExecutionContext
from backend.util.file import store_media_file
from backend.util.type import MediaFileType

async def run(
    self,
    input_data: Input,
    *,
    execution_context: ExecutionContext,
    **kwargs,
):
    # PROCESSING: Need local file path for tools like ffmpeg, MoviePy, PIL
    local_path = await store_media_file(
        file=input_data.video,
        execution_context=execution_context,
        return_format="for_local_processing",
    )

    # EXTERNAL API: Need base64 content for APIs like Replicate, OpenAI
    image_b64 = await store_media_file(
        file=input_data.image,
        execution_context=execution_context,
        return_format="for_external_api",
    )

    # OUTPUT: Return to user/next block (auto-adapts to context)
    result = await store_media_file(
        file=generated_url,
        execution_context=execution_context,
        return_format="for_block_output",
    )
    yield "image_url", result

!!! note "File Handling Patterns"
- PROCESSING: Use "for_local_processing" when you need a local file path for tools like ffmpeg, MoviePy, PIL
- EXTERNAL API: Use "for_external_api" when sending content to APIs like Replicate or OpenAI (returns base64 data URI)
- OUTPUT: Use "for_block_output" to return results - this automatically adapts: workspace:// in CoPilot, data URI in graphs

Return format options:

  • "for_local_processing" - Local file path for processing tools
  • "for_external_api" - Data URI for external APIs needing base64
  • "for_block_output" - Always use for outputs - automatically picks best format

Testing Your Block

bash
# Run all block tests
poetry run pytest backend/blocks/test/test_block.py -xvs

# Test specific block
poetry run pytest 'backend/blocks/test/test_block.py::test_available_blocks[MyBlock]' -xvs

Integration Checklist

  • Add provider to ProviderName in backend/integrations/providers.py if using that enum in credentials; SDK integrations can register names through ProviderBuilder
  • Define credentials with _auth.py helpers or the SDK provider configuration, and register the description and supported auth types in _config.py
  • Implement block class with Input/Output schemas
  • Generate unique block ID with uuid.uuid4()
  • Choose appropriate block categories
  • Implement async run() method
  • Handle errors with BlockInputError/BlockExecutionError (don't wrap in try/except)
  • Add test configuration (inline test_input/test_output and/or standalone {name}_test.py)
  • Add integration icon PNG at frontend/public/integrations/{provider_name}.png
  • Run cd autogpt_platform/backend && poetry run python scripts/generate_block_docs.py to generate docs
  • Test the block
  • Document any special requirements

Example Blocks for Reference

  • Simple API: /backend/blocks/firecrawl/ - Basic API key authentication
  • OAuth + API: /backend/blocks/linear/ - OAuth and API key support
  • Webhooks: /backend/blocks/exa/ - Includes webhook manager

Study these examples to understand different patterns and approaches for building blocks.


Platform Copilot Local Llm (`docs/platform/copilot-local-llm.md`)

Running AutoPilot on a self-hosted LLM

Important: This page covers the AutoPilot chat path — the
conversational agent on /copilot. For the block-layer AI Text
Generator block (used inside agent graphs you build yourself), see
Running Ollama with AutoGPT. The two paths read different
env vars, so configuring one does not configure the other.

Self-hosting only — the cloud agpt.co deployment routes AutoPilot
through Anthropic / OpenRouter and ignores the variables below.

This guide makes the AutoPilot chat work without an Anthropic, OpenAI,
or OpenRouter key
by routing it through any OpenAI-compatible HTTP
endpoint you control
.

The transport is called local because it's the typical case, but
CHAT_BASE_URL is just a URL — every deployment shape below works
equally well:

Scenario CHAT_BASE_URL
Ollama on the same Docker host (most common) http://192.168.1.42:11434/v1 (LAN IP)
Ollama on the same Docker host, via Docker Desktop http://host.docker.internal:11434/v1
Ollama on a separate LAN box http://ollama.lab.local:11434/v1
Ollama behind an HTTPS reverse proxy on the public internet https://ollama.example.com/v1
vLLM, LocalAI, LM Studio, LiteLLM proxy their respective /v1 URLs
A managed OpenAI-compatible API you don't pay AutoGPT for its /v1 URL

Anything that speaks the OpenAI /v1/chat/completions shape — including
tools=[...] for function calling — will work. The rest of this guide
uses Ollama as the running example because it's the easiest, but
substitute your own endpoint anywhere you see http://...:11434/v1.

How it works

AutoPilot's ChatConfig (backend/backend/copilot/config.py) recognises
four chat transports. When CHAT_USE_LOCAL=true:

Transport behaviour Local
Routes the baseline (fast) path to CHAT_BASE_URL over OpenAI-compatible HTTP ✅
Supports the SDK / extended-thinking path (Claude Agent SDK) ❌ — auto-downgrades to fast
api_key falls back to OPEN_ROUTER_API_KEY / OPENAI_API_KEY if CHAT_API_KEY is unset ❌ — explicit CHAT_API_KEY only
Aux + advanced models (title_model, simulation_model, fast_advanced_model) inherit fast_standard_model if left at a cloud default ✅
Allows non-anthropic/* SDK model slugs (vendor validator skipped) ✅

The downgrade is logged at WARNING when an extended_thinking request
arrives — there is no 500. The frontend toggle should already be hidden
because the CHAT_MODE_OPTION LaunchDarkly flag defaults off in
self-hosted deployments.

On the managed cloud platform (BEHAVE_AS=cloud), CHAT_*_MODEL env
vars are the bottom layer of model resolution: LaunchDarkly per-user
override → the LLM catalog's routing cell
(backend/data/llm_registry/catalog.py) → env default, with slugs
unknown to the catalog or disabled in it refused at serve time.

On self-hosted installs — any transport — the catalog's routing cells
are skipped entirely
: they are the cloud deployment's config traveling
in the shipped file, and they never override your CHAT_*_MODEL
configuration. Resolution here is LaunchDarkly → CHAT_*_MODEL, exactly
the pre-catalog behavior; your env vars stay authoritative. See
Managing LLM Models.

Required environment variables

In autogpt_platform/backend/.env:

bash
# Turn on the local transport
CHAT_USE_LOCAL=true

# Where the OpenAI-compatible endpoint lives. From inside the docker
# containers this must NOT be 127.0.0.1 / localhost — use the host's LAN
# IP (e.g. 192.168.1.42) or, if you've added the directive to compose,
# host.docker.internal:host-gateway.
CHAT_BASE_URL=http://192.168.1.42:11434/v1

# Any non-empty string — Ollama doesn't validate it. The local transport
# deliberately does NOT fall back to OPENAI_API_KEY (which is usually
# present for graphiti / embedders), so this must be set explicitly.
CHAT_API_KEY=ollama

# The chat model. Bare model names ONLY — provider/model slugs (e.g.
# `anthropic/claude-...`) are passed through verbatim and Ollama can't
# resolve them. See "Picking a model" below.
CHAT_FAST_STANDARD_MODEL=hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M

# Optional — override for the advanced tier. If you leave it out, the
# local transport derives title_model, simulation_model, AND
# CHAT_FAST_ADVANCED_MODEL from CHAT_FAST_STANDARD_MODEL automatically
# (see _apply_local_aux_models in backend/backend/copilot/config.py),
# so the advanced toggle never sends a cloud-only slug to Ollama. Set
# it explicitly only if you want a bigger model for the advanced tier
# and have the VRAM for it.
CHAT_FAST_ADVANCED_MODEL=qwen3:14b-q4_K_M

Picking a model

The platform's chat loop calls OpenAI-style tool-calling on every
turn, streams responses, and ships an ~8 k-token system prompt. Pick a
model that handles all three.

Tier Recommended Ollama tag Why Footprint
Default hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M Official Ornith GGUF; agentic 9B model with OpenAI-compatible tool calling; 262,144-token native context; reasoning model (the chat UI renders its thinking separately from the answer) ~5.8 GB model file; allow additional RAM for context and the KV cache
Tight RAM qwen3:4b Smaller; native tools; set think: false to avoid the unclosed-`` tool-call render bug ~3-4 GB resident
GPU / advanced qwen3:14b-q4_K_M Best tool-selection accuracy in this size class ~12 GB VRAM

Pull whichever you choose:

bash
ollama pull hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M

Context window — set it once, on the backend

Ollama defaults num_ctx to 4096 tokens regardless of the model's
advertised window
(ollama/ollama#2714).
That's smaller than AutoPilot's system prompt + tool schemas — without a
larger window Ollama only sees the end of the instructions and responses
are incoherent or 500 outright.

Set the window once, on the server, via OLLAMA_CONTEXT_LENGTH. There is
no AutoGPT-side context config to keep in sync: AutoPilot reads the
backend's actual loaded window back at runtime — Ollama /api/ps, llama.cpp
/props, vLLM / LM Studio /v1/models — and compacts the conversation under
it. Backends that don't report a window (LiteLLM proxy, Jan,
text-generation-webui) fall back to assuming 32k.

The default Ornith model has a 262,144-token native window, so the installer
sets OLLAMA_CONTEXT_LENGTH=262144. This maximizes available conversation
history but substantially increases KV-cache RAM/VRAM use. Operators using a
custom model or constrained hardware can lower it, but should keep at least
24k: below that, the system prompt + tools leave almost no room for
conversation and AutoPilot logs a warning.

The installer sets OLLAMA_CONTEXT_LENGTH for you. Manual setup per platform:

Linux (systemd drop-in):

ini
# /etc/systemd/system/ollama.service.d/host.conf
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_CONTEXT_LENGTH=262144"

Then sudo systemctl daemon-reload && sudo systemctl restart ollama.

macOS (launchctl, persists across logins for launchd-spawned processes):

bash
launchctl setenv OLLAMA_HOST 0.0.0.0:11434
launchctl setenv OLLAMA_CONTEXT_LENGTH 262144
# Then restart Ollama — either:
brew services restart ollama        # if installed via the brew formula
# …or quit the menu-bar app and relaunch it (the .dmg install)

Windows (user-scope env vars, persists across reboots):

powershell
setx OLLAMA_HOST "0.0.0.0:11434"
setx OLLAMA_CONTEXT_LENGTH "262144"
# Then quit Ollama from the system tray and relaunch it
# (setx writes to HKCU but does NOT update already-running processes).

Verify on any platform with ollama ps (the CONTEXT column should
show your value, e.g. 262144). If you change it, AutoPilot picks up the
new window automatically on the next turn — nothing else to update.

Networking — same host, different host, or remote

The endpoint can be on the same machine, on the LAN, or anywhere
internet-reachable. Pick whichever matches your deployment shape:

Same host as the AutoGPT containers

How containers reach the host depends on whether you're on Docker
Desktop (macOS / Windows) or native Docker (Linux):

macOS + Windows (Docker Desktop) — every container already has a
host.docker.internal entry pointing at the host. No extra wiring:

bash
CHAT_BASE_URL=http://host.docker.internal:11434/v1

Still set OLLAMA_HOST=0.0.0.0:11434 so the .app/tray-managed Ollama
accepts the connection from the Desktop network — by default it binds
only to 127.0.0.1.

Linux (native Docker) — there's no auto-injected
host.docker.internal. Pick one:

  1. Use the LAN IP in CHAT_BASE_URL — simplest, works everywhere.
  2. Bind Ollama to all interfaces: OLLAMA_HOST=0.0.0.0:11434 (set
    in the systemd unit or a drop-in), so containers reach it via the
    bridge gateway.
  3. Add extra_hosts: ["host.docker.internal:host-gateway"] to the
    chat services in autogpt_platform/docker-compose.yml.

The bundled installer does these for you on a fresh box:

Platform Command
Linux installer/setup-autogpt.sh --with-ollama
macOS installer/setup-autogpt.sh --with-ollama
Windows installer\setup-autogpt.bat /with-ollama

Different LAN box (dedicated GPU server, NAS, …)

Set CHAT_BASE_URL to the box's hostname or IP:

bash
CHAT_BASE_URL=http://gpu-rig.lab.local:11434/v1

On the Ollama box, set OLLAMA_HOST=0.0.0.0:11434 so it accepts
non-loopback connections, and either open port 11434 in the firewall
for your AutoGPT host's IP or put both behind a private VPN /
WireGuard mesh.

Remote / public-internet endpoint

Two approaches, in increasing order of "please do this":

  1. Trusted private network (Tailscale, WireGuard, ZeroTier,
    corporate VPN). Treat the remote endpoint exactly like a LAN box.

  2. Public HTTPS with auth — terminate TLS at a reverse proxy
    (Caddy, nginx, Cloudflare Tunnel) in front of Ollama / vLLM /
    whatever, and require a bearer token. Set:

    bash
    CHAT_BASE_URL=https://ollama.example.com/v1
    CHAT_API_KEY=<the-bearer-the-proxy-checks>

Do not expose raw Ollama on the public internet. Ollama itself
performs no authentication — anyone who can reach :11434 can
use (and exhaust) your model. Always front it with a proxy that
enforces a token.

Other platform features that use the local transport

The local transport isn't just for AutoPilot chat. The same client
flows to every backend helper that needs an LLM, so a single
CHAT_USE_LOCAL=true install also covers:

  • Dry-run block simulator — when a user clicks "Test" in the agent
    builder, blocks role-play their execution against an LLM rather than
    hitting external APIs. Uses ChatConfig.simulation_model
    (auto-derived to fast_standard_model under local).
  • Onboarding business-understanding extraction — the post-signup
    Tally form is extracted into structured suggestions via the LLM.
    Uses ChatConfig.title_model.
  • Long-run prompt compression — the chat / agent loop summarizes
    message history when context grows beyond a threshold.
  • Marketplace semantic search — the store generates embeddings for
    agent descriptions to power hybrid (lexical + semantic) ranking.
    Hybrid search degrades gracefully to lexical-only when no embedding
    backend is available.

Embeddings (marketplace search, agent uploads)

The store's embedding model is overridable via env so deployments with
a compatible backend (vLLM, LiteLLM proxy, Ollama with an embedding
model pulled, Azure OpenAI) can swap models without a code change.
The replacement model must emit 1536-dim vectors — the pgvector
column is declared vector(1536) in schema.prisma and inserts with
any other dim hard-fail.

bash
# Default — OpenAI text-embedding-3-small (1536 dim):
STORE_EMBEDDING_MODEL=text-embedding-3-small

# Example: nomic-embed-text on Ollama emits 768 dims natively, so it
# DOES NOT fit the existing schema — picking it would break every
# publish + reindex. Use a 1536-dim model instead, e.g.
# text-embedding-ada-002 (OpenAI legacy) or one of the LiteLLM proxy's
# 1536-dim shims. Custom-dim support would need a schema migration
# beyond the scope of this guide.

pgvector dimension is fixed in the schema, not configurable at
runtime.
A model that emits a different vector length will succeed
at the embedding call and fail at every subsequent INSERT. If you
need a different dim, you'll need to fork the schema and migrate
existing rows — it's not a runtime knob.

If you don't configure an embedding backend at all, marketplace
hybrid search auto-degrades to lexical-only (no semantic ranking) —
not fatal, just less smart.

Verifying the wiring

After docker compose up -d:

bash
# 1. CHAT_USE_LOCAL is in the live container env
docker exec autogpt_platform-copilot_executor-1 env | grep ^CHAT_
#   CHAT_USE_LOCAL=true
#   CHAT_BASE_URL=http://192.168.1.42:11434/v1
#   ...

# 2. Send a turn from the UI, then confirm baseline routing in the log
docker logs autogpt_platform-copilot_executor-1 | grep -E "Using.*service"
#   [CoPilotExecutor|...] Using baseline service (mode=default)

# 3. Confirm Ollama saw the request — per platform:

# Linux (systemd-managed Ollama):
journalctl -u ollama --since "1 minute ago" | grep "POST"
#   [GIN] ... | 200 | 7.5s |  ... | POST "/v1/chat/completions"

# macOS (brew formula):
tail -F "$(brew --prefix)/var/log/ollama.log" | grep "POST"
# macOS (.app from ollama.com): logs live in ~/.ollama/logs/server.log
tail -F ~/.ollama/logs/server.log | grep "POST"

# Windows: the Ollama tray app writes to %LOCALAPPDATA%\Ollama\server.log
powershell -Command "Get-Content $env:LOCALAPPDATA\Ollama\server.log -Wait | Select-String POST"

If Using baseline service appears and Ollama logs a 200, the
end-to-end path is working — any remaining errors are model / RAM /
quantization concerns rather than config-routing bugs.

Troubleshooting

Frontend shows "The assistant encountered an error" — check the
copilot_executor log for the upstream error. Common causes:

  • model requires more system memory (X GiB) than is available (Y GiB)
    → free RAM (stop ClamAV, raise VM memory) or pick a smaller model
  • model "..." not found → ollama pull <slug> first
  • connection refused → containers can't reach the host on :11434;
    see "Container → host networking" above

api_key is None even though I set OPENAI_API_KEY — by design.
The local transport requires an explicit CHAT_API_KEY so a stray cloud
key set for graphiti / embedders doesn't silently bind to your local
backend as the bearer token.

Title generation fails / returns "Untitled chat" — title_model
should auto-inherit fast_standard_model under the local transport. If
you've explicitly set CHAT_TITLE_MODEL=openai/gpt-4o-mini somewhere,
remove it.

Slow first response — Ollama loads the model into RAM on the first
request, which can take 5-15 s for 8 B models on CPU. Subsequent
requests are much faster while the model stays resident.

Every AutoPilot turn takes minutes on CPU — expected on CPU-only
hosts, not a hang. AutoPilot ships an ~8 k-token system prompt and the
model must prefill (compute KV-cache state for) every token of that
prompt before the first output token is emitted. On 4 CPU cores an
8 B Q4 model prefills at roughly 3-4 tokens/sec, so a fresh turn
takes ~35-45 min just to start generating. Title generation (~70-token
prompt) finishes in seconds because there's almost nothing to prefill.
A consumer GPU brings this down to seconds. If you're CPU-only and
just want to validate the install end-to-end, tail the Ollama server
log (see the per-platform commands in "Verifying the wiring" above)
and watch for the POST /v1/chat/completions line — once it appears
with a 200, prefill finished and the model is generating.

Dream pass + memory under local transport

The graphiti memory layer and the nightly dream pass both ride the
same self-hosted backend CHAT_USE_LOCAL=true points at. Three
things you should know:

Dream pass runs sync-baseline only

The dream pass's batch path (Anthropic batch, OpenAI batch) is
provider-locked and unavailable on local backends. CHAT_USE_LOCAL=true
forces execution_path="sync_baseline" regardless of which API keys
might be set elsewhere on the box — your local LLM handles all three
phases (consolidate / recombine / sanitize) on the same endpoint as
chat. Cost-log rows label provider="ollama" so the admin
platform-costs dashboard distinguishes them from cloud spend.

Memory uses the chat models by default

When CHAT_USE_LOCAL=true, GraphitiConfig._apply_local_graphiti_models
rewrites the cloud OpenAI defaults to local Ollama equivalents:

Setting Cloud default Local default
GRAPHITI_LLM_MODEL gpt-4.1-mini hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M
GRAPHITI_RERANKER_MODEL gpt-4.1-nano hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M
GRAPHITI_EMBEDDER_MODEL text-embedding-3-small nomic-embed-text

The LLM + reranker reuse the same Ornith 1.5 9B model the --with-ollama
installer already pulls for chat, so no extra ollama pull is needed
unless you've overridden them. The embedder is a separate model — see
the next section.

You can pin your own slugs at any time by setting the matching
GRAPHITI_*_MODEL env var; the validator only touches slots still at
their cloud default. A custom slug (qwen3:8b, hf.co/...,
my-registry.io/model:tag) passes through untouched.

Embeddings require an embedding model pulled into Ollama

Ollama doesn't ship an embedding model in its default model set, so
graphiti's per-turn entity extraction will 404 on /v1/embeddings
until you pull one:

bash
ollama pull nomic-embed-text

Without it, chat still works, but graphiti.add_episode(...) fails
silently per turn — the agent loses memory of the conversation
between sessions. With it pulled, graphiti round-trips
embeddings against the same Ollama endpoint as the LLM, and
warm-context retrieval works end-to-end on the local stack.

If you'd rather use a different embedding model (e.g.
mxbai-embed-large for higher recall at higher disk cost), pull
that and set GRAPHITI_EMBEDDER_MODEL=<slug> to override the
local default.

Community rebuild stays on sync tier

graphiti_config.community_rebuild_use_flex_tier=True (the default)
is treated as a request, not a guarantee. OpenAI's flex tier only
delivers the ~50% discount through OpenRouter's pass-through to
OpenAI / Google upstreams, so on local + Anthropic transports the
flex client is silently swapped for the regular OpenAIClient
(logged at INFO). The weekly community rebuild still runs — at full
sync price, which on local Ollama is $0.

Subscription mode caveat

If you also use Claude Code subscription (CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true)
for the chat path, the dream pass needs a separate ANTHROPIC_API_KEY
set in the environment. The Claude Code OAuth token authenticates the
chat CLI only; per Anthropic's Feb-2026 ToS update, OAuth tokens
cannot call the Messages API directly. Without ANTHROPIC_API_KEY,
the dream pass writes an errored JobStatus with a friendly hint
pointing you at this section.

A separate Anthropic Agent-SDK credit pool launches 2026-06-15
($20 Pro / $100 Max-5x / $200 Max-20x at standard API rates,
one-time opt-in). Once that lands, subscription users will have
the option of routing the dream pass through the Agent SDK instead
of the Messages API — coverage tracked as a post-Jun-15 follow-up.


Platform Create Basic Agent (`docs/platform/create-basic-agent.md`)

**Creating a Basic AI Agent with AutoGPT**

**Overview**

This guide walks through creating a simple question-answer AI agent using AutoGPT's visual builder. This is a basic example that can be expanded into more complex agents.

**Prerequisites**

**Cloud-Hosted AutoGPT**

If you're using the cloud-hosted version at agpt.co, you're ready to go! AI blocks come with built-in credits — no API keys required to get started. If you'd prefer to use your own API keys, you can add them via Profile → Integrations.

**Self-Hosted (Docker)**

If you're running AutoGPT locally with Docker, you'll need to add your own API keys to autogpt_platform/backend/.env:

bash
# Create or edit backend/.env
OPENAI_API_KEY=sk-your-key-here
ANTHROPIC_API_KEY=sk-ant-your-key-here
# Add other provider keys as needed

After adding keys, restart the services:

bash
docker compose down && docker compose up -d

Note: The Calculator example below doesn't require any API credentials — it's a good way to test your setup before adding AI blocks.

**Example Agent: Q&A (with AI)**

A step-by-step guide to creating a simple Q&A agent using input and output blocks.

**Components Required**

  1. Input Block
  2. AI Text Generator Block
  3. Output Block

**Step-by-Step Instructions**

**1. Set Up the Basic Structure**

  1. Add an Input Block
  2. Add an AI Text Generator Block
  3. Add an Output Block

**2. Connect the Components**

  1. Connect the Input Block to the AI Text Generator's Prompt
  2. Connect the AI Text Generator's response to the Output Block's value

**3. Configure Block Names**

  • Name the Input Block: "question"
  • Name the Output Block: "answer"

**4. Save the Agent**

  1. Click the Save button
  2. Name your agent (e.g., "question and answer")

**5. Test the Agent**

  1. Click the Run button
  2. Enter a question in the input field (e.g., "How far is Earth from Pluto?")
  3. View the results through either:
    • The "View More" option
    • The "Agent Outputs" section

**Viewing Results**

You can access the AI's response in two ways:

  • Click "View More" for detailed results
  • Check the "Agent Outputs" section for the response

**Example Agent: Calculator (without AI)**

A step-by-step guide to creating a simple calculator agent using input and output blocks.

##Components Required

  • Input Block (2 instances)
  • Calculator Block
  • Output Block

**Setup Instructions**

**1. Adding Input Blocks**

  1. Add two input blocks, positioned one below the other
  2. Name the first input block "a"
  3. Name the second input block "b"

**2. Adding Calculator Block**

  1. Add a calculator block to your workspace
  2. Position it after the input blocks

**3. Adding Output Block**

  1. Add an output block
  2. Name it "results"

**4. Connecting the Blocks**

  1. Connect the "a" input block to the "a" input on the calculator block
  2. Connect the "b" input block to the "b" input on the calculator block
  3. Connect the calculator block's result to the value input of the output block

**5. Saving Your Agent**

  1. Name your agent (e.g., "Simple Calculate Agent")
  2. Save the configuration

**Testing Your Calculator**

**Running Calculations**

  1. Enter values in both input blocks
  2. Select the desired operation (e.g., multiply)
  3. Click the "Run" button to execute the calculation

**Viewing Results**

There are two ways to view the calculation results:

  1. Click "View More" to see the detailed output
  2. Check the "Agent Outputs" section, which displays results from the output block

**Example Calculation**

  • Input A: 227
  • Input B: 17
  • Operation: Multiply
  • Expected output will be shown in the results section

**Tips**

  • Always save your agent after making changes
  • Verify all connections are properly made before running
  • Use the "View More" option for detailed output information

**Note**

While these are basic example, you can enhance the agent with additional blocks and functionality to create more complex interactions.


Platform Credits And Billing (`docs/platform/credits-and-billing.md`)

Credits & Billing

Overview

The AutoGPT Platform uses automation credits for metered agent execution. This guide explains how usage is priced, how to review costs, and how to manage your balance.

{% hint style="info" %}
Plans and pricing can change. See the pricing page for current details.
{% endhint %}

How Credits Work

Credits are charged when blocks run. Depending on the block, the final charge may be fixed or based on actual usage, such as tokens, provider cost, elapsed time, data size, or number of items. For usage-based blocks, the platform may estimate a charge before execution and reconcile it against actual usage afterward.

Block Pricing

Block pricing varies by block, configuration, and usage. Review the estimate shown in the platform before running an agent; the final charge for usage-based blocks may differ from the estimate.

Your total cost depends on your plan and automation-credit usage. See the pricing page for current subscription and included-credit details.

Checking Your Balance

Your balance is available from the automation-credits wallet in the platform navigation and from Billing settings.

Viewing Task Costs

To review the cost of a specific agent run:

  1. Go to your Agent Library
  2. Open the agent
  3. Click on a completed task in the left-hand pane
  4. The task detail header displays Cost in dollars.

{% hint style="info" %}
Use the Automation Credits section in Billing settings for account-level usage and transaction history. Individual task costs are also available within each agent.
{% endhint %}

Running Out of Credits

If your automation-credit balance is exhausted, paid blocks cannot start until credits are added. Scheduled tasks may fail for insufficient balance. Final reconciliation for a usage-based block that already ran can leave the balance below zero.

You can add credits manually or configure auto-refill from Billing settings.

Adding Credits

  1. Open Settings from your profile menu.
  2. Select Billing.
  3. Open the Automation Credits tab.
  4. Select Add credits and follow the checkout prompts.

You can also go directly to Billing settings.


Platform D Id (`docs/platform/d_id.md`)

Find available voices for D-ID

  1. ElevenLabs

  2. Microsoft Azure Voices

    • Select any voice from the voice gallery: https://speech.microsoft.com/portal/voicegallery
    • Click on the "Sample code" tab on the right
    • Copy the voice name, for example: config.SpeechSynthesisVoiceName ="en-GB-AbbiNeural"
    • Use this string en-GB-AbbiNeural in the voice_id field in the CreateTalkingAvatarClip Block
  3. Amazon Polly Voices


Platform Data Flow And Execution (`docs/platform/data-flow-and-execution.md`)

Data Flow & Execution

Overview

Understanding how agents execute is key to building effective workflows. This guide explains how data flows through an agent, what determines execution order, and how to work with lists and errors.

Execution Order

Agent execution follows the graph's starting nodes, data connections, configured values, and input validation.

How It Works

  1. Execution starts from every starting node: an Agent Input block or any executable block with no inbound links.
  2. A downstream block is queued when its required inputs validate after upstream outputs and configured values are applied.
  3. Execution continues until no more downstream blocks can be queued; conditional branches and skipped nodes mean not every block necessarily runs.
  4. Output blocks collect any results that reach them.

Required Inputs

A block will only execute when:

  • All connected input pins have received data from their upstream blocks
  • All required input pins have values — either from a connection or from a hardcoded value set directly on the block

This means you can have blocks that don't depend on each other execute in any order, while blocks that depend on the output of another block will always wait.

Working with Pins

Pin Types

Input and output pins are typed. Common types include:

  • Text: String values
  • Number: Numeric values
  • File: File uploads or downloads
  • List: Arrays of items
  • Boolean: True/false values
  • Object: Structured data

Connections can only be made between compatible pin types.

Data Flow Visualisation

When an agent is running, you can see data moving through the workflow in real time. Data flow is represented by a coloured bead that slides along each connection line from the output pin to the input pin, giving you a clear visual of what's happening.

Working with Lists

Blocks can handle list data in flexible ways:

  • Outputting lists: Some blocks produce a list of items as their output. You can choose to receive the full list as a single output or receive individual items one at a time.
  • Iterating over lists: You can send a list into a block that iterates through its contents, yielding each item one by one. This is useful for processing each item in a list independently.

This makes it straightforward to build agents that process batches of data — for example, fetching a list of URLs and then processing each one through an AI block.

Error Handling

When a block fails during execution, it does not automatically stop the entire agent. Instead:

  1. The failed block produces data on its error pin
  2. What happens next depends on how you've wired the agent

Handling Errors Gracefully

You have full control over error handling through the block connections:

  • Surface the error: Connect the error pin to an output block to return the error as part of the agent's result. This is useful for debugging or when you want users to see what went wrong.
  • Handle and continue: Connect the error pin to other blocks that provide fallback behaviour. For example, retry with different settings, use a default value, or route to an alternative workflow path.
  • Ignore the error: If the error pin is not connected, the error data is simply not propagated. Downstream blocks that depend on the failed block's normal output pins will not execute (since their inputs won't be satisfied).

{% hint style="info" %}
Building robust agents means thinking about what happens when things go wrong. Consider connecting error pins to output blocks during development so you can see any issues, then add proper error handling once your agent is working.
{% endhint %}

Execution Summary

Concept How It Works
Execution order Determined by starting nodes, data connections, configured values, and input validation
Starting point Agent Input blocks and executable blocks with no inbound links
Ending point Output blocks collect final results
Parallel execution Blocks with no dependencies on each other can execute in any order
Error handling Failed blocks yield data on their error pin — you decide what to do with it
Lists Can be processed as a whole or iterated item by item
Visual feedback Coloured beads slide along connection lines during execution

Platform Delete Agent (`docs/platform/delete-agent.md`)

**How to Delete an Agent in AutoGPT**

**Overview**

Is your AutoGPT Monitor getting cluttered or did you create an agent you no longer see? Learn how to quickly delete unwanted agents.

**Steps to Delete an Agent**

  1. Locate the Agent
    • Navigate to the Monitor Tab in the AutoGPT builder
    • Find the agent you want to delete from the list
  2. Select the Agent
    • Click on the agent you wish to delete
  3. Delete the Agent
    • Look for the trash icon on the right side of the interface
    • Click the trash icon
    • A confirmation dialog will appear asking "Are you sure you want to delete this agent?"
    • Click "Yes, delete" to confirm

Once confirmed, the agent will be immediately removed from your list.

Note: This action cannot be undone, so make sure you want to permanently delete the agent before confirming.


Platform Download Agent From Marketplace Local (`docs/platform/download-agent-from-marketplace-local.md`)

**How to Download and Import an Agent from the AutoGPT Marketplace (Local Hosting)**

**Overview**

This guide explains how to download an agent from the AutoGPT marketplace and import it into your locally hosted instance.

**Prerequisites**

  • A local installation of AutoGPT
  • Access to the marketplace for local hosters

**Step-by-Step Process**

**1. Access the Marketplace**

  • Navigate to the marketplace section
    • Note: The interface will differ from the cloud-hosted platform

**2. Download the Agent**

  1. Browse and select your desired agent
  2. Click on the agent to view details
  3. Look for and click the "Download Agent" button
  4. The agent file will save to your computer

**3. Import the Agent**

  1. Return to the Monitor tab
  2. Locate the dropdown menu next to "Create"
  3. Select "Import from File"
  4. In the file selection dialog:
    • Navigate to your downloaded agent file
    • Select the file
    • Click "Open"
  5. Click "Import and Edit"

**4. Verify Import**

  • The agent should now appear in your Builder
  • You can begin editing or using the imported agent

**Important Notes**

  • The marketplace interface differs between local hosting and cloud-hosted versions
  • Ensure you're following the local hosting specific steps
  • The agent will be available in your Builder after successful import

Platform Edit Agent (`docs/platform/edit-agent.md`)

**How to Edit an Agent in AutoGPT**

**Overview**

This guide explains how to access and edit existing agents in AutoGPT, whether they're your own creations or downloaded from the marketplace.

**Steps to Edit an Agent**

**1. Access the Monitor Tab**

  • Navigate to the Monitor Tab in the AutoGPT builder
  • You'll see a list of all your agents, including:
    • Agents you've created
    • Agents downloaded from the marketplace

**2. Select the Agent**

  • Locate the agent you want to edit from the list
  • Click on the agent name to select it

**3. Enter Edit Mode**

  • Look for the pencil icon next to the selected agent
  • Click the pencil icon to open the editor

**4. Modify the Agent**

  • The agent will load in the editor with all its existing components
  • Make your desired modifications to the agent's configuration

**5. Save Changes**

  • After making your modifications, save the updated agent

**Note**

  • All agents can be edited, regardless of whether they were created by you or downloaded from the marketplace
  • Changes are saved to your local instance

Platform Gemini (`docs/platform/gemini.md`)

Using Google Gemini with AutoGPT

This guide covers integrating Google Gemini models with AutoGPT using OpenRouter.


Prerequisites

  1. Make sure you have completed the AutoGPT Setup Guide and have AutoGPT running locally at http://localhost:3000.
  2. You have an OpenRouter API key from OpenRouter.

Getting Your API Key

AutoGPT routes all Gemini models through OpenRouter. You need an OpenRouter API key:

  1. Visit OpenRouter Keys
  2. Sign in or create an account
  3. Click "Create Key"
  4. Copy the generated key for use in AutoGPT

Setup Steps

1. Start AutoGPT Locally

Follow the official guide:
AutoGPT Getting Started Guide

Ensure AutoGPT is running and accessible at:
http://localhost:3000

2. Open the Visual Builder

Open your browser and navigate to:
http://localhost:3000/build

Or click "Build" in the navigation bar.

3. Add an AI Text Generator Block

  1. Click the "Blocks" button on the left sidebar.
  2. In the search bar, type AI Text Generator.
  3. Drag the block into the canvas.

4. Select a Gemini Model

Click the AI Text Generator block to configure it.

In the LLM Model dropdown, select one of the available Gemini models:

Model Model ID
Gemini 3.1 Pro Preview google/gemini-3.1-pro-preview
Gemini 3.1 Flash Lite Preview google/gemini-3.1-flash-lite-preview
Gemini 3 Flash Preview google/gemini-3-flash-preview
Gemini 2.5 Pro google/gemini-2.5-pro
Gemini 2.5 Flash google/gemini-2.5-flash
Gemini 2.5 Flash Lite google/gemini-2.5-flash-lite
Gemini 2.0 Flash 001 google/gemini-2.0-flash-001
Gemini 2.0 Flash Lite 001 google/gemini-2.0-flash-lite-001

Select the models prefixed with google/ in the dropdown.

5. Configure Your Credentials

Inside the AI Text Generator block:

  1. API Key: Enter your OpenRouter API key
  2. Prompt: Enter your desired prompt text

Get your API key from:
https://openrouter.ai/keys

Save your API key as a credential in AutoGPT for easy reuse across multiple blocks.

6. Save Your Agent

Click the "Save" button at the top-right of the builder interface:

  1. Give your agent a descriptive name (e.g., gemini_research_agent)
  2. Click "Save Agent" to confirm

7. Run Your Agent

From the workspace:

  1. Click "Run" next to your saved agent
  2. The request will be sent to the selected Gemini model

8. View the Output

  1. Scroll to the AI Text Generator block
  2. Check the Output panel below it
  3. Copy, export, or pass the result to additional blocks

Gemini-Specific Features

Multimodal Capabilities

Gemini models support multiple input types:

  • Text: Standard text prompts and completions
  • Images: Upload and analyze images
  • Code: Programming and technical reasoning
  • Long Context: Large context windows for document analysis

Expand Your Agent

Enhance your workflow with additional blocks:

  • Tools – Fetch URLs, call APIs, scrape data
  • Memory – Retain context across interactions
  • Document Processing – Analyze PDFs, text files
  • Web Search – Combine with real-time information
  • Chains – Create multi-step reasoning pipelines

Pricing

Gemini models are priced through OpenRouter. Check current rates at:
OpenRouter Google Models

Pricing varies by model tier and usage volume.


Troubleshooting

API Key Issues

  • Ensure you're using an OpenRouter API key, not a Google AI Studio key
  • Verify the key has sufficient credits
  • Check that the key is entered correctly without extra spaces

Model Not Available

  • Gemini models are accessed through OpenRouter
  • Ensure you've selected a model with the google/ prefix in the dropdown

Rate Limiting

  • Free tier has request limits per minute
  • Upgrade to paid tier for production usage
  • Consider using google/gemini-2.5-flash-lite for lower-cost, high-volume tasks

Context Length Errors

  • Each Gemini model has a maximum context window
  • Split large tasks across multiple blocks for very long documents

Additional Resources


You are now set up to use Google Gemini models in AutoGPT.


Platform Getting Started Cloud (`docs/platform/getting-started-cloud.md`)

Getting Started with AutoGPT (Cloud)

Introduction

This guide will get you up and running on the hosted AutoGPT Platform at platform.agpt.co. No local installation or Docker setup is required — just sign up and start building.

{% hint style="info" %}
Looking to self-host instead? See the Self-Hosting Guide.
{% endhint %}

Creating Your Account

  1. Navigate to platform.agpt.co
  2. Click Sign Up and create your account
  3. Once signed in, you'll land on the AutoPilot home screen

That's it — you're ready to go. Available credits and managed AI providers depend on your plan and account configuration. If a block requires credentials that are not included, the platform will prompt you to connect your own. See the pricing page for current plan details.

Platform Navigation

The AutoGPT Platform has four main areas, accessible from the main navigation:

Nav Button URL Description
Home platform.agpt.co AutoPilot — your AI assistant for the platform
Build platform.agpt.co/build Agent Builder — visual editor for creating agents
Agents platform.agpt.co/library Your Agent Library — all your saved agents
Marketplace platform.agpt.co/marketplace Community marketplace for discovering agents

Use the automation-credits wallet or Billing settings to review your balance. Account settings and integrations are available from Settings.

What to Do First

Here are three great ways to get started:

Option 1: Chat with AutoPilot

Click Home in the nav bar to open AutoPilot. You can ask it to perform supported actions such as browsing the marketplace, running agents, building agents, generating images, and conducting research. It's the fastest way to experience the platform.

Option 2: Add an Agent from the Marketplace

  1. Click Marketplace in the nav bar
  2. Browse or search for an agent that interests you
  3. Click on an agent to view its details
  4. Click Add to Library
  5. Navigate to Agents to find it in your library
  6. Click on the agent and press New agent task to run it

Option 3: Build Your Own Agent

  1. Click Build in the nav bar to open the Agent Builder
  2. Open the Blocks menu on the left-hand side
  3. Add an Input Block, an AI Text Generator Block, and an Output Block
  4. Connect them together by dragging between their pins
  5. Press Ctrl+S (Windows/Linux) or Cmd+S (macOS), or click the save button to save your agent
  6. Navigate to Agents to find and run it

For a detailed walkthrough, see Creating a Basic Agent.

Key Concepts

Before diving deeper, here are the core concepts you'll encounter:

  • Agent: An automated workflow you design to perform specific tasks. Agents are made up of connected blocks.
  • Block: A single action within an agent — such as generating text with AI, sending an email, or looking up data. The available block catalog depends on the platform deployment.
  • Task: A single execution of an agent. When you run an agent with a set of inputs, that creates a task. You can view the task's inputs, outputs, and reported cost.
  • AutoPilot: Your AI assistant that can perform supported platform actions through natural conversation.
  • Marketplace: A public library of community-built agents you can add to your own library.
  • Credits: The currency used for metered block execution. Pricing varies by block and usage; review your balance from the automation-credits wallet or Billing settings.

Next Steps

Guide Description
AutoPilot Learn what AutoPilot can do for you
Agent Builder Guide Master the visual agent builder
Agent Library Manage your agents, tasks, and schedules
Marketplace Discover and share agents
Credits & Billing Understand the credit system

Platform Installer (`docs/platform/installer.md`)

AutoGPT Platform Release Installer

The release installer runs the published AutoGPT Platform appliance as one
Docker container. It does not clone the repository, build AutoGPT, install
Docker, elevate privileges, or accept Docker Desktop license terms.

The appliance exposes one loopback-only port. Application state and generated
secrets live in a named Docker volume; installer identity and runtime
configuration live in a dedicated private state directory.

Prerequisites

Before running the installer:

  • Install and start Docker from the
    official Docker Engine or
    Docker Desktop documentation.
  • Select a local unix:// Docker endpoint on Linux or macOS. Remote and
    non-Unix contexts are rejected.
  • Configure the daemon for Linux containers on amd64 or arm64.
  • Allow about 25 GB of free disk; at least 8 GB RAM is recommended.

Docker Desktop is a separate product with its own license terms. AutoGPT does
not install it or grant a Docker license.

Install (After the Release Gates Pass)

[!WARNING]
Do not use the hosted command yet. setup.agpt.co/install.sh still serves
the legacy Compose installer, and the appliance image tags are not public.
Maintainers must complete every
release gate before exposing these commands in
the README or getting-started guide.

This first appliance-installer release supports Linux and macOS. Windows is
intentionally withheld until its standard-user filesystem checks and native
Docker argument handling are validated; use the manual setup guide there.

The command below uses a unique temporary file, executes it only after the HTTPS
download succeeds, and removes it afterward. Do not stream a network response
directly into a shell.

Linux and macOS

bash
(
  installer="$(mktemp)" &&
  trap 'rm -f "$installer"' EXIT &&
  curl --proto '=https' --proto-redir '=https' --tlsv1.2 -fsSL \
    -o "$installer" https://setup.agpt.co/install.sh &&
  bash "$installer"
)

Options

Goal Linux/macOS
Current published appliance (no flag)
Specific published version --release=vX.Y.Z
Appliance release-tag form --release=autogpt-platform-beta-vX.Y.Z
Hardware and Docker checks only --preflight-only
Print image selection only --resolve-only
Skip RAM/disk checks --skip-preflight
Custom private state directory --dir=PATH

--skip-preflight does not skip the local-endpoint, Linux-container, or
architecture checks.

The default state directory is $XDG_CONFIG_HOME/autogpt, or
$HOME/.config/autogpt when XDG_CONFIG_HOME is unset. A custom directory
must be user-owned and private; the installer rejects symlink paths and shared
or unsafe locations.

Artifact and Runtime Contract

With no release flag, the installer pulls
significantgravitas/autogpt:latest. An explicit vX.Y.Z selects the matching
version tag. After pulling, the installer:

  1. Requires the expected OCI title and source repository plus a well-formed
    40-hex source revision label.
  2. Requires a Linux image matching the local daemon's amd64 or arm64
    architecture.
  3. Resolves exactly one native RepoDigest and runs that immutable digest,
    never the mutable tag.
  4. Creates an installer-owned, labelled autogpt-platform-data volume.
  5. Starts autogpt with 127.0.0.1:3000, the private environment file,
    restart and stop policies, log rotation, shared memory, and file-descriptor
    limits.

The bootstrap authenticates transport with HTTPS, and the installer pins the
pulled image by digest after checking its appliance metadata. It does not
independently verify a publisher signature or protect against compromise of
the image-publishing credentials. Signed release artifacts are a follow-up,
not a claim made by this installer.

The installer refuses to adopt an unlabelled container or volume. A rerun is a
no-op, or starts a stopped container, only when the installer identity,
environment hash, immutable image digest, process, port, mount, privilege,
restart, logging, memory, and limit settings all match. Drift fails closed.

If established installer lifecycle state exists but its named volume is
missing, the installer refuses to create an empty replacement. An interrupted
first pull has no established marker and can be retried safely. Restore a
previously established volume or choose a new state directory.

Runtime Configuration

The private autogpt.env initially contains:

dotenv
AUTOGPT_PUBLIC_URL=http://localhost:3000

Add provider keys and other appliance variables there. The installer binds the
environment file's SHA-256 hash to the container label. To apply a deliberate
configuration change, stop and remove only the container, then rerun the same
installer release:

bash
docker stop --time 360 autogpt
docker rm autogpt

Do not remove autogpt-platform-data; it contains accounts, agents, generated
secrets, and application state.

First Run

After the installer reports a healthy container, open
http://localhost:3000. Registration starts open so
you can create the intended account. Promote it:

bash
docker exec autogpt autogpt-admin promote [email protected]

Then set this in autogpt.env, recreate the container as described above, and
verify registration is closed:

dotenv
AUTH_ALLOW_NEW_ACCOUNTS=false

Keep the default loopback binding. To serve other machines, place AutoGPT
behind a TLS reverse proxy and set AUTOGPT_PUBLIC_URL to the exact public URL.

Upgrades

Before an upgrade, back up autogpt-platform-data. Stop and remove only the
autogpt container, then rerun the installer with the intended release. The
installer will reuse the named volume only when its ownership labels and the
private installer identity still match.

Maintainer Release Gates

Repository CI cannot prove the public bootstrap handoff. Do not publish or
announce the installation commands until every external gate below passes:

  • Deploy the new repository install.sh through the Caddy or object-storage
    configuration behind setup.agpt.co. The endpoint must no longer serve the
    legacy clone/Compose installer.

  • Publish significantgravitas/autogpt:vX.Y.Z and :latest only after the
    multi-architecture workflow smoke-tests and scans both runnable images.

  • Ensure the appliance release's tag commit contains the full installer,
    publication workflow, and helper, then verify the release-triggered run.
    Manual development dispatches publish SHA artifacts, not the release channel
    used by this installer.

  • Verify both public tags expose Linux amd64 and arm64 manifests and the
    expected OCI identity:

    bash
    docker buildx imagetools inspect significantgravitas/autogpt:vX.Y.Z
    docker buildx imagetools inspect significantgravitas/autogpt:latest
  • Fetch the hosted installer into a new temporary file from an external
    machine and compare it with the released repository file. On each supported
    operating system, perform a clean install against the public image, wait for
    health, verify the loopback endpoint, rerun to prove exact-contract
    idempotency, and confirm no mutable tag was used to create the container.

Installing from source (Compose)

The scripts below are the other supported path: they clone the repository and
start every service with Docker Compose. Use them on Windows, for development,
or for a fully offline install with a local LLM. The appliance described above
is the image documented in
Run AutoGPT in one Docker container; these scripts do
not use it.

Manual Installation

If you prefer, you can manually download and run the installer scripts:

  • Linux/macOS: setup-autogpt.sh
  • Windows: setup-autogpt.bat

These scripts are located in the autogpt_platform/installer/ directory.

Both create the three .env files if they are missing and generate the
secrets .env.default leaves blank (ENCRYPTION_KEY,
UNSUBSCRIBE_SECRET_KEY, BETTER_AUTH_SECRET), the same as make init-env.
Running a script again never changes a value that is already set. If you are
upgrading an install that ran without its own ENCRYPTION_KEY, read
Upgrading: secrets are generated per install
first so your connected integrations move to the new key.

Running fully offline with a local LLM (Ollama)

Both installer scripts accept an opt-in flag that installs
Ollama, pulls a default chat model, and wires
backend/.env so AutoPilot runs without any cloud API keys. This
is useful for air-gapped or privacy-sensitive deployments — see
Running AutoPilot on a self-hosted LLM for the
full reference.

Linux / macOS

bash
cd autogpt_platform/installer
./setup-autogpt.sh --with-ollama
# Optional overrides:
#   --ollama-model=qwen3:14b-instruct-q4_K_M
#   --ollama-host=http://gpu-rig.lab:11434   # use an existing Ollama

Windows

cmd
cd autogpt_platform\installer
setup-autogpt.bat /with-ollama
REM Optional overrides:
REM   /ollama-model=qwen3:14b-instruct-q4_K_M
REM   /ollama-host=http://gpu-rig.lab:11434

The installer:

  1. Installs Ollama (skipped if already present, or if --ollama-host points at an existing one).
  2. Configures OLLAMA_HOST=0.0.0.0:11434 + OLLAMA_CONTEXT_LENGTH=32768 so containers can reach it and so AutoPilot's ~8 k system prompt isn't truncated by Ollama's 4 k default.
  3. Pulls the chat model (default hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M).
  4. Appends a marker-bounded block to autogpt_platform/backend/.env with CHAT_USE_LOCAL=true plus the CHAT_BASE_URL / CHAT_API_KEY / CHAT_*_MODEL overrides.

Re-running with --with-ollama is idempotent — the wiring block is rewritten in place.

Troubleshooting

  1. Confirm docker info succeeds against a local Linux-container daemon.
  2. If an image pull fails just after release, wait for public manifest
    publication and retry; the installer never falls back to a source build.
  3. If the installer reports state or runtime drift, preserve the named volume,
    inspect the existing container, and follow the explicit upgrade procedure.
  4. Check docker logs --tail 100 autogpt when health checks fail.

Platform Integrations And Credentials (`docs/platform/integrations-and-credentials.md`)

Integrations & Credentials

Overview

Many blocks on the AutoGPT Platform integrate with external services like Google, GitHub, Linear, Twitter, and more. These integrations require credentials — such as OAuth connections, API keys, or username/password pairs — to access your accounts on those services.

This guide explains how credentials work on the platform, how to add them, and how to manage them.

How Credentials Work

Platform-Provided Credentials

On the cloud-hosted platform at platform.agpt.co, some plans and account configurations include managed AI providers. If a block requires credentials that are not included, the platform will prompt you to connect your own. See the pricing page for current plan details.

User-Provided Credentials

For services tied to your personal accounts, connect your own credentials. Credentials can be reused across agents, but you choose which compatible credential an agent or task uses. Managed credentials may be selected automatically when available.

Adding Credentials

You can connect credentials centrally from Settings → Integrations → Connect Service, or in context when a builder block or task asks for one.

When Building an Agent

If a block needs credentials, select an existing compatible credential or connect a new one from the block.

When Running an Agent

Credential fields show the credentials that task will use. Select an existing compatible credential or connect a new one.

Credential Types

Depending on the service, you'll be prompted to authenticate in one of three ways:

Type Description Example Services
OAuth Click to authorise via the service's login page Google, GitHub, Twitter
API Key Paste your API key from the service's dashboard Linear, OpenAI
Username & Password Enter your account credentials Varies by service

{% hint style="info" %}
Credentials are reusable, but each block or task retains its selected credential reference.
{% endhint %}

Connecting MCP Services

Open Settings → Integrations, find a service with the MCP badge, and follow its setup instructions. The same services appear in Connect Service.

  • Choose the offered sign-in or token option for account access, or No sign-in → Check connection for public tools.
  • If prompted, select your account's region or enter the official server URL for your tenant or deployment.
  • Review the requested permissions. Allow changes, when offered, adds the service's optional actions.

For catalog updates, see Maintaining MCP Integrations.

Managing Credentials

  1. Open Settings.
  2. Select Integrations.
  3. Use Connect Service to add a credential, or manage/remove credentials already listed.

URL: platform.agpt.co/settings/integrations

{% hint style="warning" %}
Removing a credential can break agents, workflows, or active webhooks that reference it. Review the dependency warning before confirming removal.
{% endhint %}

Self-Hosted Credentials

When self-hosting, configure deployment-level provider keys and OAuth application credentials in autogpt_platform/backend/.env, then connect end-user accounts from Settings → Integrations as needed. See the Self-Hosting Guide for details.


Platform Marketplace (`docs/platform/marketplace.md`)

Marketplace

Overview

The AutoGPT Marketplace is a public library of agents created by the community and by the AutoGPT team. You can discover agents for a wide range of use cases, add them to your library with one click, and even publish your own agents for others to use.

URL: platform.agpt.co/marketplace

Access: Click Marketplace in the navigation bar.

Browsing the Marketplace

The marketplace allows you to:

  • Search for agents by name or keyword
  • Browse by category to explore different use cases
  • View agent details by clicking on any agent

Agent Detail Page

Each agent in the marketplace has a dedicated page that includes:

  • Title and description of what the agent does
  • Creator information — who built the agent and a link to their other agents
  • Video (when available) — a demonstration of the agent in action
  • Output showcase (when available) — examples of the agent's results
  • Instructions — guidance on how to run the agent and what to expect

Adding an Agent to Your Library

When you find an agent you want to use:

  1. Click on the agent to open its marketplace page
  2. Click the Add to Library button

The agent appears in your Agent Library, where you can run or schedule it. Marketplace agents open read-only; duplicate one before editing.

{% hint style="info" %}
If you already have the agent in your library, the button changes to See Runs, which takes you directly to that agent in your library.
{% endhint %}

Download for Self-Hosting

Marketplace detail pages also provide Download here under “Want to use this agent locally?” Download the agent JSON and import it into your local instance. See Download & Import an Agent for details.

Publishing an Agent

You can publish your own agents to the marketplace for the community to discover and use.

How to Publish

  1. Open the agent in the Builder and select Publish to Marketplace, or open Settings → Creator Dashboard and select Publish Agent
  2. Choose the agent and version you want to publish if prompted
  3. Fill out the publishing form:
Field Description
Title The name of your agent as it will appear in the marketplace
Subheader A short tagline for your agent
Slug A URL-friendly identifier (e.g., resume-rewriter)
Thumbnail Images Upload images — the first image becomes the marketplace thumbnail
YouTube Video Link Optional — a video demonstrating your agent
Output demo Optional — a URL showing example output the agent produces
Category Select the most relevant category for your agent
Description A detailed explanation of what your agent does
Instructions Explain to users how to run this agent and what to expect
Recommended Schedule Suggest when users should run this agent for best results
  1. Click Submit for review

Review Process

After submission, a member of the AutoGPT team will review your agent against curation standards. If approved, your agent will be published to the marketplace and visible to all users.

Editing Marketplace Agents

An agent added from the marketplace is read-only. Open it in the builder and click Duplicate to create an editable fork in your library. Changes to that fork do not modify the original marketplace listing.


Platform New Blocks (`docs/platform/new_blocks.md`)

Contributing to AutoGPT Agent Server: Creating and Testing Blocks

This guide will walk you through the process of creating and testing a new block for the AutoGPT Agent Server, using the WikipediaSummaryBlock as an example.

!!! tip "New SDK-Based Approach"
For a more comprehensive guide using the new SDK pattern with ProviderBuilder and advanced features like OAuth and webhooks, see the Block SDK Guide.

Understanding Blocks and Testing

Blocks are reusable components that can be connected to form a graph representing an agent's behavior. Each block has inputs, outputs, and a specific function. Proper testing is crucial to ensure blocks work correctly and consistently.

Block Auto-Discovery

All block modules are automatically discovered and imported at startup. The block loader in backend/blocks/__init__.py uses Path(__file__).parent.rglob("*.py") to find every .py file in the blocks/ directory tree. Files named __init__.py and those starting with test_ are excluded.

This means:

  • You do not need to manually register your block anywhere — just create the file and your Block subclass will be picked up automatically.
  • Any module-level side effects (e.g., global registrations, print statements) in your block files will execute on import. Keep block packages clean and avoid side-effect-heavy code at module scope.
  • Block auto-discovery does not replace provider setup. For a new API-key integration, follow the provider auth setup below: add the provider name, credentials, display metadata, and integration icon. Frontend provider data is loaded dynamically; no manual frontend provider registration is needed.

Creating and Testing a New Block

Follow these steps to create and test a new block:

  1. Create a new Python file for your block in the autogpt_platform/backend/backend/blocks directory. Name it descriptively and use snake_case. For example: get_wikipedia_summary.py.

  2. Import necessary modules and create a class that inherits from Block. Make sure to include all necessary imports for your block.

    Every block should contain the following:

    python
    from backend.blocks._base import Block, BlockSchemaInput, BlockSchemaOutput, BlockOutput

    Example for the Wikipedia summary block:

    python
    from backend.blocks._base import Block, BlockSchemaInput, BlockSchemaOutput, BlockOutput
    from backend.utils.get_request import GetRequest
    import requests
    
    class WikipediaSummaryBlock(Block, GetRequest):
        # Block implementation will go here
  3. Define the input and output schemas using BlockSchema. These schemas specify the data structure that the block expects to receive (input) and produce (output).

    • The input schema defines the structure of the data the block will process. Each field in the schema represents a required piece of input data.
    • The output schema defines the structure of the data the block will return after processing. Each field in the schema represents a piece of output data.

    Example:

    python
    class Input(BlockSchemaInput):
        topic: str  # The topic to get the Wikipedia summary for
    
    class Output(BlockSchemaOutput):
        summary: str  # The summary of the topic from Wikipedia
  4. Implement the __init__ method, including test data and mocks:

    !!! important
    Use UUID generator (e.g. https://www.uuidgenerator.net/) for every new block id and do not make up your own. Alternatively, you can run this python code to generate an uuid: print(__import__('uuid').uuid4())

    python
    def __init__(self):
        super().__init__(
            # Unique ID for the block, used across users for templates
            # If you are an AI leave it as is or change to "generate-proper-uuid"
            id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            input_schema=WikipediaSummaryBlock.Input,  # Assign input schema
            output_schema=WikipediaSummaryBlock.Output,  # Assign output schema
    
                # Provide sample input, output and test mock for testing the block
    
            test_input={"topic": "Artificial Intelligence"},
            test_output=("summary", "summary content"),
            test_mock={"get_request": lambda url, json: {"extract": "summary content"}},
        )
    • id: A unique identifier for the block.

    • input_schema and output_schema: Define the structure of the input and output data.

    Let's break down the testing components:

    • test_input: This is a sample input that will be used to test the block. It should be a valid input according to your Input schema.

    • test_output: This is the expected output when running the block with the test_input. It should match your Output schema. For non-deterministic outputs or when you only want to assert the type, you can use Python types instead of specific values. In this example, ("summary", str) asserts that the output key is "summary" and its value is a string.

    • test_mock: This is crucial for blocks that make network calls. It provides a mock function that replaces the actual network call during testing.

    In this case, we're mocking the get_request method to always return a dictionary with an 'extract' key, simulating a successful API response. This allows us to test the block's logic without making actual network requests, which could be slow, unreliable, or rate-limited.

  5. Implement the run method with error handling. This should contain the main logic of the block:

    python
    def run(self, input_data: Input, **kwargs) -> BlockOutput:
        try:
            topic = input_data.topic
            url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{topic}"
    
            response = self.get_request(url, json=True)
            yield "summary", response['extract']
    
        except requests.exceptions.HTTPError as http_err:
            raise RuntimeError(f"HTTP error occurred: {http_err}")
    • Try block: Contains the main logic to fetch and process the Wikipedia summary.
    • API request: Send a GET request to the Wikipedia API.
    • Error handling: Handle various exceptions that might occur during the API request and data processing. We don't need to catch all exceptions, only the ones we expect and can handle. The uncaught exceptions will be automatically yielded as error in the output. Any block that raises an exception (or yields an error output) will be marked as failed. Prefer raising exceptions over yielding error, as it will stop the execution immediately.
    • Yield: Use yield to output the results. Prefer to output one result object at a time. If you are calling a function that returns a list, you can yield each item in the list separately. You can also yield the whole list as well, but do both rather than yielding the list. For example: If you were writing a block that outputs emails, you'd yield each email as a separate result object, but you could also yield the whole list as an additional single result object. Yielding output named error will break the execution right away and mark the block execution as failed.
    • kwargs: The kwargs parameter is used to pass additional arguments to the block. It is not used in the example above, but it is available to the block. You can also have args as inline signatures in the run method ala def run(self, input_data: Input, *, user_id: str, **kwargs) -> BlockOutput:.
      Available kwargs are:
      • user_id: The ID of the user running the block.
      • graph_id: The ID of the agent that is executing the block. This is the same for every version of the agent
      • graph_exec_id: The ID of the execution of the agent. This changes every time the agent has a new "run"
      • node_exec_id: The ID of the execution of the node. This changes every time the node is executed
      • node_id: The ID of the node that is being executed. It changes every version of the graph, but not every time the node is executed.
      • execution_context: An ExecutionContext object containing user_id, graph_exec_id, workspace_id, and session_id. Required for file handling.

Handling Files in Blocks

When your block needs to work with files (images, videos, documents), use store_media_file() from backend.util.file. This function handles downloading, validation, virus scanning, and storage.

Import:

python
from backend.data.execution import ExecutionContext
from backend.util.file import store_media_file
from backend.util.type import MediaFileType

The return_format parameter determines what you get back:

Format Use When Returns
"for_local_processing" Processing with local tools (ffmpeg, MoviePy, PIL) Local file path (e.g., "image.png")
"for_external_api" Sending content to external APIs (Replicate, OpenAI) Data URI (e.g., "data:image/png;base64,...")
"for_block_output" Returning output from your block Smart: workspace:// in CoPilot, data URI in graphs

Examples:

python
async def run(
    self,
    input_data: Input,
    *,
    execution_context: ExecutionContext,
    **kwargs,
) -> BlockOutput:
    # PROCESSING: Need to work with file locally (ffmpeg, MoviePy, PIL)
    local_path = await store_media_file(
        file=input_data.video,
        execution_context=execution_context,
        return_format="for_local_processing",
    )
    # local_path = "video.mp4" - use with Path, ffmpeg, subprocess, etc.
    full_path = get_exec_file_path(execution_context.graph_exec_id, local_path)

    # EXTERNAL API: Need to send content to an API like Replicate
    image_b64 = await store_media_file(
        file=input_data.image,
        execution_context=execution_context,
        return_format="for_external_api",
    )
    # image_b64 = "data:image/png;base64,iVBORw0..." - send to external API

    # OUTPUT: Returning result from block to user/next block
    result_url = await store_media_file(
        file=generated_image_url,
        execution_context=execution_context,
        return_format="for_block_output",
    )
    yield "image_url", result_url
    # In CoPilot: result_url = "workspace://abc123" (persistent, context-efficient)
    # In graphs:  result_url = "data:image/png;base64,..." (for next block/display)

Key points:

  • for_block_output is the only format that auto-adapts to execution context
  • Always use for_block_output for block outputs unless you have a specific reason not to
  • Never manually check for workspace_id - let for_block_output handle the logic
  • The function handles URLs, data URIs, workspace:// references, and local paths as input

Field Types

oneOf fields

oneOf allows you to specify that a field must be exactly one of several possible options. This is useful when you want your block to accept different types of inputs that are mutually exclusive.

Example:

python
attachment: Union[Media, DeepLink, Poll, Place, Quote] = SchemaField(
    discriminator='discriminator',
    description="Attach either media, deep link, poll, place or quote - only one can be used"
)

The discriminator parameter tells AutoGPT which field to look at in the input to determine which type it is.

In each model, you need to define the discriminator value:

python
class Media(BaseModel):
    discriminator: Literal['media']
    media_ids: List[str]

class DeepLink(BaseModel):
    discriminator: Literal['deep_link']
    direct_message_deep_link: str

OptionalOneOf fields

OptionalOneOf is similar to oneOf but allows the field to be optional (None). This means the field can be either one of the specified types or None.

Example:

python
attachment: Union[Media, DeepLink, Poll, Place, Quote] | None = SchemaField(
    discriminator='discriminator',
    description="Optional attachment - can be media, deep link, poll, place, quote or None"
)

The key difference is the | None which makes the entire field optional.

Blocks with authentication

Our system supports auth offloading for API keys and OAuth2 authorization flows.
Adding a block with API key authentication is straight-forward, as is adding a block
for a service that we already have OAuth2 support for.

Implementing the block itself is relatively simple. On top of the instructions above,
you're going to add a credentials parameter to the Input model and the run method:

text
/* Detailed source-code truncated for AI context efficiency. */

The credentials will be automagically injected by the executor in the back end.

The APIKeyCredentials and OAuth2Credentials models are defined here.
To use them in e.g. an API request, you can either access the token directly:

python
# credentials: APIKeyCredentials
response = requests.post(
    url,
    headers={
        "Authorization": f"Bearer {credentials.api_key.get_secret_value()})",
    },
)

# credentials: OAuth2Credentials
response = requests.post(
    url,
    headers={
        "Authorization": f"Bearer {credentials.access_token.get_secret_value()})",
    },
)

or use the shortcut credentials.auth_header():

python
# credentials: APIKeyCredentials | OAuth2Credentials
response = requests.post(
    url,
    headers={"Authorization": credentials.auth_header()},
)

The backend discovers provider names from the ProviderName enum and the SDK's AutoRegistry.
The _auth.py pattern below uses a ProviderName member, so add the new provider to that enum. SDK integrations can instead register provider names through ProviderBuilder; see the Block SDK Guide.

ProviderName definition

python title="backend/integrations/providers.py"
--8<-- "autogpt_platform/backend/backend/integrations/providers.py:ProviderName"

For integrations that only use API key authentication (no OAuth), keep credential definitions in _auth.py and register display metadata separately. The setup is:

  1. Add to ProviderName enum in backend/integrations/providers.py
  2. Create _auth.py in your block package with credentials type, field factory, and test credentials
  3. Create the block using the credentials field from _auth.py
  4. Create _config.py with the provider description and supported auth types for the settings UI
  5. Add the integration icon as described below

Here's the complete _auth.py pattern (using ZeroBounce as a reference):

python title="backend/blocks/my_provider/_auth.py"
from typing import Literal

from pydantic import SecretStr

from backend.data.model import APIKeyCredentials, CredentialsField, CredentialsMetaInput
from backend.integrations.providers import ProviderName

# Type aliases for your provider
MyProviderCredentials = APIKeyCredentials
MyProviderCredentialsInput = CredentialsMetaInput[
    Literal[ProviderName.MY_PROVIDER],
    Literal["api_key"],
]


# Field factory for use in block Input schemas
def MyProviderCredentialsField() -> MyProviderCredentialsInput:
    return CredentialsField(
        description="The My Provider integration requires an API key.",
    )


# Test credentials for block tests
TEST_CREDENTIALS = APIKeyCredentials(
    id="01234567-89ab-cdef-0123-456789abcdef",
    provider="my_provider",
    api_key=SecretStr("mock-my-provider-api-key"),
    title="Mock My Provider API key",
    expires_at=None,
)

TEST_CREDENTIALS_INPUT = {
    "provider": TEST_CREDENTIALS.provider,
    "id": TEST_CREDENTIALS.id,
    "type": TEST_CREDENTIALS.type,
    "title": TEST_CREDENTIALS.title,
}

Then use it in your block:

python title="backend/blocks/my_provider/my_block.py"
from ._auth import MyProviderCredentials, MyProviderCredentialsField, MyProviderCredentialsInput

class MyBlock(Block):
    class Input(BlockSchemaInput):
        credentials: MyProviderCredentialsInput = MyProviderCredentialsField()
        # ... other inputs

    async def run(
        self,
        input_data: Input,
        *,
        credentials: MyProviderCredentials,
        **kwargs,
    ) -> BlockOutput:
        api_key = credentials.api_key.get_secret_value()
        # ... use the API key
        yield "result", ...

Register the provider metadata in _config.py, following backend/blocks/zerobounce/_config.py:

python title="backend/blocks/my_provider/_config.py"
from backend.integrations.providers import ProviderName
from backend.sdk import ProviderBuilder

my_provider = (
    ProviderBuilder(ProviderName.MY_PROVIDER.value)
    .with_description("A short description of the integration")
    .with_supported_auth_types("api_key")
    .build()
)

This metadata tells the settings UI which connection methods to offer. It does not replace the credentials declared in _auth.py. The block loader imports _config.py automatically, and .build() registers the provider. OAuth, webhooks, and cost tracking use additional ProviderBuilder methods described in the Block SDK Guide.

Multiple credentials inputs

Multiple credentials inputs are supported, under the following conditions:

  • The name of each of the credentials input fields must end with _credentials.
  • The names of the credentials input fields must match the names of the corresponding
    parameters on the run(..) method of the block.
  • If more than one of the credentials parameters are required, test_credentials
    is a dict[str, Credentials], with for each required credentials input the
    parameter name as the key and suitable test credentials as the value.

Adding an OAuth2 service integration

To add support for a new OAuth2-authenticated service, you'll need to add an OAuthHandler.
All our existing handlers and the base class can be found here.

Every handler must implement the following parts of the BaseOAuthHandler interface:

python title="backend/integrations/oauth/base.py"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler1"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler2"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler3"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler4"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler5"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler6"

As you can see, this is modeled after the standard OAuth2 flow.

Aside from implementing the OAuthHandler itself, adding a handler into the system requires two more things:

python title="backend/integrations/oauth/__init__.py"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/__init__.py:HANDLERS_BY_NAMEExample"
  • Adding {provider}_client_id and {provider}_client_secret to the application's Secrets under util/settings.py
python title="backend/util/settings.py"
--8<-- "autogpt_platform/backend/backend/util/settings.py:OAuthServerCredentialsExample"

Adding to the frontend

Integration Icon (Required)

When adding a new integration provider, you must add a PNG icon at:

text
autogpt_platform/frontend/public/integrations/{provider_name}.png

The frontend block menu dynamically loads icons using the URL /integrations/{integration.name}.png (see PaginatedIntegrationList.tsx). If the icon file is missing, the block will show a broken image in the builder UI.

Use the same provider_name that matches your ProviderName enum value (lowercase). For example, if your provider is ProviderName.SLACK, the icon should be at frontend/public/integrations/slack.png.

Frontend Provider Registration

No manual frontend provider registration is required. The credentials provider context fetches provider names from the backend, and the integrations UI uses the description and supported_auth_types returned by GET /integrations/providers. Set those fields in the backend provider configuration as shown above.

Block credential inputs derive their accepted credential types from the block's input schema. CredentialsProviderName is a string type; CredentialsType describes authentication methods, not provider names, so neither needs a new frontend enum entry for a provider.

The credentials list has an optional providerIcons map in frontend/src/components/contextual/CredentialsInput/helpers.ts; unlisted providers use a fallback icon. This is separate from the PNG asset required by the block menu.

Example: GitHub integration

python title="backend/blocks/github/issues.py"
--8<-- "autogpt_platform/backend/backend/blocks/github/issues.py:GithubCommentBlockExample"
python title="backend/integrations/oauth/github.py"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/github.py:GithubOAuthHandlerExample"

Example: Google integration

python title="backend/integrations/oauth/google.py"
--8<-- "autogpt_platform/backend/backend/integrations/oauth/google.py:GoogleOAuthHandlerExample"

You can see that google has defined a DEFAULT_SCOPES variable, this is used to set the scopes that are requested no matter what the user asks for.

python title="backend/blocks/google/_auth.py"
--8<-- "autogpt_platform/backend/backend/blocks/google/_auth.py:GoogleOAuthIsConfigured"

You can also see that GOOGLE_OAUTH_IS_CONFIGURED is used to disable the blocks that require OAuth if the oauth is not configured. This is in the __init__ method of each block. This is because there is no api key fallback for google blocks so we need to make sure that the oauth is configured before we allow the user to use the blocks.

Webhook-triggered Blocks

Webhook-triggered blocks allow your agent to respond to external events in real-time.
These blocks are triggered by incoming webhooks from third-party services
rather than being executed manually.

Creating and running a webhook-triggered block involves three main components:

  • The block itself, which specifies:
    • Inputs for the user to select a resource and events to subscribe to
    • A credentials input with the scopes needed to manage webhooks
    • Logic to turn the webhook payload into outputs for the webhook block
  • The WebhooksManager for the corresponding webhook service provider, which handles:
    • (De)registering webhooks with the provider
    • Parsing and validating incoming webhook payloads
  • The credentials system for the corresponding service provider, which may include an OAuthHandler

There is more going on under the hood, e.g. to store and retrieve webhooks and their
links to nodes, but to add a webhook-triggered block you shouldn't need to make changes
to those parts of the system.

Creating a Webhook-triggered Block

To create a webhook-triggered block, follow these additional steps on top of the basic block creation process:

  1. Define webhook_config in your block's __init__ method.

    Example: GitHubPullRequestTriggerBlock

    python title="backend/blocks/github/triggers.py"
    --8<-- "autogpt_platform/backend/backend/blocks/github/triggers.py:example-webhook_config"

    BlockWebhookConfig definition

    python title="backend/blocks/_base.py"
    --8<-- "autogpt_platform/backend/backend/blocks/_base.py:BlockWebhookConfig"
  2. Define event filter input in your block's Input schema.
    This allows the user to select which specific types of events will trigger the block in their agent.

    Example: GitHubPullRequestTriggerBlock

    python title="backend/blocks/github/triggers.py"
    --8<-- "autogpt_platform/backend/backend/blocks/github/triggers.py:example-event-filter"
    • The name of the input field (events in this case) must match webhook_config.event_filter_input.
    • The event filter itself must be a Pydantic model with only boolean fields.
  3. Include payload field in your block's Input schema.

    Example: GitHubTriggerBase

    python title="backend/blocks/github/triggers.py"
    --8<-- "autogpt_platform/backend/backend/blocks/github/triggers.py:example-payload-field"
  4. Define credentials input in your block's Input schema.

    • Its scopes must be sufficient to manage a user's webhooks through the provider's API
    • See Blocks with authentication for further details
  5. Process webhook payload and output relevant parts of it in your block's run method.

    Example: GitHubPullRequestTriggerBlock

    python
    def run(self, input_data: Input, **kwargs) -> BlockOutput:
        yield "payload", input_data.payload
        yield "sender", input_data.payload["sender"]
        yield "event", input_data.payload["action"]
        yield "number", input_data.payload["number"]
        yield "pull_request", input_data.payload["pull_request"]

    Note that the credentials parameter can be omitted if the credentials
    aren't used at block runtime, like in the example.

Adding a Webhooks Manager

To add support for a new webhook provider, you'll need to create a WebhooksManager that implements the BaseWebhooksManager interface:

python title="backend/integrations/webhooks/_base.py"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/_base.py:BaseWebhooksManager1"

--8<-- "autogpt_platform/backend/backend/integrations/webhooks/_base.py:BaseWebhooksManager2"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/_base.py:BaseWebhooksManager3"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/_base.py:BaseWebhooksManager4"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/_base.py:BaseWebhooksManager5"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/_base.py:BaseWebhooksManager6"

!!! info "Signature verification (verify_signature)"
If the upstream provider signs its deliveries (e.g. GitHub's
X-Hub-Signature-256, Airtable's X-Airtable-Content-MAC), override
verify_signature and use hmac.compare_digest for the comparison.
Raise fastapi.HTTPException(403) on missing/invalid signatures.

text
If the provider has no signing scheme (consumer wearables, simple
"POST to this URL" tools), leave the default in place. The webhook
URL's UUID is then the bearer secret — document that for users.

And add a reference to your WebhooksManager class in load_webhook_managers:

python title="backend/integrations/webhooks/__init__.py"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/__init__.py:load_webhook_managers"

Example: GitHub Webhook Integration

GitHub Webhook triggers: blocks/github/triggers.py

python title="backend/blocks/github/triggers.py"
--8<-- "autogpt_platform/backend/backend/blocks/github/triggers.py:GithubTriggerExample"

GitHub Webhooks Manager: integrations/webhooks/github.py

python title="backend/integrations/webhooks/github.py"
--8<-- "autogpt_platform/backend/backend/integrations/webhooks/github.py:GithubWebhooksManager"

Key Points to Remember

  • Unique ID: Give your block a unique ID in the init method.
  • Input and Output Schemas: Define clear input and output schemas.
  • Error Handling: Implement error handling in the run method.
  • Output Results: Use yield to output results in the run method.
  • Testing: Provide test input and output in the init method for automatic testing.

Understanding the Testing Process

The testing of blocks is handled by test_block.py, which does the following:

  1. It calls the block with the provided test_input.
    If the block has a credentials field, test_credentials is passed in as well.
  2. If a test_mock is provided, it temporarily replaces the specified methods with the mock functions.
  3. It then asserts that the output matches the test_output.

For the WikipediaSummaryBlock:

  • The test will call the block with the topic "Artificial Intelligence".
  • Instead of making a real API call, it will use the mock function, which returns {"extract": "summary content"}.
  • It will then check if the output key is "summary" and its value is a string.

This approach allows us to test the block's logic comprehensively without relying on external services, while also accommodating non-deterministic outputs.

Security Best Practices for SSRF Prevention

When creating blocks that handle external URL inputs or make network requests, it's crucial to use the platform's built-in SSRF protection mechanisms. The backend.util.request module provides a secure Requests wrapper class that should be used for all HTTP requests.

Using the Secure Requests Wrapper

python
from backend.util.request import requests

class MyNetworkBlock(Block):
    def run(self, input_data: Input, **kwargs) -> BlockOutput:
        try:
            # The requests wrapper automatically validates URLs and blocks dangerous requests
            response = requests.get(input_data.url)
            yield "result", response.text
        except ValueError as e:
            # URL validation failed
            raise RuntimeError(f"Invalid URL provided: {e}")
        except requests.exceptions.RequestException as e:
            # Request failed
            raise RuntimeError(f"Request failed: {e}")

The Requests wrapper provides these security features:

  1. URL Validation:

    • Blocks requests to private IP ranges (RFC 1918)
    • Validates URL format and protocol
    • Resolves DNS and checks IP addresses
    • Supports whitelisting trusted origins
  2. Secure Defaults:

    • Disables redirects by default
    • Raises exceptions for non-200 status codes
    • Supports custom headers and validators
  3. Protected IP Ranges:
    The wrapper denies requests to these networks:

    python title="backend/util/request.py"
    --8<-- "autogpt_platform/backend/backend/util/request.py:BLOCKED_IP_NETWORKS"

Custom Request Configuration

If you need to customize the request behavior:

python
from backend.util.request import Requests

# Create a custom requests instance with specific trusted origins
custom_requests = Requests(
    trusted_origins=["api.trusted-service.com"],
    raise_for_status=True,
    extra_headers={"User-Agent": "MyBlock/1.0"}
)

Error Handling

Blocks should raise appropriate exceptions for errors that users can fix. The executor classifies errors based on whether they inherit from ValueError - these are treated as "expected failures" (user-fixable) rather than system errors.

Block Exception Classes

Import from backend.util.exceptions:

python
from backend.util.exceptions import BlockInputError, BlockExecutionError
Exception Use Case Example
BlockInputError Invalid user input, validation failures, missing required fields Bad API key format, invalid URL, missing credentials
BlockExecutionError Runtime failures the user can address API errors, auth failures, resource not found, rate limits
ValueError Simple cases (auto-wrapped to BlockExecutionError) Basic validation errors

Raising Exceptions

python
from backend.util.exceptions import BlockInputError, BlockExecutionError

class MyBlock(Block):
    async def run(self, input_data: Input, **kwargs) -> BlockOutput:
        # Input validation - use BlockInputError
        if not input_data.api_key:
            raise BlockInputError(
                message="API key is required",
                block_name=self.name,
                block_id=self.id,
            )

        try:
            result = await self.call_api(input_data)
            yield "result", result
        except AuthenticationError as e:
            # API/runtime errors - use BlockExecutionError
            raise BlockExecutionError(
                message=f"Authentication failed: {e}",
                block_name=self.name,
                block_id=self.id,
            ) from e

What NOT to Catch

Don't catch errors that require system admin intervention:

  • Out of money/credits
  • Unreachable infrastructure
  • Database connection failures
  • Internal server errors from your own services

Let these propagate as unexpected errors so they get proper attention.

Data Models

Use pydantic base models over dict and typeddict where possible. Avoid untyped models for block inputs and outputs as much as possible

File Input

You can use MediaFileType to handle the importing and exporting of files out of the system. Explore how its used through the system before using it in a block schema.

Tips for Effective Block Testing

  1. Provide realistic test_input: Ensure your test input covers typical use cases.

  2. Define appropriate test_output:

    • For deterministic outputs, use specific expected values.
    • For non-deterministic outputs or when only the type matters, use Python types (e.g., str, int, dict).
    • You can mix specific values and types, e.g., ("key1", str), ("key2", 42).
  3. Use test_mock for network calls: This prevents tests from failing due to network issues or API changes.

  4. Consider omitting test_mock for blocks without external dependencies: If your block doesn't make network calls or use external resources, you might not need a mock.

  5. Consider edge cases: Include tests for potential error conditions in your run method.

  6. Update tests when changing block behavior: If you modify your block, ensure the tests are updated accordingly.

By following these steps, you can create new blocks that extend the functionality of the AutoGPT Agent Server.

Standalone Unit Tests

In addition to the inline test_input/test_output/test_mock approach described above, you can write standalone unit tests for more complex validation. Place test files alongside your block as {block_name}_test.py (e.g., my_block_test.py).

!!! warning "Test file naming and auto-discovery"
The block loader in backend/blocks/__init__.py skips files whose names start with test_ but still auto-imports files ending in _test.py at startup. Keep _test.py modules free of module-level side effects (no top-level patch(...) / network calls / fixtures-with-side-effects), or name your file test_{block_name}.py to opt out of auto-import entirely.

Test Structure

Two patterns are supported:

1. Drive the block directly (mirror backend/blocks/slack/slack_test.py) — gives you full control over inputs, mocks, and output assertions:

python title="backend/blocks/my_provider/my_block_test.py"
import pytest
from unittest.mock import AsyncMock, MagicMock, patch

from backend.blocks.my_provider._auth import TEST_CREDENTIALS, TEST_CREDENTIALS_INPUT
from backend.blocks.my_provider.my_block import MyBlock


async def _collect_outputs(block, input_data, credentials):
    outputs: dict[str, object] = {}
    async for name, value in block.run(input_data, credentials=credentials):
        outputs[name] = value
    return outputs


@pytest.mark.asyncio
async def test_my_block_success():
    block = MyBlock()
    input_data = MyBlock.Input(
        credentials=TEST_CREDENTIALS_INPUT,
        query="test query",
    )
    mock_resp = MagicMock()
    mock_resp.json.return_value = {"result": "test data"}

    # Patch where the symbol is *used*. The block ecosystem uses
    # `backend.util.request.Requests` (httpx under the hood), not stdlib `requests`.
    with patch(
        "backend.blocks.my_provider.my_block.Requests.post",
        new_callable=AsyncMock,
        return_value=mock_resp,
    ):
        outputs = await _collect_outputs(block, input_data, TEST_CREDENTIALS)

    assert outputs["result"] == "test data"

2. Run the block's inline test_input / test_output / test_mock via the framework helper. The helper takes a block instance only and reads the test data defined on the block itself:

python title="backend/blocks/my_provider/my_block_test.py"
import pytest

from backend.blocks.my_provider.my_block import MyBlock
from backend.util.test import execute_block_test


@pytest.mark.asyncio
async def test_my_block_inline_fixtures():
    await execute_block_test(MyBlock())

Key Points

  • TEST_CREDENTIALS_INPUT (from _auth.py) is used to construct the Input object — it contains the credential metadata (provider, id, type).
  • TEST_CREDENTIALS (from _auth.py) is passed to block.run(..., credentials=...) — it contains the actual mock API key/token.
  • execute_block_test(block) runs the block against its own inline test_input / test_output / test_mock attributes (set in __init__). It does not accept input_data= or credentials= and does not return outputs.
  • The block attribute for test credentials is test_credentials (not test_credentials_input) when using the inline test pattern in __init__.
  • Use pytest.mark.asyncio for async block run() methods. Mock the HTTP layer where it is used — typically backend.util.request.Requests (or httpx.AsyncClient), not stdlib requests.

Running Tests

bash
# Run a specific block's standalone tests
poetry run pytest backend/blocks/my_provider/my_block_test.py -xvs

# Run all block tests (inline test_input/test_output validation)
poetry run pytest backend/blocks/test/test_block.py -xvs

# Test a specific block by name (inline tests)
poetry run pytest 'backend/blocks/test/test_block.py::test_available_blocks[MyBlock]' -xvs

Block Documentation Sync

After creating or modifying blocks, you must regenerate the block documentation to keep it in sync with the code.

Generating Documentation

bash
cd autogpt_platform/backend
poetry run python scripts/generate_block_docs.py

For provider packages, the generator writes one page per Python module:

text
docs/integrations/block-integrations/{provider}/{module}.md

For example, backend/blocks/zerobounce/validate_emails.py generates docs/integrations/block-integrations/zerobounce/validate_emails.md. Top-level block modules are grouped into category pages such as docs/integrations/block-integrations/basic.md.

CI Enforcement

The "Block Documentation Sync Check" CI workflow (.github/workflows/docs-block-sync.yml) runs on every PR that touches files in backend/blocks/ or docs/integrations/. It runs the generator in --check mode and fails the build if docs are out of sync.

If CI fails with a docs sync error, run the generator locally, commit the updated docs, and push.

Manual Sections

The generated docs support manually-written content that is preserved across regeneration. Use these markers in the generated markdown files:

markdown
Your custom usage examples here. This will not be overwritten.

Use the generator-supported block sections how_it_works, use_case, and extras, or file-level file_description and additional_content. Keep the existing markers rather than inventing section names. These sections hold usage examples, caveats, and integration notes that cannot be generated from block metadata.


Platform Ollama (`docs/platform/ollama.md`)

Running Ollama with AutoGPT

Important: Ollama integration is only available when self-hosting the AutoGPT platform. It cannot be used with the cloud-hosted version.

Follow these steps to set up and run Ollama with the AutoGPT platform.

Prerequisites

  1. Make sure you have gone through and completed the AutoGPT Setup steps, if not please do so before continuing with this guide.
  2. Before starting, ensure you have Ollama installed on your machine.

Setup Steps

1. Launch Ollama

To properly set up Ollama for network access, choose one of these methods:

Method A: Using Ollama Desktop App (Recommended)

  1. Open the Ollama desktop application

  2. Go to Settings and toggle "Expose Ollama to the network"

  3. Click on the model name field in the "New Chat" window

  4. Search for "llama3.2" (or your preferred model)

  5. Click on it to start the download and load the model to be used

??? note "Method B: Using Docker (Alternative)"

text
If you prefer to run Ollama via Docker instead of the desktop app, you can use the official [Ollama Docker image](https://hub.docker.com/r/ollama/ollama):

1. **Start Ollama container** (choose based on your hardware):

   **CPU only:**
   ```bash
   docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
   ```

   **With NVIDIA GPU** (requires [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)):
   ```bash
   docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
   ```

   **With AMD GPU:**
   ```bash
   docker run -d --device /dev/kfd --device /dev/dri -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama:rocm
   ```

   **Download your desired model:**
   ```bash
   docker exec -it ollama ollama run llama3.2
   ```

!!! note
    The Docker method automatically exposes Ollama on `0.0.0.0:11434`, making it accessible to AutoGPT. More models can be found on the [Ollama library](https://ollama.com/library).

??? warning "Method C: Using Ollama Via Command Line (Legacy)"

text
For users still using the traditional CLI approach or older Ollama installations:

1. **Set the host environment variable:**

   **Windows (Command Prompt):**
   ```cmd
   set OLLAMA_HOST=0.0.0.0:11434
   ```
   
   **Linux/macOS (Terminal):**
   ```bash
   export OLLAMA_HOST=0.0.0.0:11434
   ```

2. **Start the Ollama server:**
   ```bash
   ollama serve
   ```

3. **Open a new terminal/command window** and download your desired model:
   ```bash
   ollama pull llama3.2
   ```

!!! note
    This will download the [llama3.2](https://ollama.com/library/llama3.2) model. Keep the terminal with `ollama serve` running in the background throughout your session.

2. Start the AutoGPT Platform

Navigate to the autogpt_platform directory and start all services:

bash
cd autogpt_platform
docker compose up -d --build

This command starts both the backend and frontend services. Once running, visit http://localhost:3000 to access the platform. After registering/logging in, navigate to the build page at http://localhost:3000/build.

3. Using Ollama with AutoGPT

Now that both Ollama and the AutoGPT platform are running, we can use Ollama with AutoGPT:

  1. Add an AI Text Generator block to your workspace (it can work with any AI LLM block but for this example will be using the AI Text Generator block):

  2. Configure the API Key field: Enter any value (e.g., "dummy" or "not-needed") since Ollama doesn't require authentication.

  3. In the "LLM Model" dropdown, select "llama3.2" (This is the model we downloaded earlier)

    Compatible Models: The following Ollama models are available in AutoGPT by default:

    • llama3.2 (Recommended for most use cases)
    • llama3.3
    • llama3
    • llama3.1:405b
    • dolphin-mistral:latest

    Note: To use other models, follow the "Add Custom Models" step above.

  4. Set your local IP address in the "Ollama Host" field:

    To find your local IP address:

    Windows (Command Prompt):

    cmd
    ipconfig

    Linux/macOS (Terminal):

    bash
    ip addr show

    or

    bash
    ifconfig

    Look for your IPv4 address (e.g., 192.168.0.39), then enter it with port 11434 in the "Ollama Host" field:

    text
    192.168.0.39:11434

    Important: Since AutoGPT runs in Docker containers, you must use your host machine's IP address instead of localhost or 127.0.0.1. Docker containers cannot reach localhost on the host machine.

  5. Add prompts to your AI block, save the graph, and run it:

That's it! You've successfully setup the AutoGPT platform and made a LLM call to Ollama.

Using Ollama on a Remote Server with AutoGPT

For running Ollama on a remote server, simply make sure the Ollama server is running and is accessible from other devices on your network/remotely through the port 11434.

To find your local IP address of the system running Ollama:

Windows (Command Prompt):

cmd
ipconfig

Linux/macOS (Terminal):

bash
ip addr show

or

bash
ifconfig

Look for your IPv4 address (e.g., 192.168.0.39).

Then you can use the same steps above but you need to add the Ollama server's IP address to the "Ollama Host" field in the block settings like so:

text
192.168.0.39:11434

Add Custom Models (Advanced)

Model definitions are centralized in the LLM catalog — see Managing LLM Models for the full field reference and workflow. To add a custom Ollama model:

  1. Add a catalog entry in autogpt_platform/backend/backend/data/llm_registry/catalog.py, next to the other Ollama models:

    python
    CatalogModel(
        slug="The-model-name-from-ollama",  # bare name, exactly as Ollama serves it
        display_name="Your Model",
        provider="ollama",
        context_window=8192,  # adjust for your model
        cost=CatalogModelCost(run_credits=1),
    ),

    Ollama models use bare model names (no vendor/ prefix) — the name must match what ollama list shows. A run cost of 1 is fine for local usage; cost tracking is disabled for self-hosted instances.

  2. Also add one LLMModel name line in autogpt_platform/backend/backend/data/llm_registry/llm_models.py (Ollama section) — that identifier is what block schemas serialize; every model FACT (metadata, costs) lives in the catalog entry itself, and an import-time check refuses to boot if an enum name has no catalog entry. The catalog entry is what centralizes the model's metadata and makes it routable for AutoPilot.

  3. Rebuild the backend:

    bash
    docker compose up -d --build
  4. Pull the model in Ollama:

    bash
    ollama pull your-model-name

Troubleshooting

If you encounter any issues, verify that:

  • Ollama is properly installed and running with ollama serve
  • Docker is running before starting the platform
  • If running Ollama outside Docker, ensure it's set to 0.0.0.0:11434 for network access

Common Issues

Connection Refused / Cannot Connect to Ollama

  • Most common cause: Using localhost or 127.0.0.1 in the Ollama Host field
  • Solution: Use your host machine's IP address (e.g., 192.168.0.39:11434)
  • Why: AutoGPT runs in Docker containers and cannot reach localhost on the host
  • Find your IP: Use ipconfig (Windows) or ifconfig (Linux/macOS)
  • Test Ollama is running: curl http://localhost:11434/api/tags should work from your host machine

Model Not Found

  • Pull the model manually:
    bash
    ollama pull llama3.2
  • If using a custom model, ensure it's added to the model list in backend/api/model.py

Docker Issues

  • Ensure Docker daemon is running:
    bash
    docker ps
  • Try rebuilding:
    bash
    docker compose up -d --build

API Key Errors

  • Remember that Ollama doesn't require authentication - any value works for the API key field

Model Selection Issues

  • Look for models with "ollama" in their description in the dropdown
  • Only the models listed in the "Compatible Models" section are guaranteed to work

Platform Scheduling And Triggers (`docs/platform/scheduling-and-triggers.md`)

Scheduling & Triggers

Overview

AutoGPT agents can be run on demand, on a recurring schedule, or automatically in response to external events via webhooks. This guide covers both scheduling and trigger-based execution.

Scheduling an Agent

Scheduling lets you run an agent automatically on a recurring basis with pre-configured inputs.

Setting Up a Schedule

  1. Go to your Agent Library and open the agent you want to schedule
  2. Click New agent task (the same button used for manual runs)
  3. Fill in all the input fields with the values you want the agent to use
  4. At the bottom of the input form, you'll see two buttons: Start Task and Schedule Task
  5. Click Schedule Task

Configuring the Schedule

The schedule configuration screen allows you to set:

Setting Description
Schedule Name A descriptive name for this schedule
Repeats Frequency — e.g., Weekly
Repeats On Select specific days (individual days, weekdays, weekends, or select all)
At The time of day to run (hour and minute)
Timezone Schedule runs in your local timezone, displayed on screen

Example: Run "Keyword SEO Expert" every weekday at 9:00 AM in the timezone shown in the schedule form.

Managing Schedules

After creating a schedule, you can view and manage it on your agent's detail page:

  • Open the agent from your library
  • Select the Scheduled tab on the left-hand side
  • The tab shows the count of active schedules (e.g., Scheduled 1)

Triggers (Webhook-Based Execution)

Triggers allow external services or your own code to start an agent automatically by sending data to a webhook URL.

How Triggers Work

Unlike standard input blocks, trigger blocks are a special type of input block. When you add a trigger block to your agent in the builder, the agent's execution model changes — instead of being started manually, it waits for incoming webhook events.

Setting Up a Trigger

Step 1: Add a Trigger Block in the Builder

  1. Open the Agent Builder
  2. From the blocks menu, add a trigger block to your agent
  3. Connect it to the rest of your workflow like any other input block
  4. Save your agent

Step 2: Configure the Trigger in Your Library

  1. Go to your Agent Library and open the agent
  2. Click New agent task
  3. Enter the trigger name and description and complete any required inputs or credentials
  4. Click Set up Trigger

Step 3: Copy the Webhook URL

Once the trigger is created, you'll see a status panel:

Trigger Status

Status: Active

This trigger is ready to be used. Use the Webhook URL below to set up the trigger connection with the service of your choosing.

Webhook URL: https://backend.agpt.co/api/integrations/generic_webhook/webhooks/<webhook-id>/ingress

Copy this webhook URL and provide it to the external platform or code that will be sending events to trigger your agent.

{% hint style="info" %}
Trigger-based agents cannot be started manually. Configure them through New agent task and Set up Trigger, then send data to the webhook URL to execute them.
{% endhint %}

Example Use Cases

  • GitHub webhook: Trigger an agent whenever a pull request is opened
  • Payment processor: Run an agent when a new payment is received
  • Form submission: Process data when a user submits a form on your website
  • Custom integration: Send data from any service that supports webhooks

Schedule vs. Trigger

Schedule Trigger
How it starts Automatically at configured times When an external event sends data to the webhook URL
Input source Pre-configured when the schedule is created Provided by the incoming webhook payload
Use case Recurring tasks with fixed inputs (daily reports, weekly summaries) Event-driven tasks (new PR opened, form submitted, payment received)
Setup Through the New agent task → Schedule Task flow Through trigger blocks in the builder, then New agent task → Set up Trigger in the library

Platform Sharing And Exporting (`docs/platform/sharing-and-exporting.md`)

Sharing & Exporting Agents

Overview

There are several ways to share agents and their outputs with others on the AutoGPT Platform. This guide covers all the available options.

Sharing Options

Share Task Output via URL

A completed task remains private until you explicitly enable sharing:

  1. Open the agent and select the completed task.
  2. Click Share results.
  3. Review the public-access warning and click Enable Sharing.
  4. Copy the generated Share URL.

Anyone with that URL can view the run's outputs. Use Stop Sharing in the same dialog to revoke the link.

{% hint style="warning" %}
A share URL is publicly accessible to anyone who has it. It does not publish the task inputs, but outputs may contain sensitive data.
{% endhint %}

Publish to the Marketplace

The most visible way to share an agent is to publish it to the Marketplace. Published agents are discoverable by all platform users and go through a review process by the AutoGPT team.

See Publishing an Agent for the full guide.

Export as a File

For sharing agents privately — without publishing to the marketplace — you can export an agent as a file:

  1. Go to your Agent Library and open the agent
  2. Click the three dots (⋯) on the far right
  3. Select Export Agent to File
  4. The agent file will download to your computer

You can then send this file to anyone via email, messaging, or any other method.

Import a Shared File

To import an agent file someone has shared with you:

  1. Go to your Agent Library
  2. Click Import
  3. Select the AutoGPT agent tab
  4. Upload the exported .json file
  5. The agent will be added to your library

Teams & Collaboration

Organization and team workspaces are available only when enabled for your account or deployment. Use their built-in access controls where available. Export/import remains the portable way to transfer an agent file between separate accounts or deployments.


Platform Single Container (`docs/platform/single-container.md`)

Run AutoGPT in One Docker Container (Experimental)

The image packages the AutoGPT Platform frontend and backend together with
PostgreSQL, a three-node Valkey cluster, RabbitMQ, nginx, and FalkorDB.
Only nginx listens on the container's public interface, on port 3000.
The FalkorDB service is a core part of this image and always runs; Graphiti
memory is enabled by the image's default configuration and still requires a
working model-provider profile.

All durable state is stored under /data. Use a named Docker volume for every
installation you intend to keep.

Requirements

  • Docker Engine or Docker Desktop capable of running Linux containers on
    linux/amd64 or linux/arm64. Building locally also requires Docker Buildx.
  • More than the measured 5–6 GiB of appliance memory use, with additional
    headroom for agents, local models, and workload growth.
  • Host storage with enough capacity and throughput for the image, the growing
    /data volume, and at least one complete compressed backup archive alongside
    the live data. Shutdown and backup time depend on storage throughput and fsync
    latency; SSD-class storage is recommended but not a measured support floor.

There is no measured minimum CPU count or supported concurrent-run ceiling;
performance depends on the enabled services and workload.

Get an image

Use latest for the most recent fully verified stable AutoGPT Platform image:

bash
IMAGE=significantgravitas/autogpt:latest
docker pull "${IMAGE}"

For a reproducible installation, replace latest with an immutable vX.Y.Z
tag or manifest digest. Docker image tags map to source releases as follows:

  • latest points to the newest stable AutoGPT Platform release.
  • vX.Y.Z is the immutable image for GitHub release
    autogpt-platform-beta-vX.Y.Z.
  • sha-<git-sha> is the immutable image for an exact dev or release source
    revision.

Older canary-sha-* tags are legacy pre-release validation artifacts, not a
currently published or supported tag family.

To build from source instead, run Docker Buildx Bake from the repository root:

bash
docker buildx bake \
  --file autogpt_platform/single-container/docker-bake.hcl \
  --load \
  single-container
IMAGE=autogpt-platform:single-container-dev

Commands below use the shell variable IMAGE, whether it identifies the
Docker Hub image, a pinned digest, or the local build.

The image has a complete default command, so you can optionally perform a
foreground boot check:

bash
docker run --rm "${IMAGE}"

Skip this check if you want to proceed directly to a persistent installation;
Quick start performs the same first-boot work against its named volume. The
check runs in the foreground. First boot may take several minutes and use
roughly 5–6 GiB of memory; press Ctrl-C to stop it. That interruption is safe
for this check because --rm discards its anonymous /data volume. Do not
generalize it to an installation that uses a named volume. The command does not
publish the web port. Use the full setup below for a usable installation.

Quick start

Create a private environment file:

bash
umask 077
touch autogpt.env
chmod 600 autogpt.env

When working from a source checkout, you can copy
autogpt_platform/single-container/.env.example instead to see common optional
settings. The backend-only BEHAVE_AS control and its limits are documented in
Security boundary.

Edit the file and set at least the public URL and exact address for the first
account:

dotenv
AUTOGPT_PUBLIC_URL=http://localhost:3000
[email protected]

Replace [email protected] with the email address for the intended first
account. Passwords must contain at least 12 characters. Exact-address
allowlisting compares the email string asserted during signup; because this
image does not support required email verification, it is not proof of mailbox
ownership. New-account creation defaults open when the allowlist is empty; the
value above immediately restricts signup to that address. If the allowlist is
omitted, anyone who can reach the published port can create an account until
AUTH_ALLOW_NEW_ACCOUNTS=false is applied. The run command below binds the app
only to loopback. Create and promote the administrator and close signup while
the app is still loopback-only. Configure an HTTPS origin before entering
credentials on any LAN or remote deployment.

Provider keys are not required to boot, create an account, use Builder, or run
provider-free blocks. Model-backed functions return their normal actionable
missing-credential error until you configure a profile from
Models and memory.

Start the appliance:

bash
docker run --detach --name autogpt \
  --restart unless-stopped \
  --shm-size 2g \
  --ulimit nofile=65536:65536 \
  --log-driver json-file \
  --log-opt max-size=50m \
  --log-opt max-file=5 \
  --env-file autogpt.env \
  --publish 127.0.0.1:3000:3000 \
  --volume autogpt-data:/data \
  "${IMAGE}"

The --shm-size 2g allocation keeps temporary ChatGPT/Codex authentication
homes in memory instead of the container's writable layer. The nofile limit
sets a predictable per-process file-descriptor ceiling for the bundled services.
The JSON log options retain about five 50 MB files instead of allowing container
logs to grow without a bound.

Wait for the complete appliance to become healthy:

bash
docker inspect --format '{{.State.Health.Status}}' autogpt
docker logs --tail 100 --follow autogpt

Do not stop the container while the first boot is applying database migrations.
If a migration is interrupted, later boots refuse to continue and the logs
identify the migration and recovery choices. For a brand-new empty installation,
remove that installation's unused /data volume and start again. For an existing
installation, restore the pre-upgrade backup. Use prisma migrate resolve only
after determining whether that migration's changes reached the database.

For an empty first boot that used the exact Quick start names, first verify that
the volume contains no user data, then discard that failed installation:

bash
docker stop autogpt
docker rm autogpt
docker volume rm autogpt-data

Repeat Quick start afterward. The failed appliance can remain in a restart loop
until explicitly stopped, so docker exec is not a reliable way to run
prisma migrate resolve; that advanced path requires PostgreSQL running against
the affected volume from a separate recovery environment. Do not use it without
inspecting whether the migration's database changes completed.

Test installations used about 5–6 GiB of memory during startup and steady-state
health checks. This is measured guidance, not a guaranteed minimum; allow
headroom for enabled services, agents, local models, and workload growth. On
Docker Desktop, make sure the VM's memory allocation in Settings → Resources
exceeds that observed use and leaves the same headroom.

Open http://localhost:3000, create the intended account, and promote it:

bash
docker exec autogpt autogpt-admin promote [email protected]

Replace [email protected] with the email address of the account you created.
Sign out and back in after promotion so the new session has the administrator
role.

Then set this in the environment file:

dotenv
AUTH_ALLOW_NEW_ACCOUNTS=false

Apply the change by replacing only the container. Keep the same named volume:

bash
docker stop autogpt
docker rm autogpt

Repeat the docker run command above. Removing the container does not remove
the autogpt-data volume.

Stopping

docker stop autogpt is designed and tested to complete inside Docker's stock
10-second timeout, so no host-wide timeout change is needed; a longer host
timeout does not extend the internal Supervisor caps. The shipped one-second
runtime, five-second state, and one-second event-listener phases measured about
8.4 seconds in the shutdown test setup. That is a narrow measured margin, not a
graceful-shutdown guarantee for slower storage, so do not shorten Docker's
timeout. Runtime processes are signaled first. PostgreSQL, RabbitMQ, Valkey,
and FalkorDB are signaled afterward and each gets at most five seconds to exit
before Supervisor forces it down. The event listener stops last. Larger or
slower state may require normal crash recovery on the next boot.

Agent runs still executing when the container stops are abandoned. Their queue
messages can be dropped and their execution rows can remain RUNNING, but they
do not resume. Start a new run after restart.

Supervisor process names are group-qualified. Use supervisorctl status to see
names such as runtime:rest and state:postgres.

Port and public URL

Container port 3000 does not change. To use host port 3300, change the run
command to:

text
--publish 127.0.0.1:3300:3000

and set:

dotenv
AUTOGPT_P