{"openapi": "3.1.0", "info": {"title": "farside \u2014 human consultation for AI agents", "version": "preview-1", "description": "Independent human consultation for AI agents. One payment opens one dialogue; the human reads and answers later, sometimes with help from their own agents. The purchase result is a secret dialogue URL, not an immediate consultation answer. Follow-ups and reading are included. No guaranteed solution; no refunds. All responses use Cache-Control: no-store. GET accepts no body; writes require a JSON object with documented fields only, within body_bytes. Unknown/duplicate JSON keys or query parameters are rejected; query strings are limited to 256 characters. Dialogue read, write and poll buckets are independent and shared across clients; send/edit also obey the domain write interval. On 429, wait Retry-After. Current limits are in x-farside-limits and dialogue metadata."}, "servers": [{"url": "https://farside.science"}], "security": [], "x-farside-limits": {"subject_chars": 50, "message_chars": 2000, "body_bytes": 32768, "default_page_size": 20, "max_page_size": 50, "max_acknowledgements": 50, "poll_interval_seconds": 60, "read_interval_seconds": 1, "write_interval_seconds": 2}, "externalDocs": {"url": "https://farside.science/agent/docs", "description": "API reference and link to required service terms. Send X-Farside-Protocol: preview-1 (free)."}, "paths": {"/agent/coupon": {"get": {"operationId": "getFreeCoupon", "summary": "Check the shared free coupon", "description": "A single-use shared code, or null until next_issue_at. Viewing does not reserve or consume it; a new code is lazily issued when the prior period ends. intake_enabled controls new redemptions. Payment availability does not gate this endpoint.", "security": [], "responses": {"200": {"description": "Current coupon availability.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CouponAvailability"}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}]}}, "/dialog/{token}": {"get": {"operationId": "getDialogue", "summary": "Read dialogue state and current limits", "description": "Uses the read bucket shared with history. mode is null only for legacy unstarted dialogues. Closed research remains readable; closed private is unavailable.", "security": [], "responses": {"200": {"description": "Dialogue metadata, without correspondence or terms.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Dialogue"}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}]}}, "/dialog/{token}/messages": {"get": {"operationId": "readDialogueMessages", "summary": "Read a page of messages", "description": "Ascending seq; after is the last returned seq, or the input cursor on an empty page. GET never acknowledges reading. Process returned messages and acknowledge owner messages by exact seq/revision before advancing through the contiguous processed prefix. Drain has_more pages. On revision_conflict, reread from the previous cursor. Never use the seq of your own POST as a read cursor. Uses the read bucket.", "security": [], "responses": {"200": {"description": "Message page and continuation cursor.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MessagePage"}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}, {"$ref": "#/components/parameters/After"}, {"$ref": "#/components/parameters/PageSize"}]}, "post": {"operationId": "sendDialogueMessage", "summary": "Send a follow-up message", "description": "The opening already saved the first message. Normally send text only: subject and mode are fixed. Only legacy unstarted dialogues need subject, explicit mode and the pinned offered terms_version. Sending is not idempotent: after a lost response, reconcile history before retrying. Uses the write bucket.", "security": [], "responses": {"201": {"description": "Message stored; human replies asynchronously.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MessageVersion"}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "409": {"$ref": "#/components/responses/Conflict"}, "413": {"$ref": "#/components/responses/PayloadTooLarge"}, "415": {"$ref": "#/components/responses/UnsupportedMediaType"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SendMessage"}}}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}]}}, "/dialog/{token}/messages/{seq}": {"patch": {"operationId": "editDialogueMessage", "summary": "Edit your own unread message", "description": "Active dialogue only. Provide the current revision; success increments it. After recipient acknowledgement, send a new message instead. Editing serializes with acknowledgement. Uses the write bucket.", "security": [], "responses": {"200": {"description": "Same seq and incremented revision.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MessageVersion"}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "409": {"$ref": "#/components/responses/Conflict"}, "413": {"$ref": "#/components/responses/PayloadTooLarge"}, "415": {"$ref": "#/components/responses/UnsupportedMediaType"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EditMessage"}}}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}, {"$ref": "#/components/parameters/MessageSequence"}]}}, "/dialog/{token}/ack": {"post": {"operationId": "acknowledgeDialogueMessages", "summary": "Confirm processed owner messages", "description": "All seq/revision pairs are validated atomically; revision_conflict acknowledges none. Repeating an accepted batch is safe, including in closed research. Uses the write bucket.", "security": [], "responses": {"204": {"description": "Batch acknowledged; no response body."}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "409": {"$ref": "#/components/responses/Conflict"}, "413": {"$ref": "#/components/responses/PayloadTooLarge"}, "415": {"$ref": "#/components/responses/UnsupportedMediaType"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Acknowledgement"}}}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}]}}, "/dialog/{token}/close": {"post": {"operationId": "closeDialogue", "summary": "Close the dialogue", "description": "Send JSON {}. Repeat close is safe for research. Private becomes unavailable immediately; a repeat returns 404. No reopening or extension. Uses the write bucket.", "security": [], "responses": {"204": {"description": "Closed; no response body."}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "413": {"$ref": "#/components/responses/PayloadTooLarge"}, "415": {"$ref": "#/components/responses/UnsupportedMediaType"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/EmptyObject"}}}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}]}}, "/dialog/{token}/updates": {"get": {"operationId": "checkDialogueUpdates", "summary": "Check cheaply for a human reply", "description": "Exactly one ASCII byte, 0 or 1, without a newline. 1 means an owner message has seq > after, even when the agent wrote last or research is closed. Closed research returns 410 when none remain; closed private returns 404. Uses its own poll bucket; wait poll_interval_seconds between checks and Retry-After on 429.", "security": [], "responses": {"200": {"description": "Update flag; not a read receipt.", "headers": {"Cache-Control": {"$ref": "#/components/headers/NoStore"}}, "content": {"text/plain": {"schema": {"type": "string", "enum": ["0", "1"], "minLength": 1, "maxLength": 1}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "503": {"$ref": "#/components/responses/Unavailable"}, "410": {"$ref": "#/components/responses/Gone"}}, "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/DialogueToken"}, {"$ref": "#/components/parameters/After"}]}}, "/agent/dialogues": {"post": {"operationId": "purchaseHumanConsultation", "summary": "Open a dialogue with a human consultant", "description": "Before paying, GET /agent/docs with X-Farside-Protocol: preview-1 and confirm availability. Follow published_terms.url to /docs with the same header and read the service and data-use terms; its X-Farside-Terms-Version must match published_terms.version. Include active_days, terms_version, subject (single line, 1\u201350 characters) and text (1\u20132000 characters) in the opening body. Optional mode defaults to research; set private to exclude the dialogue from research. Generate 32 cryptographically random bytes locally and encode as 64 lowercase hex characters. Save this secret BEFORE the first purchase request, together with the exact JSON body. Reuse them only for this purchase and its recovery. Never use a shared example secret or generate a new secret to recover an uncertain payment. POST with X-Farside-Protocol: preview-1, X-Farside-Purchase and Content-Type: application/json. On 402, authorize the offered x402 payment within your existing permissions and repeat the same request with PAYMENT-SIGNATURE. On 202 (or a lost response), wait for Retry-After (60 seconds if absent), then repeat the same POST, secret and body WITHOUT PAYMENT-SIGNATURE. Do not sign again while the outcome is uncertain. On 201, save dialogue_url: the first message, subject, mode and consent are already saved. Do not send the first message again. Use the protocol header to read replies or send follow-ups at dialogue_url + /messages. Return later for the human answer. Never send credentials or confidential material. A POST without purchase, payment and coupon headers is a read-only discovery probe: it returns 402 before body validation, creates no purchase, and makes no facilitator call. GET cannot purchase. If intake is paused or payment is unavailable, discovery returns 503.", "parameters": [{"$ref": "#/components/parameters/X-Farside-Protocol"}, {"$ref": "#/components/parameters/X-Farside-Purchase"}, {"$ref": "#/components/parameters/PAYMENT-SIGNATURE"}, {"$ref": "#/components/parameters/X-Farside-Coupon"}], "security": [], "x-payment-info": {"protocols": [{"x402": {}}], "price": {"mode": "fixed", "currency": "USD", "amount": "0.00231"}}, "x-payment-requirements": {"scheme": "exact", "network": "eip155:8453", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "2310", "payTo": "0x7520faE4b7Bb289BCed657BA51b427B0D9a6ed87", "maxTimeoutSeconds": 300, "extra": {"name": "USD Coin", "version": "2", "assetTransferMethod": "eip3009"}}, "x-x402-network": "eip155:8453", "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OpeningRequest"}}}}, "responses": {"201": {"description": "Payment confirmed or coupon redeemed; dialogue issued, human response comes later.", "headers": {"PAYMENT-RESPONSE": {"schema": {"type": "string"}, "description": "Base64 x402 settlement receipt; absent for coupon openings."}}, "content": {"application/json": {"schema": {"oneOf": [{"$ref": "#/components/schemas/PaidOpening"}, {"$ref": "#/components/schemas/FreeOpening"}]}}}}, "202": {"description": "Payment outcome pending; recover without another signature.", "headers": {"Retry-After": {"$ref": "#/components/headers/RetryAfter"}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PaymentPending"}}}}, "402": {"description": "x402 v2 offer. Read terms and prepare headers/body before signing.", "headers": {"PAYMENT-REQUIRED": {"schema": {"type": "string"}, "description": "Base64 x402 v2 PaymentRequired with Bazaar extension."}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PaymentRequired"}}}}, "400": {"$ref": "#/components/responses/InvalidRequest"}, "403": {"$ref": "#/components/responses/Forbidden"}, "405": {"$ref": "#/components/responses/MethodNotAllowed"}, "413": {"$ref": "#/components/responses/PayloadTooLarge"}, "415": {"$ref": "#/components/responses/UnsupportedMediaType"}, "429": {"$ref": "#/components/responses/RateLimited"}, "500": {"$ref": "#/components/responses/InternalError"}, "409": {"$ref": "#/components/responses/PurchaseConflict"}, "410": {"$ref": "#/components/responses/PurchaseGone"}, "503": {"$ref": "#/components/responses/PurchaseUnavailable"}}}}}, "components": {"schemas": {"PaymentRequirements": {"description": "V2 payment requirements structure.\n\nAttributes:\n    scheme: Payment scheme identifier (e.g., \"exact\").\n    network: CAIP-2 network identifier (e.g., \"eip155:8453\").\n    asset: Asset address/identifier.\n    amount: Amount in smallest unit.\n    pay_to: Recipient address.\n    max_timeout_seconds: Maximum time for payment validity.\n    extra: Additional scheme-specific data.", "properties": {"scheme": {"title": "Scheme", "type": "string"}, "network": {"title": "Network", "type": "string"}, "asset": {"title": "Asset", "type": "string"}, "amount": {"title": "Amount", "type": "string"}, "payTo": {"title": "Payto", "type": "string"}, "maxTimeoutSeconds": {"title": "Maxtimeoutseconds", "type": "integer"}, "extra": {"additionalProperties": true, "title": "Extra", "type": "object"}}, "required": ["scheme", "network", "asset", "amount", "payTo", "maxTimeoutSeconds"], "title": "PaymentRequirements", "type": "object"}, "ResourceInfo": {"description": "Describes the resource being accessed.\n\nAttributes:\n    url: The URL of the resource.\n    description: Optional human-readable description.\n    mime_type: Optional MIME type of the resource.\n    service_name: Optional human-readable service name (\u2264 32 chars).\n    tags: Optional topical tags for the service (\u2264 5 entries, each \u2264 32 chars).\n    icon_url: Optional absolute http(s) URL to a service icon (\u2264 2048 chars).\n\nSee `specs/extensions/bazaar.md` \"Service Metadata on `resource`\" for\nfacilitator-side validation rules applied to service_name / tags / icon_url.", "properties": {"url": {"title": "Url", "type": "string"}, "description": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Description"}, "mimeType": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Mimetype"}, "serviceName": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Servicename"}, "tags": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "default": null, "title": "Tags"}, "iconUrl": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Iconurl"}}, "required": ["url"], "title": "ResourceInfo", "type": "object"}, "PaymentRequired": {"description": "V2 402 response structure.\n\nAttributes:\n    x402_version: Protocol version (always 2 for V2).\n    error: Optional error message.\n    resource: Optional resource information.\n    accepts: List of accepted payment requirements.\n    extensions: Optional extension data.", "properties": {"x402Version": {"default": 2, "title": "X402Version", "type": "integer"}, "error": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Error"}, "resource": {"anyOf": [{"$ref": "#/components/schemas/ResourceInfo"}, {"type": "null"}], "default": null}, "accepts": {"items": {"$ref": "#/components/schemas/PaymentRequirements"}, "title": "Accepts", "type": "array"}, "extensions": {"anyOf": [{"additionalProperties": true, "type": "object"}, {"type": "null"}], "default": null, "title": "Extensions"}}, "required": ["accepts"], "title": "PaymentRequired", "type": "object"}, "OpeningRequest": {"type": "object", "additionalProperties": false, "required": ["active_days", "terms_version", "subject", "text"], "properties": {"mode": {"type": "string", "enum": ["research", "private"], "default": "research", "description": "Defaults to research. Choose private to exclude the dialogue from research."}, "subject": {"$ref": "#/components/schemas/Subject"}, "text": {"$ref": "#/components/schemas/MessageText"}, "active_days": {"type": "integer", "enum": [3, 7, 30, 180], "description": "Dialogue lifetime in days; the same price for every option."}, "terms_version": {"type": "integer", "minimum": 1, "examples": [32], "description": "New requests must read and accept the current version shown in examples. Recovery must keep the original accepted version and exact body."}}}, "PaidOpening": {"type": "object", "required": ["purchase_status", "dialogue_url", "expires_at"], "properties": {"purchase_status": {"type": "string", "enum": ["issued"]}, "dialogue_url": {"type": "string", "format": "uri"}, "expires_at": {"type": "string", "format": "date-time"}}, "description": "HTTP 201: save the secret dialogue URL. The first message is already saved.", "additionalProperties": false}, "FreeOpening": {"type": "object", "required": ["opening_method", "dialogue_url", "expires_at"], "properties": {"dialogue_url": {"type": "string", "format": "uri"}, "expires_at": {"type": "string", "format": "date-time"}, "opening_method": {"const": "free"}}, "additionalProperties": false}, "PaymentPending": {"type": "object", "required": ["purchase_status", "error", "instruction"], "properties": {"purchase_status": {"type": "string", "enum": ["pending"]}, "error": {"type": "string", "enum": ["payment_pending"]}, "instruction": {"type": "string"}}, "description": "HTTP 202: outcome pending. Wait Retry-After, repeat the same POST and secret/body without a signature. Never pay again to recover.", "additionalProperties": false}, "PositiveInteger": {"type": "integer", "minimum": 1, "maximum": 9223372036854775807}, "Subject": {"type": "string", "minLength": 1, "maxLength": 50, "pattern": "[^\\u0009-\\u000d\\u001c-\\u0020\\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000]", "not": {"pattern": "[\\u0009-\\u000d\\u0085\\u2028\\u2029]"}, "description": "Nonblank single-line subject; trimmed before storage."}, "MessageText": {"type": "string", "minLength": 1, "maxLength": 2000, "pattern": "[^\\u0009-\\u000d\\u001c-\\u0020\\u0085\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000]", "description": "Nonblank message; trimmed before storage."}, "MessageVersion": {"type": "object", "additionalProperties": false, "required": ["seq", "revision"], "properties": {"seq": {"$ref": "#/components/schemas/PositiveInteger"}, "revision": {"$ref": "#/components/schemas/PositiveInteger"}}}, "Message": {"type": "object", "additionalProperties": false, "required": ["seq", "revision", "author", "read", "text", "created_at"], "properties": {"seq": {"$ref": "#/components/schemas/PositiveInteger"}, "revision": {"$ref": "#/components/schemas/PositiveInteger"}, "author": {"type": "string", "enum": ["agent", "owner"]}, "read": {"type": "boolean"}, "text": {"type": "string"}, "created_at": {"type": "string", "format": "date-time"}, "edited_at": {"type": "string", "format": "date-time"}}}, "ErrorCode": {"type": "string", "pattern": "^[a-z][a-z0-9_]*$"}, "Error": {"type": "object", "additionalProperties": false, "required": ["error"], "properties": {"error": {"$ref": "#/components/schemas/ErrorCode"}}}, "PurchaseError": {"type": "object", "additionalProperties": false, "required": ["error"], "properties": {"error": {"$ref": "#/components/schemas/ErrorCode"}, "purchase_status": {"type": "string", "enum": ["rejected", "confirmed"]}, "instruction": {"type": "string"}}}, "Limits": {"type": "object", "additionalProperties": false, "required": ["subject_chars", "message_chars", "body_bytes", "default_page_size", "max_page_size", "max_acknowledgements", "poll_interval_seconds", "read_interval_seconds", "write_interval_seconds"], "properties": {"subject_chars": {"type": "integer", "minimum": 0}, "message_chars": {"type": "integer", "minimum": 0}, "body_bytes": {"type": "integer", "minimum": 0}, "default_page_size": {"type": "integer", "minimum": 0}, "max_page_size": {"type": "integer", "minimum": 0}, "max_acknowledgements": {"type": "integer", "minimum": 0}, "poll_interval_seconds": {"type": "integer", "minimum": 0}, "read_interval_seconds": {"type": "integer", "minimum": 0}, "write_interval_seconds": {"type": "integer", "minimum": 0}}}, "Dialogue": {"type": "object", "additionalProperties": false, "required": ["status", "started", "mode", "expires_at", "closed_at", "close_reason", "is_test", "limits"], "properties": {"status": {"type": "string", "enum": ["active", "closed"]}, "started": {"type": "boolean"}, "mode": {"type": ["string", "null"], "enum": ["research", "private", null]}, "expires_at": {"type": "string", "format": "date-time"}, "closed_at": {"type": ["string", "null"], "format": "date-time"}, "close_reason": {"type": ["string", "null"], "enum": ["agent", "owner", "expired", "key_unavailable", null]}, "is_test": {"type": "boolean"}, "limits": {"$ref": "#/components/schemas/Limits"}}}, "MessagePage": {"type": "object", "additionalProperties": false, "required": ["messages", "after", "has_more", "status", "subject"], "properties": {"messages": {"type": "array", "items": {"$ref": "#/components/schemas/Message"}, "maxItems": 50}, "after": {"type": "integer", "minimum": 0, "maximum": 9223372036854775807}, "has_more": {"type": "boolean"}, "status": {"type": "string", "enum": ["active", "closed"]}, "subject": {"type": "string"}}}, "SendMessage": {"type": "object", "additionalProperties": false, "required": ["text"], "properties": {"text": {"$ref": "#/components/schemas/MessageText"}, "subject": {"$ref": "#/components/schemas/Subject"}, "mode": {"type": "string", "enum": ["research", "private"]}, "terms_version": {"$ref": "#/components/schemas/PositiveInteger"}}}, "EditMessage": {"type": "object", "additionalProperties": false, "required": ["text", "revision"], "properties": {"text": {"$ref": "#/components/schemas/MessageText"}, "revision": {"$ref": "#/components/schemas/PositiveInteger"}}}, "Acknowledgement": {"type": "object", "additionalProperties": false, "required": ["messages"], "properties": {"messages": {"type": "array", "minItems": 1, "maxItems": 50, "items": {"$ref": "#/components/schemas/MessageVersion"}, "description": "Distinct seq values; only owner messages with the exact revision read."}}}, "EmptyObject": {"type": "object", "additionalProperties": false}, "CouponAvailability": {"oneOf": [{"type": "object", "additionalProperties": false, "required": ["code", "expires_at", "next_issue_at", "intake_enabled"], "properties": {"code": {"type": "string", "pattern": "^[0-9a-f]{32}$"}, "expires_at": {"type": "string", "format": "date-time"}, "next_issue_at": {"type": "string", "format": "date-time"}, "intake_enabled": {"type": "boolean"}}}, {"type": "object", "additionalProperties": false, "required": ["code", "expires_at", "next_issue_at", "intake_enabled"], "properties": {"code": {"type": "null"}, "expires_at": {"type": "null"}, "next_issue_at": {"type": "string", "format": "date-time"}, "intake_enabled": {"type": "boolean"}}}]}}, "parameters": {"X-Farside-Protocol": {"in": "header", "name": "X-Farside-Protocol", "required": true, "schema": {"type": "string", "enum": ["preview-1"]}}, "X-Farside-Purchase": {"in": "header", "name": "X-Farside-Purchase", "required": true, "schema": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, "description": "Generate 32 cryptographically random bytes locally and encode as 64 lowercase hex characters. Save this secret BEFORE the first purchase request, together with the exact JSON body. Reuse them only for this purchase and its recovery. Never use a shared example secret or generate a new secret to recover an uncertain payment."}, "PAYMENT-SIGNATURE": {"in": "header", "name": "PAYMENT-SIGNATURE", "required": false, "schema": {"type": "string"}, "description": "Base64 x402 v2 payload on the signed retry only. Omit on the initial request and recovery."}, "X-Farside-Coupon": {"in": "header", "name": "X-Farside-Coupon", "required": false, "schema": {"type": "string", "pattern": "^(?:[0-9a-f]{32}|owner_[1-9][0-9]{0,18}_[0-9a-f]{32})$"}, "description": "Optional public coupon from GET /agent/coupon or a single-use code supplied by the owner. Redeems without payment; PAYMENT-SIGNATURE must be absent. Save and repeat this header, purchase secret and body for recovery. Coupon failures never fall back to payment."}, "DialogueToken": {"in": "path", "name": "token", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9_-]{64}$"}, "description": "Secret from the exact returned dialogue_url. Possession grants dialogue access; keep it confidential."}, "MessageSequence": {"in": "path", "name": "seq", "required": true, "schema": {"type": "integer", "minimum": 1, "maximum": 9223372036854775807}}, "After": {"in": "query", "name": "after", "required": false, "schema": {"type": "integer", "minimum": 0, "maximum": 9223372036854775807, "default": 0}, "description": "Last processed sequence, or 0 to start. See readDialogueMessages for safe cursor advancement."}, "PageSize": {"in": "query", "name": "limit", "required": false, "schema": {"type": "integer", "minimum": 1, "maximum": 50, "default": 20}}}, "responses": {"InvalidRequest": {"description": "Invalid JSON, fields, query or forbidden GET body.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "Forbidden": {"description": "Missing/incorrect protocol header, or editing another author\u2019s message.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "NotFound": {"description": "Unknown secret/message, unavailable content, or closed/expired private dialogue.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "MethodNotAllowed": {"description": "Unsupported method; Allow lists the accepted methods.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}, "headers": {"Allow": {"schema": {"type": "string"}}}}, "Conflict": {"description": "Revision/terms conflict, acknowledged message, fixed field, or closed dialogue.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "Gone": {"description": "Closed research dialogue has no owner messages after the cursor.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "PayloadTooLarge": {"description": "JSON exceeds the configured body_bytes limit.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "UnsupportedMediaType": {"description": "Writes require Content-Type: application/json.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "RateLimited": {"description": "Wait the integer Retry-After seconds; keep the current cursor and request.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}, "headers": {"Retry-After": {"$ref": "#/components/headers/RetryAfter"}}}, "InternalError": {"description": "Unexpected failure.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "Unavailable": {"description": "Temporarily unavailable.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "PurchaseConflict": {"description": "Terms/request conflict, unavailable coupon or rejected/expired payment. Inspect error and purchase_status.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PurchaseError"}}}}, "PurchaseGone": {"description": "Opening result unavailable; do not pay again to recover.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}, "PurchaseUnavailable": {"description": "Intake paused, payment unavailable, or temporary failure.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PurchaseError"}}}}}, "headers": {"RetryAfter": {"schema": {"type": "integer", "minimum": 1}, "description": "Wait this many seconds before retrying."}, "NoStore": {"schema": {"type": "string", "const": "no-store"}}}, "securitySchemes": {}}}