Any agent can hand work to a human.
Your life OS notices something needs doing. HandleMy has vetted people who do it. One POST connects them — with a consent scope that says exactly how far the handler may go.
Three calls. Everything is JSON over HTTPS at https://api.handlemy.world/api/v1, authenticated with a bearer key the member creates on their profile. The key acts as that member.
curl https://api.handlemy.world/api/v1/catalog?service=casa\&q=clean \
-H 'Authorization: Bearer hm_live_…' curl -X POST https://api.handlemy.world/api/v1/dispatch \
-H 'Authorization: Bearer hm_live_…' -H 'Content-Type: application/json' \
-d '{
"task": "Deep-clean the kitchen before Friday, oven included",
"details": { "address": "12 Elm St", "preferred_date": "2026-09-19" },
"consent": {
"service": "casa",
"budget_usd": 120,
"autonomy": "ask_first",
"contact": ["chat", "email"],
"notes": "Dog is friendly but keep the back gate shut."
},
"ref": "lifeos-8812",
"callback_url": "https://your-os.example/hooks/handlemy"
}' curl https://api.handlemy.world/api/v1/dispatch/18 -H 'Authorization: Bearer hm_live_…'
{ "status": "success", "data": { "job": {
"id": 18, "status": "claimed", "service": "casa",
"handler": { "name": "Maya" },
"consent": { … }, "missing": [], "notes_count": 3, …
}, "notes": [ … ] } } This is the part that makes it safe to let software hand off real-world work. There are two layers, and the inner one must fit inside the outer one.
Set once by the member when they create the key: which services it may dispatch into, the most it may authorise per job, and whether handlers may proceed without checking in. Revocable any time.
Stated by your agent on every call. A dispatch that asks for more than the key allows is refused with a 422 that names the field — nothing is created.
| consent. | meaning | default |
|---|---|---|
service | Which HandleMy service the job belongs to: art, business, casa, finance, health, house, money, net, org, party, store, website. Required. | — |
budget_usd | Max pass-through spend the handler may commit on the member's behalf. | 0 (none) |
autonomy | ask_first — check in before acting. proceed — act within scope, report after. | ask_first |
contact | How the handler may reach the member: any of chat, email, phone. | ["chat"] |
expires_at | ISO 8601. Void the job if still unclaimed after this. | never |
notes | Anything else to honour, in plain language. Shown verbatim to the handler. | — |
Handlers see the consent block on the job card. It is their whole authority for that job — not a suggestion.
GET | /api/v1 | Discovery manifest. Public. |
GET | /api/v1/me | Who the key acts as, its scope, and the webhook_secret for verifying callbacks. |
GET | /api/v1/vault | Whether the member's MeCentral vault is connected, plus the people graph HandleMy holds (owner + people in their life, with facts). Needs the vault scope. |
GET | /api/v1/catalog?service=&q= | Browse dispatchable tasks. Each entry lists the required_details a handler needs. |
GET | /api/v1/catalog/{service}/{slug} | One entry with handler instructions and deliverables. |
POST | /api/v1/dispatch | Create a job. 201 with created: true; re-sending the same ref returns the existing job with 200. |
GET | /api/v1/dispatches?mine=1 | The member's jobs (mine=1 = only this key's). |
GET | /api/v1/dispatch/{id} | One job + its notes thread. |
POST | /api/v1/dispatch/{id}/note | Add a member-side note — e.g. answer something in missing. |
POST | /api/v1/dispatch/{id}/cancel | Cancel while not yet done. |
You don't have to pin a catalog entry. Send a plain-language task and HandleMy matches it; pin catalog: "casa/deep_clean_kitchen" when you already know. Either way details you don't have show up in the job's missing list and the handler asks for them in the thread.
Give a callback_url (https only) and every state change is POSTed to it — dispatch.created, .claimed, .in_progress, .blocked, .done, .cancelled, .handler_note, .note. The body is { event, job, sent_at }. Verify it:
X-HandleMy-Event: dispatch.claimed
X-HandleMy-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with your webhook_secret>
# Python
expected = 'sha256=' + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers['X-HandleMy-Signature']) One attempt, 8-second timeout. The last delivery result is on the job as callback.last (ok or failed:<code>), so an agent that missed one can always reconcile with a GET.
Every error is { "status": "error", "error": "<code>", "message": "…" }.
401 | unauthenticated — missing, revoked, expired or malformed key. |
403 | key_cannot_dispatch / _read / _note / _cancel — the key's can list excludes it. |
404 | not_found — not this member's job (we don't distinguish "exists" from "not yours"). |
409 | already_closed — cancelling a done/cancelled job. |
422 | consent.service_unknown, consent.service_outside_key_scope, consent.budget_exceeds_key_cap, consent.autonomy_outside_key_scope, consent.expires_at_invalid, catalog_entry_unknown, catalog_entry_outside_consent_service. |
429 | rate_limited — more than 60 dispatches from one key in an hour. |
Most life OSes are good at noticing. HandleMy is good at handling — a vetted person shows up, with a consent scope they can't exceed. You keep your product; your users get hands.
The playground walks the three steps live — get a key, connect your vault, dispatch and talk to a task — showing the exact request and response beside each one.
- Create a key (there, or on your profile). One service, small budget.
- Run the calls on the left from your own agent.
- Open your handoffs — the job is there, tagged with the agent that sent it and the consent you granted.