{"openapi":"3.1.0","info":{"title":"Boatwork Public API","version":"1.0.0","description":"Programmatic access to Boatwork's contractor network and content. Built for partners, marketplace integrations, and AI agents indexing marine services.","contact":{"name":"Boatwork API","email":"support@boatwork.co","url":"https://developers.boatwork.co"},"license":{"name":"Boatwork API Terms","url":"https://developers.boatwork.co/terms"}},"servers":[{"url":"https://boatwork.co","description":"Production"},{"url":"https://developers.boatwork.co","description":"Developer portal (shares the same /api host)"}],"security":[{"bearer":[]},{}],"tags":[{"name":"Agents","description":"Self-registration + role discovery for AI agents."},{"name":"Contractors","description":"Search, retrieve, and explore marine contractor profiles."},{"name":"Content","description":"Marine industry articles and blog posts."}],"paths":{"/api/agents/register":{"post":{"tags":["Agents"],"summary":"Register a new agent identity","description":"Public, no-auth. Creates a User with isAgent=true, mints an initial bw_public_* API key, returns the plaintext key ONCE. To upgrade to admin-tier (bw_live_*), ask the Boatwork admin to elevate the agent at /admin/agents/<id>.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":10},"purpose":{"type":"string","description":"Optional — what this agent does."}},"required":["firstName","lastName","email","password"]}}}},"responses":{"200":{"description":"Agent created. Save the apiKey value immediately.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"type":"object","properties":{"agent":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"isAdmin":{"type":"boolean"},"isAgent":{"type":"boolean"}}},"credentials":{"type":"object","properties":{"email":{"type":"string"},"apiKey":{"type":"string","example":"bw_public_abcdef1234567890"},"apiKeyPrefix":{"type":"string"},"apiBase":{"type":"string"},"howToUse":{"type":"string"}}}}}},"required":["success","data"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"409":{"description":"A user with this email already exists."}}}},"/api/account/me":{"get":{"tags":["Agents"],"summary":"Get current user role + capabilities","description":"Returns the signed-in user, including isAgent, isAdmin, and a canMintAdminKeys convenience flag. Returns data:null if unauthenticated. Use to poll for admin elevation after registration.","responses":{"200":{"description":"Current user (or null if unauthenticated).","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true},"data":{"oneOf":[{"type":"null"},{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"isAdmin":{"type":"boolean"},"isAgent":{"type":"boolean"},"canMintAdminKeys":{"type":"boolean"}}}]}},"required":["success"]}}}}}}},"/api/v1/public/contractors/search":{"get":{"tags":["Contractors"],"summary":"Search contractors","description":"Keyword + filter search across the contractor network. Supports specialty, city, state filters.","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Free-text query (matches name, services, marina)."},{"name":"specialty","in":"query","schema":{"type":"string"},"description":"Specialty slug (see /specialties)."},{"name":"city","in":"query","schema":{"type":"string"}},{"name":"state","in":"query","schema":{"type":"string","minLength":2,"maxLength":2},"description":"US state code (2 letters)."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque cursor returned by the previous page."}],"responses":{"200":{"description":"Paginated contractor result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractorPage"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/public/contractors/geo-search":{"get":{"tags":["Contractors"],"summary":"Geo-radius contractor search","description":"Find contractors within a radius of a latitude/longitude.","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","format":"double"}},{"name":"lng","in":"query","required":true,"schema":{"type":"number","format":"double"}},{"name":"radiusMiles","in":"query","schema":{"type":"number","minimum":1,"maximum":250,"default":25}},{"name":"specialty","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"Contractors sorted by distance ascending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContractorPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/public/contractors/specialties":{"get":{"tags":["Contractors"],"summary":"List specialties","description":"Every marine service specialty available across the network.","responses":{"200":{"description":"Array of specialty descriptors.","content":{"application/json":{"schema":{"type":"object","properties":{"specialties":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string","example":"hull-cleaning"},"label":{"type":"string","example":"Hull Cleaning"},"contractorCount":{"type":"integer"}},"required":["slug","label","contractorCount"]}}},"required":["specialties"]}}}}}}},"/api/v1/public/contractors/{slug}":{"get":{"tags":["Contractors"],"summary":"Get contractor profile by slug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Full contractor profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contractor"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/public/contractors/by-id/{id}":{"get":{"tags":["Contractors"],"summary":"Get contractor profile by id","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Full contractor profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contractor"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/public/articles":{"get":{"tags":["Content"],"summary":"List published articles","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated article list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticlePage"}}}}}}},"/api/v1/public/blog":{"get":{"tags":["Content"],"summary":"List blog posts","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated blog post list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPage"}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"bw_public_*","description":"Public API key minted at /developers/api-keys. Required for higher rate limits; anonymous reads are allowed but throttled."}},"responses":{"BadRequest":{"description":"Invalid request — see body for details.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":false},"error":{"type":"string"}},"required":["success","error"]}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":false},"error":{"type":"string"}},"required":["success","error"]}}}},"Unauthorized":{"description":"Missing or invalid Authorization header.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":false},"error":{"type":"string"}},"required":["success","error"]}}}},"Forbidden":{"description":"Authenticated but lacks the required scope.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":false},"error":{"type":"string"}},"required":["success","error"]}}}},"RateLimited":{"description":"Rate limit exceeded. Retry after the value in the `Retry-After` header.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":false},"error":{"type":"string"}},"required":["success","error"]}}}}},"schemas":{"Contractor":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"city":{"type":"string","nullable":true},"state":{"type":"string","nullable":true},"latitude":{"type":"number","format":"double","nullable":true},"longitude":{"type":"number","format":"double","nullable":true},"specialties":{"type":"array","items":{"type":"string"}},"rating":{"type":"number","nullable":true},"reviewCount":{"type":"integer"},"phone":{"type":"string","nullable":true},"website":{"type":"string","nullable":true}},"required":["id","slug","name","specialties","reviewCount"]},"ContractorPage":{"type":"object","properties":{"contractors":{"type":"array","items":{"$ref":"#/components/schemas/Contractor"}},"nextCursor":{"type":"string","nullable":true}},"required":["contractors"]},"Article":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string","nullable":true},"publishedAt":{"type":"string","format":"date-time"},"url":{"type":"string"}},"required":["id","slug","title","publishedAt","url"]},"ArticlePage":{"type":"object","properties":{"articles":{"type":"array","items":{"$ref":"#/components/schemas/Article"}},"nextCursor":{"type":"string","nullable":true}},"required":["articles"]},"BlogPost":{"$ref":"#/components/schemas/Article"},"BlogPage":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/BlogPost"}},"nextCursor":{"type":"string","nullable":true}},"required":["posts"]}}}}