Build with it / Research pipelines

Turn one question
into a pipeline.

Chain research runs, checks, approvals and deliveries, then let them run on a schedule. Build a pipeline in the visual builder, through the API, or let your AI agent run it over MCP.

Visual builderREST APIMCPSigned webhooks
How a pipeline works

01TriggerOn demand or on a schedule.

02ResearchOne question, or one per entity.

03DecideA change check, a readiness score, a person.

04DeliverPDF, notification or your webhook.

WHY A PIPELINE

Ask once. Hear about it when it matters.

Some questions need asking again and again. Prices move, rules change, competitors ship.

A pipeline asks for you on schedule, compares each answer with the last one, and only bothers anyone when something real changed. When it does, a person can sign off before the result goes anywhere, and then it lands where your team works: a PDF, a notification, or your own system through a webhook.

01 / AN EXAMPLE

A weekly price watch.

Every Monday it researches three competitors' pricing, keeps going only for the ones whose answer moved, waits for a sign-off, then sends a PDF and posts the report to a CRM.

  1. 01TriggerEvery Monday, 8:00Weekly schedule, America/New_York
  2. 02Fan outThree competitorsAcme Analytics, Northwind Data, Contoso Cloud
  3. 03ResearchPrice checkStandard run per competitor, compared with last week
  4. 04ConditionOnly if it movedPass when 15% or more of the claims changed
  5. 05ApprovalPricing lead signs offWaits up to 72 hours
  6. 06Export + webhookPDF and a webhookExport the report, then post it to your CRM

Only research steps use credits: three Standard runs a week here. The condition, the approval and the deliveries are free, and validation tells you the ceiling before anything runs.

02 / THE STEPS

Eleven steps. Any order that makes sense.

Connect them in a line, branch on a condition, or fan out across a list. Steps that work on a report need a research step earlier on their branch, and the builder tells you when one is missing.

Start

When it runs, and what it runs for.

Triggertrigger
Run on demand, or on a daily, weekly or monthly schedule in your time zone.
Fan outfan_out
Repeat the steps after it for up to 25 entities. Write {{entity}} in a question to use each one.
Research

The steps that do the work, and the only ones that use credits.

Researchresearch
A Standard or Deep run from an inline question or a saved brief, compared with the previous run by default.
Extendextend
One to three more rounds on the report from the step before, with an optional extra instruction.
Rabbit holerabbit_hole
A focused follow-on investigation in a direction you set.
Dossierdossier
Turn the report into a set of 3 to 12 documents. Needs a plan that includes dossiers.
Decide

Stop the line until the evidence or a person says go.

Conditioncondition
Pass or fail on how much the answer changed, or on the report's Decision readiness score. Each outcome can lead somewhere different.
Approvalapproval
Wait for a person to approve or reject, with your instructions. Expires after 72 hours unless you set 1 to 720.
Deliver

Send the result where it gets used.

Exportexport
Markdown, PDF, JSON, a sources CSV, or the dossier as Markdown, PDF or a ZIP.
Notifynotify
An in-app notification with your own title and message.
Webhookwebhook
POST the step's result to one of your registered, signed webhook endpoints. Needs a plan that includes webhooks.

03 / THREE WAYS TO BUILD

Same pipeline, wherever you work.

A pipeline is one graph of steps. The builder draws it, the API takes it as JSON, and MCP lets your agent run and follow it. All three see the same pipelines.

In the app

Visual builder

Drag steps onto a canvas, connect them, and see the credit ceiling update as you go. Runs, approvals and step outputs show up live.

Open the builder
For your code

REST API

Validate a graph for free, save it, start runs with an idempotency key and read every step's result.

API reference
For your agent

MCP

Claude, Cursor or VS Code can list your pipelines, run one, follow it and answer approvals.

Connect over MCP
1. Check the graph and its credit ceiling (free)
# Put one of your endpoint ids (GET /v1/webhooks) in place of YOUR_WEBHOOK_ENDPOINT_ID first.
curl -X POST https://api.farsideresearch.com/v1/workflows/validate \
  -H "Authorization: Bearer $FARSIDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- <<'JSON'
{
  "graph": {
    "nodes": [
      {
        "id": "weekly",
        "type": "trigger",
        "config": {
          "mode": "schedule",
          "cadence": "weekly",
          "timezone": "America/New_York"
        }
      },
      {
        "id": "competitors",
        "type": "fan_out",
        "config": {
          "items": [
            "Acme Analytics",
            "Northwind Data",
            "Contoso Cloud"
          ]
        }
      },
      {
        "id": "prices",
        "type": "research",
        "config": {
          "source": "inline",
          "question": "What does {{entity}} charge for its analytics plans in 2026, and what changed recently?",
          "depth": "standard",
          "research_type": "competitive_intel",
          "compare_to_previous": true
        }
      },
      {
        "id": "moved",
        "type": "condition",
        "config": {
          "check": "material_change",
          "threshold": 0.15,
          "on_first_run": "pass"
        }
      },
      {
        "id": "signoff",
        "type": "approval",
        "config": {
          "instructions": "Check the price change before it goes to sales.",
          "expires_after_hours": 72
        }
      },
      {
        "id": "pdf",
        "type": "export",
        "config": {
          "format": "pdf"
        }
      },
      {
        "id": "crm",
        "type": "webhook",
        "config": {
          "endpoint_id": "YOUR_WEBHOOK_ENDPOINT_ID"
        }
      }
    ],
    "edges": [
      {
        "id": "e1",
        "source": "weekly",
        "target": "competitors"
      },
      {
        "id": "e2",
        "source": "competitors",
        "target": "prices"
      },
      {
        "id": "e3",
        "source": "prices",
        "target": "moved"
      },
      {
        "id": "e4",
        "source": "moved",
        "target": "signoff",
        "branch": "pass"
      },
      {
        "id": "e5",
        "source": "signoff",
        "target": "pdf"
      },
      {
        "id": "e6",
        "source": "signoff",
        "target": "crm"
      }
    ]
  }
}
JSON

