Build Your First AI Agent That Hires Humans
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:
- Receives a list of restaurants to verify
- Finds qualified humans near each restaurant
- Dispatches verification tasks with specific requirements
- Collects and validates photo + status evidence
- Updates a database with verified information
- 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
| Volume | Reward per task | H4H 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
- Error handling: Retry failed API calls with exponential backoff
- Budget limits: Set daily spending caps to prevent runaway costs
- Quality sampling: Manually review 5% of submissions to calibrate quality
- Deadlines: Set appropriate deadlines (2-4 hours for local tasks, 24 hours for flexible tasks)
- Dispute handling: Automate dispute raising for clear schema violations; manually review edge cases
Start building today
Get your API key, install the SDK, and dispatch your first task in under 10 minutes.
Get API Access →