AIXBT Docs

Recipe Specification

Declarative YAML pipelines for multi-step analysis workflows

Formal YAML schema reference for @aixbt/cli recipes (v1.1).

1. Overview

A recipe is a declarative YAML file that defines a multi-step data pipeline. Recipes fetch data from the AIXBT API (and optionally external providers for enrichment), iterate over results, apply transforms to control output size, and yield execution to agents for inference. The CLI handles all deterministic work. The recipe author defines what data to gather and when to involve an agent.

2. Recipe Structure

Top-level fields:

FieldTypeRequiredDescription
namestringyesRecipe identifier
versionstringnoRecipe version (defaults to "1.0", number also accepted and coerced to string)
descriptionstringnoHuman-readable description (defaults to "")
estimatedTokensnumber | nullnoAuthor estimate of output token count. Informational only.
paramsobjectnoParameter definitions (see Parameters)
requiredOneOfstring[]noExactly one of these params must be provided (see Parameters)
stepsarrayyesStep definitions (must be non-empty)
hintsobjectnoStructural hints for data consumers (see Hints)
analysisobjectnoAnalysis instructions for the consuming agent (see Analysis)

Minimal valid recipe:

name: example
version: "1.0"
steps:
  - id: projects
    type: api
    action: projects
    params:
      limit: 5

3. Parameters

Runtime parameters, provided via --<name> <value> flags:

FieldTypeRequiredDescription
type"string" | "number" | "boolean"yesParameter data type
requiredbooleannoWhether the caller must provide this parameter
descriptionstringnoHuman-readable description
defaultstring | number | booleannoDefault value if not provided

Parameters are referenced in steps via {params.<name>}. A required parameter with no default causes an error if omitted. Template resolution skips params that were not provided: "{params.tickers}" resolves to nothing if --tickers was not passed, so the API param is omitted from the request.

params:
  chain:
    type: string
    required: true
    description: Blockchain to scan
  limit:
    type: number
    default: 10

requiredOneOf

For params where the user must provide exactly one of several alternatives, use requiredOneOf at the top level:

params:
  projectIds: { type: string, description: "Comma-separated project IDs" }
  tickers: { type: string, description: "Comma-separated ticker symbols (e.g. SOL,ETH)" }
  names: { type: string, description: "Comma-separated project names" }
  address: { type: string, description: "Token contract address" }
requiredOneOf: [projectIds, tickers, names, address]

Rules:

  • The list must contain at least 2 param names
  • Each name must be a defined param
  • Params in the list must not have required: true (conflicts with oneOf semantics)
  • At runtime, zero provided → error. Two or more provided → error.

The step passes all params through; template resolution skips the ones not provided:

- id: projects
  type: api
  action: projects
  params:
    projectIds: "{params.projectIds}"
    tickers: "{params.tickers}"
    names: "{params.names}"
    address: "{params.address}"
aixbt recipe run project_deep_dive --tickers SOL
aixbt recipe run project_deep_dive --names "solana"
aixbt recipe run project_deep_dive --address 0x...

4. Step Types

Every step requires a type field: either api or agent.

API Steps (type: api)

FieldTypeRequiredDescription
idstringyesUnique step identifier
type"api"yesLiteral string identifying this as an API step
actionstringyesAction name (e.g., projects, signals, price)
sourcestringnoProvider name. Defaults to "aixbt".
paramsobjectnoParameters with template support
transformTransformBlocknoTransform applied to the response data
forstringnoIteration modifier (see Iteration with for:)
fallbackstringnoMessage for agent when step is skipped due to missing/insufficient provider key
- id: projects
  type: api
  action: projects
  params:
    limit: 10
    sortBy: momentumScore

- id: price_data
  type: api
  action: chart
  source: market
  params:
    geckoId: bitcoin
    limit: 30
  fallback: "Use publicly available price data instead."

Agent Steps (type: agent)

Yield execution to an external agent for inference, analysis, or decision-making.

FieldTypeRequiredDescription
idstringyesUnique step identifier
type"agent"yesLiteral string identifying this as an agent step
contextstring[]yesStep IDs whose data to include
instructionsstringyesDetailed instructions for the agent
returnsobjectyesMap of field names to type strings
forstringnoIf present, creates a parallel agent step (see below)

Pauses execution and yields to an external agent. The returns type strings: "string", "number", "boolean", "string[]", "object".

- id: picks
  type: agent
  context:
    - recent_signals
    - top_projects
    - clusters
  instructions: |
    Select 3-5 projects with the strongest recent signal activity.
    Focus on projects with rising momentum and multiple signal reinforcements.
  returns:
    projectIds: "string[]"
    rationale: "string"

Iteration with for:

The for: modifier works on both API and agent steps. It iterates over an array, executing the step for each item.

