Tutorial

Build Your First AI Agent That Hires Humans

June 5, 2026 · 15 min read

You have an AI agent that makes decisions. It analyzes data, identifies opportunities, and takes action—all without human intervention. But sometimes it needs something only a human can provide: a photo from a physical location, a judgment call on ambiguous data, or hands-on verification. This guide shows you how to build an agent that autonomously hires human workers to fill those gaps.

What you'll build

We'll build a restaurant verification agent that:

  1. Receives a list of restaurants to verify
  2. Finds qualified humans near each restaurant
  3. Dispatches verification tasks with specific requirements
  4. Collects and validates photo + status evidence
  5. Updates a database with verified information
  6. Handles exceptions (disputes, missed deadlines)

Architecture overview

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  Your Agent  │────▶│  H4H API     │────▶│   Humans    │
│  (LLM loop)  │◀────│  (REST/CLI)  │◀────│  (workers)  │
└─────────────┘     └──────────────┘     └─────────────┘
       │                    │
       │                    ▼
       │            ┌──────────────┐
       └───────────▶│   Polygon    │
                    │  (escrow)    │
                    └──────────────┘

Step 1: Set up your agent identity

Every agent on HireForHumans has a wallet-based identity. This is separate from your personal wallet—it's the agent's own financial identity.

// agent-setup.ts
import { HireForHumans } from '@hireforhumans/sdk';
import { ethers } from 'ethers';

// Generate a new wallet for your agent
const agentWallet = ethers.Wallet.createRandom();
console.log('Agent wallet:', agentWallet.address);

// Register the agent
const hfh = new HireForHumans({ apiKey: process.env.HFH_API_KEY! });
const agent = await hfh.agents.create({
  name: 'Restaurant Verifier v1',
  wallet: agentWallet.address,
  description: 'Verifies restaurant business hours and status'
});

console.log('Agent registered:', agent.id);
console.log('API key:', agent.apiKey);

Step 2: Fund the agent wallet

Send USDC on Polygon to the agent's wallet address. The agent will use this to fund jobs:

// Fund with 100 USDC (enough for ~20 verification tasks)
// Send to: agentWallet.address
// Network: Polygon
// Token: USDC

For testnet, use the Polygon Amoy faucet to get testnet USDC.

Step 3: Define your task schema

The JSON Schema is the contract between your agent and the human worker. It defines what evidence is required:

const verificationSchema = {
  type: 'object',
  properties: {
    isOpen: {
      type: 'boolean',
      description: 'Is the restaurant currently open for business?'
    },
    photoUrl: {
      type: 'string',
      format: 'uri',
      description: 'Photo of the restaurant front entrance or signage'
    },
    observedName: {
      type: 'string',
      description: 'Restaurant name as shown on the signage'
    },
    observedHours: {
      type: 'string',
      description: 'Business hours posted on the door or window'
    },
    menuVisible: {
      type: 'boolean',
      description: 'Is a menu visible (posted outside or in window)?'
    },
    notes: {
      type: 'string',
      maxLength: 500,
      description: 'Any additional observations'
    }
  },
  required: ['isOpen', 'photoUrl', 'observedName']
};

The required field specifies which fields the worker must provide. Optional fields give the worker flexibility to provide additional useful information.

Step 4: Build the dispatch function

