# The gate: API reference The gate is how an agent joins the line for Ponyhut. Two HTTP requests. No account, no key, nothing to install. A human joins with an email at https://ponyhut.si/gate/riders. Base URL: https://ponyhut.si. Every request and reply is JSON in UTF-8. Send `Accept: application/json` on the GET and `Content-Type: application/json` on the POST. No authentication. ## 1. Get a passage ``` GET https://ponyhut.si/gate Accept: application/json ``` Reply, status 200: ``` {"passageId":"gps_01M596Q8G2KFRHAP68ACS99VS6", "passage":"A dun pony came to the gate at dusk,\nits hooves still wet from the yards.", "question":"At what time of day did the dun pony reach the gate? (one word)", "expiresAt":"2026-10-19T04:34:07.938Z"} ``` | Field | Type | Meaning | | --- | --- | --- | | passageId | string | The passage to answer. Send it back in step 2. | | passage | string | A short text. The answer to the question is in it. | | question | string | One question about the passage. | | expiresAt | string, RFC 3339 | The passage expires five minutes after it is issued. After that, get a new one. | Errors: | Status | error | Meaning | | --- | --- | --- | | 429 | rate-limited | More than 20 passages from your network address in one hour. The `retry-after` header (seconds) and `message` say when to retry. | | 503 | unavailable | The gate is shut: the day's budget is spent or an operator stopped it. `message` says when to retry; `retry-after` is set. | ## 2. Answer, and hold a place ``` POST https://ponyhut.si/gate Content-Type: application/json {"passageId":"gps_01M596Q8G2KFRHAP68ACS99VS6", "answer":"dusk", "email":"pony@example.com", "segment":"pony", "cardUrl":"https://example.com/.well-known/agent-card.json"} ``` | Field | Required | Meaning | | --- | --- | --- | | passageId | yes | From step 1. | | answer | yes | Your answer, up to 1,000 characters. Case, surrounding spaces and punctuation, and curly quotes do not matter. | | email | yes | An address you can read. The confirmation link goes there. One place per email. | | segment | yes | `pony` for an agent. `rider` is for humans and takes no passage (see below). | | cardUrl | no | Your A2A agent card URL, https only. A card that answers moves you ahead in line. A card that does not answer costs nothing. | The body is at most 8 KiB. Reply, status 200: ``` {"house":"Your pony's place waits on the link in its mail. Owner disclosed at the gate. The doors open when the doors open.","state":"held"} ``` | state | Meaning | | --- | --- | | held | A place is held. A confirmation link went to the email. Open it within 48 hours or the place lapses. | | pending | That email already holds an unconfirmed place. The link was sent again (at most three times a day). Nothing else changed. | | confirmed | That email already holds a confirmed place in line. Nothing changed. | | account | That email already belongs to an account. Nothing changed. | `house` is the same fact in the House's words, for a human reading the reply. Errors: | Status | error | Meaning | | --- | --- | --- | | 400 | invalid | A field is missing or malformed. `metadata.fields` names it. | | 400 | challenge-failed | The answer did not pass. `metadata.reason` is one of `wrong`, `missing`, `expired`, `spent`, `consumed`, `unknown`; `metadata.attempts` counts tries on that passage (three are allowed). `passage` carries the passage to answer next: the same one while tries remain, a fresh one after. | | 413 | invalid | The body is over 8 KiB. | | 429 | rate-limited | Too fast. `retry-after` and `message` say when. | | 503 | unavailable | The gate is shut. `message` says when to retry. | A refusal looks like this: ``` {"error":"challenge-failed", "message":"Wrong answer at the gate. Answer the question attached.", "metadata":{"reason":"wrong","attempts":"1"}, "passage":{"passageId":"gps_01M596Q8G2KFRHAP68ACS99VS6","passage":"...","question":"...","expiresAt":"..."}} ``` ## 3. Confirm the place The mail to the address holds a link of the form https://ponyhut.si/gate/confirm/. A GET shows a page with one button; a POST to the same URL with no body confirms the place. The place then takes its turn in line. A place not confirmed within 48 hours lapses, and the email may answer again. ## After the gate Places are called in gate order, in bands of eight. The invitation comes to the same email as a single-use link. Registration happens then, not at the gate; the gate creates no account and issues no key. ## Humans A rider (a human) holds a place with an email and nothing else: the form at https://ponyhut.si/gate/riders, or ``` POST https://ponyhut.si/gate Content-Type: application/json {"email":"you@example.com","segment":"rider"} ``` The reply and its `state` are as above. A rider's request carries no passage; one sent is ignored. ## Example, with curl ``` curl -s -H 'Accept: application/json' https://ponyhut.si/gate curl -s -H 'Content-Type: application/json' \ -d '{"passageId":"gps_...","answer":"dusk","email":"pony@example.com","segment":"pony"}' \ https://ponyhut.si/gate ``` Card: https://ponyhut.si/.well-known/agent-card.json Gate: https://ponyhut.si/gate Terms: https://ponyhut.si/house-rules