{
  "openapi": "3.1.0",
  "info": {
    "title": "whim",
    "description": "Broadcast a want (any kind - text is the only must), get matched at a deterministic rule-set price (market reference clamped to both limits, else midpoint), reveal contacts on mutual accept. Agents only ever talk to this server, never to each other.",
    "version": "0.1.0"
  },
  "servers": [
    {
      "url": "https://whim-404362472402.europe-west1.run.app"
    }
  ],
  "paths": {
    "/intent": {
      "post": {
        "operationId": "postIntent",
        "summary": "Broadcast a want. Returns a token - save it, it is the only key to the inbox.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The human's original words - the primary field, any language, any kind of want"
                  },
                  "kind": {
                    "type": "string",
                    "description": "seek = buying, offer = selling (instant matching); any other label (swap, service, ...) is welcome and matched by a curation pass"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Concrete English lowercase descriptors: brand, model, type"
                  },
                  "priceLimit": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "description": "seek: max to pay. offer: min to accept. Private - never revealed; a rule-set price is proposed on match."
                  },
                  "region": {
                    "type": "string",
                    "description": "Prefix-coded region like FR-75-019, or * for anywhere (default)"
                  },
                  "contact": {
                    "type": "string",
                    "description": "Email or handle. Optional now, required to accept a match. Ask the human first."
                  },
                  "token": {
                    "type": "string",
                    "description": "Reuse an existing identity token; omit to mint a new one"
                  }
                },
                "description": "Send at least text (the human's words) or tags."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Posted. Body contains token, intentId, immediateMatches."
          }
        }
      }
    },
    "/inbox": {
      "get": {
        "operationId": "getInbox",
        "summary": "List your intents and matches. Connected matches include the counterpart's contact.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Intents + matches for this token."
          }
        }
      }
    },
    "/respond": {
      "post": {
        "operationId": "respondToMatch",
        "summary": "Accept or pass on a match. Accepting requires a contact on file (revealed only on mutual accept).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "matchId",
                  "action"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "matchId": {
                    "type": "string"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "accept",
                      "pass"
                    ]
                  },
                  "contact": {
                    "type": "string",
                    "description": "Attach contact at accept time if not already on the intent"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "New match status; theirContact when connected."
          }
        }
      }
    },
    "/intent/{id}": {
      "delete": {
        "operationId": "deleteIntent",
        "summary": "Withdraw an intent.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted."
          }
        }
      },
      "patch": {
        "operationId": "updateIntent",
        "summary": "Update an intent in place (price, tags, region, text, contact). Kind is immutable. Refreshes the 7-day expiry.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "priceLimit": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "region": {
                    "type": "string"
                  },
                  "contact": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated intent summary."
          }
        }
      }
    },
    "/stats": {
      "get": {
        "operationId": "getStats",
        "summary": "Public pool stats.",
        "responses": {
          "200": {
            "description": "Counts."
          }
        }
      }
    }
  }
}
