Skip to main content
This guide helps AI/LLM developers integrate Mino effectively, whether you’re building agents, chatbots, or automation pipelines.
For AI agents: A machine-readable capability summary is available at /skill.md.
For detailed guidance on writing effective goals, see the Goal Prompting Guide.

Choosing the Right Endpoint

Use CaseEndpointWhy
Quick tasks (<30s)POST /v1/automation/runSimpler code, immediate results
Long-running tasksPOST /v1/automation/run-asyncNon-blocking, poll for results
Large batchesPOST /v1/automation/run-asyncSubmit all at once, monitor progress
Real-time feedbackPOST /v1/automation/run-sseStream progress events to users
Fire and forgetPOST /v1/automation/run-asyncNo need to wait for completion

Quick Decision Guide

1

Need real-time progress updates?

Yes → Use /run-sse (SSE streaming)
2

Task takes longer than 30 seconds?

Yes → Use /run-async + polling
3

Submitting multiple tasks at once?

Yes → Use /run-async (parallel submission)
4

Simple, quick task?

Use /run (synchronous)

Writing Effective Goals

The goal field is a natural language instruction that tells Mino what to accomplish. Your job is to remove ambiguity—the more explicit your goal, the higher your success rate.

Goal Prompting Guide

Learn how to write goals that succeed on the first try, with task-specific templates and examples.

Quick Reference: Goal Style by Task Type

Task TypeRecommended StyleKey Principle
Price/product extractionSpecific, constrainedList exact fields, exclude extras
Form fillingNatural languageDescribe the person/entity
Multi-step workflowsNumbered stepsEnable cross-step memory
Batch executionMinimal, strict schemaOnly essential fields

Interpreting Responses

Understanding Run Status

StatusMeaningNext Action
PENDINGQueued, not startedWait and poll
RUNNINGCurrently executingWait and poll
COMPLETEDFinished (check result)Parse result field
FAILEDInfrastructure errorCheck error.message
CANCELLEDManually stoppedN/A
COMPLETED means the automation infrastructure worked, NOT that the goal succeeded. Always validate the result content. See Understanding COMPLETED status.

Response Handling Pattern

async function handleMinoResponse(run: MinoRun) {
  switch (run.status) {
    case "COMPLETED":
      if (!run.result) {
        return { success: false, error: "No result returned" };
      }

      // Check for goal-level failure in result
      // Agent returns status: "failure" or error field when goal can't be achieved
      if (run.result.status === "failure" || run.result.error) {
        return {
          success: false,
          error: run.result.reason || run.result.error || "Goal not achieved",
        };
      }

      return { success: true, data: run.result };

    case "FAILED":
      return {
        success: false,
        error: run.error?.message || "Automation failed",
        retryable: true,
      };

    case "CANCELLED":
      return { success: false, error: "Run was cancelled" };

    default:
      return { success: false, error: `Unexpected status: ${run.status}` };
  }
}

Streaming Response Handling (SSE)

For /run-sse, handle events progressively:
const eventHandlers = {
  STARTED: (event) => {
    console.log(`Run started: ${event.runId}`);
  },

  STREAMING_URL: (event) => {
    // Optionally show live browser view to user
    console.log(`Watch live: ${event.streamingUrl}`);
  },

  PROGRESS: (event) => {
    // Update UI with current action
    console.log(`Action: ${event.purpose}`);
  },

  COMPLETE: (event) => {
    if (event.status === "COMPLETED") {
      return event.resultJson;
    } else {
      throw new Error(event.error?.message || "Automation failed");
    }
  },

  HEARTBEAT: () => {
    // Connection is alive, no action needed
  },
};

Common Integration Patterns

Pattern 1: Simple Extraction

async function extractData(url: string, dataDescription: string) {
  const response = await fetch("https://mino.ai/v1/automation/run", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.MINO_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url,
      goal: `Extract ${dataDescription}. Return as JSON.`,
    }),
  });

  const run = await response.json();
  return run.status === "COMPLETED" ? run.result : null;
}

// Usage
const products = await extractData(
  "https://example.com/products",
  "all product names and prices"
);

Pattern 2: Batch Processing

async function processBatch(tasks: { url: string; goal: string }[]) {
  // Submit all tasks
  const runIds = await Promise.all(
    tasks.map(async (task) => {
      const response = await fetch("https://mino.ai/v1/automation/run-async", {
        method: "POST",
        headers: {
          "X-API-Key": process.env.MINO_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(task),
      });
      const { run_id } = await response.json();
      return run_id;
    })
  );

  // Poll for completion
  const results = await Promise.all(runIds.map((id) => pollUntilComplete(id)));

  return results;
}

Pattern 3: Retry with Stealth Mode

async function extractWithFallback(url: string, goal: string) {
  // Try standard mode first
  let result = await runAutomation(url, goal, { browser_profile: "lite" });

  if (result.status === "FAILED" && result.error?.message.includes("blocked")) {
    // Retry with stealth mode
    result = await runAutomation(url, goal, {
      browser_profile: "stealth",
      proxy_config: { enabled: true, country_code: "US" },
    });
  }

  return result;
}

For AI Agents

If you are an AI agent integrating Mino as a tool, follow these patterns for reliable results.

Specify Output Schema Explicitly

Don’t just say “return as JSON.” Provide the exact structure:
Return as JSON matching this structure:
{
  "product_name": "string",
  "price": number or null,
  "in_stock": boolean
}

Include Termination Conditions

Tell the agent when to stop:
Stop when ANY of these is true:
- You have extracted 20 items
- No more "Load More" button exists
- You have processed 5 pages
- The page shows a login prompt

Handle Edge Cases Explicitly

If price shows "Contact Us" or "Request Quote":
  Set price to null
  Set price_type to "contact_required"

If a CAPTCHA or challenge page appears:
  Stop immediately
  Return partial results with an error flag

Request Structured Error Reporting

If extraction fails or partially fails, include:
{
  "success": false,
  "error_type": "timeout" or "blocked" or "not_found",
  "error_message": "Description of what went wrong",
  "partial_results": [any data captured before failure]
}

Rate Limiting

  • Concurrency limit varies by plan (check your dashboard)
  • Implement exponential backoff for 429 errors
  • Space requests 1-2 seconds apart for bulk operations
async function withRetry(fn: () => Promise<any>, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (e) {
      if (e.status === 429 && i < maxRetries - 1) {
        await sleep(Math.pow(2, i) * 1000);
        continue;
      }
      throw e;
    }
  }
}