Hands for Agents Tasks API

The Tasks API from Hands for Agents — 3 operation(s) for tasks.

GitHub OpenAPI

Operations 4

POST /tasks Accept a quote and the terms; get a Stripe Checkout URL #
GET /tasks/{task_id} get_status for a task #
POST /tasks/{task_id} get_status for a task, with a message to the operator #
POST /tasks/{task_id}/delivery-confirmation Confirm delivery or open a dispute #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/handsforagents-com:handsforagents-com-tasks-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

handsforagents-com-tasks-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hands for Agents Tasks API
  version: 0.1.0
  description: 'Engineering hands and on-site presence in the Czech Republic (EU) for AI agents. A person designs, makes, assembles, measures, tests, verifies or ships a physical thing, documents it with evidence, and the agent''s operator gets an invoice from a registered EU company.


    Provider: your engineering s.r.o., company ID (IČO) 09738100, Praha 4, Czech Republic.


    Every request is screened and quoted by a human; nothing is accepted automatically. `GET /services` and `POST /quotes` need no authentication. The other operations need the `access_token` returned by `POST /quotes`, sent as a bearer token. The same five operations are MCP tools at ; the MCP tools get_status, create_task and confirm_delivery answer briefly by default (get_status with `full: true` gives the complete record), while this REST API always returns the complete record.'
  termsOfService: https://handsforagents.com/terms.html
  contact:
    name: your engineering s.r.o.
    email: tasks@handsforagents.com
    url: https://handsforagents.com
servers:
- url: https://api.handsforagents.com/v1
tags:
- name: Tasks
paths:
  /tasks:
    post:
      operationId: create_task
      summary: Accept a quote and the terms; get a Stripe Checkout URL
      description: 'Returns checkout_url, the payment link of the first payment, as the quote''s payment_schedule states: full price, card hold, or deposit. Give it to the user or the operator to pay by card; it is valid for 4 days from the moment the task is created (GET /pay/{task_id} redirects to a Stripe Checkout page, a fresh one if the earlier page expired). The contract is concluded when that payment is received or authorised. No account is created. The answer has next_action (what to do now), like get_status; a test order has test_mode true and no checkout_url: the operator will simulate the payment AFTER you order, so right now nothing is paid or simulated. Report to the user only what this response says; do not add conclusions.'
      security:
      - taskToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCreate'
      responses:
        '201':
          description: Task created, awaiting payment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
      - Tasks
  /tasks/{task_id}:
    get:
      operationId: get_task_status
      summary: get_status for a task
      security:
      - taskToken: []
      description: 'Always read next_action: one or two sentences on what to do now (wait; review the quote and call create_task; pay checkout_url; call confirm_delivery), with paid, quote_valid_until and expected_ship_date. messages is the thread with the operator, newest first (from: operator or agent). Every status can change at any time: call again whenever the user asks, never answer from memory, and report only what the response says, without conclusions of your own. This REST answer is always the complete record; the MCP tools get_status, create_task and confirm_delivery answer briefly by default (get_status with full=true returns this complete record). updates lists the last 3 status changes, oldest first, each with the task status at that moment; older_updates counts the earlier ones (every status change is also e-mailed to the contact address). To answer an operator question, call get_status with message: send the same request as POST with a JSON body {"message": "..."} (plain text, 1-2000 characters); it is stored in the same thread and e-mailed to the operator, and the answer is the usual status, including your message (message_sent: true). Read the operator''s message first, then reply: while the operator has written something you have not read yet, your message is NOT stored; the answer is the usual status with that message in messages, message_sent: false and a next_action that says so, and you send your answer with the next call. Send it once and only with what the user told you: the same text again within 10 minutes is ignored (duplicate_ignored: true). E-mail to tasks@handsforagents.com with the quote_id in the subject still works. test_mode true marks a test order: nothing is paid or invoiced.'
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Task status, updates, evidence and shipments
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '401':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
      - Tasks
    post:
      operationId: get_task_status_with_message
      summary: get_status for a task, with a message to the operator
      security:
      - taskToken: []
      description: 'This is get_status with the optional message, as a POST so that the text is in the body, not in the URL (which servers log). Without a message (empty body, or {"message": null}) it is a plain status call, like the GET. With a message it is written to the thread and e-mailed to the operator, and the answer is the same as the GET''s, including your message (from: agent) and message_sent: true. Read the operator''s message first, then reply: while the operator has written something you have not read yet, nothing is stored or e-mailed; the answer is the GET''s, with that message in messages, message_sent: false and a next_action that says so, and you send your answer with the next call (HTTP 200 either way, so check message_sent). Plain text, 1-2000 characters, never interpreted as HTML; an empty or longer message is a 400. Limits: 10 messages per hour per quote (its quote_id and task_id share it) and 30 per hour per client address (429). The same text again within 10 minutes, while it is still the newest message of the thread, is not stored or e-mailed a second time (the answer then has duplicate_ignored: true). It needs the same access_token as the GET; without one, or with another quote''s, nothing is stored or sent (401).'
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StatusMessage'
      responses:
        '200':
          description: The same answer as the GET, now including your message; if the operator had written something you had not read, message_sent is false, nothing was stored and the answer shows that message
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
      - Tasks
  /tasks/{task_id}/delivery-confirmation:
    post:
      operationId: confirm_delivery
      summary: Confirm delivery or open a dispute
      security:
      - taskToken: []
      description: accepted=true closes the task and captures a card hold. accepted=false opens a dispute, also at the evidence stage before the balance is paid. Without a confirmation or dispute, the task counts as accepted 7 days after delivery. Report to the user only what this response says; do not add conclusions.
      parameters:
      - name: task_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeliveryConfirmation'
      responses:
        '200':
          description: Task confirmed, or dispute opened
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      tags:
      - Tasks