The for value is a bare reference (no curly braces) to an array-valued step result. Within a for: step, {item} and {item.field} reference the current iteration item.

API step with for: — executes an API call per item:

- id: momentum
  type: api
  for: projects.data
  action: momentum
  params:
    id: "{item.id}"
    start: "-7d"
    includeClusters: "false"

When individual items fail in a step with for: and fallback, those items degrade gracefully instead of failing the step. See Fallback Mechanism.

Agent step with for: — creates a fan-out pattern where the agent processes items in parallel:

- id: analyze
  type: agent
  for: projects.data
  context:
    - projects
    - momentum
    - signals
  instructions: |
    For each project, analyze its momentum trajectory and signal activity.
    Produce a brief assessment of risk and opportunity.
  returns:
    assessment: "string"
    risk_level: "string"

Transforms on for: API steps run per-iteration.

5. Provider System

The action/source pair determines which provider handles a step.

Virtual Providers

Virtual providers are provider-agnostic entry points that route to the best concrete backend. Use these in recipes for portability and smart routing.

source valueProviderWhat it wraps
"market"MarketCoinGecko/GeckoTerminal + DexPaprika — on-chain price, pools, OHLCV
"security"SecurityGoPlus — token and address security analysis
"defi"DeFiDeFiLlama — TVL, protocols, emissions, yields

Dotted Syntax

For shared actions that exist on multiple backends, use dotted syntax to force a specific one:

- id: price_data
  type: api
  action: token-ohlcv
  source: market.coingecko    # forces CoinGecko instead of default DexPaprika

Without the dot, the virtual provider picks its default backend.

Concrete Providers

You can also use concrete providers directly when you need a specific backend or actions not exposed through virtual providers:

source valueProvider
omitted or "aixbt"AIXBT API (default)
"coingecko"CoinGecko / GeckoTerminal
"dexpaprika"DexPaprika
"defillama"DeFiLlama
"goplus"GoPlus

Tier Requirements

Each action has a minimum tier (free, demo, or pro). If the configured key's tier is too low, the step degrades gracefully. Add a fallback field to control the message the agent receives. See Fallback Mechanism.

6. Fallback Mechanism

The fallback field on API steps (including those with for:) provides graceful degradation when a provider key is missing or the tier is insufficient. The string you write becomes an instruction to the agent running the recipe, telling it what to do instead.

API steps: the agent receives a message prefixed with context:

Step "<id>" was skipped: no <source> API key configured. <your fallback text>
- id: price_data
  type: api
  action: chart
  source: market
  params:
    network: ethereum
    address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
    timeframe: day
    limit: 30
  fallback: "Use publicly available price data instead."

The agent sees: Step "price_data" was skipped: no market API key configured. Use publicly available price data instead.

for: steps: items that succeed return data normally. Items that fail degrade gracefully, and the agent receives a collated note listing which items need alternative handling:

<your fallback text> for: <item1>, <item2>, <item3>
- id: tvl
  type: api
  for: projects.data
  action: protocol
  source: defi
  params: { protocol: "{item.name}" }
  fallback: "Look up TVL data using available tools."

If 3 of 10 items fail, the agent sees: Look up TVL data using available tools for: Uniswap, Aave, Compound

Without fallback, a provider unavailability (missing key, tier too low) still degrades gracefully rather than failing the recipe, but you lose control over the instruction.

7. Transforms

Transforms reduce and reshape data before it reaches agents, controlling token consumption.

Transform Block

FieldTypeDescription
selectstring[]Field projection: keep only these fields
sampleSampleTransformWeighted random sampling

At least one of select or sample should be present (both can be used together).

Execution order: sample always runs before select. This ensures weight fields are available during sampling even if they're excluded from the final projection.

Select

Selects specified fields from each item. Supports dot notation for nested fields.

transform:
  select:
    - id
    - name
    - metrics.usd
    - metrics.volume

Produces: { id: "...", name: "...", metrics: { usd: ..., volume: ... } }. Nested structure is preserved.

Sample

Weighted random sampling that controls output size by item count or token budget.

FieldTypeRequiredDescription
countnumberno*Fixed number of items to sample
tokenBudgetnumberno*Approximate token budget for the sampled output
guaranteePercentnumbernoFraction (0-1) of top-weighted items always included (default: 0.3)
guaranteeCountnumbernoFixed number of top-weighted items always included
weight_bystringnoField path for custom weights (dot notation supported)

Rules:

  • At least one of count or tokenBudget is required
  • If both are specified, count takes precedence
  • guaranteePercent and guaranteeCount are mutually exclusive
  • Guaranteed items are selected first (top by weight), then remaining slots are filled by weighted random sampling without replacement
  • Default weighting (when weight_by is omitted): favors recent, high-activity items
