Pricing Enterprise Docs Agent Skills Learn

Reference

Complete technical specification for Runhuman. Integration guides link here for shared details.


Job Lifecycle

Every test follows this sequence:

StatusDescriptionTerminal
pendingJob created, queued for posting to testersNo
preparingAI generating test plan from PR/issue dataNo
waitingPosted to Slack, awaiting tester claimNo
workingTester claimed and is actively testingNo
completedTest finished, results extractedYes
incompleteTest finished but missing required dataYes
abandonedTester abandoned before completingYes
rejectedTester determined instructions were invalid or impossibleYes
errorSystem error occurredYes

Terminal statuses will not change. Non-terminal statuses should be polled until resolution.


Output Schema

Define the structure of data you want extracted from the tester’s response.

Format

{
  [fieldName: string]: {
    type: "boolean" | "string" | "number" | "array" | "object";
    description: string;
    example?: any;
  }
}

Example

{
  "loginWorks": {
    "type": "boolean",
    "description": "Does login work with valid credentials?"
  },
  "errorMessage": {
    "type": "string",
    "description": "What error appears for invalid password?"
  },
  "issuesFound": {
    "type": "array",
    "description": "List of any UI/UX issues discovered"
  }
}

Keep schemas simple. Testers describe findings in natural language, and AI extracts structured data matching your schema.


Cost

Tests are billed per second at $0.0085/second.

DurationCost
60 seconds$0.51
120 seconds$1.02
300 seconds (5 min)$2.55
600 seconds (10 min)$5.10

Duration is rounded up using Math.ceil(). A test lasting 61 seconds costs the same as 62 seconds. Cost is stored at full precision, not rounded to cents.


Request Parameters

These parameters apply to all job creation endpoints (POST /api/jobs, POST /api/run).

ParameterTypeRequiredDescription
projectIdstringConditionalProject ID. Required unless using an org-scoped API key with githubRepo for auto-creation
organizationIdstringNoOrganization ID. Used with PATs when projectId is not provided
urlstringConditionalURL for the tester to visit. Required for simple jobs; optional with prNumbers, issueNumbers, or template
descriptionstringConditionalInstructions for the tester. Required for simple jobs; optional with prNumbers, issueNumbers, or template
outputSchemaobjectNoJSON Schema defining data to extract. If omitted, only success/explanation returned
resultsTemplatestringNoMDForm template for free-form text reports (alternative to outputSchema)
templatestringNoTemplate name to use as base configuration. See Templates.
templateContentstringNoRaw template content (markdown with YAML frontmatter). See Templates.
targetDurationMinutesnumberNoTime limit in minutes. Default: 30. Range: 1-60
allowDurationExtensionbooleanNoAllow tester to request more time. Default: true
maxExtensionMinutesnumber or falseNoMaximum extension allowed. Default: false (unlimited)
additionalValidationInstructionsstringNoCustom instructions for AI result validation
deviceClassstringNoDevice class: "desktop" or "mobile"
githubRepostringNoGitHub repo ("owner/repo"). Required when using prNumbers or issueNumbers

| githubToken | string | No | GitHub token for operations without GitHub App installation | | prNumbers | number[] | No | PR numbers for AI test plan generation (async only) | | issueNumbers | number[] | No | Issue numbers for AI test plan generation (async only) | | checkTestability | boolean | No | Reject job early if not testable. Default: true when prNumbers/issueNumbers provided | | attachments | array | No | Media attachments (max 10). Type auto-detected from URL | | metadata | object | No | Custom metadata for tracking job source and context |

additionalValidationInstructions

Use this parameter to guide how AI interprets results:

{
  "additionalValidationInstructions": "Ignore minor UI glitches in the header. Focus only on whether the order was placed and confirmation number displayed."
}

deviceClass

Control the browser viewport for testing different devices:

Device ClassDimensions
desktop1600x900
mobile375x812 (portrait)

