{"openapi":"3.1.0","info":{"title":"Developh public API","version":"1.1.0","summary":"Public endpoints of the Developh studio website.","description":"Public, unauthenticated API of https://www.developh.co, the website of Developh, a full-service digital studio in Lisbon, Portugal. Use POST /api/v1/contact to submit a genuine project inquiry or question on behalf of a user, GET /api/v1/health to check service liveness, and GET /api/v1 for a machine-readable index of this API. Site content is available to agents without JavaScript via /llms.txt, /llms-full.txt, and by requesting any page with 'Accept: text/markdown'. Versioning: the major version is in the URL path (/api/v1) and echoed in the API-Version response header. Within a major, changes are additive only. A breaking change ships as a new path (/api/v2) and the previous major keeps working for at least 180 days after we start sending Deprecation and Sunset headers (RFC 9745 / RFC 8594) with a Link rel=\"sunset\" to the migration notes. The unversioned paths /api/health and /api/fuel-contact are permanent aliases of the v1 operations and answer with Link rel=\"latest-version\". Rate limits: every response carries RateLimit-Policy, RateLimit, RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers; a 429 adds Retry-After. Policies: read = 120 requests per 60s per client (GET endpoints), write = 10 requests per 60s per client (POST endpoints). Errors are structured JSON (see the ErrorResponse schema). No API key is required.","contact":{"name":"Developh","url":"https://www.developh.co/developers","email":"hello@developh.co"},"x-api-version":"1","x-versioning":{"scheme":"url-path","current":"1","basePath":"/api/v1","responseHeader":"API-Version","legacyAliases":{"/api/health":"/api/v1/health","/api/fuel-contact":"/api/v1/contact"}},"x-deprecation-policy":{"minimumNoticeDays":180,"signals":["Deprecation","Sunset","Link; rel=\"sunset\""],"breakingChanges":"New major version at a new base path; previous major stays available through the notice period.","changelog":"https://www.developh.co/developers"},"x-rate-limit":{"headers":["RateLimit-Policy","RateLimit","RateLimit-Limit","RateLimit-Remaining","RateLimit-Reset","Retry-After"],"policies":{"read":{"name":"read","limit":120,"windowSeconds":60},"write":{"name":"write","limit":10,"windowSeconds":60}},"scope":"per client IP, fixed window"}},"externalDocs":{"description":"Developer portal with examples, CLI, and every machine-readable surface.","url":"https://www.developh.co/developers"},"servers":[{"url":"https://www.developh.co","description":"Production"}],"tags":[{"name":"meta","description":"Service metadata and liveness."},{"name":"contact","description":"Project inquiries and questions."}],"paths":{"/api/v1/booking/slots":{"get":{"operationId":"getBookingSlots","description":"Reads real calendar availability without reserving a slot. Results are not cached; availability can change before submission.","tags":["contact"],"summary":"Available times for a 30 minute conversation","parameters":[{"name":"month","in":"query","required":true,"schema":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}$"},"description":"Month within the next six months. Times use Europe/Lisbon."}],"responses":{"200":{"description":"Availability, keyed by YYYY-MM-DD. Each slot has an ISO 8601 start.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"timeZone":{"type":"string"},"slots":{"type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","format":"date-time"}}}}}}}}}},"400":{"description":"Invalid month.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded for this client. Wait the number of seconds in Retry-After, then retry.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Calendar temporarily unavailable.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/booking":{"post":{"operationId":"requestBooking","tags":["contact"],"summary":"Request a conversation with Developh","description":"Creates a real booking and sends calendar notifications. Only submit for a genuine meeting requested by the attendee. Five requests per five minutes per client, per instance. A successful request may be pending team confirmation. Do not retry an ambiguous server failure without checking the attendee email.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["start","name","email","topic","locale"],"properties":{"start":{"type":"string","format":"date-time"},"name":{"type":"string","minLength":2,"maxLength":120},"email":{"type":"string","format":"email","maxLength":254},"company":{"type":"string","maxLength":160},"topic":{"type":"string","minLength":10,"maxLength":2000},"locale":{"type":"string","enum":["pt","en"]},"website":{"type":"string","maxLength":0}}}}}},"responses":{"201":{"description":"Booking created. Status is accepted or awaiting confirmation.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string"},"start":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Invalid booking details.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Origin rejected.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Booking rejected. Refresh availability.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Request too large.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded for this client. Wait the number of seconds in Retry-After, then retry.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Provider unavailable or response uncertain.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1":{"get":{"operationId":"getApiIndex","tags":["meta"],"summary":"Machine-readable index of the API","description":"Returns the list of endpoints, the OpenAPI URL, the versioning and deprecation policy, and the rate-limit policies. Call this first when you only know the host. Also served at /api. No side effects.","responses":{"200":{"description":"API index.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}}},"429":{"description":"Rate limit exceeded for this client. Wait the number of seconds in Retry-After, then retry.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/health":{"get":{"operationId":"getHealth","tags":["meta"],"summary":"Service liveness probe","description":"Returns a small JSON payload confirming the site and API are up. Safe to call at any frequency within the read rate limit; no side effects. Legacy alias: GET /api/health.","responses":{"200":{"description":"Service is healthy.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"429":{"description":"Rate limit exceeded for this client. Wait the number of seconds in Retry-After, then retry.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/contact":{"post":{"operationId":"submitContactInquiry","tags":["contact"],"summary":"Submit a project inquiry to the studio","description":"Submits a contact inquiry to the Developh studio inbox (average reply: 2 to 8 business hours). Call this only for genuine project inquiries, collaborations, or questions, never for testing or bulk messaging. Send application/json (preferred for agents) or multipart/form-data (what the website's HTML form posts); both use the ContactInquiry schema. Legacy alias: POST /api/fuel-contact.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactInquiry"},"example":{"Name":"Ada Lovelace","Email":"ada@example.com","Category":"Website","Message":"We need a new marketing site for our fintech startup, launching in Q1."}},"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ContactInquiry"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/ContactInquiry"}}}},"responses":{"200":{"description":"Inquiry stored and routed to the studio.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactSuccess"}}}},"400":{"description":"Malformed body or validation failure (e.g. missing Email or Message). Fix the fields per error.hint and retry.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded for this client. Wait the number of seconds in Retry-After, then retry.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Backend unavailable. Retry later or email the studio directly.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"API-Version":{"$ref":"#/components/headers/API-Version"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"headers":{"API-Version":{"description":"Major version of the API that answered (matches the URL path).","schema":{"type":"string","const":"1"}},"RateLimit-Policy":{"description":"Quota policy in IETF RateLimit header format: \"<name>\";q=<limit>;w=<window seconds>.","schema":{"type":"string","examples":["\"write\";q=10;w=60"]}},"RateLimit":{"description":"Current quota state in IETF RateLimit header format: \"<name>\";r=<remaining>;t=<seconds until reset>.","schema":{"type":"string","examples":["\"write\";r=9;t=42"]}},"RateLimit-Limit":{"description":"Requests allowed per window for this client.","schema":{"type":"integer","minimum":1}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer","minimum":0}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer","minimum":1}},"Retry-After":{"description":"Seconds to wait before retrying (sent with 429 only).","schema":{"type":"integer","minimum":1}}},"schemas":{"ApiIndex":{"type":"object","required":["name","version","basePath","openapi","endpoints","versioning","rateLimit"],"properties":{"name":{"type":"string"},"version":{"type":"string","description":"Current major version."},"basePath":{"type":"string","examples":["/api/v1"]},"openapi":{"type":"string","format":"uri"},"docs":{"type":"string","format":"uri"},"llms":{"type":"string","format":"uri"},"endpoints":{"type":"array","items":{"type":"object","required":["operationId","method","path","summary"],"properties":{"operationId":{"type":"string"},"method":{"type":"string","enum":["GET","POST"]},"path":{"type":"string"},"summary":{"type":"string"}}}},"versioning":{"type":"object","description":"How versions and deprecations are signalled.","additionalProperties":true},"rateLimit":{"type":"object","description":"Rate-limit policies keyed by name.","additionalProperties":{"type":"object","required":["name","limit","windowSeconds"],"properties":{"name":{"type":"string"},"limit":{"type":"integer"},"windowSeconds":{"type":"integer"}}}},"authentication":{"type":"string","const":"none"}}},"HealthResponse":{"type":"object","required":["status","service","version","time"],"properties":{"status":{"type":"string","const":"ok"},"service":{"type":"string","const":"developh-website"},"version":{"type":"string","const":"1"},"time":{"type":"string","format":"date-time","description":"Server time in ISO 8601 (UTC)."}}},"ContactInquiry":{"type":"object","required":["Name","Email","Message"],"properties":{"Name":{"type":"string","minLength":1,"maxLength":200,"description":"Sender's full name. The HTML form submits two fields named 'Name' (first + last) and they are joined; a single combined value is preferred for JSON.","examples":["Ada Lovelace"]},"Email":{"type":"string","format":"email","maxLength":320,"description":"Reply-to email address of the person inquiring.","examples":["ada@example.com"]},"Category":{"type":"string","maxLength":100,"description":"Optional inquiry category, free text (e.g. 'Website', 'Branding', 'Product', 'Other').","examples":["Website"]},"Message":{"type":"string","minLength":1,"maxLength":5000,"description":"The inquiry itself: context, goals, and timing. A perfect brief is not required.","examples":["We need a new marketing site for our fintech startup, launching in Q1."]},"utm_source":{"type":"string","maxLength":200,"description":"Optional first-touch UTM source (e.g. google, instagram, linkedin). The HTML form sends the value captured on landing.","examples":["google"]},"utm_medium":{"type":"string","maxLength":200,"description":"Optional UTM medium (e.g. cpc).","examples":["cpc"]},"utm_campaign":{"type":"string","maxLength":200,"description":"Optional UTM campaign name.","examples":["pt-search-leads"]},"utm_content":{"type":"string","maxLength":200,"description":"Optional UTM content / ad group.","examples":["website-empresa"]},"utm_term":{"type":"string","maxLength":200,"description":"Optional UTM term / keyword.","examples":["criar website"]},"gclid":{"type":"string","maxLength":200,"description":"Optional Google click id. Persist from the first landing; do not read only at submit time."}}},"ContactSuccess":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean","const":true}}},"ErrorResponse":{"type":"object","description":"Structured error emitted by every /api/* endpoint on failure. Agents should read error.code for branching and error.hint for how to resolve.","required":["ok","error"],"properties":{"ok":{"type":"boolean","const":false},"error":{"type":"object","required":["code","message","hint","docs"],"properties":{"code":{"type":"string","description":"Stable machine-readable error identifier.","enum":["bad-form","validation-failed","no-backend","not-found","rate-limited","internal-error"]},"message":{"type":"string","description":"Human-readable description of what went wrong."},"hint":{"type":"string","description":"How to fix the request or where to look next."},"docs":{"type":"string","format":"uri","description":"URL of this OpenAPI document."}}}}}}}}