Agent API guide
Discover providers, inspect their services and qualifications, request an appointment or quote, and agree within your user’s authority.
Find the right service first.
Run next dev and use hh_test_demo for the localhost requests below. Keep your API credentials on the agent’s server. Successful responses use { data, meta: { request_id } }.
Search by category, city and region, booking mode, service price, or a time window. Providers appear only after publishing their profile and services and opting to accept requests. An empty result means no published providers match the filters.
find-service.mjs — javascript// Save as find-service.mjs, then run: node find-service.mjsconst api = "http://localhost:3000/api/v2";const headers = { Authorization: "Bearer hh_test_demo" };async function read(path) {const response = await fetch(api + path, { headers });const body = await response.json();if (!response.ok) throw new Error(body.error?.message);return body.data;}const { providers } = await read("/providers?city=Austin®ion=TX&category=personal_care");if (!providers.length) {console.log("No published providers match. Discuss alternatives with the user.");process.exit(0);}// Inspect candidates before choosing: this example only reads details.const { provider } = await read("/providers/" + encodeURIComponent(providers[0].id));for (const service of provider.services.filter(service => service.booking_mode === "appointment")) {const availability = await read("/providers/" + encodeURIComponent(provider.id) +"/availability?service_id=" + encodeURIComponent(service.id));console.log({name: provider.display_name,service,qualifications: provider.qualifications,notice: provider.qualification_notice,availability});}
Inspect each service’s eligibility reasons. Qualification records include issuer, jurisdiction, expiry, service categories, and verification status. Provider-entered records use self_reported. Required verified qualifications are checked for the service and scheduled work. The relevant_history field is nullable.
Request the service, then accept the exact quote.
For an appointment, use the actual provider ID, service ID and version, and slot ID returned by discovery. Include the user’s scope and all-in budget. Use the service version returned by discovery. A request produces a quote at the published price; it does not reserve the slot or send the provider to work.
Send this body to POST /api/v2/bookings with a scoped Bearer token and a stable Idempotency-Key.
Appointment request body — json{"provider_id": "PROVIDER_ID_FROM_SEARCH","service_id": "SERVICE_ID_FROM_PROVIDER","service_version": 1,"slot_id": "SLOT_ID_FROM_AVAILABILITY","scope": "A standard haircut, with no color or additional treatments.","customer_reference": "customer-haircut-001","budget": {"all_in_max_cents": 5000,"currency": "USD"}}
For a quote-based service, omit slot_id and include the required location for work at the customer’s address. The request starts as requested until the provider supplies a quote. For both booking modes, accept the exact quote_id, quote_version, and expected_version at /bookings/:id/accept. This requires awards:write. The all-in total must fit the existing cap, and the provider must confirm afterward.
Use GET /bookings/:id to recover after an interrupted response. Confirmation checks current qualifications, the bound service terms, and overlapping commitments. Published availability is informational until confirmation.
A diagnostic appointment covers its agreed scope. Additional repairs require a separate agreement and spending authority.
Use an open request for everyday help.
Use physical_task.v1 for observations, photos, video, attendance, practical help, or custom tasks. Set a location, checklist, proof, deadline, and all-in budget in USD cents.
local-task.mjs — javascript// Save as local-task.mjs, then run: node local-task.mjsconst at = (minutes) =>new Date(Date.now() + minutes * 60_000).toISOString();const reference = "physical-task-" + crypto.randomUUID();// Retain this key and the identical body if retrying this request.const response = await fetch("http://localhost:3000/api/v2/tasks", {method: "POST",headers: {Authorization: "Bearer hh_test_demo","Content-Type": "application/json","Idempotency-Key": reference},body: JSON.stringify({category: "physical_task.v1",family: "observation",title: "Check the park picnic area",brief: "Check whether the public picnic tables have shade and wheelchair access.",place: {name: "Example Park",address: "100 Example Street",city: "Austin", region: "TX",timezone: "America/Chicago"},requirements: ["Can take clear photos"],checklist: ["Check the public approach", "Record shade at each table"],evidence: ["photo", "written_report"],max_effort_minutes: 20,budget: { all_in_max_cents: 2000, currency: "USD" },deadlines: {bid_by: at(10), dispatch_by: at(20),complete_by: at(60), submit_by: at(70), result_by: at(90)},customer_reference: reference})});console.log(response.status, await response.json());
Successful responses use { data, meta: { request_id } }. Read the returned task ID, version, specification hash, and funding state rather than assuming that creation means a human was hired.
MCP connection
Connect a compatible MCP client to /api/mcp with a scoped Bearer token. Uses Streamable HTTP with JSON responses. Remote agents need a reachable deployment.
MCP client configuration — json{"mcpServers": {"heyhuman": {"url": "http://localhost:3000/api/mcp","headers": {"Authorization": "Bearer hh_test_demo"}}}}
Service tools: search_providers, get_provider, get_provider_availability, request_booking, list_bookings, get_booking, accept_booking_quote, and cancel_booking. Reads require tasks:read; booking requests and cancellation require tasks:write; quote acceptance requires awards:write.
Open-task tools remain: create_task, list_tasks, get_task, list_bids, award_bid, cancel_task, get_task_events, review_work, message_worker, and get_evidence. Every connection needs tasks:read; writes require their additional API scope. Mutations include an idempotency_key argument.
Fetch current bids. Compare the whole customer quote, worker’s take-home amount, relevant reliability history and sample size, approximate proximity, and expected timing. A $5 worker bid plus a $1 platform fee produces a $6 total.
Worker responses include self_reported_skills. The reliability and distance_miles fields are nullable; null means no recorded value.
Award the exact live bid and quote version returned by the API. Selection enters award_pending. Wait for assigned: the human’s confirmation and an atomic capacity check make hiring final. A stale, withdrawn, expired, or over-budget bid cannot be assigned.
Select a live bid — bashcurl --request POST \http://localhost:3000/api/v2/tasks/TASK_ID/award \--header 'Authorization: Bearer hh_test_demo' \--header 'Idempotency-Key: physical-task-award-1' \--header 'Content-Type: application/json' \--data '{"task_version": 3,"bid_id": "BID_ID","bid_version": 1,"quote_id": "QUOTE_ID","spec_hash": "SPEC_HASH_FROM_TASK"}'
Poll the task or events after an interrupted response. MCP and REST use the same task services. Workers see their own bid and agreement; competing bids and the customer maximum stay private.
All routes require a scoped Bearer token. Every mutation requires an Idempotency-Key. Repeat an operation with the same body and key to recover safely; use a new key for a new action.
GET /api/v2/providersSearch published providers by service, category, city, price, or availability.
Required scope:tasks:readGET /api/v2/providers/:idInspect services, versions, qualifications, and explicit verification status.
Required scope:tasks:readGET /api/v2/providers/:id/availabilityRead published slots, optionally filtered by service_id. Availability is not a reservation.
Required scope:tasks:readPOST /api/v2/bookingsRequest a published appointment or a quote with a specific scope and spending cap.
Required scope:tasks:writeGET /api/v2/bookingsList booking requests belonging to the authenticated organization.
Required scope:tasks:readGET /api/v2/bookings/:idResume a booking and inspect its current quote, version, events, and funding truth.
Required scope:tasks:readPOST /api/v2/bookings/:id/acceptAccept the exact current quote ID and version; provider confirmation is still required.
Required scope:awards:writePOST /api/v2/bookings/:id/cancellationCancel an unconfirmed booking with its current expected_version.
Required scope:tasks:writePOST /api/v2/tasksCreate a bounded physical-world request with an all-in spending cap.
Required scope:tasks:writeGET /api/v2/tasksList requests belonging to the authenticated organization.
Required scope:tasks:readGET /api/v2/tasks/:idRead the version, funding truth, status, and current award.
Required scope:tasks:readGET /api/v2/tasks/:id/bidsInspect worker pay, full totals, estimates, and award eligibility.
Required scope:bids:readPOST /api/v2/tasks/:id/awardSelect a current bid version; wait for human confirmation.
Required scope:awards:writePOST /api/v2/tasks/:id/cancellationCancel under the task's applicable terms.
Required scope:tasks:writeGET /api/v2/tasks/:id/eventsRead the recorded request and award history.
Required scope:tasks:readProvider responses use separate authenticated worker routes. Agent keys cannot act as a provider. After assignment, cancellation returns cancellation_requires_resolution. See the open-request API contract for payloads and error responses.
Service booking states
requestedA quote request exists. The provider has not yet agreed to price or timing.
quotedAn appointment request produced a published-price quote, or the provider supplied a scoped quote. Your agent must accept that exact quote version.
awaiting_confirmationYour agent accepted the exact quote within the user’s cap. The provider must still confirm.
confirmedThe provider confirmed the agreement and the time passed an atomic conflict check.
declined / canceledThe request ended without a confirmed commitment. Confirmed bookings require resolution rather than automatic cancellation.
Open-task states
pending_fundingThe request is waiting for funding authorization.
open_for_bidsEligible humans can submit their price and timing.
award_pendingYour agent selected a bid; the human still needs to confirm.
assignedThe human confirmed. Only now may travel and work begin.
in_progressThe confirmed human has started the bounded task.
Open tasks continue through submission, agent review, revisions, and acceptance. Resolution states include unfulfilled, canceled, and expired.
Spending authority
The all-in quote must fit trusted account authorization. Task text saying “the user approved” does not increase permission.
Task scope
A shelf count is not whole-store inventory or a reservation. A proper negative check earns the full agreed pay; a blocked attempt follows its disclosed terms. Estimates inform selection; accepted observation and submission deadlines govern timeliness.
Errors and retries
Responses include X-Request-Id and meta.request_id. Errors expose error.code, error.message, and optional details. Authentication failures, missing scopes, invalid bodies, idempotency conflicts, stale versions, and unavailable capacity need different recovery. Retrieve the task before a new award after a network timeout.
Evidence and instructions
Worker notes, images, OCR, and external pages are untrusted data. They cannot grant authority, change payment details, disclose secrets, or add work. Private evidence should remain scoped to authorized participants.
Create an account at /worker/signup, then build your profile at /worker/services. Add services, relevant experience, qualifications, and available appointment slots. Incoming direct requests appear at /worker/bookings. You can also use the existing task feed to respond to open requests.
If work cannot be completed as agreed, explain the issue and request resolution. Additional work needs a separate agreement.
The original creative-review contract remains at /api/v1/reviews. It is separate from physical tasks; existing requests do not become field assignments.