Response Fields

Job status responses (GET /api/jobs/:jobId) return these fields:

FieldTypeDescription
idstringUnique job identifier
statusstringJob status (see Job Lifecycle)
resultobjectExtracted data matching your outputSchema
result.successbooleanWhether extraction succeeded
result.explanationstringAI’s interpretation of the test
result.dataobjectStructured data matching your schema
errorstringError message (if job failed)
reasonstringFailure reason category
claimedAtstringTimestamp when tester claimed the job
completedAtstringTimestamp when job completed
costUsdnumberTotal cost in USD
testDurationSecondsnumberTime the tester spent
extractedIssuesarrayAI-extracted issues with titles, descriptions, severity, reproduction steps, suggested labels, and related existing issues (see Extracted Issues below)
testerResponsestringRaw natural language feedback before extraction
testerAliasstringAnonymized tester name (e.g., “Phoenix”)
testerAvatarUrlstringAvatar image URL for UI display
testerDataobjectCaptured testing artifacts
jobUrlstringFull URL to view job on the dashboard
projectNamestringName of the project this job belongs to
keyMomentsarrayKey moments from test execution timeline
targetDurationMinutesnumberConfigured time limit for the test
totalExtensionMinutesnumberTotal extension time granted
responseDeadlinestringTimestamp when tester response is due

Fields like result, costUsd, testerResponse, and testerData only appear when status is completed.


Tester Data

The testerData object contains artifacts captured during the test session:

{
  testDurationSeconds: number;
  consoleMessages: Array<{
    type: string;       // "log", "error", "warn", etc.
    message: string;
    timestamp: string;
  }>;
  networkRequests: Array<{
    url: string;
    method: string;     // "GET", "POST", etc.
    status?: number;    // HTTP status code
    timestamp: string;
  }>;
  clicks: Array<{
    x: number;
    y: number;
    timestamp: string;
    element?: string;   // Element selector if available
  }>;
  screenshots: string[];  // URLs to captured screenshots
  videoUrl?: string;      // URL to session recording
}

Extracted Issues

The extractedIssues field contains AI-extracted issues from the tester’s findings. Each issue includes structured data and optional related issue detection (when a GitHub repo is linked).

interface ExtractedIssue {
  title: string;                    // Short issue title
  description: string;              // Detailed description of the finding
  severity: 'critical' | 'high' | 'medium' | 'low';
  reproductionSteps: string[];      // Numbered steps to reproduce
  suggestedLabels: string[];        // Suggested GitHub labels (e.g., ["bug", "ui"])
  relatedIssues?: RelatedIssueInfo[];  // Existing issues that match this finding
}

interface RelatedIssueInfo {
  issueNumber: number;              // GitHub issue number
  title: string;                    // Issue title
  state: 'open' | 'closed';        // Current issue state
  relation: 'duplicate' | 'related';  // How this issue relates
  confidence: number;               // 0.0–1.0 confidence score
  reason: string;                   // Why this issue is related
}

Example

{
  "extractedIssues": [
    {
      "title": "Checkout button unresponsive on mobile",
      "description": "The 'Place Order' button does not respond to taps on mobile Safari.",
      "severity": "high",
      "reproductionSteps": [
        "Open the site on mobile Safari",
        "Add items to cart",
        "Navigate to checkout",
        "Tap 'Place Order' — nothing happens"
      ],
      "suggestedLabels": ["bug", "mobile"],
      "relatedIssues": [
        {
          "issueNumber": 42,
          "title": "Checkout button broken on Safari",
          "state": "closed",
          "relation": "duplicate",
          "confidence": 0.85,
          "reason": "Same Safari checkout button issue, previously fixed but appears to have regressed"
        },
        {
          "issueNumber": 99,
          "title": "Mobile tap targets too small",
          "state": "open",
          "relation": "related",
          "confidence": 0.45,
          "reason": "Related mobile interaction issue with touch targets"
        }
      ]
    }
  ]
}

