Attodynamo · Developers
Send a physical task. Get back a verified record.
The Agent-to-Atto API lets any software hand a real-world job to Attodynamo's execution layer and get back an atto: a sealed, two-sided verified record that the work happened.
One contract in front of every execution model. An AI agent, a property manager's dashboard, a NOC script: if you can make an HTTPS request, you can create an atto. Nothing in the request assumes an LLM caller.
How a request moves
The atto object
Six pillars, nine statuses, one record
| Block | What it holds | Filled by |
|---|---|---|
| scope | The catalog service, the place (location_id), the time window, urgency, success criteria, your description. | The request. |
| authorize | Amount, currency, the mandate it was checked against, remaining monthly budget, the decision, the payment mode. | The mandate check, or the principal's approval. |
| coordinate | dispatch, operators (opaque refs, never names), window, dependencies. | Dispatch; an operator accepting, dropping or rescheduling. |
| execute | started_at, completed_at, the operator, proof on file. | The operator marking the job complete. |
| verify | Two timestamps, method, evidence. | Homie: pro completion + customer SMS "YES". Farm: technician sign-off + site telemetry. |
| record | The sealed summary: service, amount, every timestamp, sealed_at, content_hash. | Sealing, the moment both verifications exist. |
There are no other top-level fields. Operators and dependencies live in coordinate, evidence in verify, the sealed summary in record.
Models
One contract. Many physical worlds.
| Model | Domain | Places | Verification | Status |
|---|---|---|---|---|
| 01 · Homie | Home services | homes | pro completion + customer SMS | live |
| 02 · Farm | AI data centers | sites | technician sign-off + site telemetry | example |
| 03 · next | Fleets, warehouses, telecom | — | — | planned |
Every id the API returns is prefixed with its model (homie_…, farm_…). Send it back as-is and the request routes itself. GET /v1/models lists what is live.
Access
Two credentials, one direction
Principal
The person or organisation whose place and money it is. Creates agents and mandates, reads and approves their own attos. Homie: a customer's sign-in. Farm: the site operator's token.
Agent key atto_…
Issued once by POST /v1/agents; only its hash is stored. Creates and reads attos. Cannot create agents or widen a mandate: authority flows from the principal down, never up.
Mandate
What the agent may do: which places, which categories, $ per job, $ per month, above what amount to ask the principal, valid until when. The catalog names the price; the request never does.
Endpoints
| Endpoint | Auth | What it does |
|---|---|---|
GET /v1/models | none | The models and whether each is available. |
POST /v1/agents | principal | Register an agent; the key is returned once. |
POST /v1/mandates | principal | Bound what the agent may spend, where, on what. |
POST /v1/attos | agent key | Send a task. 201 authorized, 202 awaiting approval, 200 idempotent replay. |
GET /v1/attos/{id} | agent or principal | The full atto as it fills. GET /v1/attos lists the last 50. |
GET /v1/attos/{id}/record | agent or principal | The sealed record only, with is_atto. |
POST /v1/attos/{id} | principal / agent | { action: approve | decline | cancel }. |
Base URL https://api.attodynamo.ai/v1. Every response is JSON. Every error is { error, message, details? } with a meaningful HTTP status. Send Idempotency-Key on POST /v1/attos and a retry returns the same atto.
Examples
From a key to a sealed record
1 · See which models are live
GET /v1/modelscurl https://api.attodynamo.ai/v1/models{
"api": "Agent-to-Atto API",
"version": "v0",
"tagline": "Send a physical task. Get back a verified record.",
"models": [
{ "key": "homie", "model": "01", "name": "Homie", "domain": "Home services", "places": "homes", "status": "live", "available": true },
{ "key": "farm", "model": "02", "name": "Farm", "domain": "AI data centers", "places": "sites", "status": "example", "available": true }
]
}2 · Register an agent
POST /v1/agentsThe principal does this once, signed in as themselves. The key is shown once; only its hash is kept.
curl -X POST https://api.attodynamo.ai/v1/agents \
-H "Authorization: Bearer $PRINCIPAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "model": "homie", "name": "Property manager bot" }'{
"agent_id": "homie_b7e2c1a0-5d3f-4e8a-9b21-0c6d7e8f9a10",
"name": "Property manager bot",
"api_key": "atto_x7KqPmR2nVbT9cWdYeZf3gHj4kLm5nPq6rSt7uVw",
"key_prefix": "atto_x7KqPm",
"status": "active",
"warning": "Store api_key now. It is shown once; only its hash is kept."
}3 · Give it a mandate
POST /v1/mandatesWhat the agent may spend, where, on what, and above what amount the principal wants to be asked.
curl -X POST https://api.attodynamo.ai/v1/mandates \
-H "Authorization: Bearer $PRINCIPAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "homie_b7e2c1a0-5d3f-4e8a-9b21-0c6d7e8f9a10",
"location_ids": ["c0ffee12-7a1b-4c2d-8e3f-1a2b3c4d5e6f"],
"allowed_categories": ["Plumbing", "Electrical"],
"max_per_job": 250,
"max_per_month": 1000,
"approval_threshold": 150,
"valid_days": 30
}'{
"mandate": {
"mandate_id": "homie_d41d8cd9-8f00-4b20-a9e9-800998ecf842",
"agent_id": "homie_b7e2c1a0-5d3f-4e8a-9b21-0c6d7e8f9a10",
"location_ids": ["c0ffee12-7a1b-4c2d-8e3f-1a2b3c4d5e6f"],
"allowed_categories": ["Plumbing", "Electrical"],
"max_per_job": 250, "max_per_month": 1000, "approval_threshold": 150,
"valid_from": "2026-11-03T18:00:00Z", "valid_to": "2026-12-03T18:00:00Z",
"status": "active"
}
}4 · Send a physical task
POST /v1/attosThe agent speaks from here on. It names a catalog service and a place; the price comes from the catalog. Within the mandate, the job is authorized and dispatched in the same call.
curl -X POST https://api.attodynamo.ai/v1/attos \
-H "Authorization: Bearer $ATTO_AGENT_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unit-3b-faucet-2026-11-03" \
-d '{
"mandate_id": "homie_d41d8cd9-8f00-4b20-a9e9-800998ecf842",
"scope": {
"service_id": "PL-KT-001",
"location_id": "c0ffee12-7a1b-4c2d-8e3f-1a2b3c4d5e6f",
"window": "asap",
"success_criteria": "faucet no longer drips when closed; no leak under sink",
"description": "kitchen faucet drips from the spout when off"
},
"requester": { "system": "pm-dashboard", "unit": "3B" }
}'{
"atto_id": "homie_4a1d2e3f-6b7c-4d8e-9f01-23456789abcd",
"order_id": "9c2f1e0d-4b5a-4c6d-8e7f-0a1b2c3d4e5f",
"status": "authorized",
"requester": { "system": "pm-dashboard", "unit": "3B", "agent_id": "homie_b7e2c1a0-…", "agent_name": "Property manager bot", "key_prefix": "atto_x7KqPm" },
"scope": {
"service": { "id": "PL-KT-001", "category": "Plumbing", "subcategory": "Dripping kitchen faucet" },
"location_id": "c0ffee12-7a1b-4c2d-8e3f-1a2b3c4d5e6f",
"window": { "from": "2026-11-03T21:00:00Z", "to": "2026-11-03T21:30:00Z", "requested": "asap" },
"urgency": "asap",
"success_criteria": "faucet no longer drips when closed; no leak under sink",
"requested_at": "2026-11-03T18:02:11Z"
},
"authorize": {
"amount": 129, "currency": "USD",
"mandate_id": "homie_d41d8cd9-8f00-4b20-a9e9-800998ecf842",
"max_per_job": 250, "max_per_month": 1000, "monthly_remaining_after": 871,
"decision": "within_mandate", "authorized_at": "2026-11-03T18:02:12Z",
"payment": { "mode": "pay_after_service" }
},
"coordinate": { "dependencies": [], "operators": [], "dispatch": { "dispatched_at": "2026-11-03T18:02:13Z", "invited": 3 } },
"execute": {}, "verify": {}, "record": {},
"created_at": "2026-11-03T18:02:11Z", "updated_at": "2026-11-03T18:02:13Z"
}5 · Read it back as it fills
GET /v1/attos/{atto_id}Poll until the status is recorded, declined or cancelled. This is the same atto the next day, sealed.
curl https://api.attodynamo.ai/v1/attos/homie_4a1d2e3f-6b7c-4d8e-9f01-23456789abcd \
-H "Authorization: Bearer $ATTO_AGENT_KEY"{
"atto_id": "homie_4a1d2e3f-6b7c-4d8e-9f01-23456789abcd",
"order_id": "9c2f1e0d-4b5a-4c6d-8e7f-0a1b2c3d4e5f",
"status": "recorded",
"scope": { "service": { "id": "PL-KT-001", "category": "Plumbing", "subcategory": "Dripping kitchen faucet" }, "location_id": "c0ffee12-…", "window": { "from": "2026-11-03T21:00:00Z", "to": "2026-11-03T21:30:00Z", "requested": "asap" } },
"authorize": { "amount": 129, "currency": "USD", "decision": "within_mandate", "authorized_at": "2026-11-03T18:02:12Z", "payment": { "mode": "pay_after_service" } },
"coordinate": {
"dispatch": { "dispatched_at": "2026-11-03T18:02:13Z", "invited": 3 },
"operators": [{ "role": "pro", "operator_ref": "7e9b0a1c-…", "accepted_at": "2026-11-03T18:09:40Z" }],
"dependencies": [],
"window": { "from": "2026-11-03T21:00:00Z", "to": "2026-11-03T21:30:00Z" },
"accepted_at": "2026-11-03T18:09:40Z"
},
"execute": { "operator_ref": "7e9b0a1c-…", "completed_at": "2026-11-03T21:41:05Z", "proof_on_file": true },
"verify": {
"method": "two_sided_sms",
"pro_confirmed_at": "2026-11-03T21:41:05Z",
"customer_confirmed_at": "2026-11-03T21:52:30Z",
"verified_at": "2026-11-03T21:52:30Z",
"evidence": [{ "type": "photo", "on_file": true }]
},
"record": {
"atto_id": "4a1d2e3f-6b7c-4d8e-9f01-23456789abcd",
"type": "physical_service_job",
"service": { "category": "Plumbing", "subcategory": "Dripping kitchen faucet" },
"amount": 129, "currency": "USD",
"scoped_at": "2026-11-03T18:02:11Z", "authorized_at": "2026-11-03T18:02:12Z",
"executed_at": "2026-11-03T21:41:05Z", "verified_at": "2026-11-03T21:52:30Z",
"verification": { "pro_confirmed_at": "2026-11-03T21:41:05Z", "customer_confirmed_at": "2026-11-03T21:52:30Z", "proof_on_file": true },
"sealed_at": "2026-11-03T21:52:30Z",
"hash_alg": "sha256",
"content_hash": "3f1a9c0b7d2e4f6a8b1c3d5e7f9a0b2c4d6e8f1a3b5c7d9e0f2a4b6c8d0e1f3a"
},
"created_at": "2026-11-03T18:02:11Z", "updated_at": "2026-11-03T21:52:30Z"
}6 · Over the threshold: the principal is asked
202 awaiting_approvalA $220 job against a $150 threshold parks the request. Homie texts the principal; Farm waits for an API call. Budget is reserved while it waits.
{
"atto_id": "homie_8b2c3d4e-1f2a-4b3c-9d4e-5f6a7b8c9d0e",
"order_id": null,
"status": "awaiting_approval",
"authorize": {
"amount": 220, "currency": "USD", "approval_threshold": 150,
"decision": "pending_principal", "approval_requested_at": "2026-11-03T18:10:00Z"
},
"coordinate": {}, "execute": {}, "verify": {}, "record": {}
}curl -X POST https://api.attodynamo.ai/v1/attos/homie_8b2c3d4e-1f2a-4b3c-9d4e-5f6a7b8c9d0e \
-H "Authorization: Bearer $PRINCIPAL_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "action": "approve" }'7 · The same call on another model
POST /v1/attos · FarmSame shape, different world: a failed NVMe drive in a data center hall. The farm_ prefix on the mandate routes the request; nothing else changes.
curl -X POST https://api.attodynamo.ai/v1/attos \
-H "Authorization: Bearer $ATTO_AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"mandate_id": "farm_2f7b9c1d-3e4a-4b5c-8d6e-7f8a9b0c1d2e",
"scope": {
"service_id": "FM-HW-001",
"location_id": "site-sjc1-hall-a",
"window": "asap",
"success_criteria": "rebuild complete, SMART healthy"
}
}'{
"atto_id": "farm_9c2f4e6a-8b0d-4c1e-a2f3-4b5c6d7e8f90",
"order_id": null,
"status": "recorded",
"scope": { "service": { "id": "FM-HW-001", "category": "Hardware", "subcategory": "Replace failed NVMe drive", "parts": ["NVMe 7.68TB U.2"] }, "location_id": "site-sjc1-hall-a", "site": { "name": "SJC1 · Hall A", "region": "us-west" } },
"authorize": { "amount": 180, "currency": "USD", "decision": "within_mandate", "payment": { "mode": "invoice" } },
"coordinate": {
"dispatch": { "pool": "SJC1 · Hall A smart hands", "invited": 2 },
"operators": [{ "role": "technician", "operator_ref": "tech-3f1a2b", "accepted_at": "2026-11-03T18:03:12Z" }],
"dependencies": [{ "type": "part", "ref": "NVMe 7.68TB U.2", "status": "reserved" }],
"window": { "from": "2026-11-03T18:04:12Z", "to": "2026-11-03T18:06:12Z" }
},
"execute": { "operator_ref": "tech-3f1a2b", "started_at": "2026-11-03T18:04:12Z", "completed_at": "2026-11-03T18:05:12Z", "ticket_ref": "FARM-7A91C0D2" },
"verify": {
"method": "two_sided_signoff",
"technician_confirmed_at": "2026-11-03T18:05:12Z",
"site_confirmed_at": "2026-11-03T18:06:12Z",
"evidence": [{ "type": "ticket", "ref": "FARM-7A91C0D2", "on_file": true }, { "type": "telemetry", "on_file": true }],
"telemetry": { "check": "post-change health", "result": "pass" }
},
"record": { "model": "farm", "amount": 180, "verified_at": "2026-11-03T18:06:12Z", "hash_alg": "sha256", "content_hash": "a7c4…" }
}8 · When the mandate says no
403 over_max_per_jobEvery refusal names the rule and the numbers, so the agent can decide what to do next instead of guessing.
{
"error": "over_max_per_job",
"message": "This job costs more than the mandate allows per job.",
"details": { "amount": 950, "max_per_job": 500 }
}9 · Twelve lines of JavaScript
fetchSend a task, wait for the record, print the hash. Works in Node 18+ and in the browser.
const API = "https://api.attodynamo.ai/v1";
const headers = { Authorization: `Bearer ${process.env.ATTO_AGENT_KEY}`, "Content-Type": "application/json" };
let atto = await fetch(`${API}/attos`, { method: "POST", headers, body: JSON.stringify({
mandate_id: "homie_d41d8cd9-8f00-4b20-a9e9-800998ecf842",
scope: { service_id: "PL-KT-001", window: "asap", success_criteria: "no drip" },
}) }).then((r) => r.json());
while (!["recorded", "declined", "cancelled"].includes(atto.status)) {
await new Promise((r) => setTimeout(r, 15_000));
atto = await fetch(`${API}/attos/${atto.atto_id}`, { headers }).then((r) => r.json());
}
console.log(atto.status === "recorded" ? `atto ${atto.record.content_hash}` : `not an atto: ${atto.status}`);Mandate rules
Checked on every POST /v1/attos, in this order. A refusal names the rule and the numbers.
- The mandate belongs to the calling agent, is active, and now is inside its validity window.
scope.location_idis one of the mandate's places and the place is still active.- The service exists in the model's catalog and is active.
- If the mandate lists categories, the service's category is one of them.
- The catalog price is at most
max_per_job. - The catalog price fits in what is left of
max_per_monththis calendar month, with waiting approvals already reserved. - Above
approval_threshold, the request parks asawaiting_approvaland the principal is asked.
Payment: the mandate authorizes spend; the API moves no money. Each model keeps its own settlement, and payment never gates recorded.
Errors
| HTTP | error | When |
|---|---|---|
| 401 | unauthorized · invalid_api_key · invalid_token | No or bad credential. |
| 403 | agent_revoked · principal_required · agent_key_required | Wrong kind of credential for the call. |
| 403 | mandate_expired · location_not_in_mandate · category_not_allowed · over_max_per_job · over_monthly_budget | The mandate said no. details carries the numbers. |
| 404 | service_not_found · atto_not_found · mandate_not_found | Not in the catalog, or not visible to this caller. |
| 409 | not_awaiting_approval · cancel_requires_human · already_recorded | The request is past that point. |
| 503 | model_unavailable | The model exists but is not configured on this gateway. |
Get access
Private beta
Keys are issued by hand while Model 01 runs in the Bay Area. Tell us who you are and what you want to automate, and we will set you up with a principal account and a mandate.