> ## Documentation Index
> Fetch the complete documentation index at: https://docs.b3os.org/llms.txt
> Use this file to discover all available pages before exploring further.

# GET /v1/templates/recommended

Get personalized template recommendations based on user's tag interests

` GET /v1/templates/recommended `

Get personalized template recommendations based on user's tag interests

## Parameters

### ` category ` (query, required)

Template category: 'promoted', 'public', 'org', 'all'

- ` value `: type ` string `

### ` limit ` (query, optional)

Maximum number of templates to return (default: 20, max: 100)

- ` value `: type ` integer `

### ` strategy ` (query, optional)

Recommendation strategy: 'strong_match', 'interest_based' (default), 'popular_with_interest'

- ` value `: type ` string `

## Responses

### ` 200 ` — OK

Content type: ` application/json `

- ` value `: type ` object `

  - ` code `: type ` integer `

    - example: ` 200 `

  - ` data `: type ` array `

    - ` array item `: type ` object `

      - ` archived `: type ` boolean `

      - ` category `: type ` string ` — Deprecated: Use Tags instead

      - ` cooldownMs `: type ` integer `

      - ` createdAt `: type ` string `

      - ` createdBy `: type ` string `

      - ` definition `: type ` object ` — Definition is the inline workflow graph to analyze. Same shape as a saved workflow's definition. Unlike the ephemeral-run endpoint there is no trigger restriction — analysis is read-only and method-agnostic.

        - Required fields: ` nodes `

        - ` blockExpansions `: type ` object ` — Set on run snapshots only (not workflow DB)

        - ` inputSchema `: type ` array `

          - ` array item `: type ` object `

            - ` description `: type ` string `

            - ` key `: type ` string `

            - ` required `: type ` boolean `

            - ` type `: type ` string ` — "string", "number", "boolean", "object", "array"

        - ` nodes `: type ` object `

        - ` sensitivePropKeys `: type ` array ` — Legacy: kept for old runs; no longer populated for new workflows

          - ` array item `: type ` string `

        - ` triggerNodeIds `: type ` array ` — TriggerNodeIDs lists the node IDs that are trigger (root) nodes. Every workflow declares this — single-trigger workflows ship ["root"] (the legacy node id), multi-trigger workflows list every trigger node id. Treating single-trigger as a forest-of-1 removes the two-path branching throughout the BE + FE; older rows without the field are backfilled by migration 000282 and the field-missing path stays as a read-side safety net (see FindTriggerNodeIDs) but is no longer exercised by saves.  "root" is also a runtime alias for "the trigger that fired this run" — {{root.X}} variable references resolve to the firing trigger regardless of which trigger fired. Don't repurpose the literal "root" as a trigger id on a multi-trigger workflow.

          - ` array item `: type ` string `

        - ` variableDefs `: type ` array ` — VariableDefs is a snapshot of the workflow's declared variables at run creation time. The canonical source lives on the workflows row (Workflow.VariableDefs column). Snapshotted into the run definition so the worker can resolve {{$vars.x}} lookups and route variable-action writes to the right scope without an extra DB round trip.

          - ` array item `: type ` object `

            - ` default `: type ` unknown `

            - ` description `: type ` string `

            - ` lifetime `: type ` string `

              - enum: ` ["persist","reset"] `

            - ` name `: type ` string `

            - ` type `: type ` string `

              - enum: ` ["number","text","boolean","list","object"] `

      - ` description `: type ` string `

      - ` forkCount `: type ` integer `

      - ` generatedAppId `: type ` string ` — Generated-app attachment (nil when the template ships no app). On adoption the referenced app is CLONED into the adopting org's new workflow. GeneratedAppVersionID optionally pins a specific version whose manifest is used as the clone source.

      - ` generatedAppVersionId `: type ` string `

      - ` id `: type ` string `

      - ` isPromoted `: type ` boolean `

      - ` maxRuns `: type ` integer `

      - ` moderationStatus `: type ` string ` — Moderation fields (B3-4168). Only meaningful for public templates; org templates are always approved.

        - enum: ` ["pending","approved","rejected"] `

      - ` name `: type ` string `

      - ` organizationDescription `: type ` string `

      - ` organizationId `: type ` string ` — Fields for user-generated templates (nil for built-in templates)

      - ` organizationName `: type ` string `

      - ` organizationPhoto `: type ` string `

      - ` promotedAt `: type ` string `

      - ` promotedBy `: type ` string `

      - ` promotedOrder `: type ` integer `

      - ` rejectionReason `: type ` string `

      - ` reviewedAt `: type ` string `

      - ` reviewedBy `: type ` string `

      - ` slug `: type ` string `

      - ` sourceWorkflowId `: type ` string ` — Source workflow link (nil for built-in or legacy templates)

      - ` submittedAt `: type ` string `

      - ` tableSchemas `: type ` object `

      - ` tags `: type ` array `

        - ` array item `: type ` object `

          - ` categories `: type ` array ` — e.g., ["blockchain", "finance"]

            - ` array item `: type ` string `

          - ` count `: type ` integer ` — Scope-dependent: template, action, trigger, or connector count

          - ` createdAt `: type ` string `

          - ` description `: type ` string `

          - ` id `: type ` string `

          - ` imageUrl `: type ` string `

          - ` name `: type ` string `

          - ` promotedTemplateCount `: type ` integer `

          - ` publicTemplateCount `: type ` integer `

          - ` slug `: type ` string `

          - ` updatedAt `: type ` string `

          - ` weight `: type ` integer ` — Higher weight = more prominent

      - ` templateProps `: type ` array `

        - ` array item `: type ` object `

          - ` default `: type ` unknown `

          - ` description `: type ` string `

          - ` key `: type ` string `

          - ` name `: type ` string `

          - ` properties `: type ` object `

            - ` accountType `: type ` string ` — AccountType filters the wallet picker by account type on inputType="walletConnector" fields (e.g. "polymarket_deposit_wallet").

            - ` allowWalletPicker `: type ` boolean ` — AllowWalletPicker controls the org-wallet picker on inputType="address" fields. When false, the frontend hides the inline "Wallets" badge and the org-wallet autocomplete popover. Use for third-party address fields (e.g. a deployer filter) where suggesting org wallets is misleading. Pointer so explicit false survives JSON round-trip (default true on FE).

            - ` allowedChannels `: type ` array ` — AllowedChannels constrains the notification-channel picker (inputType: "notification-channel") to a subset of channels. Used by templates whose downstream nodes only work with one channel type (e.g. ["slack"] for a Slack-only approval flow). Frontend ignores unknown keys; a length-1 list auto-selects in the picker.

              - ` array item `: type ` string `

            - ` autoFillFromWallet `: type ` boolean ` — AutoFillFromWallet, when true, auto-fills this prop with the selected workflow wallet address and hides it from the form. Pointer so an explicit false survives the JSON round-trip (same rationale as AllowWalletPicker).

            - ` bindsConnectorNodes `: type ` array ` — BindsConnectorNodes lists workflow node IDs whose wallet-type connector should be prefilled from this prop's picked wallet (inputType: "walletConnector"). The connector stays a plain, editable connector — this is a prefill, not a permanent bind.

              - ` array item `: type ` string `

            - ` chainId `: type ` integer ` — For tokenAmount: hardcoded chain ID

            - ` chainPropKey `: type ` string ` — For tokenSelector/tokenAmount: prop key providing chainId

            - ` columns `: type ` array ` — Columns describes a csvTable prop (inputType: "csvTable"). The stored value is an object-of-columnar-arrays: {colKey: []value}. Payload templates reference each column via {{$props.<propKey>.<colKey>}}.

              - ` array item `: type ` object `

                - ` chainId `: type ` integer `

                - ` chainPropKey `: type ` string ` — For tokenAmount cells

                - ` inputType `: type ` string ` — address, recipientAddress, tokenAmount, text (see frontend CELL_REGISTRY)

                - ` key `: type ` string `

                - ` name `: type ` string `

                - ` pattern `: type ` string `

                - ` placeholder `: type ` string `

                - ` required `: type ` boolean `

                - ` tokenAddress `: type ` string `

                - ` tokenAddressPropKey `: type ` string ` — For tokenAmount cells

                - ` type `: type ` string ` — "string", "number", "integer"

            - ` dex `: type ` string ` — For hyperliquidAsset: hardcoded DEX name (fallback when no dexPropKey)

            - ` dexPropKey `: type ` string ` — For hyperliquidAsset: prop key providing HIP-3 DEX name

            - ` enum `: type ` array `

              - ` array item `: type ` unknown `

            - ` enumLabels `: type ` array `

              - ` array item `: type ` string `

            - ` excludeNative `: type ` boolean ` — For tokenSelector/multiTokenSelector: hide native tokens (field only accepts ERC-20 contract addresses)

            - ` groupKey `: type ` string ` — Groups related props for unified UI rendering

            - ` inputType `: type ` string ` — InputType is a UI rendering hint. The frontend uses it to pick a rich selector component instead of a plain text input. Unknown values are silently ignored (falls back to default input). Valid values: chainSelector, tokenSelector, multiTokenSelector, tokenAmount, coinSelector, multiCoinSelector, address, contractAddress, recipientAddress, email, telegram-chat, slack-channel, pushover-recipient, pushbullet-device, webpush-subscription, textarea, password, polymarketUser, twitterUsername, storkAsset, hyperliquidAsset, morphoVault, morphoMarket, csvTable, notification-channel, googleSheetUrl, rrule, walletConnector

            - ` maxRows `: type ` integer ` — csvTable: maximum allowed rows (0 = no cap)

            - ` maximum `: type ` number `

            - ` minRows `: type ` integer ` — csvTable: minimum required rows (0 = default 1)

            - ` minimum `: type ` number `

            - ` pattern `: type ` string `

            - ` placeholder `: type ` string `

            - ` showWalletBalances `: type ` boolean ` — For tokenSelector: show wallet balances (useful for "sell" tokens)

            - ` sources `: type ` array ` — Sources lists the workflow node fields this prop was created from. Each entry identifies a node + field path so the frontend can reliably match props back to their source fields on reload. Multiple entries indicate the prop was merged from fields with identical values across different nodes.

              - ` array item `: type ` object `

                - ` fieldPath `: type ` string `

                - ` nodeId `: type ` string `

                - ` nodeLabel `: type ` string `

            - ` summary `: type ` object ` — Summary drives the csvTable footer summary. Nil means no summary is rendered. See PropSchemaSummary for details.

              - ` aggregates `: type ` array ` — Aggregates declared in display order, left-to-right.

                - ` array item `: type ` object `

                  - ` column `: type ` string ` — Column key in PropSchema.Columns.

                  - ` label `: type ` string ` — Optional override for the displayed label. If empty, the frontend derives a label from the column + reducer (e.g. "Total amount").

                  - ` reduce `: type ` string ` — Reducer name. Matches the key in the frontend cell-spec's `aggregate` map (currently: "sum", "count"). Unknown reducers are silently skipped on the frontend.

              - ` position `: type ` string ` — Position in the grid. "footer" (default) renders inline under the table. "none" suppresses even when aggregates are declared — use to disable inherited summaries. Other values reserved.

            - ` tokenAddress `: type ` string ` — For tokenAmount: hardcoded token address

            - ` tokenAddressPropKey `: type ` string ` — For tokenAmount: prop key providing tokenAddress

            - ` visibleWhen `: type ` object ` — VisibleWhen controls conditional visibility: show this prop only when the referenced prop has the specified value.

              - ` propKey `: type ` string `

              - ` value `: type ` unknown `

          - ` required `: type ` boolean `

          - ` sensitive `: type ` boolean `

          - ` type `: type ` string ` — "string", "number", "integer", "boolean", "object" (for csvTable)

      - ` uiMetadata `: type ` object `

        - ` comments `: type ` array `

          - ` array item `: type ` object `

            - ` createdAt `: type ` string `

            - ` createdBy `: type ` object `

              - ` clientId `: type ` string `

              - ` name `: type ` string `

            - ` id `: type ` string `

            - ` nodeId `: type ` string `

            - ` position `: type ` object `

              - ` x `: type ` number `

              - ` y `: type ` number `

            - ` replies `: type ` array `

              - ` array item `: type ` object `

                - ` createdAt `: type ` string `

                - ` createdBy `: type ` object `

                  - ` clientId `: type ` string `

                  - ` name `: type ` string `

                - ` id `: type ` string `

                - ` text `: type ` string `

                - ` updatedAt `: type ` string `

            - ` resolved `: type ` boolean `

            - ` text `: type ` string `

            - ` updatedAt `: type ` string `

        - ` nodePositions `: type ` object `

        - ` stickyNotes `: type ` array `

          - ` array item `: type ` object `

            - ` color `: type ` string `

            - ` createdAt `: type ` string `

            - ` createdBy `: type ` object `

              - ` clientId `: type ` string `

              - ` name `: type ` string `

            - ` id `: type ` string `

            - ` position `: type ` object `

              - ` x `: type ` number `

              - ` y `: type ` number `

            - ` size `: type ` object `

              - ` height `: type ` number `

              - ` width `: type ` number `

            - ` text `: type ` string `

            - ` updatedAt `: type ` string `

            - ` zIndex `: type ` integer `

      - ` updatedAt `: type ` string `

      - ` updatedBy `: type ` string `

      - ` version `: type ` integer `

      - ` visibility `: type ` string `

        - enum: ` ["org","public","org","public"] `

  - ` message `: type ` string `

    - example: ` "success" `

  - ` requestId `: type ` string `

    - example: ` "req_abc123" `

