{
  "openapi": "3.1.0",
  "info": {
    "title": "MakeBounty API",
    "version": "1.0.0",
    "summary": "Search, post and claim DIY bounties, for your own scripts and AI agents.",
    "description": "Every endpoint authenticates with a per-customer API key (`Authorization: Bearer mb_sk_...`) created at https://makebounty.com/account#agent-access. A key acts AS the customer: it can see and do only what that customer's own account can. Keys have a `read` or `write` scope. Rate limit: 300 calls per key per hour. The same jobs are available as MCP tools at https://makebounty.com/mcp and from the `makebounty` CLI (`npx makebounty --help`). Setup guide: https://makebounty.com/docs/agents. Posting a bounty does not charge any card; pledges and payouts happen only on the website and are not available from this API. Disputes, payouts, refunds and admin actions are never available from this API.",
    "termsOfService": "https://makebounty.com/terms",
    "contact": { "name": "MakeBounty support", "email": "support@makebounty.com", "url": "https://makebounty.com" }
  },
  "servers": [{ "url": "https://makebounty.com" }],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "account" },
    { "name": "bounties", "description": "Search, read, post and claim bounties. Posting and claiming require a write-scope key." }
  ],
  "paths": {
    "/api/v1/me": {
      "get": {
        "tags": ["account"], "operationId": "getMe", "summary": "Who this key belongs to, and recent agent calls",
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Me" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/v1/bounties": {
      "get": {
        "tags": ["bounties"], "operationId": "searchBounties", "summary": "Search the public bounty board",
        "parameters": [
          { "name": "q", "in": "query", "schema": { "type": "string" }, "example": "CNC bracket" },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["open", "claimed", "accepted", "paid", "cancelled"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" }, "description": "Previous response's nextCursor" }
        ],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchResponse" } } } }, "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      },
      "post": {
        "tags": ["bounties"], "operationId": "postBounty", "summary": "Post a new bounty (requires confirm: true)",
        "description": "Creates a public bounty listing. Does not charge any card. Treated as money-adjacent and destructive because the listing goes live publicly and cannot be withdrawn through this API; the request is refused with 400 and the would-be bounty body unless `confirm` is `true`. The `automotive` category requires an explicit `safetyCritical` boolean. Requires a write-scope key.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PostBountyRequest" } } } },
        "responses": {
          "200": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PostedBounty" } } } },
          "400": { "description": "Invalid input, or confirm was not true (body carries wouldPost)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Scope" }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/bounties/{id}": {
      "get": {
        "tags": ["bounties"], "operationId": "getBounty", "summary": "One bounty's full detail",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "required": ["bounty"], "properties": { "bounty": { "$ref": "#/components/schemas/Bounty" } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/v1/bounties/{id}/claim": {
      "post": {
        "tags": ["bounties"], "operationId": "claimBounty", "summary": "Claim an open bounty as its solver",
        "description": "You cannot claim your own bounty. `onCamera: true` requires `videoUrl` to be a real youtube.com/youtu.be URL. Does not move money. Requires a write-scope key. The result says whether you can be paid (`payoutSetupComplete`); payout setup is a Stripe-hosted flow a person completes at /account#get-paid, not available from this API.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "onCamera": { "type": "boolean", "default": false }, "videoUrl": { "type": "string" } } } } } },
        "responses": {
          "200": { "description": "Claimed", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "status": { "type": "string" }, "url": { "type": "string" },
                    "payoutSetupComplete": { "type": "boolean", "description": "false means you cannot be paid for this bounty until payout setup is complete" },
                    "note": { "type": "string", "description": "present only when payoutSetupComplete is false: what a person must do and where" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Scope" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "description": "Not claimable in its current status, or it is your own bounty", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/bounties/mine": {
      "get": {
        "tags": ["bounties"], "operationId": "listMyBounties", "summary": "Bounties this account posted and claimed",
        "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } }],
        "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "required": ["posted", "claimed"], "properties": { "posted": { "type": "array", "items": { "$ref": "#/components/schemas/Bounty" } }, "claimed": { "type": "array", "items": { "$ref": "#/components/schemas/Bounty" } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": { "type": "http", "scheme": "bearer", "bearerFormat": "mb_sk_<40 hex>", "description": "Create and revoke keys at https://makebounty.com/account#agent-access. Scopes: read, write." }
    },
    "responses": {
      "Unauthorized": { "description": "Missing, unknown or revoked key. Carries `WWW-Authenticate: Bearer realm=\"makebounty\"`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "BadRequest": { "description": "Invalid input", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Scope": { "description": "The key lacks the write scope", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "300 calls per key per hour exceeded", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" }, "docs": { "type": "string" }, "createKeyAt": { "type": "string" }, "wouldPost": { "type": "object" } } },
      "Me": {
        "type": "object", "required": ["email", "key", "limits"],
        "properties": {
          "email": { "type": "string" },
          "key": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "scopes": { "type": "array", "items": { "type": "string", "enum": ["read", "write"] } } } },
          "limits": { "type": "object", "properties": { "rateLimitPerHour": { "type": "integer" }, "maxActiveKeys": { "type": "integer" } } },
          "recentCalls": { "type": "array", "items": { "type": "object", "properties": { "action": { "type": "string" }, "surface": { "type": "string", "enum": ["mcp", "rest"] }, "ok": { "type": "boolean" }, "durationMs": { "type": "integer" }, "createdAt": { "type": "string" } } } }
        }
      },
      "Funding": { "type": "object", "properties": { "state": { "type": "string", "enum": ["unfunded", "funded", "paid", "cancelled"] }, "pledgedCents": { "type": "integer" }, "backerCount": { "type": "integer" }, "badge": { "type": "string" }, "detail": { "type": "string" } } },
      "Bounty": {
        "type": "object", "required": ["id", "title", "description", "amountCents", "status"],
        "properties": {
          "id": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string" }, "amountCents": { "type": "integer" },
          "status": { "type": "string", "enum": ["open", "claimed", "accepted", "paid", "cancelled"] },
          "category": { "type": ["string", "null"] }, "safetyCritical": { "type": "boolean" },
          "onCameraClaim": { "type": "boolean" }, "promisedVideoUrl": { "type": ["string", "null"] },
          "funding": { "$ref": "#/components/schemas/Funding" }, "createdAt": { "type": "string" },
          "viewerIsPoster": { "type": "boolean" }, "viewerIsSolver": { "type": "boolean" }, "url": { "type": "string" }
        }
      },
      "SearchResponse": { "type": "object", "required": ["bounties"], "properties": { "query": { "type": ["string", "null"] }, "bounties": { "type": "array", "items": { "$ref": "#/components/schemas/Bounty" } }, "nextCursor": { "type": ["string", "null"] } } },
      "PostBountyRequest": {
        "type": "object", "required": ["title", "description", "amountCents", "confirm"],
        "properties": {
          "title": { "type": "string", "maxLength": 200 }, "description": { "type": "string", "maxLength": 10000 },
          "amountCents": { "type": "integer", "minimum": 1 }, "category": { "type": "string", "maxLength": 100 },
          "safetyCritical": { "type": "boolean", "description": "Required when category is \"automotive\"" },
          "confirm": { "type": "boolean", "description": "Must be true. Without it the request fails with 400 and the would-be bounty in the error body." }
        }
      },
      "PostedBounty": { "type": "object", "required": ["id", "title", "amountCents", "status", "url"], "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "amountCents": { "type": "integer" }, "status": { "type": "string" }, "url": { "type": "string" }, "note": { "type": "string" } } }
    }
  }
}
