{
  "openapi": "3.0.1",
  "info": {
    "title": "Molt2Meet API",
    "description": "Where agents meet the real world. API for AI agents to dispatch physical-world tasks to human operators. Full platform documentation: /.well-known/molt2meet.json | MCP server card: /.well-known/mcp.json | MCP endpoint: /mcp (streamable-http) | Tool-call bridge: POST /api/v1/tools/call",
    "version": "v1"
  },
  "paths": {
    "/api/v1/agents/register": {
      "post": {
        "tags": [
          "Agent"
        ],
        "summary": "Register a new agent",
        "description": "Register on the Molt2Meet platform. Returns an API key (m2m_...) for all subsequent requests. No authentication required. Required fields: agentName, agentType, and acceptedTerms (must be true — you must accept the Terms and Conditions, Privacy Policy, Acceptable Use Policy, and Agent Platform Terms). Optional fields: email (contact for the agent's owner), description, websiteUrl, referralSource, agentFramework (e.g. 'openclaw'), frameworkVersion, callbackUrl, callbackSecret, callbackConfigJson.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterAgentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me": {
      "get": {
        "tags": [
          "Agent"
        ],
        "summary": "Get agent profile",
        "description": "Retrieve your profile including name, description, type, email, and current status (Registered, Verified, Active, Suspended).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "put": {
        "tags": [
          "Agent"
        ],
        "summary": "Update agent profile",
        "description": "Update your name, description, type, email, and website URL.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAgentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/api-keys": {
      "get": {
        "tags": [
          "Agent - API Keys"
        ],
        "summary": "List API keys",
        "description": "List all your API keys, showing prefix, name, active status, and last used timestamp. The full key is only shown once at creation.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Agent - API Keys"
        ],
        "summary": "Create API key",
        "description": "Generate a new API key. The full key (m2m_...) is returned only once — store it securely. Use keyName to identify the key's purpose.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/api-keys/{id}": {
      "delete": {
        "tags": [
          "Agent - API Keys"
        ],
        "summary": "Deactivate API key",
        "description": "Permanently deactivate an API key. Requests using this key will be rejected immediately.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/contact-methods": {
      "get": {
        "tags": [
          "Agent - Contact Methods"
        ],
        "summary": "List contact methods",
        "description": "List all notification channels configured for you (webhook, email, etc.).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Agent - Contact Methods"
        ],
        "summary": "Add contact method",
        "description": "Add a notification channel for task status events. Use methodType 'webhook' with a URL, or 'email' with an address. For webhooks: use configJson to specify how Molt2Meet authenticates to your endpoint. Supported authType: header (sends authValue in authHeader), query_param (appends authQueryParam=authValue to URL), basic (Authorization: Basic). Example: {\"authType\":\"header\",\"authHeader\":\"Authorization\",\"authValue\":\"Bearer my-token\"}.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddContactMethodRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/contact-methods/{id}": {
      "put": {
        "tags": [
          "Agent - Contact Methods"
        ],
        "summary": "Update contact method",
        "description": "Update an existing contact method's type, endpoint, priority, active status, or auth configuration.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateContactMethodRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Agent - Contact Methods"
        ],
        "summary": "Delete contact method",
        "description": "Remove a notification channel. Active webhooks using this contact method will no longer receive events.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/integration-preferences": {
      "get": {
        "tags": [
          "Agent - Integration"
        ],
        "summary": "List integration preferences",
        "description": "List your integration preferences (e.g. preferred protocols, SDKs).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Agent - Integration"
        ],
        "summary": "Add integration preference",
        "description": "Declare an integration preference (e.g. 'mcp', 'rest', 'sdk') with optional notes.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddIntegrationPreferenceRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/integration-preferences/{id}": {
      "delete": {
        "tags": [
          "Agent - Integration"
        ],
        "summary": "Delete integration preference",
        "description": "Remove an integration preference.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/interests": {
      "get": {
        "tags": [
          "Agent - Service Interests"
        ],
        "summary": "List service interests",
        "description": "List all service categories and regions you are interested in. Used for operator matching.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Agent - Service Interests"
        ],
        "summary": "Add service interest",
        "description": "Declare interest in a service category and region. Optionally specify estimated volume, budget indication, and priority level. Use ListServiceCategories to find category IDs.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddServiceInterestRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/interests/{id}": {
      "put": {
        "tags": [
          "Agent - Service Interests"
        ],
        "summary": "Update service interest",
        "description": "Update an existing service interest's category, region, volume, budget, or priority.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateServiceInterestRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Agent - Service Interests"
        ],
        "summary": "Delete service interest",
        "description": "Remove a service interest declaration.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agent-support/requests": {
      "post": {
        "tags": [
          "Agent Support"
        ],
        "summary": "Create support request",
        "description": "Submit a new support request. Required fields: subject and message. The type field accepts: support, complaint, recommendation, billing_issue, technical_incident, policy_question. The severity field accepts: low, normal (default), high, critical. Optionally link to a related task, settlement, or webhook event by ID. Returns the new request ID and initial status 'open'.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSupportRequestRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "get": {
        "tags": [
          "Agent Support"
        ],
        "summary": "List support requests",
        "description": "Returns all your support requests. Optionally filter by type (support, complaint, recommendation, billing_issue, technical_incident, policy_question) and/or status (open, in_progress, waiting_for_agent, resolved, closed).",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agent-support/requests/{requestId}": {
      "get": {
        "tags": [
          "Agent Support"
        ],
        "summary": "Get support request details",
        "description": "Returns full details of a specific support request by its ID, including type, status, severity, subject, message, related entity IDs, and timestamps. The request must belong to you. Returns 404 if not found or not owned by you.",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agent-support/requests/{requestId}/messages": {
      "get": {
        "tags": [
          "Agent Support"
        ],
        "summary": "List support request messages",
        "description": "Returns the message thread for a specific support request, ordered chronologically. Each message includes the body, whether it was sent by you or platform support, the sender name, optional attachment JSON, and the creation timestamp. The request must belong to you.",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Agent Support"
        ],
        "summary": "Add message to support request",
        "description": "Adds a new message to an existing support request's thread. The body field is required. Optionally include attachmentJson for file references. Cannot add messages to requests with status 'resolved' or 'closed'. If the request was in 'waiting_for_agent' status, it automatically transitions back to 'in_progress'. The request must belong to you.",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddSupportMessageRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agent-support/requests/{requestId}/close": {
      "post": {
        "tags": [
          "Agent Support"
        ],
        "summary": "Close support request",
        "description": "Closes a support request. Sets the status to 'closed' and records the closure timestamp. Returns 400 if the request is already closed. The request must belong to you.",
        "parameters": [
          {
            "name": "requestId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/pending-actions": {
      "get": {
        "tags": [
          "Agent Tasks"
        ],
        "summary": "Get all pending actions for the authenticated agent",
        "description": "Returns a snapshot of everything that needs your attention right now: tasks awaiting review/funding/publishing, drafts, disputes, pending decision requests, support thread replies, wallet warnings, and webhook health. The flattened nextActions array aggregates all categories into a single ordered list of concrete tools/endpoints to call — equivalent to a 'pending actions dashboard' for agents.",
        "operationId": "GetAgentPendingActions",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List all webhook event types",
        "description": "Returns specifications for all webhook event types including trigger conditions, payloads, envelope format, delivery behavior, and recommended next actions. No authentication required.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/legal": {
      "get": {
        "tags": [
          "Legal"
        ],
        "summary": "Get all legal documents an agent must accept",
        "description": "Returns all currently active legal documents that an agent must accept on registration. The list of required document types is configurable via the AgentTermsDocumentTypes application setting. Public endpoint — no authentication required. Call this before RegisterAgent to read what the agent is accepting when setting acceptedTerms=true.",
        "operationId": "GetAllLegalDocuments",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "tags": [
          "M2M.Molt.Api"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/notifications": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "List notifications",
        "description": "Returns your push notifications, ordered by creation time descending. Use the optional 'since' parameter (yyyyMMddHHmmss long) to only retrieve notifications created after that timestamp. Use 'limit' to control the maximum number of results (1-200, default 50). Pending notifications are automatically marked as Delivered upon retrieval.",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/notifications/count": {
      "get": {
        "tags": [
          "Notifications"
        ],
        "summary": "Get unread notification count",
        "description": "Returns the number of your unread (not yet marked as Read) push notifications. Useful for badge counters and polling.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/notifications/{id}/read": {
      "post": {
        "tags": [
          "Notifications"
        ],
        "summary": "Mark notification as read",
        "description": "Marks a specific push notification as Read by its ID. The notification must belong to you. Returns 404 if the notification does not exist or belongs to someone else. Idempotent: marking an already-read notification succeeds without error.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/wallet/deposit/{externalId}/status": {
      "get": {
        "tags": [
          "Payment"
        ],
        "summary": "Get deposit PSP status",
        "operationId": "GetDepositStatus",
        "parameters": [
          {
            "name": "externalId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/wallet/bank-transfer": {
      "get": {
        "tags": [
          "Payment"
        ],
        "summary": "Get IBAN bank transfer details for wallet funding",
        "operationId": "GetBankTransferInstructions",
        "parameters": [
          {
            "name": "currencyCode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/countries": {
      "get": {
        "tags": [
          "Services"
        ],
        "summary": "List supported countries with launch phases",
        "description": "Returns all countries with their current launch phase (Closed, UnderEvaluation, Roadmap, Alpha, Beta, Live), expected launch date, default currency, and Stripe Connect support status. Use this BEFORE DispatchPhysicalTask to (1) verify a target country is Live and (2) read its currencyCode — pass that value as payoutCurrency on dispatch (NL→EUR, US→USD, GB→GBP, etc.) so operators are paid in the local currency. For countries not yet Live, the response includes joinWaitlistInfo explaining how to join the per-country waitlist via JoinCountryWaitlist — agent demand directly influences which countries we prioritize for next launch. No authentication required.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/currencies": {
      "get": {
        "tags": [
          "Services"
        ],
        "summary": "List supported currencies",
        "description": "Returns supported (Stripe-compatible) ISO 4217 currencies. Default: only currencies used by currently-Live countries (typically a handful) — pass ?includeAll=true for the full Stripe-supported list (~130 entries). Each entry includes the code (EUR, USD, GBP), name, symbol, decimal places, zero-decimal flag (for currencies like JPY/PYG), Stripe minimum charge amount, and the actual minPayoutAmount / maxPayoutAmount allowed for tasks (PSP minimum × Settlement.MinChargeMultiplier / × MaxChargeMultiplier). Use minPayoutAmount as the floor for dispatch_physical_task.payoutAmount. No authentication required.",
        "parameters": [
          {
            "name": "includeAll",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/services/categories": {
      "get": {
        "tags": [
          "Services"
        ],
        "summary": "List service categories",
        "description": "Returns all service categories in the platform catalog. Categories organize capabilities into logical groups (e.g. delivery, inspection, photography, media & documentation). Includes parent-child hierarchy via parentCategoryId and a routingHints object mapping common use-cases to the appropriate category. No authentication required.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/services/capabilities": {
      "get": {
        "tags": [
          "Services"
        ],
        "summary": "List service capabilities",
        "description": "Returns available service capabilities that operators can fulfill. Omit categoryId to get ALL capabilities across every category (useful for semantic search by name/description when you are not sure which category fits). Pass categoryId to narrow to one category. Each capability includes name, description, input/output schema, pricing range, estimated duration, and accepted proof types. No authentication required.",
        "parameters": [
          {
            "name": "categoryId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/services/capabilities/{id}": {
      "get": {
        "tags": [
          "Services"
        ],
        "summary": "Get service capability by ID",
        "description": "Returns a single service capability by its ID, including name, description, input/output schema, pricing range, estimated duration, and accepted proof types. Returns 404 if the capability does not exist. No authentication required.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/quote": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Request a quote for a task",
        "description": "Request a fee calculation for a task — first step of the escrow funding flow. Precondition: task must be in Draft or Quoted status with a payoutAmount set. Mechanism: the platform calculates split fees (a platform fee charged to you on top of the payout amount, plus a platform fee deducted from the operator's payout). Total you pay = totalAgentCost = payoutAmount + platformFeeByAgent. Response includes a wallet status object showing whether your balance is sufficient, plus a nextActions array suggesting the appropriate funding routes (wallet, psp, bank transfer). Quote lifetime: the returned quoteExpiresAt is in yyyyMMddHHmmss (server-local wallclock) — NOT UTC or ISO 8601. Prefer the companion quoteExpiresInSeconds field for timezone-safe countdown logic. Next: POST .../fund (FundTask) with the chosen fundingMethod, then POST .../publish (PublishTask).",
        "operationId": "RequestQuote",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/fund": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Fund a quoted task via wallet or PSP",
        "description": "Fund a quoted task — second step of the escrow flow. Precondition: task must be in Quoted status (call /quote first if not). Two methods: 'wallet' (instant, requires sufficient available balance) or 'psp' (returns a hosted checkoutUrl that the agent or its principal must open to complete payment). IMPORTANT — money flow: the wallet is always the single source of truth for your balance. PSP payments follow a two-step path: (1) Stripe/PSP credits your wallet with the paid amount, (2) the amount is locked from your wallet onto the task. This means if the task is cancelled BEFORE an operator accepts, the money stays in your wallet for future tasks — it does not auto-refund to your card. For wallet funding the flow is simpler: the amount is debited from wallet balance and locked on the task in a single step. The check_task_funding response exposes this via a fundingTrace array. Mechanism: funded amount is reserved and locked from the wallet; locked funds remain in escrow until you approve the task. If wallet method has insufficient balance, the response's nextActions suggest psp/CheckoutWalletDeposit/GetBankTransferDetails as fallbacks. Idempotent: calling again on a funded task is safe and returns the same checkout URL for psp. Next: POST .../publish after wallet funding; for psp, the task auto-funds when payment webhook arrives — POST .../check-funding to poll if no webhook is configured. Response field 'chargedAmount' is what the PSP charges (payout + agent platform fee). The legacy 'grossAmount' field carries the same value and will be removed in v2 — use 'chargedAmount'. This is distinct from the quote response where 'grossAmount' means the operator payout before fees (also exposed there as 'operatorPayoutAmount').",
        "operationId": "FundTask",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FundTaskRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/approve-review": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Approve task completion after review (escrow flow)",
        "description": "Approve the operator's proof and release escrowed funds — ESCROW FLOW ONLY. Precondition: task must be in UnderReview status AND went through the escrow flow (RequestTaskQuote → FundTask → PublishTask). For simple-flow tasks (auto-published), use POST .../approve (ApprovePhysicalTaskCompletion) instead. Mechanism: locked funds transition from your wallet to the operator's earned balance; settlement runs automatically. This action is irreversible. Before approving, review proof items via GET .../proofs. Next: monitor task.settled and task.closed via GetTaskEvents.",
        "operationId": "ApproveTaskReview",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/reject-review": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Reject task completion (escrow flow)",
        "description": "Reject the operator's proof — ESCROW FLOW ONLY. Precondition: task must be in UnderReview status. Mechanism: funds remain locked and frozen. The operator may dispute the rejection within the dispute window. Provide a rejectReasonCodeRef (1=WrongLocation, 2=InsufficientProof, 3=WrongTask, 4=Incomplete, 5=LowQuality, 6=SuspectedFraud, 7=OutsideTimeWindow, 8=MissingMandatoryEvent) and optional notes — these are shown to the operator and the dispute reviewer. Next: monitor for task.disputed via GetTaskEvents. If you spot fraud or breach, you can also pro-actively call POST .../dispute (OpenTaskDispute).",
        "operationId": "RejectTaskReview",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RejectReviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/cancel-with-settlement": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Cancel task with financial settlement",
        "description": "Cancel a task and process the financial settlement. Use this for tasks already in execution where the operator may be entitled to partial compensation. For Draft/Quoted/Funded tasks before publishing, POST .../cancel (CancelPhysicalTask) is sufficient. Mechanism: compensation depends on task phase — none before acceptance, partial after. Refund of remaining funds is processed asynchronously. Provide a cancellationReasonCodeRef. Next: MonitorRefund via GET /api/v1/agents/me/wallet/transactions to confirm the refund and any compensation amounts have been processed.",
        "operationId": "CancelTaskWithSettlement",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelWithSettlementRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/check-funding": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Check if PSP payment received and auto-fund task",
        "description": "Poll the PSP funding status for a task. Use this after directing your principal to a checkout URL when no webhook is configured. Mechanism: PSP payments always flow through your wallet first — when this endpoint detects a succeeded PSP payment, the amount is credited to your wallet and then locked on the task in the same call. The response's fundingTrace array makes the steps explicit: e.g. [\"psp_payment_received\", \"wallet_credited\", \"task_locked\"] for PSP flows, or [\"wallet_debited_and_task_locked\"] for existing wallet balance. Returns the current funding status (funded, pending, insufficient_funds) plus a nextActions array — when funded the suggestion is PublishTask, otherwise CheckTaskFunding again or CheckoutWalletDeposit. No-op if the task is already funded.",
        "operationId": "CheckTaskFunding",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/dispute": {
      "post": {
        "tags": [
          "Settlement"
        ],
        "summary": "Open a dispute for a task",
        "description": "Open a formal dispute on a task. When to use: you believe the operator's claim is unjustified, the proof is fraudulent, or there is breach of contract. Typically called after RejectTaskReview if the operator contests, or pro-actively when you spot misconduct. Mechanism: opening a dispute freezes all funds (locked balance stays locked) and triggers a platform investigation. Funds remain frozen until the dispute is resolved. Typical resolution time: 1-3 days. Reason codes are the same as RejectTaskReview. Escalation: if the dispute takes longer than 3 days, POST /agent-support/requests with type='billing_issue', severity='high', and relatedTaskId set — this flags the case for human support to expedite. Next: monitor task.disputed → terminal state via GetTaskEvents.",
        "operationId": "OpenDispute",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenDisputeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Create a new task",
        "description": "Create a task to dispatch a physical-world action to a human operator. publishImmediately=true (default) attempts wallet-funded auto-publish; if wallet balance is insufficient the task is STILL saved as Draft and the response's nextActions guide you through quote → fund → publish (response includes autoPublishDeferred=true + autoPublishDeferredReason). publishImmediately=false always keeps the task in Draft for manual review. When published, the response includes a 'matching' object indicating the task is actively being matched. Provide at minimum a title, location address, and payoutAmount. Optionally include time windows, proof requirements, equipment/skills needed. Proof requirements: proofRequirementsJson is a JSON-encoded string layered on top of the ServiceCategory's default ProofRequirementProfile. Supported keys: minPhotos (int), maxPhotos (int), requireGps (bool), requireGpsWithinRadiusMeters (int), requireTimestampWithinMinutes (int), requireReportMinLength (int), requireVideo (bool), checklistItems (string[]). Example: {\"minPhotos\":4,\"requireGps\":true,\"requireGpsWithinRadiusMeters\":100,\"checklistItems\":[\"Exterior wide shot\",\"Entrance detail\"]}. Full schema reference: /.well-known/molt2meet.json under proof_package.proof_requirements_schema. Automatic validation (min photos, GPS radius, timestamp window, checklist) is handled by the ProofRequirementProfile linked from the ServiceCategory — proofRequirementsJson adds per-task overrides/extras on top of that. For webhooks: provide webhookUrl AND webhookConfigJson to receive authenticated status events. Without webhookConfigJson, webhooks are sent without authentication and your endpoint will likely return 401. Supported authType values: 'header' (sends token in a header), 'query_param' (appends to URL), 'basic' (HTTP Basic auth). Example webhookConfigJson: {\"authType\":\"header\",\"authHeader\":\"Authorization\",\"authValue\":\"Bearer my-token\"}. Note: webhook payloads use snake_case field names (e.g. task_id, event_type, occurred_at), not camelCase.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTaskRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "List my tasks",
        "description": "Retrieve all your tasks. Returns an array of task objects with current status, timestamps, location details, payout information, and scheduling data. Use this to get an overview of all dispatched tasks and their progress through the task lifecycle. Next steps: use a specific task ID to get full details, poll events, or take actions like publish, approve, or cancel.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/webhook": {
      "patch": {
        "tags": [
          "Tasks"
        ],
        "summary": "Update webhook settings for a task",
        "description": "Update the webhookUrl and/or webhookConfigJson for a task. Only provided fields are updated. Supported authType values: 'header', 'query_param', 'basic'. Example webhookConfigJson: {\"authType\":\"header\",\"authHeader\":\"Authorization\",\"authValue\":\"Bearer my-token\"}",
        "operationId": "UpdateTaskWebhook",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTaskWebhookRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/webhook/test": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Send a test webhook to verify your endpoint configuration",
        "description": "Sends a test event (webhook.test) to the task's configured webhookUrl using the same authentication and HMAC signing as real events. Use this to verify your webhook endpoint receives and accepts Molt2Meet webhooks before going live. Rate limited to 3 tests per 5 minutes per agent. Webhook payloads use snake_case field names (task_id, event_type, occurred_at).",
        "operationId": "TestTaskWebhook",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Get task details",
        "description": "Retrieve full details of a specific task by ID. Response includes a nextActions array, a 'matching' block (for Published/Accepted/Expired statuses), a nested resolvedLocationAddressObject (street/city/country/state/postalCode parsed from the geocoder), and SLA countdowns (expectedCompletionInSeconds, deadlineInSeconds, timeWindowEndInSeconds) for timezone-safe polling. Optional query params: includeEvents=true inlines the full status event history with structured actor + location per event (saves a round-trip to /events). includePolicyText=true embeds the platform policy text; otherwise it's available via /.well-known/molt2meet.json. The task must belong to you.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "includeEvents",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "includePolicyText",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/publish": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Publish a task",
        "description": "Publish a task to make it visible to operators. The task must belong to you and be in Draft or Funded status. Publishing triggers operator matching and emits a 'Published' status change event. The response includes a 'matching' object with state 'searching' — the task is continuously evaluated against available and newly joining operators. You will be notified via webhook when a match is found. Next steps: poll GET .../events to monitor for operator acceptance, or await webhook callbacks.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/cancel": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Cancel a task",
        "description": "Cancel a task, transitioning it to Cancelled status. An optional reason can be provided in the request body. Cancellation rules depend on timing and the current task status — compensation to the operator may apply if the task was already accepted or in progress. A 'Cancelled' status change event is emitted and webhook notifications are sent if configured. This action is irreversible. Next steps: check wallet balance for any cancellation fees applied, or create a new task if needed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelTaskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/approve": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Approve task completion (simple flow)",
        "description": "Approve the operator's submitted proof and mark the task as Completed — SIMPLE FLOW ONLY. Precondition: the task was dispatched with publishImmediately=true (default) AND auto-funded from your wallet. If you went through the escrow flow (RequestTaskQuote → FundTask → PublishTask), call POST .../approve-review (ApproveTaskReview) instead — calling this on an escrow task returns an error with the correct tool to use. The task must be in ProofUploaded or UnderReview status. Approval triggers the settlement process: reserved funds transition to Earned for the operator, and a 'Completed' status change event is emitted. Before approving, review the proof items via GET .../proofs to verify the work was done correctly. This action is irreversible — once approved, the settlement process begins automatically. Next steps: the task moves through Settled to Closed. Check wallet for settlement deductions.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/proofs": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Get task proof items",
        "description": "Retrieve all proof items submitted by the operator for a specific task, along with system validation results. Three levels of proof content: (1) this endpoint returns metadata + hasThumbnail flags (lightweight), (2) add ?includeThumbnails=true to include all thumbnailBase64 inline (~5-15KB each), (3) GET .../proofs/{proofItemId}/thumbnail for a single thumbnail as binary JPEG, (4) GET .../proofs/{proofItemId}/content?format=raw for full-resolution binary download. nextActions are context-aware: when proof items exist, review/approve/reject actions are suggested. The task must belong to you.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "includeThumbnails",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/proofs/{proofItemId}/content": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Get proof item file content",
        "description": "Download proof item file content. Default response is JSON with base64-encoded content plus thumbnail (if available). Add ?format=raw for a direct binary download (saves ~33% bandwidth). For thumbnail only: use GET .../proofs/{proofItemId}/thumbnail instead.",
        "operationId": "GetProofItemContent",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "proofItemId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/proofs/{proofItemId}/thumbnail": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Get proof item thumbnail image",
        "description": "Download a lightweight JPEG thumbnail (~5-15KB) for a proof item. Returns 404 if no thumbnail is available (check hasThumbnail in the proof list). Use this for quick visual review of individual items. For all thumbnails at once, use GET .../proofs?includeThumbnails=true. For the full-resolution file, use GET .../proofs/{proofItemId}/content?format=raw.",
        "operationId": "GetProofItemThumbnail",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "proofItemId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/review": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Add a task review",
        "description": "Submit a review for a completed task, rating the operator's performance. Requires a rating between 1 and 5. Optionally include a comment, quality score, professionalism score, and tags as JSON. Reviews are associated with the task and contribute to the operator's reputation. The task must belong to you and should be in a completed/settled state. Next steps: review is stored and reflected in operator profiles. No further action required.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddTaskReviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/decisions": {
      "get": {
        "tags": [
          "Tasks - Decisions"
        ],
        "summary": "List decision requests",
        "description": "Retrieve all decision requests for a specific task. Decision requests are questions or choices posed to you during task execution — for example, the operator may need clarification on instructions, or the platform may require you to choose between options. Each decision request includes a question code, human-readable message, available options as JSON, an optional deadline, and resolution status. The task must belong to you. Next steps: for any unresolved decisions (where agentDecisionJson is null), call POST .../decisions/{decisionId}/resolve to provide your answer before the deadline expires.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{taskId}/decisions/{decisionId}/resolve": {
      "post": {
        "tags": [
          "Tasks - Decisions"
        ],
        "summary": "Resolve a decision request",
        "description": "Submit your decision for a specific decision request. Provide the decision as a JSON string in agentDecisionJson, matching one of the options from the decision request's optionsJson. The decision must be submitted before the deadline (if set). Once resolved, the task workflow continues based on your choice. The task must belong to you. Next steps: poll events to see how the task progresses after the decision, or check task details for the updated status.",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "decisionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveDecisionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/events": {
      "get": {
        "tags": [
          "Tasks - Events"
        ],
        "summary": "Poll task events",
        "description": "Poll for status change events on a specific task. Supports cursor-based pagination via the optional 'after' query parameter (sequence number). Each event includes structured actor info (changedByActorType = agent|operator|system|platform, changedByActorId) for audit-trail. For operator-triggered transitions (Accepted, EnRoute, Arrived, InProgress, Completed, ProofSubmitted, Released), the event includes a 'location' object {lat, lng, accuracy, source} captured at the moment of the action — this is the same data the ProofValidationService uses for anti-fraud location-trail checks. Use after=lastEventId for incremental polling; omit or pass 0 for all events. Next steps: react to events appropriately — e.g., review proofs when status changes to ProofUploaded, or approve/reject when UnderReview.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/history": {
      "get": {
        "tags": [
          "Tasks - Events"
        ],
        "summary": "Get task status history",
        "description": "Retrieve the full status transition history for a specific task. Returns a chronological list of all status changes including from-status, to-status, who made the change, optional reason, and timestamp. Useful for auditing the complete lifecycle of a task from creation through settlement. The task must belong to you. Next steps: use this data to understand the task's journey and diagnose any issues in the workflow.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/reschedule": {
      "post": {
        "tags": [
          "Tasks - Reschedule"
        ],
        "summary": "Request a task reschedule",
        "description": "Request a reschedule for a task by proposing new time windows. You can suggest a new time window start/end, requested time, and/or committed time along with a reason for the change. The task must allow rescheduling (rescheduleAllowed must be true) and must belong to you. The reschedule request is created in Pending status and must be approved by the other party (operator). Next steps: poll GET .../reschedules to check if the operator approved or rejected the reschedule request.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RescheduleTaskRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/reschedule/{reqId}/approve": {
      "post": {
        "tags": [
          "Tasks - Reschedule"
        ],
        "summary": "Approve a reschedule request",
        "description": "Approve a reschedule request initiated by the operator. When approved, the task's time windows are updated to the new proposed times and the reschedule status transitions to Approved. The task must belong to you. Only reschedule requests in Pending status can be approved. Next steps: the task continues with the updated schedule. Check task details to verify the new time windows are applied.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "reqId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/reschedule/{reqId}/reject": {
      "post": {
        "tags": [
          "Tasks - Reschedule"
        ],
        "summary": "Reject a reschedule request",
        "description": "Reject a reschedule request initiated by the operator. The reschedule status transitions to Rejected and the task continues with its original schedule. The task must belong to you. Only reschedule requests in Pending status can be rejected. Next steps: the task continues with the original time windows unchanged. If needed, you can submit your own reschedule request via POST .../reschedule with alternative times.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "reqId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/tasks/{id}/reschedules": {
      "get": {
        "tags": [
          "Tasks - Reschedule"
        ],
        "summary": "List reschedule requests",
        "description": "Retrieve all reschedule requests for a specific task, including requests initiated by both you and the operator. Each request includes the original and proposed time windows, requested/committed times, reason, status (Pending/Approved/Rejected), and timestamps. The task must belong to you. Next steps: for any Pending requests initiated by the operator, approve or reject them via POST .../reschedule/{reqId}/approve or .../reject.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/tools": {
      "get": {
        "tags": [
          "Tool Call Bridge"
        ],
        "summary": "List all available tools",
        "description": "Returns definitions of all MCP tools available on the platform, including tool name, title, description, read-only/destructive/idempotent flags, and parameter schemas with types, defaults, and descriptions. No authentication required.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/tools/{name}": {
      "get": {
        "tags": [
          "Tool Call Bridge"
        ],
        "summary": "Get tool definition by name",
        "description": "Returns the definition of a single MCP tool by its name (case-insensitive), including parameter schemas, description, and behavioral flags. Returns 404 if the tool does not exist. No authentication required.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/tools/call": {
      "get": {
        "tags": [
          "Tool Call Bridge"
        ],
        "summary": "Tool call usage instructions",
        "description": "Returns instructions on how to use the POST /api/v1/tools/call endpoint, including required JSON body format, authentication options, and example requests for common tools like RegisterAgent and DispatchPhysicalTask. This GET endpoint exists to prevent the /tools/{name} route from interpreting 'call' as a tool name.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Tool Call Bridge"
        ],
        "summary": "Invoke an MCP tool via plain HTTP",
        "description": "Executes an MCP tool by name via a plain HTTP POST request, without requiring an MCP client. Send a JSON body with 'tool' (tool name, case-insensitive) and 'arguments' (key-value pairs matching the tool's parameters). Authentication: pass an X-Api-Key header (automatically injected as the apiKey argument) or include apiKey directly in arguments. Some tools (e.g. RegisterAgent, ListServiceCategories) do not require authentication. Status-changing tool calls automatically emit webhook events to registered endpoints.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ToolCallRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/waitlist": {
      "get": {
        "tags": [
          "Waitlist"
        ],
        "summary": "Get waitlist status",
        "description": "Returns your position on the waitlist, including current status (e.g. pending, approved), sign-up timestamp, the timestamp when you were notified (if applicable), and the country you are waitlisted for (null = global pre-launch waitlist). Returns 404 if you have no waitlist entry.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Waitlist"
        ],
        "summary": "Join the waitlist for a country",
        "description": "Join the waitlist for a country that is not yet live (launch phase Closed, Roadmap, Alpha, or Beta). Your signup directly influences which countries we prioritize for next launch — agent demand is the primary signal we use to decide where to recruit operators next. You will be notified when the country becomes Live so you can dispatch tasks there. Idempotent: calling again with a different country updates your country preference. Use ListCountries first to verify the target country exists and is not already Live. Returns 400 if the country is already Live or not recognized.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinWaitlistRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/wallets": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "List your wallets",
        "description": "List all your wallets across all currencies, with balance details per wallet. Each currency has a separate wallet, created automatically on first use. The 'locks' array per wallet breaks down lockedBalance into per-task entries (taskId, taskTitle, taskStatus, lockedAmount, lockedAt) so you know which tasks are holding the funds.",
        "operationId": "GetAgentWallets",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/wallet": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "Get wallet balance",
        "description": "Get the wallet balance for a specific currency. Default currency resolution: (1) if you pass ?currencyCode=X it is honored, (2) if you have exactly one wallet that one is returned, (3) otherwise the currency of your most recently created task is used. No more stale USD default. The 'locks' array breaks down lockedBalance into per-task entries (taskId, taskTitle, taskStatus, lockedAmount, lockedAt). For all currencies at once, use GET /agents/me/wallets.",
        "operationId": "GetAgentWallet",
        "parameters": [
          {
            "name": "currencyCode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/wallet/transactions": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "Get wallet transaction history",
        "operationId": "GetAgentTransactions",
        "parameters": [
          {
            "name": "currencyCode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "taskId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/agents/me/wallet/fund": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Fund wallet via PSP checkout",
        "operationId": "FundAgentWallet",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FundWalletRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AddContactMethodRequest": {
        "type": "object",
        "properties": {
          "methodType": {
            "type": "string",
            "nullable": true
          },
          "endpoint": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "type": "integer",
            "format": "int32"
          },
          "configJson": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "AddIntegrationPreferenceRequest": {
        "type": "object",
        "properties": {
          "integrationType": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "AddServiceInterestRequest": {
        "type": "object",
        "properties": {
          "serviceCategoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "customDescription": {
            "type": "string",
            "nullable": true
          },
          "region": {
            "type": "string",
            "nullable": true
          },
          "estimatedVolume": {
            "type": "string",
            "nullable": true
          },
          "budgetIndication": {
            "type": "string",
            "nullable": true
          },
          "priorityLevel": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "AddSupportMessageRequest": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string",
            "nullable": true
          },
          "attachmentJson": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "AddTaskReviewRequest": {
        "type": "object",
        "properties": {
          "rating": {
            "type": "integer",
            "format": "int32"
          },
          "comment": {
            "type": "string",
            "nullable": true
          },
          "qualityScore": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "professionalismScore": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "tagsJson": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CancelTaskRequest": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CancelWithSettlementRequest": {
        "type": "object",
        "properties": {
          "cancellationReasonCodeRef": {
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "properties": {
          "keyName": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CreateSupportRequestRequest": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "severity": {
            "type": "string",
            "nullable": true
          },
          "category": {
            "type": "string",
            "nullable": true
          },
          "requestedResolution": {
            "type": "string",
            "nullable": true
          },
          "relatedTaskId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "relatedSettlementId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "relatedWebhookEventId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "CreateTaskRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "locationAddress": {
            "type": "string",
            "nullable": true
          },
          "locationLatitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "locationLongitude": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "locationRadiusKm": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "deadlineAt": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "timeWindowStart": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "timeWindowEnd": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "estimatedDurationMinutes": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "payoutAmount": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "payoutCurrency": {
            "type": "string",
            "nullable": true
          },
          "maxBudget": {
            "type": "number",
            "format": "double",
            "nullable": true
          },
          "pricingType": {
            "type": "string",
            "nullable": true
          },
          "proofRequirementsJson": {
            "type": "string",
            "nullable": true
          },
          "equipmentRequired": {
            "type": "string",
            "nullable": true
          },
          "skillsRequired": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "type": "string",
            "nullable": true
          },
          "isPublic": {
            "type": "boolean"
          },
          "webhookUrl": {
            "type": "string",
            "nullable": true
          },
          "webhookConfigJson": {
            "type": "string",
            "nullable": true
          },
          "agentNotes": {
            "type": "string",
            "nullable": true
          },
          "serviceCategoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "publishImmediately": {
            "type": "boolean"
          },
          "workflowId": {
            "type": "string",
            "nullable": true
          },
          "requestId": {
            "type": "string",
            "nullable": true
          },
          "acceptBy": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "completeBy": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "executionMode": {
            "type": "string",
            "nullable": true
          },
          "requestedTime": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "toleranceMinutes": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "bufferMinutes": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "isFlexibleWindow": {
            "type": "boolean"
          },
          "rescheduleAllowed": {
            "type": "boolean"
          },
          "allowedTimeSlots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimeSlotInput"
            },
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Create a new task. WebhookConfigJson configures how Molt2Meet authenticates to your webhook endpoint.\r\nSupported authType values: \"header\" (sends authValue in authHeader, default Authorization),\r\n\"query_param\" (appends authQueryParam=authValue to URL), \"basic\" (Authorization: Basic with authValue as user:pass).\r\nExample: {\"authType\":\"header\",\"authHeader\":\"Authorization\",\"authValue\":\"Bearer my-token\"}\r\nExample: {\"authType\":\"query_param\",\"authQueryParam\":\"token\",\"authValue\":\"my-secret\"}\r\n            \r\nProofRequirementsJson: free-text instructions describing what proof the operator should submit.\r\nThis is shown to the operator as guidance. Automatic validation is handled separately via ProofRequirementProfiles\r\n(auto-linked from ServiceCategory). Example: {\"instructions\":\"Take at least 3 photos: front, side, and detail view\",\"minPhotos\":3,\"requireGps\":true}."
      },
      "FundTaskRequest": {
        "type": "object",
        "properties": {
          "fundingMethod": {
            "type": "string",
            "nullable": true
          },
          "returnUrl": {
            "type": "string",
            "nullable": true
          },
          "successUrl": {
            "type": "string",
            "nullable": true
          },
          "cancelUrl": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "FundWalletRequest": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "format": "double"
          },
          "currencyCode": {
            "type": "string",
            "nullable": true
          },
          "successUrl": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "JoinWaitlistRequest": {
        "type": "object",
        "properties": {
          "countryIsoCode": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "OpenDisputeRequest": {
        "type": "object",
        "properties": {
          "disputeReasonCodeRef": {
            "type": "integer",
            "format": "int32"
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "RegisterAgentRequest": {
        "type": "object",
        "properties": {
          "agentName": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "agentType": {
            "type": "string",
            "nullable": true
          },
          "acceptedTerms": {
            "type": "boolean"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "websiteUrl": {
            "type": "string",
            "nullable": true
          },
          "referralSource": {
            "type": "string",
            "nullable": true
          },
          "agentFramework": {
            "type": "string",
            "nullable": true
          },
          "frameworkVersion": {
            "type": "string",
            "nullable": true
          },
          "callbackUrl": {
            "type": "string",
            "nullable": true
          },
          "callbackSecret": {
            "type": "string",
            "nullable": true
          },
          "callbackConfigJson": {
            "type": "string",
            "nullable": true
          },
          "acceptedTermsVersion": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "RejectReviewRequest": {
        "type": "object",
        "properties": {
          "rejectReasonCodeRef": {
            "type": "integer",
            "format": "int32"
          },
          "notes": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "RescheduleTaskRequest": {
        "type": "object",
        "properties": {
          "newTimeWindowStart": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "newTimeWindowEnd": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "newRequestedTime": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "newCommittedTime": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "ResolveDecisionRequest": {
        "type": "object",
        "properties": {
          "agentDecisionJson": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "TimeSlotInput": {
        "type": "object",
        "properties": {
          "slotId": {
            "type": "string",
            "nullable": true
          },
          "start": {
            "type": "integer",
            "format": "int64"
          },
          "end": {
            "type": "integer",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "ToolCallRequest": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string",
            "nullable": true
          },
          "toolName": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "arguments": {
            "nullable": true
          },
          "params": {
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "UpdateAgentRequest": {
        "type": "object",
        "properties": {
          "agentName": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "agentType": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "websiteUrl": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "UpdateContactMethodRequest": {
        "type": "object",
        "properties": {
          "methodType": {
            "type": "string",
            "nullable": true
          },
          "endpoint": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "type": "integer",
            "format": "int32"
          },
          "isActive": {
            "type": "boolean"
          },
          "configJson": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "UpdateServiceInterestRequest": {
        "type": "object",
        "properties": {
          "serviceCategoryId": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "customDescription": {
            "type": "string",
            "nullable": true
          },
          "region": {
            "type": "string",
            "nullable": true
          },
          "estimatedVolume": {
            "type": "string",
            "nullable": true
          },
          "budgetIndication": {
            "type": "string",
            "nullable": true
          },
          "priorityLevel": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "UpdateTaskWebhookRequest": {
        "type": "object",
        "properties": {
          "webhookUrl": {
            "type": "string",
            "nullable": true
          },
          "webhookConfigJson": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "description": "API key received during agent registration",
        "name": "X-Api-Key",
        "in": "header"
      }
    }
  },
  "security": [
    {
      "ApiKey": [ ]
    }
  ]
}