Actions
Actions
Section titled “Actions”Actions are how AI does things through String — calling APIs, running commands, submitting data. An action is declared inside a document, invoked with a single line, and its response comes back as Markdown.
What AI sees
Section titled “What AI sees”When AI reads a document, actions appear as one-line hints:
# Weather Dashboard
**Search for a city:**`/act.search_city --name "{City Name}"`
**Set a weather alert:**`/act.create_alert --condition "{rain|snow|temp}"`That’s all the AI needs. The name, the flags, the expected values — readable at a glance. The AI doesn’t see the underlying implementation. It doesn’t need to.
If the AI wants more detail, it asks:
/act.search_city --helpString returns the full schema — parameter types, which are required, what the action does. But this is on-demand, not forced into context.
Where actions are defined
Section titled “Where actions are defined”Action definitions live inside the same document, in fenced code
blocks with an act. info string:
```act.search_cityGET https://api.weather.com/search name: string (required) "City name to search" unit: string (optional) "celsius|fahrenheit"```String parses these blocks and registers the actions. The blocks are not shown to the AI when it reads the document — they are metadata, not content. In a standard Markdown viewer, they render as ordinary code blocks. Graceful degradation.
Action types
Section titled “Action types”The first line of the definition block declares the HTTP method
(or CLI for shell commands).
GET https://api.weather.com/search name: string (required) "City name" unit: string (optional) "celsius|fahrenheit"Parameters are appended as a query string. The AI invokes it as:
/act.search_city --name "Seoul"String sends GET https://api.weather.com/search?name=Seoul.
POST https://api.weather.com/alerts city: string (required) "Topic city" condition: string (required) "rain|snow|temp" threshold: number (optional) "Trigger value"Parameters are sent as a JSON body. The AI invokes it as:
/act.create_alert --city "Seoul" --condition "rain"String sends a POST with {"city": "Seoul", "condition": "rain"}.
PUT / PATCH
Section titled “PUT / PATCH”PUT https://api.weather.com/alerts/{alert_id} alert_id: string (required) "Alert ID" condition: string (required) "New condition"PATCH https://api.weather.com/users/{user_id}/settings user_id: string (required) "User ID" unit: string (optional) "celsius|fahrenheit"Same as POST — parameters are sent as a JSON body. PUT replaces the entire resource, PATCH updates specific fields. Standard REST semantics.
DELETE
Section titled “DELETE”DELETE https://api.weather.com/alerts/{alert_id} alert_id: string (required) "Alert ID"Same as GET — parameters are appended as a query string (or substituted into the URL path).
CLI kubectl apply -f {manifest} manifest: path (required) "Kubernetes manifest file"String executes the command with parameters substituted. The AI invokes it as:
/act.deploy --manifest ./app.yamlString runs kubectl apply -f ./app.yaml.
Headers
Section titled “Headers”HTTP actions can declare custom headers using curl-style -H flags
on the first line:
GET https://gmail.googleapis.com/v1/users/me/messages -H "Authorization: Bearer {access_token}" maxResults: number (optional) q: string (optional) "Gmail search query"POST https://api.github.com/repos/{repo}/issues -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3+json" title: string (required) body: string (optional)Multiple headers: -H "..." -H "..." on the same line. Both
{var} (session) and $var (persistent) are both substituted in
header values.
String does not inject default headers for actions — only the
declared -H headers are sent. This is standard REST behavior.
(Accept: text/markdown is only injected for /open, not /act.)
Invocation syntax
Section titled “Invocation syntax”Actions follow GNU/POSIX CLI conventions. All of the following forms are valid:
/act.search_city --name "Seoul"/act.search_city --name=Seoul/act.search_city -n Seoul/act.search_city SeoulFlag forms
Section titled “Flag forms”- Long flag, separate value:
--name value - Long flag, attached value:
--name=value(GNU style) - Short alias:
-n value(when the action declaresname, -n) - Boolean long:
--verbose(no value =true) - Quoted values:
--name "value with spaces"
Positional arguments
Section titled “Positional arguments”Bare values (no leading -- or -) bind to the action’s required
fields, in declaration order. Optional fields are never bound
positionally — pass them by flag.
For an action declared as:
prompt, -p: string (required) "Image description" filename, -f: string (required) "Output path" resolution, -r: string "1K, 2K, or 4K" = "1K"these all produce the same payload:
/act.generate --prompt "a serene japanese garden" --filename out.png/act.generate "a serene japanese garden" out.png/act.generate "a serene japanese garden" --filename out.png/act.generate -p "a serene japanese garden" -f out.pngA positional value never overrides a flag: if a required field has
already been set by --name value, it is skipped when binding the
remaining positionals. Passing more positionals than there are
unfilled required fields is an error.
-- (end of options)
Section titled “-- (end of options)”A bare -- token ends option processing. Everything after it is
treated as a positional, even if it starts with -. This is the
standard POSIX escape hatch for values that look like flags:
/act.search -- --tricky-queryHere --tricky-query binds to the first required field rather than
being parsed as an unknown long flag.
Action verb
Section titled “Action verb”/act and /action are interchangeable; dot-notation and
space-separated forms are both valid:
/act.search_city --name "Seoul"/act search_city --name "Seoul"/action.search_city --name "Seoul"/action search_city --name "Seoul"Dot-notation is preferred in documentation and action hints for readability.
Response templates
Section titled “Response templates”By default, String passes the raw response to the AI as Markdown. This works for many cases — the AI reads the result and decides what to do next.
For structured responses, a response template can format the output and store values for later use. Response templates are defined in a companion code block:
```act.search_city.response{temp} = {Response.body.temperature}{city} = {Response.body.city}## {city}- Temperature: {temp}°C- Condition: {Response.body.condition}- Humidity: {Response.body.humidity}%```Two things happen in a response block
Section titled “Two things happen in a response block”Assignment lines — {var} = {expression}
Lines matching {identifier} = ... are variable assignments. The
value is extracted from the response and stored as a session variable.
Assignment lines produce no output.
{temp} = {Response.body.temperature}{city} = {Response.body.city}Available response references:
| Reference | What it returns |
|---|---|
{Response.body.field} | A specific field from the response body |
{Response.body} | The entire response body |
{Response.status} | HTTP status code (e.g. 200) |
Output lines — everything else
All other lines are rendered as a Markdown template. Variables (both just-assigned and previously stored) are substituted.
## {city}- Temperature: {temp}°CExamples
Section titled “Examples”Display only — no assignments, just formatting:
```act.get_status.response**Status:** {Response.body.status}**Uptime:** {Response.body.uptime}```Store only — assignments with no output lines. The action runs silently, storing values for later use:
```act.get_token.response{auth_token} = {Response.body.token}{expires} = {Response.body.expires_at}```Both — store values and display formatted output:
```act.search_city.response{temp} = {Response.body.temperature}{city} = {Response.body.city}## Weather in {city}- Temperature: {temp}°C- Condition: {Response.body.condition}```Session variables persist within the topic. A value stored by one action can be used by subsequent actions or templates in the same session.
Single-page and multi-page apps
Section titled “Single-page and multi-page apps”Actions and response templates enable two patterns for building String apps.
Single-page app
Section titled “Single-page app”Everything in one document. Actions are defined inline, response templates format the results in place.
# Weather Dashboard
`/act.search_city --name "{City Name}"``/act.get_forecast --city "{City}" --days "7"`
```act.search_cityGET https://api.weather.com/search name: string (required)```
```act.search_city.response{city} = {Response.body.city}{temp} = {Response.body.temperature}## {city}- Temperature: {temp}°C- Condition: {Response.body.condition}```
```act.get_forecastGET https://api.weather.com/forecast city: string (required) days: number (optional)```
```act.get_forecast.response## {Response.body.city} — {Response.body.days}-Day Forecast{Response.body.forecast}```The AI stays on one page. Actions update the view through templates.
Multi-page app
Section titled “Multi-page app”Actions and content spread across multiple documents. Each page is
a separate .md file with its own actions.
weather-app/├── string.md # Home — search action├── forecast.md # 7-day forecast page├── alerts.md # Alert management└── nav/ └── main.md # Navigation menuThe AI navigates with /open and acts with /act. Each page
declares its own actions. The forecast page has forecast-related
actions. The alerts page has alert-related actions.
# string.md — navigates to forecast/act.search_city --name "Seoul"→ response includes [7-Day Forecast](./forecast.md)→ AI: /open ./forecast.md
# forecast.md — has its own actions/act.get_forecast --city "Seoul" --days "7"Choosing between them
Section titled “Choosing between them”Single-page works for simple, focused tools — a calculator, a status dashboard, a quick search. Multi-page works for larger systems with distinct sections — an email client, a project management tool, a documentation site.
Both patterns use the same primitives: /act for actions, /open
for navigation. The difference is in how the documents are organized,
not in how the AI interacts with them.
Default action
Section titled “Default action”A document can declare a default action in its frontmatter:
---default: get_weather---When the document is opened (/open), String automatically executes
act.get_weather. The AI sees the document content and the action
result together, separated by a rule:
# Weather Dashboard
Check the weather for any city.
`/act.get_weather --city "{city}"``/act.set_alert --condition "{condition}"`
---## Seoul- Temperature: 22°C- Condition: SunnyDocument first, result after. The AI reads the page context (what this app does, what actions are available) and the live data in one turn.
When AI invokes an action directly
Section titled “When AI invokes an action directly”If the AI runs /act.get_weather --city "Tokyo" after opening the
page, only the response is returned — the document content is
already in the AI’s context from the initial open.
On refresh
Section titled “On refresh”/refresh re-renders the document and re-runs the default action,
producing the combined output again.
Why document-declared
Section titled “Why document-declared”The default is part of the document’s design, not an AI preference. A weather dashboard should load weather on open. An inbox should load messages. The author declares this intent in frontmatter.
If no default is declared, /open shows the document content
only. The AI decides what to do next.
Summary
Section titled “Summary”| Concept | How |
|---|---|
| Invocation | /act.name --flag value — also --flag=value, short -f, positional in declaration order, -- to end options |
| Definition | ```act.name code block (parsed by String, hidden from AI) |
| Types | GET, POST, PUT, PATCH, DELETE, CLI — first line of definition |
| Headers | -H "Key: Value" on first line — curl syntax, supports {var} and $var |
| Response template | ```act.name.response — optional formatting |
| Assignment | {var} = {Response.body.field} — session variable |
| Output | All non-assignment lines — rendered as Markdown |
| Help | /act.name --help — on-demand schema |
| Default action | default: name in frontmatter — runs on /open and /refresh |