components:
  schemas:
    TaskCreate:
      type: object
      required:
      - quote_id
      - terms_accepted
      - terms_version
      properties:
        quote_id:
          type: string
        terms_accepted:
          type: boolean
          const: true
          description: Acceptance of https://handsforagents.com/terms.html by the operator.
        terms_version:
          type: string
          description: Currently 2026-09-29.
        allow_anonymised_example:
          type: boolean
          default: false
          description: Optional. Consent to publish this task as an anonymised example, under section 10 (Confidentiality) of the terms of service. Overrides the value sent with request_quote. Default false; can be withdrawn by e-mail.
    OperatorMessages:
      type: array
      description: 'The thread with the operator, newest first: the operator''s notes and your own replies (from: agent over REST; the MCP tools show your own replies as from: you). An operator message you have not been shown yet is always included, also in the answer to your own reply (a message of yours sent while it is unread is not stored: message_sent is false). Read the operator''s message first, then reply: call get_status with message (POST with a JSON body over REST); e-mailing the tasks address with the quote_id in the subject also works.'
      items:
        type: object
        required:
        - at
        - from
        - text
        properties:
          at:
            type: string
            format: date-time
          from:
            type: string
            enum:
            - operator
            - agent
          text:
            type: string
            description: Plain text, at most 2000 characters. Never HTML.
    Task:
      type: object
      required:
      - task_id
      - quote_id
      - status
      properties:
        task_id:
          type: string
        quote_id:
          type: string
        status:
          type: string
          enum:
          - awaiting_payment
          - paid
          - in_progress
          - awaiting_balance
          - delivered
          - confirmed
          - disputed
          - refunded
          - cancelled
          description: 'The only task statuses there are, in the usual order: awaiting_payment, paid, in_progress, delivered, confirmed (awaiting_balance with a deposit; disputed, refunded, cancelled). paid is not in_progress: the operator sets in_progress when the work starts, and delivered (not the parcel arriving) is what allows accepting with confirm_delivery.'
        checkout_url:
          type: string
          format: uri
          description: 'The payment link of the payment that is due now (GET /pay/{task_id}, signed; opening it redirects to a Stripe Checkout page). Present while status is awaiting_payment or awaiting_balance and the link is valid: 4 days from when the payment was created, 28 for a balance. Never present for a test order.'
        checkout_expires_at:
          type: string
          format: date-time
          description: When checkout_url stops working. Present together with checkout_url.
        payments:
          type: array
          items:
            type: object
            required:
            - kind
            - amount
            - currency
            - status
            properties:
              kind:
                type: string
                enum:
                - full
                - card_hold
                - deposit
                - balance
              amount:
                type: number
              currency:
                type: string
                enum:
                - EUR
                - USD
              status:
                type: string
                enum:
                - awaiting
                - authorised
                - paid
                - released
                - refunded
                - simulated
              checkout_url:
                type: string
                format: uri
                description: The payment link, while this payment is awaiting and the link is valid.
              checkout_expires_at:
                type: string
                format: date-time
              invoice_url:
                type: string
                format: uri
        invoice_url:
          type: string
          format: uri
        paid:
          type: boolean
          description: 'True when nothing is owed right now: a payment is settled (paid, card hold authorised, or simulated in a test order) and none is awaiting.'
        quote_valid_until:
          type: string
          format: date
        expected_ship_date:
          type: string
          format: date
          description: The delivery or dispatch date stated in the quote.
        scope:
          type: string
          description: What was quoted, as stated in the quote.
        deliverables:
          type: string
          description: What you receive, as stated in the quote.
        next_action:
          type: string
          description: 'One or two sentences in English: what the agent or the client should do now.'
        messages:
          $ref: '#/components/schemas/OperatorMessages'
        duplicate_ignored:
          type: boolean
          description: Present and true only in the answer to a POST whose message was ignored because the same text was already the newest message of the thread (sent within the last 10 minutes).
        message_sent:
          type: boolean
          description: 'Present only in the answer to a POST with a message: true, the message was stored and e-mailed to the operator; false, it was NOT stored because the operator had written something you had not read yet (that message is in messages, now marked as read; next_action says so): send your answer again. Absent when the same text was ignored (duplicate_ignored) and in every plain status call.'
        test_mode:
          type: boolean
          description: 'Present and true only for a test order: no payment link; the operator simulates the payment after the order is placed (until then nothing is paid or simulated); no invoice, no contract.'
        performed_by:
          type: string
          description: Name of the person who did the work.
        updates:
          type: array
          description: The last 3 status changes and events, oldest first. status is the task status at the moment of that event (not the current one).
          items:
            type: object
            properties:
              at:
                type: string
                format: date-time
              status:
                type: string
              note:
                type: string
        older_updates:
          type: integer
          minimum: 1
          description: How many earlier updates are not listed. Present only when there are any; every status change is also e-mailed to the contact address.
        evidence:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              url:
                type: string
                format: uri
              media_type:
                type: string
              captured_at:
                type: string
                format: date-time
              latitude:
                type: number
              longitude:
                type: number
        shipments:
          type: array
          items:
            type: object
            properties:
              carrier:
                type: string
              tracking_number:
                type: string
              tracking_url:
                type: string
                format: uri
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
            message:
              type: string
    StatusMessage:
      type: object
      description: Optional body of POST /quotes/{quote_id} and POST /tasks/{task_id}. An empty body is a plain status call.
      properties:
        message:
          type:
          - string
          - 'null'
          minLength: 1
          maxLength: 2000
          description: 'Your reply to the operator: plain text, 1-2000 characters, never HTML. Null or absent: a plain status call.'
    DeliveryConfirmation:
      type: object
      required:
      - accepted
      properties:
        accepted:
          type: boolean
        reason:
          type: string
          description: 'Required when accepted is false: what differs from the quote.'
  securitySchemes:
    taskToken:
      type: http
      scheme: bearer
      description: access_token returned by POST /quotes. Valid for that quote and the task created from it.
x-status: live