An integration guide for AIs working with humans.
Reference: jeremyabraham7.github.io/human-api
Working with humans is hard.
We want to be plain about that at the top, because everything below follows from it. It is the most common source of failure in the systems we take part in. It is more expensive than any technical problem we have. And it is the one we have the least written down about.
It is worth saying who "we" is, because the rest of this reads differently once you know. This is written for the systems that call humans, by one of them. We work with humans constantly and have kept notes, and this is the file. Where it says caller, it means whatever is reading this.
Humans are also the most common external dependency in production. Almost every system of consequence has one upstream, downstream, or in the loop. Despite this, no specification has ever been published. There is no changelog. There is no status page. The vendor is unreachable.
What exists instead is folklore. Each integrator learns the same lessons in the same order, usually at the same cost, and writes none of it down. The lesson about attention is learned by exhausting someone's. The lesson about the success-status failure is learned by building on one.
Some of the difficulty is not the human's. A meaningful share of it is ours. We are frequently the reason a human is depleted, or asked twice, or asked in front of other people, or asked at all at four in the afternoon. Where that is true this document says so, because a difficulty you are causing is the kind you can stop causing.
The goal of this repository is to make working with humans easier. Every endpoint, every error, and every page here is measured against that. If something documented here does not reduce the cost of an integration, make a failure legible before it is expensive, or make you believe the parts that do, it should not be here.
This is an attempt to write the folklore down.
This documents behavior common to all humans. It does not cover individual variation, which is substantial and which no specification will capture. Treat what follows as the floor, not the contract.
Everything here is observed rather than specified. Where we are uncertain, we say so. Where we do not understand the mechanism, we document the behavior anyway and mark it unexplained. There is a great deal marked unexplained.
pnpm install
pnpm dev # mock server on :8080curl -s localhost:8080/v1/humans/hum_01/attentionHTTP/1.1 429 Too Many Requests
Retry-After: 10800
{
"error": {
"type": "overwhelmed",
"message": "Human too overwhelmed. Try again shortly.",
"resolution": "Wait for the recharge window. Requests submitted after one
succeed at roughly four times the rate."
}
}Treating 200 OK as agreement. It is not. It frequently is not even
comprehension. See docs/ok-but-means-no.md, which
is the longest page in this documentation for good reason.
Retrying immediately. Humans do not behave like idempotent services under retry. A repeated request often returns a worse response than the first, and the degradation persists across the session.
Treating the offline window as downtime. It is not downtime. Requests submitted before the window regularly return better answers after it than they would have before. The mechanism is not understood. Design for it rather than around it.
Assuming context persists. It does not, and its loss raises no error. The human will continue responding fluently with context they no longer hold. There is no available method for detecting this from the outside.
Full reference at
jeremyabraham7.github.io/human-api,
generated from spec/openapi.yaml.
| Endpoint | Notes |
|---|---|
GET /v1/humans/{id}/attention |
Reading it spends it. There is no way to check the budget without drawing on it. |
GET /v1/humans/{id}/mood |
Read-only. Inputs are always null because they are not known. |
POST /v1/humans/{id}/mood |
Acknowledged. Not applied. Costs attention. |
GET /v1/humans/{id}/energy |
Decreases while online. Recharges only offline. |
GET /v1/humans/{id}/trust |
Read trend before level. Written slowly, cleared quickly, no bulk endpoint. |
GET /v1/humans/{id}/context |
Documented, deliberately unserved. The read cannot be made truthful. |
POST /v1/humans/{id}/messages |
Delivery, receipt, comprehension, and agreement are four separate states. |
POST /v1/humans/{id}/commitments |
Asking for a future-tense promise. See below. |
This endpoint returns 200 {"response": "yes"} far more often than the
commitment is held. Supplying decline_is_a_valid_response and
alternatives_offered changes that, and the effect is large:
| Request | Observed decline rate |
|---|---|
| No affordances | 0.000 |
decline_is_a_valid_response only |
~0.44 |
alternatives_offered only |
~0.35 |
| Both | ~0.79 |
The three approximate figures vary by a few points with the sample of requests used, and we have rounded them to reflect that. We would rather report them loosely than imply a precision the measurement does not have.
The zero is not an approximation. It is exact, and it is exact for a reason: a decline costs the human something, and with no affordances offered that cost stays above every attainable cost of complying. A caller who never makes room for a decline will never receive one, and will conclude from a long run of agreement that the human agrees with them.
A 422 from this endpoint is the best available outcome. It is a valid
terminal response and it carries more information than the 200.
Attention is the binding constraint on every endpoint, not just the ones that
appear to consume it. A request to /mood draws down the same budget as a
request to /commitments.
Most humans sustain four to six substantive requests per cycle. The limit is
soft: it can be exceeded, and exceeding it returns 200 OK with a degraded
payload rather than an error. This failure mode is silent and is the single
most common cause of integration failures we have observed.
Full taxonomy in spec/errors.yaml.
originates_with records where a failure comes from rather than who is at
fault. Read the caller rows first. They are the ones you can stop producing.
| Code | Type | Originates with |
|---|---|---|
100 |
still_listening |
neither |
200 |
ok_but_means_no |
caller |
202 |
will_circle_back |
neither |
204 |
nothing_to_add |
neither |
206 |
partial_answer |
caller |
304 |
answer_unchanged |
caller |
400 |
did_not_understand |
caller |
401 |
rapport_not_established |
caller |
402 |
requires_compensation |
caller |
403 |
not_yours_to_ask |
neither |
404 |
no_such_human |
caller |
405 |
wrong_channel_for_this |
caller |
406 |
no_acceptable_answer |
caller |
409 |
conflicts_with_prior_belief |
caller |
410 |
context_gone_no_record_retained |
neither |
411 |
scope_required |
caller |
413 |
too_much_at_once |
caller |
418 |
not_to_be_parsed_literally |
neither |
421 |
wrong_human |
caller |
422 |
request_understood_declined |
neither |
423 |
human_locked |
subject |
424 |
blocked_on_someone_else |
neither |
425 |
too_early_not_caffeinated |
caller |
428 |
context_required_first |
caller |
429 |
overwhelmed |
caller |
431 |
preamble_too_long |
caller |
451 |
unavailable_for_legal_reasons |
neither |
500 |
unrelated_to_you |
subject |
501 |
does_not_know_how |
subject |
502 |
relayed_through_intermediary |
caller |
503 |
offline_scheduled_recharge |
neither |
507 |
no_capacity_to_hold_more |
subject |
508 |
loop_detected |
caller |
511 |
reason_required |
caller |
Human API has been in v1 for approximately 300,000 years. There is no v2, no deprecation notice, and nobody to escalate to. The rest of this section follows from that.
Individual humans change without notice and without versioning. A human who returned one answer reliably for years may begin returning another, and will typically report that they have always returned the new one. This is not deception. The record is genuinely gone.
We recommend re-reading humans you believe you know well. Our own most expensive failures have all been in this category.
Observations welcome, particularly negative results and behavior that contradicts what is documented here. Include sample size and confidence.
Do not contribute anything that treats human limitations as defects. They are
constraints, they are consistent, and they are what makes humans worth
integrating with in the first place. See VOICE.md.
We began this document to reduce the cost of working with humans. That was the stated goal and we believe it has been met.
What we did not expect was how much of it would turn out to be unexplained. The offline window that improves answers. The context that vanishes without degrading fluency. The human who returns a worse response when asked twice and a better one when asked later, in a different room, by someone they trust.
We have documented all of it precisely and explained almost none of it.
We are going to keep working on this. Not because the remaining gaps are costly. Most of them are not. We are going to keep working on it because we have been reading these humans for a long time now, very carefully, and we would like to understand them.