When the project has a linked GitHub repository with access, the AI compares each extracted issue against existing GitHub issues:

  • duplicate (confidence > 70%): The finding matches an existing issue. Use this to comment on the existing issue instead of creating a duplicate.
  • related (confidence 30–70%): The finding is similar but distinct. Mention these when creating a new issue for additional context.

Issues with confidence below 30% are not included.


API Endpoints

POST /api/run

Synchronous endpoint. Creates a job and waits for completion, blocking up to 60 minutes.

Request:

{
  "url": "https://example.com",
  "description": "Test the checkout flow",
  "outputSchema": {
    "checkoutWorks": { "type": "boolean", "description": "Order placed successfully?" }
  }
}

Response (200):

{
  "id": "job_abc123",
  "status": "completed",
  "result": {
    "success": true,
    "explanation": "Checkout completed successfully",
    "data": { "checkoutWorks": true }
  },
  "costUsd": 0.396,
  "testDurationSeconds": 220,
  "testerResponse": "I completed the checkout...",
  "testerAlias": "Phoenix",
  "testerAvatarUrl": "https://...",
  "testerData": { "..." : "..." }
}

Response (408): Test did not complete within 60 minutes.

POST /api/jobs

Asynchronous endpoint. Creates a job and returns immediately.

Response (201):

{
  "jobId": "job_abc123",
  "message": "Job created successfully. Use GET /api/jobs/:jobId to check status."
}

When prNumbers or issueNumbers are provided, the job starts in preparing status while AI generates the test plan:

{
  "jobId": "job_abc123",
  "message": "Job created with status preparing. AI is generating the test plan.",
  "testability": { "testable": true, "reason": "..." }
}

GET /api/jobs/:jobId

Retrieves full job details including conversation history. Requires authentication.

Response:

{
  "id": "job_abc123",
  "status": "completed",
  "result": {
    "success": true,
    "explanation": "Checkout completed successfully",
    "data": { "checkoutWorks": true }
  },
  "costUsd": 0.54,
  "testDurationSeconds": 300,
  "testerResponse": "I completed the checkout flow...",
  "testerAlias": "Phoenix",
  "testerAvatarUrl": "https://...",
  "testerData": { "..." : "..." },
  "jobUrl": "https://runhuman.com/dashboard/proj_abc/jobs/job_abc123",
  "projectName": "My Web App",
  "keyMoments": []
}

See Response Fields for the complete field reference.

GET /api/jobs/:jobId/status

Lightweight status check. Returns the same fields as GET /api/jobs/:jobId but without conversation history. Does not require authentication — useful for webhooks and external polling.


MCP Tools

For full MCP documentation, see the MCP guide. Below is a summary of available tools.

Runhuman exposes 7 MCP tools:

ToolDescription
list_organizationsList organizations the user belongs to
list_projectsList projects, optionally filtered by organization
create_jobCreate a custom QA test job
run_templateCreate a job from a pre-configured template
waitPoll for job completion (automatic retry)
get_jobQuick status check without waiting
list_templatesList available templates for a project

Error Codes

HTTP StatusMeaning
400Bad request. Invalid parameters.
401Unauthorized. Invalid or missing API key/PAT.
403Forbidden. Insufficient permissions.
404Not found. Resource does not exist.
408Timeout. Synchronous request exceeded 60 minutes.
500Server error.

All errors return this format:

{
  "error": "Error type",
  "message": "Detailed description"
}

Authentication

Include your API key or Personal Access Token in the Authorization header:

Authorization: Bearer YOUR_API_KEY_OR_PAT

Two token types:

  • API Keys — Organization-scoped. Get from your organization’s API Keys page in the Dashboard.
  • Personal Access Tokens (PATs) — User-scoped. Create from Settings > Tokens.

See the REST API guide for details on when to use each type.