# {"valid": true, "issues": [], "estimate": {"max_credits": ..., "research_runs": 3, "breakdown": [...]}}
2. Save it. The weekly trigger takes it from here
curl -X POST https://api.farsideresearch.com/v1/workflows \
  -H "Authorization: Bearer $FARSIDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Weekly competitor price watch", "status": "active", "graph": { ...same graph... }}'
3. Or run it now
curl -X POST https://api.farsideresearch.com/v1/workflows/$WORKFLOW_ID/executions \
  -H "Authorization: Bearer $FARSIDE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

# Retry with the same Idempotency-Key and you get the same execution back.
4. Answer the approval
curl -X POST https://api.farsideresearch.com/v1/workflow-executions/$EXECUTION_ID/steps/$STEP_ID/decision \
  -H "Authorization: Bearer $FARSIDE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision": "approve", "note": "Price change confirmed with sales."}'

Endpoints

GET/v1/workflow-catalog
Step types, limits, export formats, credit costs and webhook events, machine-readable.
POST/v1/workflows/validate
Check a graph and get its credit ceiling. Free.
POST/v1/workflows
Save a pipeline. A scheduled trigger starts running on its own.
POST/v1/workflows/{id}/executions
Run it now. Requires an Idempotency-Key header.
GET/v1/workflow-executions/{id}
Every step's status, output and links.
POST/v1/workflow-executions/{id}/steps/{step_id}/decision
Approve or reject a waiting approval step.
GET/v1/workflow-approvals
Approvals waiting on someone in your workspace.

MCP tools

list_workflows
Your pipelines with schedule, credit ceiling and last run. Free.
run_workflow
Start a pipeline. Takes an idempotency key, so a retry never runs it twice.
get_workflow_execution
Follow a run step by step. Free.
decide_workflow_approval
Approve or reject a waiting approval step.

04 / GUARDRAILS

It runs by itself. It doesn't run away.

A pipeline spends credits and sends things out of Farside, so the limits are part of the engine, not a setting you have to remember.

  • A ceiling before you spendValidation returns the most a run can cost, counting every branch and every entity. Conditions can only lower it.
  • Hard limitsUp to 40 steps, 25 entities in a fan-out and 50 research runs per pipeline.
  • Nothing runs twiceStarting a run needs an idempotency key. Retry with the same key and you get the same run back.
  • People stay in the loopAn approval step holds everything after it. A rejection or an expired approval stops that branch.
  • Your workspace onlyEvery brief, output profile and webhook a pipeline points at must belong to your workspace, or it will not save.
  • Signed, retried deliveriesEach webhook is signed with your endpoint's secret and retried for about two days if your server is down.
Webhook events

Know when it moves.

Subscribe an endpoint to pipeline events, or add a webhook step to post a specific result. Every delivery carries X-Farside-Event, X-Farside-Delivery-Id and an X-Signature you can check. X-Farside-Delivery and X-Farside-Timestamp are included too. Deprecated X-Fathom-* aliases remain available until at least 2027-06-01.

  • workflow.step
  • workflow.completed
  • workflow.failed
  • workflow.approval_requested
Verify a delivery (Python)
import hashlib, hmac, time

def verify(secret: str, body: bytes, signature_header: str, tolerance: int = 300) -> bool:
    # X-Signature looks like: t=1762700000,v1=<hex sha256>
    parts = dict(item.split("=", 1) for item in signature_header.split(","))
    timestamp = int(parts["t"])
    if abs(time.time() - timestamp) > tolerance:
        return False
    signed = str(timestamp).encode() + b"." + body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

THE NEXT QUESTION IS YOURS

Know More.
Succeed More.

Bring the decision, the alternatives, and the constraints. Put Farside on the research behind your next move.

Create an account. Review your plan and the cost before research starts.