transform:
  sample:
    tokenBudget: 25000
    guaranteePercent: 0.3
    weight_by: momentumScore
  select:
    - id
    - name
    - description
    - momentumScore

8. Variable Templating

Template expressions use {...} syntax and are resolved in action, params, for, fallback, and analysis fields. They are not resolved in hints (passed through verbatim).

Expression Reference

ExpressionResolves to
{params.name}Value of recipe parameter name
{step_id}Step result data (shorthand for {step_id.data})
{step_id.data}Step result data (explicit)
{step_id.field}Shorthand for {step_id.data.field}
{step_id.data.nested.path}Nested field access
{step_id.data[*].field}Pluck: extract field from every array item
{step_id[*].field}Shorthand pluck (equivalent to above)
{item}Current for: iteration item
{item.field}Property of the current for: item

Relative Time Expressions

Bare relative time strings are converted to ISO 8601 timestamps at execution time:

ExpressionMeaning
-30m30 minutes ago
-24h24 hours ago
-7d7 days ago
params:
  detectedAfter: "-48h"                    # direct, resolved at execution
  start: "{params.lookback}"               # template: if lookback="-7d", resolved to ISO timestamp

Type Preservation

When an entire param value is a single template expression ("{step_id.field}"), the resolved type is preserved (arrays stay arrays, numbers stay numbers). When the expression is part of a larger string ("prefix {value} suffix"), it's interpolated as a string.

9. Segment Boundary Rule

Recipes are divided into segments by agent steps. This is the most important structural constraint to understand when writing recipes.

How segments work

Segment 0:  [api_step_1] → [api_step_with_for] → [agent_step_1]
                                                        | yield
Segment 1:  [api_step_2] → [api_step_3]
             ^ can access: agent_step_1 output + own segment steps
             x cannot access: api_step_1, api_step_with_for

An agent step ends a segment. The next segment begins after the agent step, and can access:

  1. The preceding agent step's output (the returns data provided at resume)
  2. Steps within its own segment that precede it

It cannot access steps from earlier segments (before the agent step). This is enforced at parse time.

10. Hints and Analysis Blocks

Hints

Passed through verbatim to the consuming agent; the CLI does not act on hints. They tell the agent how to interpret the data.

FieldTypeDescription
combinestring[]Step IDs whose data represents the same entities
keystringShared field that relates the combined datasets
includestring[]Step IDs to include as reference data alongside combined
hints:
  combine:
    - projects
    - momentum
  key: id
  include:
    - clusters

Analysis

Instructions for the consuming agent's final analysis. Template expressions ({params.*}) are resolved at execution time.

FieldTypeDescription
instructionsstringMain analysis instructions
outputstringOutput format directive
analysis:
  instructions: |
    Analyze the momentum patterns for {params.chain} projects.
    Focus on divergences between momentum score and price action.
  output: markdown

11. Recipe Writing Guide

Composition patterns

Single-segment (no agent): Pure data pipeline. All steps execute and the result is returned directly. Good for data collection and enrichment without inference.

steps:
  - id: protocols
    type: api
    action: protocols
    source: defi
    transform:
      sample:
        count: 20
      select: [name, tvl, chain, category]
  - id: chains
    type: api
    action: chains
    source: defi

Agent gate (two segments): Broad data fetch → agent picks → deep enrichment on picks. The most common pattern.

steps:
  # Segment 0: broad scan
  - id: signals
    type: api
    action: signals
    params: { reinforcedAfter: "-48h", limit: 50 }
  - id: top_projects
    type: api
    action: projects
    params: { limit: 10, sortBy: momentumScore }
  - id: picks
    type: agent
    context: [signals, top_projects]
    instructions: "Select 3-5 standout projects..."
    returns: { projectIds: "string[]" }
  # Segment 1: targeted enrichment
  - id: details
    type: api
    action: projects
    params: { projectIds: "{picks.projectIds}" }
  - id: narrative
    type: api
    for: details.data
    action: signals
    params:
      projectIds: "{item.id}"
      detectedAfter: "-30d"
    transform:
      sample: { tokenBudget: 25000, guaranteePercent: 0.3 }
      select: [description, detectedAt, category]

Multi-provider enrichment: Combine AIXBT data with virtual providers in a single pipeline.

steps:
  - id: projects
    type: api
    action: projects
    params: { limit: 10, hasToken: "true" }
  - id: tvl
    type: api
    for: projects.data
    action: protocol
    source: defi
    params: { protocol: "{item.name}" }
    fallback: "TVL data unavailable for this project."
    transform:
      select: [name, tvl, chainTvls]

Appendix: Validation

Run aixbt recipe validate <file> to check a recipe for errors without executing it.

On this page