{"openapi":"3.1.0","info":{"title":"JustAutomate public API","version":"1.0.0","summary":"Read-only API for the public website of JustAutomate: search and read pages, the process workshop price and contact options.","description":"JustAutomate (justautomate.ai) is a Polish B2B agency that designs and implements AI agents, process automation and system integrations for companies in Poland, Germany, Austria, Switzerland and English-speaking markets.\n\nThis API is **read-only** and exposes only what is public on the website. It never submits forms, books meetings or sends messages: to get in touch, give the user the contact link or e-mail address from `GET /api/v1/contact`. The same data is available over MCP at https://justautomate.ai/mcp.\n\n**Authentication.** None required. An optional OAuth 2.1 bearer token (scope `mcp:read`, see https://justautomate.ai/auth.md) identifies the agent and raises the rate limit. An invalid or expired token gets `401` with a `WWW-Authenticate` header pointing at https://justautomate.ai/.well-known/oauth-protected-resource; `GET /api/v1/identity` always needs a token.\n\n**Rate limits.** 120 requests per 60 seconds per IP address, 600 per agent with a token. Every response carries `RateLimit-Policy` (IETF RateLimit header fields) and `X-RateLimit-Limit`. A `429` carries `Retry-After` (seconds) and `RateLimit` with the remaining quota (0). A batch counts as one request.\n\n**Errors.** Every 4xx and 5xx response is `application/problem+json` (RFC 9457) with `type`, `title`, `status`, `detail`, `instance` and a machine-readable `code`. Each `type` URI points at its section of https://justautomate.ai/api.md.\n\n**Pagination.** `GET /api/v1/pages` is cursor-based: pass `next_cursor` from the response as `cursor` until it is `null`. The next page is also in the `Link: <...>; rel=\"next\"` header. Cursors are opaque.\n\n**Versioning and deprecation.** The major version is in the path (`/api/v1`). Within v1 changes are additive only: new endpoints, new optional parameters, new response fields. Clients must ignore unknown fields. A breaking change ships as `/api/v2`; v1 then keeps working and its responses carry a `Deprecation` header (RFC 9745) from the day the decision is made and a `Sunset` header (RFC 8594) with the removal date, at least 90 days later. `info.version` follows SemVer.\n\n**Batch and retries.** `POST /api/v1/batch` runs up to 10 GET operations in one request. All operations are read-only and have no side effects, so every request, including a batch, is safe to retry. An optional `Idempotency-Key` header on the batch is accepted and echoed back.\n\nAll prices are net, excluding VAT. The only public price is the process workshop; implementation is quoted after it.","termsOfService":"https://justautomate.ai/regulamin/","contact":{"name":"JustAutomate","email":"contact@justautomate.ai","url":"https://justautomate.ai/en/contact/"},"license":{"name":"Proprietary. Public content of justautomate.ai, free to read and quote with attribution.","url":"https://justautomate.ai/regulamin/"}},"externalDocs":{"description":"Developer portal: quickstart, self-serve token, testing. Reference in Markdown: /api.md","url":"https://justautomate.ai/developers/"},"servers":[{"url":"https://justautomate.ai","description":"Production"}],"security":[{},{"agentOAuth":["mcp:read"]}],"tags":[{"name":"content","description":"Search and read public pages."},{"name":"offer","description":"Process workshop: the paid first step, with public prices per market."},{"name":"contact","description":"How a person can reach JustAutomate."},{"name":"agents","description":"Agent identity and batch reads."},{"name":"meta","description":"API description and status."}],"paths":{"/api/v1":{"get":{"operationId":"getApiRoot","tags":["meta"],"summary":"Describe API v1","description":"Entry point of API v1: what the API covers and the full URL of every endpoint.","responses":{"200":{"description":"API description.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiRoot"}}}},"401":{"$ref":"#/components/responses/InvalidToken"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/v1/health":{"get":{"operationId":"getHealth","tags":["meta"],"summary":"Check API health","description":"Whether the site index is loaded, with the number of pages and workshop markets and when the index was built. Returns 503 when the data is not available.","responses":{"200":{"description":"Healthy.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"401":{"$ref":"#/components/responses/InvalidToken"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Site data not available.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/api/v1/search":{"get":{"operationId":"searchSite","tags":["content"],"summary":"Search the website","description":"Full-text search over the public pages of justautomate.ai (services, products, industry offers, guides, blog posts and city pages in Polish, English and German). Returns titles, descriptions and URLs ranked by relevance; read a result with getPage.","parameters":[{"$ref":"#/components/parameters/Query"},{"$ref":"#/components/parameters/Lang"},{"$ref":"#/components/parameters/PageType"},{"$ref":"#/components/parameters/SearchLimit"}],"responses":{"200":{"description":"Search results.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResults"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/InvalidToken"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/pages":{"get":{"operationId":"listPages","tags":["content"],"summary":"List pages","description":"Public pages, optionally filtered by kind and language, with cursor pagination. City pages (about 1,400 near-identical local landing pages) are listed only when type is \"city\".","parameters":[{"$ref":"#/components/parameters/PageType"},{"$ref":"#/components/parameters/Lang"},{"$ref":"#/components/parameters/PageLimit"},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"One page of results.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"},"Link":{"$ref":"#/components/headers/Link"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/InvalidToken"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/page":{"get":{"operationId":"getPage","tags":["content"],"summary":"Read a page as Markdown","description":"The content of one public page as clean Markdown (no navigation, scripts or forms), with title, description and canonical URL in the front matter. Accepts a full URL or a path. Old addresses are followed to their current page.","parameters":[{"$ref":"#/components/parameters/Url"}],"responses":{"200":{"description":"Page content.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageContent"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/InvalidToken"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/workshop":{"get":{"operationId":"getWorkshopOffer","tags":["offer"],"summary":"Get the process workshop offer","description":"The process workshop is how every JustAutomate engagement starts: the team maps how a process really runs, lists automation candidates with estimated hours saved, sets the implementation order and quotes the implementation. Returns the public price per week (net), usual duration, deliverables and the offer page for each market.","parameters":[{"$ref":"#/components/parameters/Market"}],"responses":{"200":{"description":"Workshop offers.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkshopOffers"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/InvalidToken"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/contact":{"get":{"operationId":"getContactOptions","tags":["contact"],"summary":"Get contact options","description":"How a person can reach JustAutomate or book the free 30-minute consultation: e-mail, phone and the contact form in Polish, English or German. Nothing here submits forms or books meetings: pass the link or address to the user.","parameters":[{"$ref":"#/components/parameters/Lang"}],"responses":{"200":{"description":"Contact options.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactOptions"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/InvalidToken"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/identity":{"get":{"operationId":"getAgentIdentity","tags":["agents"],"summary":"Identify the calling agent","description":"Who the bearer token belongs to and which rate limit applies. Requires a token; without one returns 401 with WWW-Authenticate pointing at the protected resource metadata.","security":[{"agentOAuth":["mcp:read"]}],"responses":{"200":{"description":"The agent behind the token.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIdentity"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/agent/identity":{"get":{"operationId":"getAgentIdentityWellKnown","tags":["agents"],"summary":"Identify the calling agent (agent-auth address)","description":"Same as getAgentIdentity, at the address agent-auth clients look for.","security":[{"agentOAuth":["mcp:read"]}],"responses":{"200":{"description":"The agent behind the token.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentIdentity"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/v1/batch":{"post":{"operationId":"batchRead","tags":["agents"],"summary":"Run several reads in one request","description":"Runs 1 to 10 GET operations on /api/v1 endpoints (except batch and identity) and returns each status and body in order. Read-only and free of side effects, so it is always safe to retry; counts as one request against the rate limit. An optional Idempotency-Key is accepted and echoed back.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"}}}},"responses":{"200":{"description":"Results in request order.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"},"Idempotency-Key":{"$ref":"#/components/headers/Idempotency-Key"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/InvalidToken"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"agentOAuth":{"type":"oauth2","description":"Optional. Identifies the agent and raises the rate limit to 600 requests per minute. Dynamic client registration and anonymous agent registration are described at https://justautomate.ai/auth.md.","flows":{"authorizationCode":{"authorizationUrl":"https://justautomate.ai/oauth/authorize/","tokenUrl":"https://justautomate.ai/oauth/token/","refreshUrl":"https://justautomate.ai/oauth/token/","scopes":{"mcp:read":"Read public content of justautomate.ai (REST API and MCP server)."}}}}},"parameters":{"Query":{"name":"q","in":"query","required":true,"description":"What to look for, in any of the site languages, e.g. \"process workshop price\", \"KI-Agentur Wien\", \"automatyzacja faktur\".","schema":{"type":"string","minLength":1,"maxLength":200}},"Lang":{"name":"lang","in":"query","required":false,"description":"Only pages in this language.","schema":{"type":"string","enum":["pl","en","de"]}},"PageType":{"name":"type","in":"query","required":false,"description":"Only pages of this kind.","schema":{"type":"string","enum":["home","section","offer","guide","blog","about","contact","city","legal","other"]}},"SearchLimit":{"name":"limit","in":"query","required":false,"description":"Maximum number of results.","schema":{"type":"integer","minimum":1,"maximum":20,"default":8}},"PageLimit":{"name":"limit","in":"query","required":false,"description":"Maximum number of pages in one response.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},"Cursor":{"name":"cursor","in":"query","required":false,"description":"Opaque cursor: next_cursor from the previous response. Omit for the first page.","schema":{"type":"string","maxLength":64}},"Url":{"name":"url","in":"query","required":true,"description":"Page URL on justautomate.ai or a path starting with \"/\", e.g. \"/en/services/process-workshop/\".","schema":{"type":"string","minLength":1,"maxLength":500}},"Market":{"name":"market","in":"query","required":false,"description":"PL (Poland, PLN), DE (Germany), AT (Austria), CH (Switzerland, CHF) or INTL (English-speaking clients, EUR). Omit to get all.","schema":{"type":"string","enum":["PL","DE","AT","CH","INTL"]}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional client-chosen key (1 to 255 visible ASCII characters), echoed back. The batch has no side effects, so a retry with or without the key is always safe.","schema":{"type":"string","minLength":1,"maxLength":255}}},"headers":{"RateLimit-Policy":{"description":"Quota policy (IETF RateLimit header fields): \"ip\";q=120;w=60 without a token, \"agent\";q=600;w=60 with one.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (IETF RateLimit header fields). Sent with 429, where it is 0.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the window. Sent with 429, where it is 0.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"Link":{"description":"RFC 8288 link to the next page: <url>; rel=\"next\". Absent on the last page.","schema":{"type":"string"}},"Idempotency-Key":{"description":"The Idempotency-Key sent with the request, echoed back.","schema":{"type":"string"}},"Deprecation":{"description":"Present only once this endpoint or API version is deprecated: the date the deprecation took effect (RFC 9745). Absent on every endpoint today.","schema":{"type":"string"}},"Sunset":{"description":"Present only once a removal date is set (RFC 8594): the HTTP-date after which the endpoint stops answering, at least 90 days after Deprecation first appeared.","schema":{"type":"string"}}},"responses":{"BadRequest":{"description":"A parameter or the request body is invalid (code invalid-parameter or invalid-body).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"Unauthorized":{"description":"A bearer token is required (code unauthorized).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"WWW-Authenticate":{"description":"Bearer resource_metadata=\"https://justautomate.ai/.well-known/oauth-protected-resource\", scope=\"mcp:read\"","schema":{"type":"string"}}}},"InvalidToken":{"description":"The bearer token is invalid or expired (code invalid-token). Call without a token or get a new one.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"WWW-Authenticate":{"description":"Bearer resource_metadata=\"https://justautomate.ai/.well-known/oauth-protected-resource\", scope=\"mcp:read\", error=\"invalid_token\"","schema":{"type":"string"}}}},"NotFound":{"description":"No such public page or endpoint (code not-found).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"PayloadTooLarge":{"description":"The request body is too large (code payload-too-large).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"UnsupportedMediaType":{"description":"The request body is not JSON (code unsupported-media-type).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"TooManyRequests":{"description":"Rate limit exceeded (code rate-limited). Wait Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"}}},"InternalError":{"description":"Unexpected error (code internal-error). Retry later.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"Unavailable":{"description":"Site data is temporarily unavailable (code unavailable). Wait Retry-After seconds.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"schemas":{"Problem":{"type":"object","description":"Error in RFC 9457 format.","required":["type","title","status","code"],"properties":{"type":{"type":"string","format":"uri","description":"Link to the description of this error in /api.md."},"title":{"type":"string","description":"Short summary of the error type."},"status":{"type":"integer","description":"HTTP status code."},"detail":{"type":"string","description":"What went wrong in this request and how to fix it."},"instance":{"type":"string","description":"Path of the request that failed."},"code":{"type":"string","enum":["invalid-parameter","invalid-body","unsupported-media-type","payload-too-large","unauthorized","invalid-token","not-found","method-not-allowed","rate-limited","unavailable","internal-error"],"description":"Machine-readable error code."}}},"PageSummary":{"type":"object","required":["title","url","markdownUrl","type","lang"],"properties":{"title":{"type":"string"},"url":{"type":"string","format":"uri","description":"Canonical URL of the page."},"markdownUrl":{"type":"string","format":"uri","description":"The same page as Markdown."},"description":{"type":"string","description":"Meta description. City pages have none."},"type":{"type":"string","enum":["home","section","offer","guide","blog","about","contact","city","legal","other"]},"lang":{"type":"string","enum":["pl","en","de"]}}},"SearchResults":{"type":"object","required":["query","data"],"properties":{"query":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PageSummary"}},"note":{"type":"string"}}},"PageList":{"type":"object","required":["data","total","limit","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PageSummary"}},"total":{"type":"integer","description":"Number of pages matching the filters."},"limit":{"type":"integer"},"next_cursor":{"type":["string","null"],"description":"Pass as cursor to get the next page; null on the last page."}}},"PageContent":{"type":"object","required":["url","markdownUrl","markdown","truncated"],"properties":{"url":{"type":"string","format":"uri"},"markdownUrl":{"type":"string","format":"uri"},"markdown":{"type":"string","description":"Page content as Markdown with YAML front matter (title, description, url, lang)."},"truncated":{"type":"boolean","description":"True when the page was longer than 120000 characters and was cut."}}},"WorkshopOffer":{"type":"object","required":["market","lang","currency","name","price","priceNote","summary","includes","notes","url"],"properties":{"market":{"type":"string","enum":["PL","DE","AT","CH","INTL"]},"lang":{"type":"string","enum":["pl","en","de"]},"currency":{"type":"string","description":"ISO 4217 code."},"name":{"type":"string"},"price":{"type":"string","description":"Price per week as shown on the offer page, net."},"priceNote":{"type":"string","description":"Unit and usual duration."},"summary":{"type":"string"},"includes":{"type":"array","items":{"type":"string"}},"notes":{"type":"array","items":{"type":"string"}},"url":{"type":"string","format":"uri","description":"Offer page."}}},"WorkshopOffers":{"type":"object","required":["data","pricing","nextStep"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WorkshopOffer"}},"pricing":{"type":"string"},"nextStep":{"type":"string"}}},"ContactOptions":{"type":"object","required":["company","email","phone","contactForm","languages"],"properties":{"company":{"type":"string"},"email":{"type":["string","null"],"format":"email"},"phone":{"type":["string","null"]},"contactForm":{"description":"Contact page: one URL when lang is given, otherwise a map from language to URL.","oneOf":[{"type":"string","format":"uri"},{"type":"object","additionalProperties":{"type":"string","format":"uri"}}]},"languages":{"type":"array","items":{"type":"string"}},"consultation":{"type":"string"},"note":{"type":"string"}}},"AgentIdentity":{"type":"object","required":["authenticated","sub","scope","resource","rateLimit"],"properties":{"authenticated":{"type":"boolean","const":true},"sub":{"type":"string","description":"Agent identifier from the token."},"scope":{"type":"string"},"resource":{"type":"string","format":"uri"},"rateLimit":{"type":"object","required":["policy","limit","windowSeconds"],"properties":{"policy":{"type":"string"},"limit":{"type":"integer"},"windowSeconds":{"type":"integer"}}}}},"Health":{"type":"object","required":["ok","version"],"properties":{"ok":{"type":"boolean"},"version":{"type":"string"},"pages":{"type":"integer"},"workshopMarkets":{"type":"integer"},"generated":{"type":"string","format":"date-time"},"error":{"type":"string"}}},"ApiRoot":{"type":"object","required":["name","version","apiVersion","openapi","endpoints"],"properties":{"name":{"type":"string"},"version":{"type":"string"},"apiVersion":{"type":"string"},"description":{"type":"string"},"openapi":{"type":"string","format":"uri"},"documentation":{"type":"string","format":"uri"},"mcp":{"type":"string","format":"uri"},"authentication":{"type":"string"},"endpoints":{"type":"object","additionalProperties":{"type":"string"}}}},"BatchRequest":{"type":"object","required":["requests"],"properties":{"requests":{"type":"array","minItems":1,"maxItems":10,"items":{"type":"object","required":["path"],"properties":{"id":{"type":["string","integer"],"description":"Echoed back in the result; defaults to the position."},"method":{"type":"string","enum":["GET"],"default":"GET"},"path":{"type":"string","description":"Path with query string, e.g. \"/api/v1/search?q=workshop\".","pattern":"^/api/v1"}}}}}},"BatchResponse":{"type":"object","required":["responses"],"properties":{"responses":{"type":"array","items":{"type":"object","required":["id","status","body"],"properties":{"id":{"type":["string","integer"]},"status":{"type":"integer"},"body":{"description":"The body the same GET would return: its success schema or Problem.","type":"object"}}}}}}}}}