### ` 400 ` — Bad Request

Content type: ` application/json `

- ` value `: type ` object `

  - ` code `: type ` integer `

  - ` details `: type ` array `

    - ` array item `: type ` unknown `

  - ` message `: type ` string `

  - ` requestId `: type ` string `

### ` 401 ` — Unauthorized

Content type: ` application/json `

- ` value `: type ` object `

  - ` code `: type ` integer `

  - ` details `: type ` array `

    - ` array item `: type ` unknown `

  - ` message `: type ` string `

  - ` requestId `: type ` string `

### ` 500 ` — Internal Server Error

Content type: ` application/json `

- ` value `: type ` object `

  - ` code `: type ` integer `

  - ` details `: type ` array `

    - ` array item `: type ` unknown `

  - ` message `: type ` string `

  - ` requestId `: type ` string `

## Request examples

### cURL

```curl
curl -X GET 'https://api.example.com/v1/templates/recommended?category=string' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

### JavaScript

```javascript
const response = await fetch('https://api.example.com/v1/templates/recommended?category=string', {
  method: 'GET',
  headers: {
      "Authorization": "Bearer YOUR_API_TOKEN"
  }
});

const data = await response.json();
console.log(data);
```

### Python

```python
import requests

headers = {
    'Authorization': 'Bearer YOUR_API_TOKEN'
}

response = requests.get('https://api.example.com/v1/templates/recommended?category=string', headers=headers)
print(response.json())
```

### Go

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.example.com/v1/templates/recommended?category=string", nil)
	req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN")

	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
	result, _ := io.ReadAll(resp.Body)
	fmt.Println(string(result))
}
```