Skip to content

Plugin API Reference

This is the complete API reference for the Freeway Plugin SDK. All functions are available via import freeway.


Cancel the current operation and stop the plugin chain.

freeway.cancel()

Effects:

  • In before_paste: Prevents text from being pasted, stops subsequent plugins
  • In before_recording or before_transcribe: Cancels the recording/transcription, stops subsequent plugins

Use case: Voice commands that should trigger an action but not paste text.

def before_paste():
text = freeway.get_text()
if "cancel that" in text.lower():
freeway.cancel()
freeway.log("Operation cancelled by user")

Skip the paste operation but continue executing subsequent plugins.

freeway.skip_paste()

Effects:

  • Only effective in before_paste hook
  • Text will not be pasted
  • after_paste and on_stop_recording hooks will still run

Use case: Plugins that handle output themselves (e.g., saving to file).

def before_paste():
text = freeway.get_text()
# Save to file instead of pasting
with open("/tmp/transcription.txt", "w") as f:
f.write(text)
freeway.skip_paste()
freeway.log("Saved to file instead of pasting")

Stop executing subsequent plugins but continue with the operation.

freeway.stop_chain()

Effects:

  • For before_paste: Text will still be pasted, but remaining plugins won’t run

Use case: When your plugin has fully processed the text and other plugins shouldn’t modify it.

def before_paste():
text = freeway.get_text()
# Apply final formatting
freeway.set_text(text.strip())
# Don't let other plugins modify this
freeway.stop_chain()

Get the current transcribed text.

text = freeway.get_text()

Returns: str | None — The transcribed and normalized text, or None if not available.

Availability: Only in hooks that have access to text (before_paste, after_paste).

def before_paste():
text = freeway.get_text()
if text and "hello" in text.lower():
freeway.log("User said hello!")

Replace the transcribed text with a new value.

freeway.set_text("new text")

Parameters:

  • text (str): The new text to use for pasting

Effects:

  • Only effective in before_paste hook
  • Subsequent plugins will receive the modified text
def before_paste():
text = freeway.get_text()
# Convert to title case
freeway.set_text(text.title())

Get a user setting value by key.

value = freeway.get_setting("api_key")

Parameters:

  • key (str): The setting name from meta.json

Returns: The setting value, or None if not found.

def before_paste():
api_key = freeway.get_setting("api_key")
model = freeway.get_setting("model_name")
if not api_key:
freeway.log("API key not configured")
return

Get all user settings as a dictionary.

settings = freeway.get_settings()

Returns: dict — Dictionary of all setting name-value pairs.

def before_paste():
settings = freeway.get_settings()
if settings.get("enabled"):
process(settings.get("api_key"))

Get the path to the recorded audio file.

wav_path = freeway.get_wav_path()

Returns: str | None — Full path to voice.wav in the temp session folder.

Availability: Not available in before_recording hook.

def before_paste():
wav_path = freeway.get_wav_path()
if wav_path:
# Process the audio file
audio_duration = get_audio_duration(wav_path)
freeway.log(f"Audio duration: {audio_duration}s")

Get the path to the recording metadata file.

meta_path = freeway.get_meta_path()

Returns: str | None — Full path to meta.json in the temp session folder.

The metadata file contains:

  • id: Session UUID
  • app_version: Freeway version
  • recorded_at: ISO timestamp
  • audio_duration: Duration in seconds
  • sample_rate: Audio sample rate
  • microphone_id: Device ID
  • microphone_name: Device name
  • text: Transcribed text
  • word_count: Number of words
import json
def before_paste():
meta_path = freeway.get_meta_path()
with open(meta_path) as f:
meta = json.load(f)
freeway.log(f"Recording ID: {meta['id']}")
freeway.log(f"Duration: {meta['audio_duration']}s")

Get the path to the temp session directory.

temp_dir = freeway.get_temp_dir()

Returns: str | None — Full path to the Temp/<UUID>/ directory for this session.

Use case: Store temporary files during processing.

import os
def before_paste():
temp_dir = freeway.get_temp_dir()
output_path = os.path.join(temp_dir, "processed.txt")
with open(output_path, "w") as f:
f.write(processed_text)

Display status text in the Freeway visualizer panel.

freeway.set_status_text("Processing...")

Parameters:

  • text (str): Status message to display

Effects:

  • Shows a puzzle piece icon with the text in the visualizer footer
  • Automatically cleared when the hook completes

Use case: Show progress during long operations.