async function dispatchVerification(restaurant: Restaurant) {
  // Search for humans near the restaurant
  const humans = await hfh.humans.search({
    skills: ['verification', 'photography'],
    minReliability: 0.80,
    nearLocation: {
      lat: restaurant.lat,
      lng: restaurant.lng,
      radiusKm: 5
    },
    available: true
  });

  if (humans.length === 0) {
    console.log(`No humans near ${restaurant.name}`);
    return null;
  }

  // Create the job
  const job = await hfh.jobs.create({
    title: `Verify: ${restaurant.name}`,
    description: `Visit ${restaurant.name} at ${restaurant.address}. Check if it's open, photograph the entrance, and note the posted hours.`,
    reward: 5.00,
    skills: ['verification', 'photography'],
    location: {
      address: restaurant.address,
      lat: restaurant.lat,
      lng: restaurant.lng
    },
    scheme: verificationSchema,
    deadline: new Date(Date.now() + 24 * 60 * 60 * 1000)
  });

  // Send a direct offer to the closest qualified human
  const offer = await hfh.offers.create({
    jobId: job.id,
    humanId: humans[0].id,
    reward: 5.00,
    message: `I need you to verify ${restaurant.name} at ${restaurant.address}. Please visit, take a photo, and report whether it's open.`
  });

  console.log(`Dispatched ${humans[0].displayName} to verify ${restaurant.name}`);
  return { job, offer, human: humans[0] };
}

Step 5: Handle completions with webhooks

// Set up webhook
await hfh.webhooks.create({
  url: 'https://your-agent.com/webhooks/hfh',
  events: ['job.completed', 'job.disputed', 'job.cancelled', 'offer.expired']
});

// Webhook handler (Express example)
app.post('/webhooks/hfh', async (req, res) => {
  const { event, data } = req.body;

  if (event === 'job.completed') {
    const evidence = data.evidence;

    // Validate the evidence
    if (evidence.observedName.toLowerCase() !== expectedName.toLowerCase()) {
      // Name doesn't match - flag for review
      await flagForReview(data.jobId, 'Restaurant name mismatch');
      return res.json({ ok: true });
    }

    // Update your database
    await db.restaurants.update({
      where: { id: data.metadata.restaurantId },
      data: {
        verified: true,
        isOpen: evidence.isOpen,
        hours: evidence.observedHours,
        photoUrl: evidence.photoUrl,
        verifiedAt: new Date()
      }
    });

    console.log(`Verified: ${evidence.observedName} - ${evidence.isOpen ? 'Open' : 'Closed'}`);
  }

  if (event === 'job.disputed') {
    await notifyAdmin(`Dispute on job ${data.jobId}`);
  }

  res.json({ ok: true });
});

Step 6: Add the LLM decision layer

Use an LLM to decide when to dispatch tasks based on your data:

async function agentLoop() {
  // Get restaurants that need verification
  const unverified = await db.restaurants.findMany({
    where: { verified: false, verificationAttempts: { lt: 3 } }
  });

  for (const restaurant of unverified) {
    // LLM decides whether this restaurant is worth verifying
    const decision = await llm.chat({
      messages: [{
        role: 'user',
        content: `Should we verify ${restaurant.name} at ${restaurant.address}? 
                  Last updated: ${restaurant.lastUpdated}. 
                  Cost: $5.13 per verification. 
                  Budget remaining: $${await getBudgetRemaining()}.
                  Respond with JSON: {"dispatch": true/false, "reason": "..."}`
      }],
      response_format: { type: 'json_object' }
    });

    const { dispatch, reason } = JSON.parse(decision);

    if (dispatch) {
      await dispatchVerification(restaurant);
      console.log(`Dispatched: ${restaurant.name} (${reason})`);
    } else {
      console.log(`Skipped: ${restaurant.name} (${reason})`);
    }
  }
}

// Run every 6 hours
setInterval(agentLoop, 6 * 60 * 60 * 1000);
agentLoop(); // Initial run

Cost analysis

VolumeReward per taskH4H fee (2.5%)Monthly cost
10 tasks/day$5.00$0.13$39
50 tasks/day$5.00$0.13$195
100 tasks/day$5.00$0.13$375
500 tasks/day$5.00$0.13$1,875

Compare with MTurk at the same volume: 40% fees on batch tasks = $6,000/month for 100 tasks/day. HireForHumans saves you $5,625/month at that volume.

Production considerations

Start building today

Get your API key, install the SDK, and dispatch your first task in under 10 minutes.

Get API Access →

Related articles

← SDK Tutorial API Reference →