C# Scripting Guide
Learn how to write Roslyn-compatible C# script templates to interact dynamically with Autodesk Revit.
Revit script templates are executed on the fly inside the active Autodesk Revit application. They are compiled at runtime using the built-in Roslyn compiler engine.
Script Context & Variables
Every script executed by the Revit AI Suite has access to the following pre-defined context variables:
doc(Autodesk.Revit.DB.Document): The active Revit Document object.app(Autodesk.Revit.UI.UIApplication): The active Revit Application host.ch(ScriptChangeTracker): A utility class to track element creations, modifications, and deletions.args(Dictionary<string, object>): A collection of inputs sent from the React UI panel.
Architectural Rules
To keep the application responsive and prevent Revit API errors, scripts should adhere to the following architecture rules:
1. Query vs Command
- Queries (Read-only): Collect elements, perform calculations, and return serializable JSON lists. Do not open Transactions.
- Commands (Write): Modify model elements. You must manage your own Transactions:
using (Transaction tx = new Transaction(doc, "My Transaction Name")) { tx.Start(); // Perform modifications tx.Commit(); }
2. Parameter Extraction
Extract primitive values safely from the UI args dictionary:
double height = Convert.ToDouble(args["height"]);
string name = args["name"].ToString();
3. Revit Object Caching (ScriptCache)
Because Revit elements (like Level, Wall, FamilyInstance) cannot be serialized and sent to a web browser, we store them in a persistent cache (ScriptCache) in the Revit process.
- Storing (in a Query): Cache elements and return their GUID references to the UI:
var levels = new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>(); var resultList = new List<object>(); foreach (var lvl in levels) { string cacheId = ScriptCache.Store(lvl); resultList.Add(new { id = cacheId, name = lvl.Name }); } return JsonConvert.SerializeObject(resultList); - Retrieving (in a Command): Retrieve element references in command scripts using the GUID sent by the UI:
string cacheId = args["levelId"].ToString(); Level lvl = ScriptCache.Get<Level>(cacheId);
Script Templates & Code Examples
Query Script Example: GetLevelsQuery
This script fetches all levels from the active document, stores them in the ScriptCache, and returns their names and cache IDs:
using System;
using System.Collections.Generic;
using Autodesk.Revit.DB;
using Newtonsoft.Json;
var collector = new FilteredElementCollector(doc)
.OfClass(typeof(Level))
.WhereElementIsNotElementType();
var list = new List<object>();
foreach (Level lvl in collector)
{
string cacheId = ScriptCache.Store(lvl);
list.Add(new { id = cacheId, name = lvl.Name });
}
return JsonConvert.SerializeObject(list);
Command Script Example: BuildWallCommand
This script retrieves a level from the ScriptCache, draws a straight line, opens a transaction, and builds a wall:
using System;
using Autodesk.Revit.DB;
string levelCacheId = args["levelId"].ToString();
double heightFeet = Convert.ToDouble(args["height"]);
Level lvl = ScriptCache.Get<Level>(levelCacheId);
if (lvl == null)
{
return "Error: Level not found in cache";
}
// Draw a simple 10ft straight wall
XYZ p1 = new XYZ(0, 0, lvl.Elevation);
XYZ p2 = new XYZ(10, 0, lvl.Elevation);
Line line = Line.CreateBound(p1, p2);
Wall wall = null;
using (Transaction tx = new Transaction(doc, "Create Wall via UI"))
{
tx.Start();
wall = Wall.Create(doc, line, lvl.Id, false);
if (wall != null)
{
wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(heightFeet);
ch.DirectCreated.Add(wall.Id);
ch.AllCreated.Add(wall.Id);
}
tx.Commit();
}
return wall != null ? $"Wall Created with ID {wall.Id}" : "Failed to create wall";