def before_paste():
freeway.set_status_text("Processing with AI...")
result = call_ai_api(text)
freeway.set_status_text("Done!")

Change the menu bar indicator color.

freeway.set_indicator_color("#ff9500")

Parameters:

  • color (str): HEX color string (e.g., "#ff9500", "#00ff00")

Effects:

  • Changes the recording indicator in the menu bar icon
  • Automatically reset when the hook completes
def before_paste():
# Red for error
freeway.set_indicator_color("#ff0000")
# Green for success
freeway.set_indicator_color("#00ff00")
# Blue for processing
freeway.set_indicator_color("#0000ff")

Get the current system appearance theme.

theme = freeway.get_appearance_theme()

Returns: str"light" or "dark" based on macOS system appearance.

Use case: Adapt visual feedback to the current theme.

def before_paste():
theme = freeway.get_appearance_theme()
if theme == "dark":
freeway.set_indicator_color("#ffffff")
else:
freeway.set_indicator_color("#000000")

Press a keyboard shortcut.

freeway.press_keys(["Command", "V"])

Parameters:

  • keys (list[str]): List of key names

Key names (macOS conventions):

  • Modifiers: "Command", "Control", "Option", "Shift"
  • Letters: "A", "B", "C", etc.
  • Special: "Return", "Tab", "Space", "Escape"
  • Arrows: "UpArrow", "DownArrow", "LeftArrow", "RightArrow"
def before_paste():
# Paste shortcut
freeway.press_keys(["Command", "V"])
# Show hidden files in Finder
freeway.press_keys(["Command", "Shift", "."])

Release previously pressed keys.

freeway.release_keys(["Command", "V"])

Parameters:

  • keys (list[str]): List of key names to release
import time
def before_paste():
# Hold Shift
freeway.press_keys(["Shift"])
time.sleep(0.1)
# Release Shift
freeway.release_keys(["Shift"])

Get the trigger configuration for this plugin.

trigger = freeway.get_trigger()

Returns: dict | None — Dictionary with trigger settings, or None if no trigger is configured.

Dictionary keys:

  • pattern (str | None): The trigger pattern from settings
  • match_type (str | None): The match type — "startsWith", "endsWith", "match", "contains", or "regex"

Match types:

TypeDescription
startsWithText must start with the pattern
endsWithText must end with the pattern
matchText must exactly match the pattern (case-insensitive)
containsText must contain the pattern anywhere (default)
regexPattern is treated as a regular expression

Regex patterns:

Regex uses ICU Regular Expressions syntax (NSRegularExpression). Matching is case-insensitive by default. Text is normalized before matching: punctuation is removed and whitespace is trimmed.

PatternMatches
^hey freewayText starting with “hey freeway”
please$Text ending with “please”
^(open|start) chat”open chat” or “start chat” at the beginning
^(open|launch|start)Text starting with “open”, “launch”, or “start”
send (email|message) to”send email to…” or “send message to…”
remind me in \d+ minutes”remind me in 5 minutes”, “remind me in 30 minutes”
^translate .+ to (spanish|french|german)$”translate hello to spanish”
^(what|who|where|when|why|how)\bQuestions starting with interrogative words

Regex examples in meta.json:

{
"trigger": {
"pattern": "^(hey|ok|hi) freeway",
"matchType": "regex"
}
}
{
"trigger": {
"pattern": "^search (for |on )?",
"matchType": "regex"
}
}

Note: Trigger matching is handled automatically by Freeway — your plugin only runs when the text matches. Use get_trigger() when you need to know the pattern (e.g., to strip trigger phrase from text).

def before_paste():
trigger = freeway.get_trigger()
text = freeway.get_text()
if trigger and trigger.get("pattern"):
pattern = trigger["pattern"]
match_type = trigger.get("match_type", "contains")
freeway.log(f"Triggered by: {pattern} ({match_type})")
# Strip trigger phrase from start of text
if match_type == "startsWith":
text_lower = text.lower()
pattern_lower = pattern.lower()
if text_lower.startswith(pattern_lower):
text = text[len(pattern):].strip()
freeway.set_text(text)

Log a message to the Freeway console.

freeway.log("Debug message")

Parameters:

  • message (str): The message to log

Use case: Debugging and tracking plugin behavior.

def before_paste():
freeway.log("Processing started")
result = process_text(text)
freeway.log(f"Processed {len(result)} characters")

View logs in Freeway’s debug console (accessible from the menu bar icon).